add functions/vendor files

This commit is contained in:
Reed Allman
2017-06-11 02:05:36 -07:00
parent 6ee9c1fa0a
commit f2c7aa5ee6
7294 changed files with 1629834 additions and 0 deletions

2
vendor/github.com/fsouza/go-dockerclient/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
# temporary symlink for testing
testing/data/symlink

32
vendor/github.com/fsouza/go-dockerclient/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,32 @@
language: go
sudo: required
go:
- 1.7.x
- 1.8.x
- tip
os:
- linux
- osx
env:
matrix:
- GOARCH=amd64 DOCKER_VERSION=1.12.6
- GOARCH=386 DOCKER_VERSION=1.12.6
- GOARCH=amd64 DOCKER_VERSION=1.13.1
- GOARCH=386 DOCKER_VERSION=1.13.1
global:
- GO_TEST_FLAGS=-race
- DOCKER_HOST=tcp://127.0.0.1:2375
install:
- make testdeps
- travis_retry travis-scripts/install-docker.bash
script:
- travis-scripts/run-tests.bash
services:
- docker
matrix:
fast_finish: true
exclude:
- os: osx
env: GOARCH=amd64 DOCKER_VERSION=1.12.6
- os: osx
env: GOARCH=386 DOCKER_VERSION=1.12.6

171
vendor/github.com/fsouza/go-dockerclient/AUTHORS generated vendored Normal file
View File

@@ -0,0 +1,171 @@
# This is the official list of go-dockerclient authors for copyright purposes.
Abhishek Chanda
Adam Bell-Hanssen
Adrien Kohlbecker
Aldrin Leal
Alex Dadgar
Alfonso Acosta
André Carvalho
Andreas Jaekle
Andrew Snodgrass
Andrews Medina
Andrey Sibiryov
Andy Goldstein
Anirudh Aithal
Antonio Murdaca
Artem Sidorenko
Arthur Rodrigues
Ben Marini
Ben McCann
Ben Parees
Benno van den Berg
Bradley Cicenas
Brendan Fosberry
Brian Lalor
Brian P. Hamachek
Brian Palmer
Bryan Boreham
Burke Libbey
Carlos Diaz-Padron
Carson A
Cássio Botaro
Cesar Wong
Cezar Sa Espinola
Changping Chen
Cheah Chu Yeow
cheneydeng
Chris Bednarski
Christian Stewart
CMGS
Colin Hebert
Craig Jellick
Damon Wang
Dan Williams
Daniel, Dao Quang Minh
Daniel Garcia
Daniel Hiltgen
Darren Shepherd
Dave Choi
David Huie
Dawn Chen
Dinesh Subhraveti
Drew Wells
Ed
Elias G. Schneevoigt
Erez Horev
Eric Anderson
Eric Mountain
Erwin van Eyk
Ethan Mosbaugh
Ewout Prangsma
Fabio Rehm
Fatih Arslan
Felipe Oliveira
Flavia Missi
Florent Aide
Francisco Souza
Frank Groeneveld
George Moura
Grégoire Delattre
Guilherme Rezende
Guillermo Álvarez Fernández
Harry Zhang
He Simei
Ivan Mikushin
James Bardin
James Nugent
Januar Wayong
Jari Kolehmainen
Jason Wilder
Jawher Moussa
Jean-Baptiste Dalido
Jeff Mitchell
Jeffrey Hulten
Jen Andre
Jérôme Laurens
Jim Minter
Johan Euphrosine
John Hughes
Jorge Marey
Julian Einwag
Kamil Domanski
Karan Misra
Ken Herner
Kevin Lin
Kevin Xu
Kim, Hirokuni
Kostas Lekkas
Kyle Allan
Liron Levin
Lior Yankovich
Liu Peng
Lorenz Leutgeb
Lucas Clemente
Lucas Weiblen
Lyon Hill
Mantas Matelis
Marguerite des Trois Maisons
Mariusz Borsa
Martin Sweeney
Máximo Cuadros Ortiz
Michael Schmatz
Michal Fojtik
Mike Dillon
Mrunal Patel
Nate Jones
Nguyen Sy Thanh Son
Nicholas Van Wiggeren
Nick Ethier
niko83
Omeid Matten
Orivej Desh
Paul Bellamy
Paul Morie
Paul Weil
Peter Edge
Peter Jihoon Kim
Phil Lu
Philippe Lafoucrière
Radek Simko
Rafe Colton
Raphaël Pinson
Reed Allman
RJ Catalano
Rob Miller
Robbert Klarenbeek
Robert Williamson
Roman Khlystik
Russell Haering
Salvador Gironès
Sam Rijs
Sami Wagiaalla
Samuel Archambault
Samuel Karp
Seth Jennings
Shane Xie
Silas Sewell
Simon Eskildsen
Simon Menke
Skolos
Soulou
Sridhar Ratnakumar
Summer Mousa
Sunjin Lee
Swaroop Ramachandra
Tarsis Azevedo
Tim Schindler
Timothy St. Clair
Tobi Knaup
Tom Wilkie
Tonic
ttyh061
upccup
Victor Marmol
Vincenzo Prignano
Vlad Alexandru Ionescu
Weitao Zhou
Wiliam Souza
Ye Yin
Yu, Zou
Yuriy Bogdanov

View File

@@ -0,0 +1,6 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
You can find the Docker license at the following link:
https://raw.githubusercontent.com/docker/docker/master/LICENSE

22
vendor/github.com/fsouza/go-dockerclient/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2013-2017, go-dockerclient authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

41
vendor/github.com/fsouza/go-dockerclient/Makefile generated vendored Normal file
View File

@@ -0,0 +1,41 @@
.PHONY: \
all \
lint \
vet \
fmt \
fmtcheck \
pretest \
test \
integration \
clean
all: test
lint:
@ go get -v github.com/golang/lint/golint
[ -z "$$(golint . | grep -v 'type name will be used as docker.DockerInfo' | grep -v 'context.Context should be the first' | tee /dev/stderr)" ]
vet:
go vet ./...
fmt:
gofmt -s -w .
fmtcheck:
[ -z "$$(gofmt -s -d . | tee /dev/stderr)" ]
testdeps:
go get -d -t ./...
pretest: testdeps lint vet fmtcheck
gotest:
go test $(GO_TEST_FLAGS) ./...
test: pretest gotest
integration:
go test -tags docker_integration -run TestIntegration -v
clean:
go clean ./...

View File

@@ -0,0 +1,112 @@
# go-dockerclient
[![Travis Build Status](https://travis-ci.org/fsouza/go-dockerclient.svg?branch=master)](https://travis-ci.org/fsouza/go-dockerclient)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/4m374pti06ubg2l7?svg=true)](https://ci.appveyor.com/project/fsouza/go-dockerclient)
[![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://godoc.org/github.com/fsouza/go-dockerclient)
This package presents a client for the Docker remote API. It also provides
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/).
It currently supports the Docker API up to version 1.23.
This package also provides support for docker's network API, which is a simple
passthrough to the libnetwork remote API. Note that docker's network API is
only available in docker 1.8 and above, and only enabled in docker if
DOCKER_EXPERIMENTAL is defined during the docker build process.
For more details, check the [remote API
documentation](http://docs.docker.com/engine/reference/api/docker_remote_api/).
## Example
```go
package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
)
func main() {
endpoint := "unix:///var/run/docker.sock"
client, err := docker.NewClient(endpoint)
if err != nil {
panic(err)
}
imgs, err := client.ListImages(docker.ListImagesOptions{All: false})
if err != nil {
panic(err)
}
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)
}
}
```
## Using with TLS
In order to instantiate the client for a TLS-enabled daemon, you should use
NewTLSClient, passing the endpoint and path for key and certificates as
parameters.
```go
package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
)
func main() {
endpoint := "tcp://[ip]:[port]"
path := os.Getenv("DOCKER_CERT_PATH")
ca := fmt.Sprintf("%s/ca.pem", path)
cert := fmt.Sprintf("%s/cert.pem", path)
key := fmt.Sprintf("%s/key.pem", path)
client, _ := docker.NewTLSClient(endpoint, cert, key, ca)
// use client
}
```
If using [docker-machine](https://docs.docker.com/machine/), or another
application that exports environment variables `DOCKER_HOST`,
`DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, you can use NewClientFromEnv.
```go
package main
import (
"fmt"
"github.com/fsouza/go-dockerclient"
)
func main() {
client, _ := docker.NewClientFromEnv()
// use client
}
```
See the documentation for more details.
## Developing
All development commands can be seen in the [Makefile](Makefile).
Commited code must pass:
* [golint](https://github.com/golang/lint) (with some exceptions, see the Makefile).
* [go vet](https://golang.org/cmd/vet/)
* [gofmt](https://golang.org/cmd/gofmt)
* [go test](https://golang.org/cmd/go/#hdr-Test_packages)
Running `make test` will check all of these. If your editor does not
automatically call ``gofmt -s``, `make fmt` will format all go files in this
repository.

20
vendor/github.com/fsouza/go-dockerclient/appveyor.yml generated vendored Normal file
View File

@@ -0,0 +1,20 @@
version: '{build}'
platform: x64
clone_depth: 2
clone_folder: c:\gopath\src\github.com\fsouza\go-dockerclient
environment:
GOPATH: c:\gopath
matrix:
- GOVERSION: 1.7.5
- GOVERSION: 1.8.1
install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.zip
- 7z x go%GOVERSION%.windows-amd64.zip -y -oC:\ > NUL
build_script:
- go get -d -t ./...
test_script:
- go test -v ./...
matrix:
fast_finish: true

182
vendor/github.com/fsouza/go-dockerclient/auth.go generated vendored Normal file
View File

@@ -0,0 +1,182 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
)
// ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed.
var ErrCannotParseDockercfg = errors.New("Failed to read authentication from dockercfg")
// AuthConfiguration represents authentication options to use in the PushImage
// method. It represents the authentication in the Docker index server.
type AuthConfiguration struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Email string `json:"email,omitempty"`
ServerAddress string `json:"serveraddress,omitempty"`
}
// AuthConfigurations represents authentication options to use for the
// PushImage method accommodating the new X-Registry-Config header
type AuthConfigurations struct {
Configs map[string]AuthConfiguration `json:"configs"`
}
// AuthConfigurations119 is used to serialize a set of AuthConfigurations
// for Docker API >= 1.19.
type AuthConfigurations119 map[string]AuthConfiguration
// dockerConfig represents a registry authentation configuration from the
// .dockercfg file.
type dockerConfig struct {
Auth string `json:"auth"`
Email string `json:"email"`
}
// NewAuthConfigurationsFromFile returns AuthConfigurations from a path containing JSON
// in the same format as the .dockercfg file.
func NewAuthConfigurationsFromFile(path string) (*AuthConfigurations, error) {
r, err := os.Open(path)
if err != nil {
return nil, err
}
return NewAuthConfigurations(r)
}
func cfgPaths(dockerConfigEnv string, homeEnv string) []string {
var paths []string
if dockerConfigEnv != "" {
paths = append(paths, path.Join(dockerConfigEnv, "config.json"))
}
if homeEnv != "" {
paths = append(paths, path.Join(homeEnv, ".docker", "config.json"))
paths = append(paths, path.Join(homeEnv, ".dockercfg"))
}
return paths
}
// NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from
// system config files. The following files are checked in the order listed:
// - $DOCKER_CONFIG/config.json if DOCKER_CONFIG set in the environment,
// - $HOME/.docker/config.json
// - $HOME/.dockercfg
func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) {
err := fmt.Errorf("No docker configuration found")
var auths *AuthConfigurations
pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME"))
for _, path := range pathsToTry {
auths, err = NewAuthConfigurationsFromFile(path)
if err == nil {
return auths, nil
}
}
return auths, err
}
// NewAuthConfigurations returns AuthConfigurations from a JSON encoded string in the
// same format as the .dockercfg file.
func NewAuthConfigurations(r io.Reader) (*AuthConfigurations, error) {
var auth *AuthConfigurations
confs, err := parseDockerConfig(r)
if err != nil {
return nil, err
}
auth, err = authConfigs(confs)
if err != nil {
return nil, err
}
return auth, nil
}
func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
buf := new(bytes.Buffer)
buf.ReadFrom(r)
byteData := buf.Bytes()
confsWrapper := struct {
Auths map[string]dockerConfig `json:"auths"`
}{}
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
if len(confsWrapper.Auths) > 0 {
return confsWrapper.Auths, nil
}
}
var confs map[string]dockerConfig
if err := json.Unmarshal(byteData, &confs); err != nil {
return nil, err
}
return confs, nil
}
// authConfigs converts a dockerConfigs map to a AuthConfigurations object.
func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) {
c := &AuthConfigurations{
Configs: make(map[string]AuthConfiguration),
}
for reg, conf := range confs {
data, err := base64.StdEncoding.DecodeString(conf.Auth)
if err != nil {
return nil, err
}
userpass := strings.SplitN(string(data), ":", 2)
if len(userpass) != 2 {
return nil, ErrCannotParseDockercfg
}
c.Configs[reg] = AuthConfiguration{
Email: conf.Email,
Username: userpass[0],
Password: userpass[1],
ServerAddress: reg,
}
}
return c, nil
}
// AuthStatus returns the authentication status for Docker API versions >= 1.23.
type AuthStatus struct {
Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"`
IdentityToken string `json:"IdentityToken,omitempty" yaml:"IdentityToken,omitempty" toml:"IdentityToken,omitempty"`
}
// AuthCheck validates the given credentials. It returns nil if successful.
//
// For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty.`
//
// See https://goo.gl/6nsZkH for more details.
func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) {
var authStatus AuthStatus
if conf == nil {
return authStatus, errors.New("conf is nil")
}
resp, err := c.do("POST", "/auth", doOptions{data: conf})
if err != nil {
return authStatus, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return authStatus, err
}
if len(data) == 0 {
return authStatus, nil
}
if err := json.Unmarshal(data, &authStatus); err != nil {
return authStatus, err
}
return authStatus, nil
}

162
vendor/github.com/fsouza/go-dockerclient/auth_test.go generated vendored Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
"testing"
)
func TestAuthConfigurationSearchPath(t *testing.T) {
var testData = []struct {
dockerConfigEnv string
homeEnv string
expectedPaths []string
}{
{"", "", []string{}},
{"", "home", []string{path.Join("home", ".docker", "config.json"), path.Join("home", ".dockercfg")}},
{"docker_config", "", []string{path.Join("docker_config", "config.json")}},
{"a", "b", []string{path.Join("a", "config.json"), path.Join("b", ".docker", "config.json"), path.Join("b", ".dockercfg")}},
}
for _, tt := range testData {
paths := cfgPaths(tt.dockerConfigEnv, tt.homeEnv)
if got, want := strings.Join(paths, ","), strings.Join(tt.expectedPaths, ","); got != want {
t.Errorf("cfgPaths: wrong result. Want: %s. Got: %s", want, got)
}
}
}
func TestAuthConfigurationsFromFile(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "go-dockerclient-auth-test")
if err != nil {
t.Errorf("Unable to create temporary directory for TestAuthConfigurationsFromFile: %s", err)
}
defer os.RemoveAll(tmpDir)
authString := base64.StdEncoding.EncodeToString([]byte("user:pass"))
content := fmt.Sprintf("{\"auths\":{\"foo\": {\"auth\": \"%s\"}}}", authString)
configFile := path.Join(tmpDir, "docker_config")
if err = ioutil.WriteFile(configFile, []byte(content), 0600); err != nil {
t.Errorf("Error writing auth config for TestAuthConfigurationsFromFile: %s", err)
}
auths, err := NewAuthConfigurationsFromFile(configFile)
if err != nil {
t.Errorf("Error calling NewAuthConfigurationsFromFile: %s", err)
}
if _, hasKey := auths.Configs["foo"]; !hasKey {
t.Errorf("Returned auths did not include expected auth key foo")
}
}
func TestAuthLegacyConfig(t *testing.T) {
auth := base64.StdEncoding.EncodeToString([]byte("user:pa:ss"))
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
ac, err := NewAuthConfigurations(read)
if err != nil {
t.Error(err)
}
c, ok := ac.Configs["docker.io"]
if !ok {
t.Error("NewAuthConfigurations: Expected Configs to contain docker.io")
}
if got, want := c.Email, "user@example.com"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Username, "user"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Password, "pa:ss"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.ServerAddress, "docker.io"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got)
}
}
func TestAuthBadConfig(t *testing.T) {
auth := base64.StdEncoding.EncodeToString([]byte("userpass"))
read := strings.NewReader(fmt.Sprintf(`{"docker.io":{"auth":"%s","email":"user@example.com"}}`, auth))
ac, err := NewAuthConfigurations(read)
if err != ErrCannotParseDockercfg {
t.Errorf("Incorrect error returned %v\n", err)
}
if ac != nil {
t.Errorf("Invalid auth configuration returned, should be nil %v\n", ac)
}
}
func TestAuthAndOtherFields(t *testing.T) {
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
read := strings.NewReader(fmt.Sprintf(`{
"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}},
"detachKeys": "ctrl-e,e",
"HttpHeaders": { "MyHeader": "MyValue" }}`, auth))
ac, err := NewAuthConfigurations(read)
if err != nil {
t.Error(err)
}
c, ok := ac.Configs["docker.io"]
if !ok {
t.Error("NewAuthConfigurations: Expected Configs to contain docker.io")
}
if got, want := c.Email, "user@example.com"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Username, "user"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Password, "pass"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.ServerAddress, "docker.io"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got)
}
}
func TestAuthConfig(t *testing.T) {
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
read := strings.NewReader(fmt.Sprintf(`{"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}}}`, auth))
ac, err := NewAuthConfigurations(read)
if err != nil {
t.Error(err)
}
c, ok := ac.Configs["docker.io"]
if !ok {
t.Error("NewAuthConfigurations: Expected Configs to contain docker.io")
}
if got, want := c.Email, "user@example.com"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Username, "user"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.Password, "pass"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got)
}
if got, want := c.ServerAddress, "docker.io"; got != want {
t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got)
}
}
func TestAuthCheck(t *testing.T) {
fakeRT := &FakeRoundTripper{status: http.StatusOK}
client := newTestClient(fakeRT)
if _, err := client.AuthCheck(nil); err == nil {
t.Fatalf("expected error on nil auth config")
}
// test good auth
if _, err := client.AuthCheck(&AuthConfiguration{}); err != nil {
t.Fatal(err)
}
*fakeRT = FakeRoundTripper{status: http.StatusUnauthorized}
if _, err := client.AuthCheck(&AuthConfiguration{}); err == nil {
t.Fatal("expected failure from unauthorized auth")
}
}

162
vendor/github.com/fsouza/go-dockerclient/build_test.go generated vendored Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/docker/docker/pkg/archive"
)
func TestBuildImageMultipleContextsError(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
var buf bytes.Buffer
opts := BuildImageOptions{
Name: "testImage",
NoCache: true,
CacheFrom: []string{"a", "b", "c"},
SuppressOutput: true,
RmTmpContainer: true,
ForceRmTmpContainer: true,
InputStream: &buf,
OutputStream: &buf,
ContextDir: "testing/data",
}
err := client.BuildImage(opts)
if err != ErrMultipleContexts {
t.Errorf("BuildImage: providing both InputStream and ContextDir should produce an error")
}
}
func TestBuildImageContextDirDockerignoreParsing(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
if err := os.Symlink("doesnotexist", "testing/data/symlink"); err != nil {
t.Errorf("error creating symlink on demand: %s", err)
}
defer func() {
if err := os.Remove("testing/data/symlink"); err != nil {
t.Errorf("error removing symlink on demand: %s", err)
}
}()
workingdir, err := os.Getwd()
var buf bytes.Buffer
opts := BuildImageOptions{
Name: "testImage",
NoCache: true,
CacheFrom: []string{"a", "b", "c"},
SuppressOutput: true,
RmTmpContainer: true,
ForceRmTmpContainer: true,
OutputStream: &buf,
ContextDir: filepath.Join(workingdir, "testing", "data"),
}
err = client.BuildImage(opts)
if err != nil {
t.Fatal(err)
}
reqBody := fakeRT.requests[0].Body
tmpdir, err := unpackBodyTarball(reqBody)
if err != nil {
t.Fatal(err)
}
defer func() {
if err = os.RemoveAll(tmpdir); err != nil {
t.Fatal(err)
}
}()
files, err := ioutil.ReadDir(tmpdir)
if err != nil {
t.Fatal(err)
}
foundFiles := []string{}
for _, file := range files {
foundFiles = append(foundFiles, file.Name())
}
expectedFiles := []string{
".dockerignore",
"Dockerfile",
"barfile",
"ca.pem",
"cert.pem",
"key.pem",
"server.pem",
"serverkey.pem",
"symlink",
}
if !reflect.DeepEqual(expectedFiles, foundFiles) {
t.Errorf(
"BuildImage: incorrect files sent in tarball to docker server\nexpected %+v, found %+v",
expectedFiles, foundFiles,
)
}
}
func TestBuildImageSendXRegistryConfig(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
var buf bytes.Buffer
opts := BuildImageOptions{
Name: "testImage",
NoCache: true,
SuppressOutput: true,
RmTmpContainer: true,
ForceRmTmpContainer: true,
OutputStream: &buf,
ContextDir: "testing/data",
AuthConfigs: AuthConfigurations{
Configs: map[string]AuthConfiguration{
"quay.io": {
Username: "foo",
Password: "bar",
Email: "baz",
ServerAddress: "quay.io",
},
},
},
}
encodedConfig := "eyJjb25maWdzIjp7InF1YXkuaW8iOnsidXNlcm5hbWUiOiJmb28iLCJwYXNzd29yZCI6ImJhciIsImVtYWlsIjoiYmF6Iiwic2VydmVyYWRkcmVzcyI6InF1YXkuaW8ifX19Cg=="
if err := client.BuildImage(opts); err != nil {
t.Fatal(err)
}
xRegistryConfig := fakeRT.requests[0].Header["X-Registry-Config"][0]
if xRegistryConfig != encodedConfig {
t.Errorf(
"BuildImage: X-Registry-Config not set currectly: expected %q, got %q",
encodedConfig,
xRegistryConfig,
)
}
}
func unpackBodyTarball(req io.ReadCloser) (tmpdir string, err error) {
tmpdir, err = ioutil.TempDir("", "go-dockerclient-test")
if err != nil {
return
}
err = archive.Untar(req, tmpdir, &archive.TarOptions{
Compression: archive.Uncompressed,
NoLchown: true,
})
return
}

43
vendor/github.com/fsouza/go-dockerclient/change.go generated vendored Normal file
View 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 docker
import "fmt"
// ChangeType is a type for constants indicating the type of change
// in a container
type ChangeType int
const (
// ChangeModify is the ChangeType for container modifications
ChangeModify ChangeType = iota
// ChangeAdd is the ChangeType for additions to a container
ChangeAdd
// ChangeDelete is the ChangeType for deletions from a container
ChangeDelete
)
// Change represents a change in a container.
//
// See https://goo.gl/Wo0JJp for more details.
type Change struct {
Path string
Kind ChangeType
}
func (change *Change) String() string {
var kind string
switch change.Kind {
case ChangeModify:
kind = "C"
case ChangeAdd:
kind = "A"
case ChangeDelete:
kind = "D"
}
return fmt.Sprintf("%s %s", kind, change.Path)
}

View File

@@ -0,0 +1,24 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import "testing"
func TestChangeString(t *testing.T) {
var tests = []struct {
change Change
expected string
}{
{Change{"/etc/passwd", ChangeModify}, "C /etc/passwd"},
{Change{"/etc/passwd", ChangeAdd}, "A /etc/passwd"},
{Change{"/etc/passwd", ChangeDelete}, "D /etc/passwd"},
{Change{"/etc/passwd", 33}, " /etc/passwd"},
}
for _, tt := range tests {
if got := tt.change.String(); got != tt.expected {
t.Errorf("Change.String(): want %q. Got %q.", tt.expected, got)
}
}
}

1035
vendor/github.com/fsouza/go-dockerclient/client.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
// Copyright 2017 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.
// +build !windows
package docker
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"sort"
"sync"
"testing"
"time"
)
func TestClientDoConcurrentStress(t *testing.T) {
var reqs []*http.Request
var mu sync.Mutex
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mu.Lock()
reqs = append(reqs, r)
mu.Unlock()
})
var nativeSrvs []*httptest.Server
for i := 0; i < 3; i++ {
srv, cleanup, err := newNativeServer(handler)
if err != nil {
t.Fatal(err)
}
defer cleanup()
nativeSrvs = append(nativeSrvs, srv)
}
var tests = []struct {
testCase string
srv *httptest.Server
scheme string
withTimeout bool
withTLSServer bool
withTLSClient bool
}{
{testCase: "http server", srv: httptest.NewUnstartedServer(handler), scheme: "http"},
{testCase: "native server", srv: nativeSrvs[0], scheme: nativeProtocol},
{testCase: "http with timeout", srv: httptest.NewUnstartedServer(handler), scheme: "http", withTimeout: true},
{testCase: "native with timeout", srv: nativeSrvs[1], scheme: nativeProtocol, withTimeout: true},
{testCase: "http with tls", srv: httptest.NewUnstartedServer(handler), scheme: "https", withTLSServer: true, withTLSClient: true},
{testCase: "native with client-only tls", srv: nativeSrvs[2], scheme: nativeProtocol, withTLSServer: false, withTLSClient: nativeProtocol == unixProtocol}, // TLS client only works with unix protocol
}
for _, tt := range tests {
t.Run(tt.testCase, func(t *testing.T) {
reqs = nil
var client *Client
var err error
endpoint := tt.scheme + "://" + tt.srv.Listener.Addr().String()
if tt.withTLSServer {
tt.srv.StartTLS()
} else {
tt.srv.Start()
}
defer tt.srv.Close()
if tt.withTLSClient {
certPEMBlock, certErr := ioutil.ReadFile("testing/data/cert.pem")
if certErr != nil {
t.Fatal(certErr)
}
keyPEMBlock, certErr := ioutil.ReadFile("testing/data/key.pem")
if certErr != nil {
t.Fatal(certErr)
}
client, err = NewTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, nil)
} else {
client, err = NewClient(endpoint)
}
if err != nil {
t.Fatal(err)
}
if tt.withTimeout {
client.SetTimeout(time.Minute)
}
n := 50
wg := sync.WaitGroup{}
var paths []string
errsCh := make(chan error, 3*n)
waiters := make(chan CloseWaiter, n)
for i := 0; i < n; i++ {
path := fmt.Sprintf("/%05d", i)
paths = append(paths, "GET"+path)
paths = append(paths, "POST"+path)
paths = append(paths, "HEAD"+path)
wg.Add(1)
go func() {
defer wg.Done()
_, clientErr := client.do("GET", path, doOptions{})
if clientErr != nil {
errsCh <- clientErr
}
clientErr = client.stream("POST", path, streamOptions{})
if clientErr != nil {
errsCh <- clientErr
}
cw, clientErr := client.hijack("HEAD", path, hijackOptions{})
if clientErr != nil {
errsCh <- clientErr
} else {
waiters <- cw
}
}()
}
wg.Wait()
close(errsCh)
close(waiters)
for cw := range waiters {
cw.Wait()
cw.Close()
}
for err = range errsCh {
t.Error(err)
}
var reqPaths []string
for _, r := range reqs {
reqPaths = append(reqPaths, r.Method+r.URL.Path)
}
sort.Strings(paths)
sort.Strings(reqPaths)
if !reflect.DeepEqual(reqPaths, paths) {
t.Fatalf("expected server request paths to equal %v, got: %v", paths, reqPaths)
}
})
}
}

815
vendor/github.com/fsouza/go-dockerclient/client_test.go generated vendored Normal file
View File

@@ -0,0 +1,815 @@
// Copyright 2013 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"time"
"golang.org/x/net/context"
)
func TestNewAPIClient(t *testing.T) {
endpoint := "http://localhost:4243"
client, err := NewClient(endpoint)
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
// test native endpoints
endpoint = nativeRealEndpoint
client, err = NewClient(endpoint)
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if !client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be true, got false")
}
if client.requestedAPIVersion != nil {
t.Errorf("Expected requestedAPIVersion to be nil, got %#v.", client.requestedAPIVersion)
}
}
func newTLSClient(endpoint string) (*Client, error) {
return NewTLSClient(endpoint,
"testing/data/cert.pem",
"testing/data/key.pem",
"testing/data/ca.pem")
}
func TestNewTSLAPIClient(t *testing.T) {
endpoint := "https://localhost:4243"
client, err := newTLSClient(endpoint)
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if !client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be true, got false")
}
if client.requestedAPIVersion != nil {
t.Errorf("Expected requestedAPIVersion to be nil, got %#v.", client.requestedAPIVersion)
}
}
func TestNewVersionedClient(t *testing.T) {
endpoint := "http://localhost:4243"
client, err := NewVersionedClient(endpoint, "1.12")
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" {
t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion)
}
if client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be false, got true")
}
}
func TestNewVersionedClientFromEnv(t *testing.T) {
endpoint := "tcp://localhost:2376"
endpointURL := "http://localhost:2376"
os.Setenv("DOCKER_HOST", endpoint)
os.Setenv("DOCKER_TLS_VERIFY", "")
client, err := NewVersionedClientFromEnv("1.12")
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if client.endpointURL.String() != endpointURL {
t.Errorf("Expected endpointURL %s. Got %s.", endpoint, client.endpoint)
}
if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" {
t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion)
}
if client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be false, got true")
}
}
func TestNewVersionedClientFromEnvTLS(t *testing.T) {
endpoint := "tcp://localhost:2376"
endpointURL := "https://localhost:2376"
base, _ := os.Getwd()
os.Setenv("DOCKER_CERT_PATH", filepath.Join(base, "/testing/data/"))
os.Setenv("DOCKER_HOST", endpoint)
os.Setenv("DOCKER_TLS_VERIFY", "1")
client, err := NewVersionedClientFromEnv("1.12")
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if client.endpointURL.String() != endpointURL {
t.Errorf("Expected endpointURL %s. Got %s.", endpoint, client.endpoint)
}
if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.12" {
t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.12", reqVersion)
}
if client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be false, got true")
}
}
func TestNewTLSVersionedClient(t *testing.T) {
certPath := "testing/data/cert.pem"
keyPath := "testing/data/key.pem"
caPath := "testing/data/ca.pem"
endpoint := "https://localhost:4243"
client, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.14" {
t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.14", reqVersion)
}
if client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be false, got true")
}
}
func TestNewTLSVersionedClientNoClientCert(t *testing.T) {
certPath := "testing/data/cert_doesnotexist.pem"
keyPath := "testing/data/key_doesnotexist.pem"
caPath := "testing/data/ca.pem"
endpoint := "https://localhost:4243"
client, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
if reqVersion := client.requestedAPIVersion.String(); reqVersion != "1.14" {
t.Errorf("Wrong requestAPIVersion. Want %q. Got %q.", "1.14", reqVersion)
}
if client.SkipServerVersionCheck {
t.Error("Expected SkipServerVersionCheck to be false, got true")
}
}
func TestNewTLSVersionedClientInvalidCA(t *testing.T) {
certPath := "testing/data/cert.pem"
keyPath := "testing/data/key.pem"
caPath := "testing/data/key.pem"
endpoint := "https://localhost:4243"
_, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
if err == nil {
t.Errorf("Expected invalid ca at %s", caPath)
}
}
func TestNewTLSVersionedClientInvalidCANoClientCert(t *testing.T) {
certPath := "testing/data/cert_doesnotexist.pem"
keyPath := "testing/data/key_doesnotexist.pem"
caPath := "testing/data/key.pem"
endpoint := "https://localhost:4243"
_, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
if err == nil {
t.Errorf("Expected invalid ca at %s", caPath)
}
}
func TestNewClientInvalidEndpoint(t *testing.T) {
cases := []string{
"htp://localhost:3243", "http://localhost:a",
"", "http://localhost:8080:8383", "http://localhost:65536",
"https://localhost:-20",
}
for _, c := range cases {
client, err := NewClient(c)
if client != nil {
t.Errorf("Want <nil> client for invalid endpoint, got %#v.", client)
}
if !reflect.DeepEqual(err, ErrInvalidEndpoint) {
t.Errorf("NewClient(%q): Got invalid error for invalid endpoint. Want %#v. Got %#v.", c, ErrInvalidEndpoint, err)
}
}
}
func TestNewClientNoSchemeEndpoint(t *testing.T) {
cases := []string{"localhost", "localhost:8080"}
for _, c := range cases {
client, err := NewClient(c)
if client == nil {
t.Errorf("Want client for scheme-less endpoint, got <nil>")
}
if err != nil {
t.Errorf("Got unexpected error scheme-less endpoint: %q", err)
}
}
}
func TestNewTLSClient(t *testing.T) {
var tests = []struct {
endpoint string
expected string
}{
{"tcp://localhost:2376", "https"},
{"tcp://localhost:2375", "https"},
{"tcp://localhost:4000", "https"},
{"http://localhost:4000", "https"},
}
for _, tt := range tests {
client, err := newTLSClient(tt.endpoint)
if err != nil {
t.Error(err)
}
got := client.endpointURL.Scheme
if got != tt.expected {
t.Errorf("endpointURL.Scheme: Got %s. Want %s.", got, tt.expected)
}
}
}
func TestEndpoint(t *testing.T) {
client, err := NewVersionedClient("http://localhost:4243", "1.12")
if err != nil {
t.Fatal(err)
}
if endpoint := client.Endpoint(); endpoint != client.endpoint {
t.Errorf("Client.Endpoint(): want %q. Got %q", client.endpoint, endpoint)
}
}
func TestGetURL(t *testing.T) {
var tests = []struct {
endpoint string
path string
expected string
}{
{"http://localhost:4243/", "/", "http://localhost:4243/"},
{"http://localhost:4243", "/", "http://localhost:4243/"},
{"http://localhost:4243", "/containers/ps", "http://localhost:4243/containers/ps"},
{"tcp://localhost:4243", "/containers/ps", "http://localhost:4243/containers/ps"},
{"http://localhost:4243/////", "/", "http://localhost:4243/"},
{nativeRealEndpoint, "/containers", "/containers"},
}
for _, tt := range tests {
client, _ := NewClient(tt.endpoint)
client.endpoint = tt.endpoint
client.SkipServerVersionCheck = true
got := client.getURL(tt.path)
if got != tt.expected {
t.Errorf("getURL(%q): Got %s. Want %s.", tt.path, got, tt.expected)
}
}
}
func TestGetFakeNativeURL(t *testing.T) {
var tests = []struct {
endpoint string
path string
expected string
}{
{nativeRealEndpoint, "/", "http://unix.sock/"},
{nativeRealEndpoint, "/", "http://unix.sock/"},
{nativeRealEndpoint, "/containers/ps", "http://unix.sock/containers/ps"},
}
for _, tt := range tests {
client, _ := NewClient(tt.endpoint)
client.endpoint = tt.endpoint
client.SkipServerVersionCheck = true
got := client.getFakeNativeURL(tt.path)
if got != tt.expected {
t.Errorf("getURL(%q): Got %s. Want %s.", tt.path, got, tt.expected)
}
}
}
func TestError(t *testing.T) {
fakeBody := ioutil.NopCloser(bytes.NewBufferString("bad parameter"))
resp := &http.Response{
StatusCode: 400,
Body: fakeBody,
}
err := newError(resp)
expected := Error{Status: 400, Message: "bad parameter"}
if !reflect.DeepEqual(expected, *err) {
t.Errorf("Wrong error type. Want %#v. Got %#v.", expected, *err)
}
message := "API error (400): bad parameter"
if err.Error() != message {
t.Errorf("Wrong error message. Want %q. Got %q.", message, err.Error())
}
}
func TestQueryString(t *testing.T) {
v := float32(2.4)
f32QueryString := fmt.Sprintf("w=%s&x=10&y=10.35", strconv.FormatFloat(float64(v), 'f', -1, 64))
jsonPerson := url.QueryEscape(`{"Name":"gopher","age":4}`)
var tests = []struct {
input interface{}
want string
}{
{&ListContainersOptions{All: true}, "all=1"},
{ListContainersOptions{All: true}, "all=1"},
{ListContainersOptions{Before: "something"}, "before=something"},
{ListContainersOptions{Before: "something", Since: "other"}, "before=something&since=other"},
{ListContainersOptions{Filters: map[string][]string{"status": {"paused", "running"}}}, "filters=%7B%22status%22%3A%5B%22paused%22%2C%22running%22%5D%7D"},
{dumb{X: 10, Y: 10.35000}, "x=10&y=10.35"},
{dumb{W: v, X: 10, Y: 10.35000}, f32QueryString},
{dumb{X: 10, Y: 10.35000, Z: 10}, "x=10&y=10.35&zee=10"},
{dumb{v: 4, X: 10, Y: 10.35000}, "x=10&y=10.35"},
{dumb{T: 10, Y: 10.35000}, "y=10.35"},
{dumb{Person: &person{Name: "gopher", Age: 4}}, "p=" + jsonPerson},
{nil, ""},
{10, ""},
{"not_a_struct", ""},
}
for _, tt := range tests {
got := queryString(tt.input)
if got != tt.want {
t.Errorf("queryString(%v). Want %q. Got %q.", tt.input, tt.want, got)
}
}
}
func TestAPIVersions(t *testing.T) {
var tests = []struct {
a string
b string
expectedALessThanB bool
expectedALessThanOrEqualToB bool
expectedAGreaterThanB bool
expectedAGreaterThanOrEqualToB bool
}{
{"1.11", "1.11", false, true, false, true},
{"1.10", "1.11", true, true, false, false},
{"1.11", "1.10", false, false, true, true},
{"1.11-ubuntu0", "1.11", false, true, false, true},
{"1.10", "1.11-el7", true, true, false, false},
{"1.9", "1.11", true, true, false, false},
{"1.11", "1.9", false, false, true, true},
{"1.1.1", "1.1", false, false, true, true},
{"1.1", "1.1.1", true, true, false, false},
{"2.1", "1.1.1", false, false, true, true},
{"2.1", "1.3.1", false, false, true, true},
{"1.1.1", "2.1", true, true, false, false},
{"1.3.1", "2.1", true, true, false, false},
}
for _, tt := range tests {
a, _ := NewAPIVersion(tt.a)
b, _ := NewAPIVersion(tt.b)
if tt.expectedALessThanB && !a.LessThan(b) {
t.Errorf("Expected %#v < %#v", a, b)
}
if tt.expectedALessThanOrEqualToB && !a.LessThanOrEqualTo(b) {
t.Errorf("Expected %#v <= %#v", a, b)
}
if tt.expectedAGreaterThanB && !a.GreaterThan(b) {
t.Errorf("Expected %#v > %#v", a, b)
}
if tt.expectedAGreaterThanOrEqualToB && !a.GreaterThanOrEqualTo(b) {
t.Errorf("Expected %#v >= %#v", a, b)
}
}
}
func TestPing(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
err := client.Ping()
if err != nil {
t.Fatal(err)
}
}
func TestPingFailing(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusInternalServerError}
client := newTestClient(fakeRT)
err := client.Ping()
if err == nil {
t.Fatal("Expected non nil error, got nil")
}
expectedErrMsg := "API error (500): "
if err.Error() != expectedErrMsg {
t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error())
}
}
func TestPingFailingWrongStatus(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusAccepted}
client := newTestClient(fakeRT)
err := client.Ping()
if err == nil {
t.Fatal("Expected non nil error, got nil")
}
expectedErrMsg := "API error (202): "
if err.Error() != expectedErrMsg {
t.Fatalf("Expected error to be %q, got: %q", expectedErrMsg, err.Error())
}
}
func TestPingErrorWithNativeClient(t *testing.T) {
srv, cleanup, err := newNativeServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("aaaaaaaaaaa-invalid-aaaaaaaaaaa"))
}))
if err != nil {
t.Fatal(err)
}
defer cleanup()
srv.Start()
defer srv.Close()
endpoint := nativeBadEndpoint
client, err := NewClient(endpoint)
if err != nil {
t.Fatal(err)
}
err = client.Ping()
if err == nil {
t.Fatal("Expected non nil error, got nil")
}
}
func TestClientStreamTimeoutNotHit(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "%d\n", i)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(100 * time.Millisecond)
}
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
stdout: &w,
inactivityTimeout: 300 * time.Millisecond,
})
if err != nil {
t.Fatal(err)
}
expected := "0\n1\n2\n3\n4\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
func TestClientStreamInactivityTimeout(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "%d\n", i)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(500 * time.Millisecond)
}
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
stdout: &w,
inactivityTimeout: 100 * time.Millisecond,
})
if err != ErrInactivityTimeout {
t.Fatalf("expected request canceled error, got: %s", err)
}
expected := "0\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
func TestClientStreamContextDeadline(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "abc\n")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(500 * time.Millisecond)
fmt.Fprint(w, "def\n")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
stdout: &w,
context: ctx,
})
if err != context.DeadlineExceeded {
t.Fatalf("expected %s, got: %s", context.DeadlineExceeded, err)
}
expected := "abc\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
func TestClientStreamContextCancel(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "abc\n")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(500 * time.Millisecond)
fmt.Fprint(w, "def\n")
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(200 * time.Millisecond)
cancel()
}()
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
stdout: &w,
context: ctx,
})
if err != context.Canceled {
t.Fatalf("expected %s, got: %s", context.Canceled, err)
}
expected := "abc\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
var mockPullOutput = `{"status":"Pulling from tsuru/static","id":"latest"}
{"status":"Already exists","progressDetail":{},"id":"a6aa3b66376f"}
{"status":"Pulling fs layer","progressDetail":{},"id":"106572778bf7"}
{"status":"Pulling fs layer","progressDetail":{},"id":"bac681833e51"}
{"status":"Pulling fs layer","progressDetail":{},"id":"7302e23ef08a"}
{"status":"Downloading","progressDetail":{"current":621,"total":621},"progress":"[==================================================\u003e] 621 B/621 B","id":"bac681833e51"}
{"status":"Verifying Checksum","progressDetail":{},"id":"bac681833e51"}
{"status":"Download complete","progressDetail":{},"id":"bac681833e51"}
{"status":"Downloading","progressDetail":{"current":1854,"total":1854},"progress":"[==================================================\u003e] 1.854 kB/1.854 kB","id":"106572778bf7"}
{"status":"Verifying Checksum","progressDetail":{},"id":"106572778bf7"}
{"status":"Download complete","progressDetail":{},"id":"106572778bf7"}
{"status":"Extracting","progressDetail":{"current":1854,"total":1854},"progress":"[==================================================\u003e] 1.854 kB/1.854 kB","id":"106572778bf7"}
{"status":"Extracting","progressDetail":{"current":1854,"total":1854},"progress":"[==================================================\u003e] 1.854 kB/1.854 kB","id":"106572778bf7"}
{"status":"Downloading","progressDetail":{"current":233019,"total":21059403},"progress":"[\u003e ] 233 kB/21.06 MB","id":"7302e23ef08a"}
{"status":"Downloading","progressDetail":{"current":462395,"total":21059403},"progress":"[=\u003e ] 462.4 kB/21.06 MB","id":"7302e23ef08a"}
{"status":"Downloading","progressDetail":{"current":8490555,"total":21059403},"progress":"[====================\u003e ] 8.491 MB/21.06 MB","id":"7302e23ef08a"}
{"status":"Downloading","progressDetail":{"current":20876859,"total":21059403},"progress":"[=================================================\u003e ] 20.88 MB/21.06 MB","id":"7302e23ef08a"}
{"status":"Verifying Checksum","progressDetail":{},"id":"7302e23ef08a"}
{"status":"Download complete","progressDetail":{},"id":"7302e23ef08a"}
{"status":"Pull complete","progressDetail":{},"id":"106572778bf7"}
{"status":"Extracting","progressDetail":{"current":621,"total":621},"progress":"[==================================================\u003e] 621 B/621 B","id":"bac681833e51"}
{"status":"Extracting","progressDetail":{"current":621,"total":621},"progress":"[==================================================\u003e] 621 B/621 B","id":"bac681833e51"}
{"status":"Pull complete","progressDetail":{},"id":"bac681833e51"}
{"status":"Extracting","progressDetail":{"current":229376,"total":21059403},"progress":"[\u003e ] 229.4 kB/21.06 MB","id":"7302e23ef08a"}
{"status":"Extracting","progressDetail":{"current":458752,"total":21059403},"progress":"[=\u003e ] 458.8 kB/21.06 MB","id":"7302e23ef08a"}
{"status":"Extracting","progressDetail":{"current":11239424,"total":21059403},"progress":"[==========================\u003e ] 11.24 MB/21.06 MB","id":"7302e23ef08a"}
{"status":"Extracting","progressDetail":{"current":21059403,"total":21059403},"progress":"[==================================================\u003e] 21.06 MB/21.06 MB","id":"7302e23ef08a"}
{"status":"Pull complete","progressDetail":{},"id":"7302e23ef08a"}
{"status":"Digest: sha256:b754472891aa7e33fc0214e3efa988174f2c2289285fcae868b7ec8b6675fc77"}
{"status":"Status: Downloaded newer image for 192.168.50.4:5000/tsuru/static"}
`
func TestClientStreamJSONDecode(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(mockPullOutput))
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
err = client.stream("POST", "/image/create", streamOptions{
stdout: &w,
useJSONDecoder: true,
})
if err != nil {
t.Fatal(err)
}
expected := `latest: Pulling from tsuru/static
a6aa3b66376f: Already exists
106572778bf7: Pulling fs layer
bac681833e51: Pulling fs layer
7302e23ef08a: Pulling fs layer
bac681833e51: Verifying Checksum
bac681833e51: Download complete
106572778bf7: Verifying Checksum
106572778bf7: Download complete
7302e23ef08a: Verifying Checksum
7302e23ef08a: Download complete
106572778bf7: Pull complete
bac681833e51: Pull complete
7302e23ef08a: Pull complete
Digest: sha256:b754472891aa7e33fc0214e3efa988174f2c2289285fcae868b7ec8b6675fc77
Status: Downloaded newer image for 192.168.50.4:5000/tsuru/static
`
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
type terminalBuffer struct {
bytes.Buffer
}
func (b *terminalBuffer) FD() uintptr {
return os.Stdout.Fd()
}
func (b *terminalBuffer) IsTerminal() bool {
return true
}
func TestClientStreamJSONDecodeWithTerminal(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(mockPullOutput))
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
var w terminalBuffer
err = client.stream("POST", "/image/create", streamOptions{
stdout: &w,
useJSONDecoder: true,
})
if err != nil {
t.Fatal(err)
}
expected := "latest: Pulling from tsuru/static\n\n" +
"\x1b[1A\x1b[1K\x1b[K\ra6aa3b66376f: Already exists \r\x1b[1B\n" +
"\x1b[1A\x1b[1K\x1b[K\r106572778bf7: Pulling fs layer \r\x1b[1B\n" +
"\x1b[1A\x1b[1K\x1b[K\rbac681833e51: Pulling fs layer \r\x1b[1B\n" +
"\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Pulling fs layer \r\x1b[1B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Downloading [==================================================>] 621B/621B\r\x1b[2B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Verifying Checksum \r\x1b[2B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Download complete \r\x1b[2B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Downloading [==================================================>] 1.854kB/1.854kB\r\x1b[3B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Verifying Checksum \r\x1b[3B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Download complete \r\x1b[3B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Extracting [==================================================>] 1.854kB/1.854kB\r\x1b[3B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Extracting [==================================================>] 1.854kB/1.854kB\r\x1b[3B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Downloading [> ] 233kB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Downloading [=> ] 462.4kB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Downloading [====================> ] 8.491MB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Downloading [=================================================> ] 20.88MB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Verifying Checksum \r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Download complete \r\x1b[1B\x1b[3A\x1b[1K\x1b[K\r106572778bf7: Pull complete \r\x1b[3B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Extracting [==================================================>] 621B/621B\r\x1b[2B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Extracting [==================================================>] 621B/621B\r\x1b[2B\x1b[2A\x1b[1K\x1b[K\rbac681833e51: Pull complete \r\x1b[2B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Extracting [> ] 229.4kB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Extracting [=> ] 458.8kB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Extracting [==========================> ] 11.24MB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Extracting [==================================================>] 21.06MB/21.06MB\r\x1b[1B\x1b[1A\x1b[1K\x1b[K\r7302e23ef08a: Pull complete \r\x1b[1BDigest: sha256:b754472891aa7e33fc0214e3efa988174f2c2289285fcae868b7ec8b6675fc77\n" +
"Status: Downloaded newer image for 192.168.50.4:5000/tsuru/static\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
func TestClientDoContextDeadline(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(500 * time.Millisecond)
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
_, err = client.do("POST", "/image/create", doOptions{
context: ctx,
})
if err != context.DeadlineExceeded {
t.Fatalf("expected %s, got: %s", context.DeadlineExceeded, err)
}
}
func TestClientDoContextCancel(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(500 * time.Millisecond)
}))
client, err := NewClient(srv.URL)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(100 * time.Millisecond)
cancel()
}()
_, err = client.do("POST", "/image/create", doOptions{
context: ctx,
})
if err != context.Canceled {
t.Fatalf("expected %s, got: %s", context.Canceled, err)
}
}
func TestClientStreamTimeoutNativeClient(t *testing.T) {
srv, cleanup, err := newNativeServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 5; i++ {
fmt.Fprintf(w, "%d\n", i)
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
time.Sleep(500 * time.Millisecond)
}
}))
if err != nil {
t.Fatal(err)
}
defer cleanup()
srv.Start()
defer srv.Close()
client, err := NewClient(nativeProtocol + "://" + srv.Listener.Addr().String())
if err != nil {
t.Fatal(err)
}
var w bytes.Buffer
err = client.stream("POST", "/image/create", streamOptions{
setRawTerminal: true,
stdout: &w,
inactivityTimeout: 100 * time.Millisecond,
})
if err != ErrInactivityTimeout {
t.Fatalf("expected request canceled error, got: %s", err)
}
expected := "0\n"
result := w.String()
if result != expected {
t.Fatalf("expected stream result %q, got: %q", expected, result)
}
}
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)
res := &http.Response{
StatusCode: rt.status,
Body: ioutil.NopCloser(body),
Header: make(http.Header),
}
for k, v := range rt.header {
res.Header.Set(k, v)
}
return res, nil
}
func (rt *FakeRoundTripper) Reset() {
rt.requests = nil
}
type person struct {
Name string
Age int `json:"age"`
}
type dumb struct {
T int `qs:"-"`
v int
W float32
X int
Y float64
Z int `qs:"zee"`
Person *person `qs:"p"`
}

View File

@@ -0,0 +1,32 @@
// Copyright 2016 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.
// +build !windows
package docker
import (
"context"
"net"
"net/http"
"github.com/hashicorp/go-cleanhttp"
)
// initializeNativeClient initializes the native Unix domain socket client on
// Unix-style operating systems
func (c *Client) initializeNativeClient() {
if c.endpointURL.Scheme != unixProtocol {
return
}
socketPath := c.endpointURL.Path
tr := cleanhttp.DefaultTransport()
tr.Dial = func(network, addr string) (net.Conn, error) {
return c.Dialer.Dial(network, addr)
}
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return c.Dialer.Dial(unixProtocol, socketPath)
}
c.nativeHTTPClient = &http.Client{Transport: tr}
}

View File

@@ -0,0 +1,68 @@
// +build !windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
)
const (
nativeProtocol = unixProtocol
nativeRealEndpoint = "unix:///var/run/docker.sock"
nativeBadEndpoint = "unix:///tmp/echo.sock"
)
func TestNewTSLAPIClientUnixEndpoint(t *testing.T) {
srv, cleanup, err := newNativeServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}))
if err != nil {
t.Fatal(err)
}
defer cleanup()
srv.Start()
defer srv.Close()
endpoint := nativeProtocol + "://" + srv.Listener.Addr().String()
client, err := newTLSClient(endpoint)
if err != nil {
t.Fatal(err)
}
if client.endpoint != endpoint {
t.Errorf("Expected endpoint %s. Got %s.", endpoint, client.endpoint)
}
rsp, err := client.do("GET", "/", doOptions{})
if err != nil {
t.Fatal(err)
}
data, err := ioutil.ReadAll(rsp.Body)
if err != nil {
t.Fatal(err)
}
if string(data) != "ok" {
t.Fatalf("Expected response to be %q, got: %q", "ok", string(data))
}
}
func newNativeServer(handler http.Handler) (*httptest.Server, func(), error) {
tmpdir, err := ioutil.TempDir("", "socket")
if err != nil {
return nil, nil, err
}
socketPath := filepath.Join(tmpdir, "docker_test_stress.sock")
l, err := net.Listen("unix", socketPath)
if err != nil {
return nil, nil, err
}
srv := httptest.NewUnstartedServer(handler)
srv.Listener = l
return srv, func() { os.RemoveAll(tmpdir) }, nil
}

View File

@@ -0,0 +1,46 @@
// +build windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"context"
"net"
"net/http"
"time"
"github.com/Microsoft/go-winio"
"github.com/hashicorp/go-cleanhttp"
)
const namedPipeConnectTimeout = 2 * time.Second
type pipeDialer struct {
dialFunc func(network, addr string) (net.Conn, error)
}
func (p pipeDialer) Dial(network, address string) (net.Conn, error) {
return p.dialFunc(network, address)
}
// initializeNativeClient initializes the native Named Pipe client for Windows
func (c *Client) initializeNativeClient() {
if c.endpointURL.Scheme != namedPipeProtocol {
return
}
namedPipePath := c.endpointURL.Path
dialFunc := func(network, addr string) (net.Conn, error) {
timeout := namedPipeConnectTimeout
return winio.DialPipe(namedPipePath, &timeout)
}
tr := cleanhttp.DefaultTransport()
tr.Dial = dialFunc
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
return dialFunc(network, addr)
}
c.Dialer = &pipeDialer{dialFunc}
c.nativeHTTPClient = &http.Client{Transport: tr}
}

View File

@@ -0,0 +1,44 @@
// +build windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"fmt"
"net/http"
"net/http/httptest"
"sync"
"github.com/Microsoft/go-winio"
)
const (
nativeProtocol = namedPipeProtocol
nativeRealEndpoint = "npipe:////./pipe/docker_engine"
nativeBadEndpoint = "npipe:////./pipe/godockerclient_test_echo"
)
var (
// namedPipeCount is used to provide uniqueness in the named pipes generated
// by newNativeServer
namedPipeCount int
// namedPipesAllocateLock protects namedPipeCount
namedPipeAllocateLock sync.Mutex
)
func newNativeServer(handler http.Handler) (*httptest.Server, func(), error) {
namedPipeAllocateLock.Lock()
defer namedPipeAllocateLock.Unlock()
pipeName := fmt.Sprintf("//./pipe/godockerclient_test_%d", namedPipeCount)
namedPipeCount++
l, err := winio.ListenPipe(pipeName, &winio.PipeConfig{MessageMode: true})
if err != nil {
return nil, nil, err
}
srv := httptest.NewUnstartedServer(handler)
srv.Listener = l
return srv, func() {}, nil
}

1547
vendor/github.com/fsouza/go-dockerclient/container.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,108 @@
// +build !windows
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bufio"
"bytes"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/hashicorp/go-cleanhttp"
)
func TestExportContainerViaUnixSocket(t *testing.T) {
content := "exported container tar content"
var buf []byte
out := bytes.NewBuffer(buf)
tempSocket := tempfile("export_socket")
defer os.Remove(tempSocket)
endpoint := "unix://" + tempSocket
u, _ := parseEndpoint(endpoint, false)
client := Client{
HTTPClient: cleanhttp.DefaultClient(),
Dialer: &net.Dialer{},
endpoint: endpoint,
endpointURL: u,
SkipServerVersionCheck: true,
}
listening := make(chan string)
done := make(chan int)
containerID := "4fa6e0f0c678"
go runStreamConnServer(t, "unix", tempSocket, listening, done, containerID)
<-listening // wait for server to start
opts := ExportContainerOptions{ID: containerID, OutputStream: out}
err := client.ExportContainer(opts)
<-done // make sure server stopped
if err != nil {
t.Errorf("ExportContainer: caugh error %#v while exporting container, expected nil", err.Error())
}
if out.String() != content {
t.Errorf("ExportContainer: wrong stdout. Want %#v. Got %#v.", content, out.String())
}
}
func TestStatsTimeoutUnixSocket(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "socket")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
socketPath := filepath.Join(tmpdir, "docker_test.sock")
t.Logf("socketPath=%s", socketPath)
l, err := net.Listen("unix", socketPath)
if err != nil {
t.Fatal(err)
}
received := make(chan bool)
defer l.Close()
go func() {
conn, connErr := l.Accept()
if connErr != nil {
t.Logf("Failed to accept connection: %s", connErr)
return
}
breader := bufio.NewReader(conn)
req, connErr := http.ReadRequest(breader)
if connErr != nil {
t.Logf("Failed to read request: %s", connErr)
return
}
if req.URL.Path != "/containers/c/stats" {
t.Logf("Wrong URL path for stats: %q", req.URL.Path)
return
}
received <- true
time.Sleep(2 * time.Second)
}()
client, _ := NewClient("unix://" + socketPath)
client.SkipServerVersionCheck = true
errC := make(chan error, 1)
statsC := make(chan *Stats)
done := make(chan bool)
defer close(done)
go func() {
errC <- client.Stats(StatsOptions{ID: "c", Stats: statsC, Stream: true, Done: done, Timeout: time.Millisecond})
close(errC)
}()
err = <-errC
e, ok := err.(net.Error)
if !ok || !e.Timeout() {
t.Errorf("Failed to receive timeout error, got %#v", err)
}
recvTimeout := 2 * time.Second
select {
case <-received:
return
case <-time.After(recvTimeout):
t.Fatalf("Timeout waiting to receive message after %v", recvTimeout)
}
}

168
vendor/github.com/fsouza/go-dockerclient/env.go generated vendored Normal file
View 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
}

351
vendor/github.com/fsouza/go-dockerclient/env_test.go generated vendored Normal file
View File

@@ -0,0 +1,351 @@
// 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
if err := env.SetList("SOME", list); err != nil {
t.Error(err)
}
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")
}

391
vendor/github.com/fsouza/go-dockerclient/event.go generated vendored Normal file
View File

@@ -0,0 +1,391 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"errors"
"fmt"
"io"
"math"
"net"
"net/http"
"net/http/httputil"
"sync"
"sync/atomic"
"time"
)
// APIEvents represents events coming from the Docker API
// The fields in the Docker API changed in API version 1.22, and
// events for more than images and containers are now fired off.
// To maintain forward and backward compatibility, go-dockerclient
// replicates the event in both the new and old format as faithfully as possible.
//
// For events that only exist in 1.22 in later, `Status` is filled in as
// `"Type:Action"` instead of just `Action` to allow for older clients to
// differentiate and not break if they rely on the pre-1.22 Status types.
//
// The transformEvent method can be consulted for more information about how
// events are translated from new/old API formats
type APIEvents struct {
// New API Fields in 1.22
Action string `json:"action,omitempty"`
Type string `json:"type,omitempty"`
Actor APIActor `json:"actor,omitempty"`
// Old API fields for < 1.22
Status string `json:"status,omitempty"`
ID string `json:"id,omitempty"`
From string `json:"from,omitempty"`
// Fields in both
Time int64 `json:"time,omitempty"`
TimeNano int64 `json:"timeNano,omitempty"`
}
// APIActor represents an actor that accomplishes something for an event
type APIActor struct {
ID string `json:"id,omitempty"`
Attributes map[string]string `json:"attributes,omitempty"`
}
type eventMonitoringState struct {
// `sync/atomic` expects the first word in an allocated struct to be 64-bit
// aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details.
lastSeen int64
sync.RWMutex
sync.WaitGroup
enabled bool
C chan *APIEvents
errC chan error
listeners []chan<- *APIEvents
}
const (
maxMonitorConnRetries = 5
retryInitialWaitTime = 10.
)
var (
// ErrNoListeners is the error returned when no listeners are available
// to receive an event.
ErrNoListeners = errors.New("no listeners present to receive event")
// ErrListenerAlreadyExists is the error returned when the listerner already
// exists.
ErrListenerAlreadyExists = errors.New("listener already exists for docker events")
// ErrTLSNotSupported is the error returned when the client does not support
// TLS (this applies to the Windows named pipe client).
ErrTLSNotSupported = errors.New("tls not supported by this client")
// EOFEvent is sent when the event listener receives an EOF error.
EOFEvent = &APIEvents{
Type: "EOF",
Status: "EOF",
}
)
// AddEventListener adds a new listener to container events in the Docker API.
//
// The parameter is a channel through which events will be sent.
func (c *Client) AddEventListener(listener chan<- *APIEvents) error {
var err error
if !c.eventMonitor.isEnabled() {
err = c.eventMonitor.enableEventMonitoring(c)
if err != nil {
return err
}
}
return c.eventMonitor.addListener(listener)
}
// RemoveEventListener removes a listener from the monitor.
func (c *Client) RemoveEventListener(listener chan *APIEvents) error {
err := c.eventMonitor.removeListener(listener)
if err != nil {
return err
}
if c.eventMonitor.listernersCount() == 0 {
c.eventMonitor.disableEventMonitoring()
}
return nil
}
func (eventState *eventMonitoringState) addListener(listener chan<- *APIEvents) error {
eventState.Lock()
defer eventState.Unlock()
if listenerExists(listener, &eventState.listeners) {
return ErrListenerAlreadyExists
}
eventState.Add(1)
eventState.listeners = append(eventState.listeners, listener)
return nil
}
func (eventState *eventMonitoringState) removeListener(listener chan<- *APIEvents) error {
eventState.Lock()
defer eventState.Unlock()
if listenerExists(listener, &eventState.listeners) {
var newListeners []chan<- *APIEvents
for _, l := range eventState.listeners {
if l != listener {
newListeners = append(newListeners, l)
}
}
eventState.listeners = newListeners
eventState.Add(-1)
}
return nil
}
func (eventState *eventMonitoringState) closeListeners() {
for _, l := range eventState.listeners {
close(l)
eventState.Add(-1)
}
eventState.listeners = nil
}
func (eventState *eventMonitoringState) listernersCount() int {
eventState.RLock()
defer eventState.RUnlock()
return len(eventState.listeners)
}
func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool {
for _, b := range *list {
if b == a {
return true
}
}
return false
}
func (eventState *eventMonitoringState) enableEventMonitoring(c *Client) error {
eventState.Lock()
defer eventState.Unlock()
if !eventState.enabled {
eventState.enabled = true
atomic.StoreInt64(&eventState.lastSeen, 0)
eventState.C = make(chan *APIEvents, 100)
eventState.errC = make(chan error, 1)
go eventState.monitorEvents(c)
}
return nil
}
func (eventState *eventMonitoringState) disableEventMonitoring() error {
eventState.Lock()
defer eventState.Unlock()
eventState.closeListeners()
eventState.Wait()
if eventState.enabled {
eventState.enabled = false
close(eventState.C)
close(eventState.errC)
}
return nil
}
func (eventState *eventMonitoringState) monitorEvents(c *Client) {
var err error
for eventState.noListeners() {
time.Sleep(10 * time.Millisecond)
}
if err = eventState.connectWithRetry(c); err != nil {
// terminate if connect failed
eventState.disableEventMonitoring()
return
}
for eventState.isEnabled() {
timeout := time.After(100 * time.Millisecond)
select {
case ev, ok := <-eventState.C:
if !ok {
return
}
if ev == EOFEvent {
eventState.disableEventMonitoring()
return
}
eventState.updateLastSeen(ev)
go eventState.sendEvent(ev)
case err = <-eventState.errC:
if err == ErrNoListeners {
eventState.disableEventMonitoring()
return
} else if err != nil {
defer func() { go eventState.monitorEvents(c) }()
return
}
case <-timeout:
continue
}
}
}
func (eventState *eventMonitoringState) connectWithRetry(c *Client) error {
var retries int
eventState.RLock()
eventChan := eventState.C
errChan := eventState.errC
eventState.RUnlock()
err := c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan)
for ; err != nil && retries < maxMonitorConnRetries; retries++ {
waitTime := int64(retryInitialWaitTime * math.Pow(2, float64(retries)))
time.Sleep(time.Duration(waitTime) * time.Millisecond)
eventState.RLock()
eventChan = eventState.C
errChan = eventState.errC
eventState.RUnlock()
err = c.eventHijack(atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan)
}
return err
}
func (eventState *eventMonitoringState) noListeners() bool {
eventState.RLock()
defer eventState.RUnlock()
return len(eventState.listeners) == 0
}
func (eventState *eventMonitoringState) isEnabled() bool {
eventState.RLock()
defer eventState.RUnlock()
return eventState.enabled
}
func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
eventState.RLock()
defer eventState.RUnlock()
eventState.Add(1)
defer eventState.Done()
if eventState.enabled {
if len(eventState.listeners) == 0 {
eventState.errC <- ErrNoListeners
return
}
for _, listener := range eventState.listeners {
listener <- event
}
}
}
func (eventState *eventMonitoringState) updateLastSeen(e *APIEvents) {
eventState.Lock()
defer eventState.Unlock()
if atomic.LoadInt64(&eventState.lastSeen) < e.Time {
atomic.StoreInt64(&eventState.lastSeen, e.Time)
}
}
func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan chan error) error {
uri := "/events"
if startTime != 0 {
uri += fmt.Sprintf("?since=%d", startTime)
}
protocol := c.endpointURL.Scheme
address := c.endpointURL.Path
if protocol != "unix" && protocol != "npipe" {
protocol = "tcp"
address = c.endpointURL.Host
}
var dial net.Conn
var err error
if c.TLSConfig == nil {
dial, err = c.Dialer.Dial(protocol, address)
} else {
netDialer, ok := c.Dialer.(*net.Dialer)
if !ok {
return ErrTLSNotSupported
}
dial, err = tlsDialWithDialer(netDialer, protocol, address, c.TLSConfig)
}
if err != nil {
return err
}
conn := httputil.NewClientConn(dial, nil)
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
return err
}
res, err := conn.Do(req)
if err != nil {
return err
}
go func(res *http.Response, conn *httputil.ClientConn) {
defer conn.Close()
defer res.Body.Close()
decoder := json.NewDecoder(res.Body)
for {
var event APIEvents
if err = decoder.Decode(&event); err != nil {
if err == io.EOF || err == io.ErrUnexpectedEOF {
c.eventMonitor.RLock()
if c.eventMonitor.enabled && c.eventMonitor.C == eventChan {
// Signal that we're exiting.
eventChan <- EOFEvent
}
c.eventMonitor.RUnlock()
break
}
errChan <- err
}
if event.Time == 0 {
continue
}
if !c.eventMonitor.isEnabled() || c.eventMonitor.C != eventChan {
return
}
transformEvent(&event)
eventChan <- &event
}
}(res, conn)
return nil
}
// transformEvent takes an event and determines what version it is from
// then populates both versions of the event
func transformEvent(event *APIEvents) {
// if event version is <= 1.21 there will be no Action and no Type
if event.Action == "" && event.Type == "" {
event.Action = event.Status
event.Actor.ID = event.ID
event.Actor.Attributes = map[string]string{}
switch event.Status {
case "delete", "import", "pull", "push", "tag", "untag":
event.Type = "image"
default:
event.Type = "container"
if event.From != "" {
event.Actor.Attributes["image"] = event.From
}
}
} else {
if event.Status == "" {
if event.Type == "image" || event.Type == "container" {
event.Status = event.Action
} else {
// Because just the Status has been overloaded with different Types
// if an event is not for an image or a container, we prepend the type
// to avoid problems for people relying on actions being only for
// images and containers
event.Status = event.Type + ":" + event.Action
}
}
if event.ID == "" {
event.ID = event.Actor.ID
}
if event.From == "" {
event.From = event.Actor.Attributes["image"]
}
}
}

291
vendor/github.com/fsouza/go-dockerclient/event_test.go generated vendored Normal file
View File

@@ -0,0 +1,291 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bufio"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"time"
)
func TestEventListeners(t *testing.T) {
testEventListeners("TestEventListeners", t, httptest.NewServer, NewClient)
}
func TestTLSEventListeners(t *testing.T) {
testEventListeners("TestTLSEventListeners", t, func(handler http.Handler) *httptest.Server {
server := httptest.NewUnstartedServer(handler)
cert, err := tls.LoadX509KeyPair("testing/data/server.pem", "testing/data/serverkey.pem")
if err != nil {
t.Fatalf("Error loading server key pair: %s", err)
}
caCert, err := ioutil.ReadFile("testing/data/ca.pem")
if err != nil {
t.Fatalf("Error loading ca certificate: %s", err)
}
caPool := x509.NewCertPool()
if !caPool.AppendCertsFromPEM(caCert) {
t.Fatalf("Could not add ca certificate")
}
server.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
}
server.StartTLS()
return server
}, func(url string) (*Client, error) {
return NewTLSClient(url, "testing/data/cert.pem", "testing/data/key.pem", "testing/data/ca.pem")
})
}
func testEventListeners(testName string, t *testing.T, buildServer func(http.Handler) *httptest.Server, buildClient func(string) (*Client, error)) {
response := `{"action":"pull","type":"image","actor":{"id":"busybox:latest","attributes":{}},"time":1442421700,"timeNano":1442421700598988358}
{"action":"create","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716853979870}
{"action":"attach","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716894759198}
{"action":"start","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716983607193}
{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
{"Action":"create","Actor":{"Attributes":{"HAProxyMode":"http","HealthCheck":"HttpGet","HealthCheckArgs":"http://127.0.0.1:39051/status/check","ServicePort_8080":"17801","image":"datanerd.us/siteeng/sample-app-go:latest","name":"sample-app-client-go-69818c1223ddb5"},"ID":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16"},"Type":"container","from":"datanerd.us/siteeng/sample-app-go:latest","id":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16","status":"create","time":1459133932,"timeNano":1459133932961735842}`
wantResponse := []*APIEvents{
{
Action: "pull",
Type: "image",
Actor: APIActor{
ID: "busybox:latest",
Attributes: map[string]string{},
},
Status: "pull",
ID: "busybox:latest",
Time: 1442421700,
TimeNano: 1442421700598988358,
},
{
Action: "create",
Type: "container",
Actor: APIActor{
ID: "5745704abe9caa5",
Attributes: map[string]string{
"image": "busybox",
},
},
Status: "create",
ID: "5745704abe9caa5",
From: "busybox",
Time: 1442421716,
TimeNano: 1442421716853979870,
},
{
Action: "attach",
Type: "container",
Actor: APIActor{
ID: "5745704abe9caa5",
Attributes: map[string]string{
"image": "busybox",
},
},
Status: "attach",
ID: "5745704abe9caa5",
From: "busybox",
Time: 1442421716,
TimeNano: 1442421716894759198,
},
{
Action: "start",
Type: "container",
Actor: APIActor{
ID: "5745704abe9caa5",
Attributes: map[string]string{
"image": "busybox",
},
},
Status: "start",
ID: "5745704abe9caa5",
From: "busybox",
Time: 1442421716,
TimeNano: 1442421716983607193,
},
{
Action: "create",
Type: "container",
Actor: APIActor{
ID: "dfdf82bd3881",
Attributes: map[string]string{
"image": "base:latest",
},
},
Status: "create",
ID: "dfdf82bd3881",
From: "base:latest",
Time: 1374067924,
},
{
Action: "start",
Type: "container",
Actor: APIActor{
ID: "dfdf82bd3881",
Attributes: map[string]string{
"image": "base:latest",
},
},
Status: "start",
ID: "dfdf82bd3881",
From: "base:latest",
Time: 1374067924,
},
{
Action: "stop",
Type: "container",
Actor: APIActor{
ID: "dfdf82bd3881",
Attributes: map[string]string{
"image": "base:latest",
},
},
Status: "stop",
ID: "dfdf82bd3881",
From: "base:latest",
Time: 1374067966,
},
{
Action: "destroy",
Type: "container",
Actor: APIActor{
ID: "dfdf82bd3881",
Attributes: map[string]string{
"image": "base:latest",
},
},
Status: "destroy",
ID: "dfdf82bd3881",
From: "base:latest",
Time: 1374067970,
},
{
Action: "create",
Type: "container",
Status: "create",
From: "datanerd.us/siteeng/sample-app-go:latest",
ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16",
Time: 1459133932,
TimeNano: 1459133932961735842,
Actor: APIActor{
ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16",
Attributes: map[string]string{
"HAProxyMode": "http",
"HealthCheck": "HttpGet",
"HealthCheckArgs": "http://127.0.0.1:39051/status/check",
"ServicePort_8080": "17801",
"image": "datanerd.us/siteeng/sample-app-go:latest",
"name": "sample-app-client-go-69818c1223ddb5",
},
},
},
}
server := buildServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rsc := bufio.NewScanner(strings.NewReader(response))
for rsc.Scan() {
w.Write([]byte(rsc.Text()))
w.(http.Flusher).Flush()
time.Sleep(10 * time.Millisecond)
}
}))
defer server.Close()
client, err := buildClient(server.URL)
if err != nil {
t.Errorf("Failed to create client: %s", err)
}
client.SkipServerVersionCheck = true
listener := make(chan *APIEvents, 10)
defer func() {
time.Sleep(10 * time.Millisecond)
if err = client.RemoveEventListener(listener); err != nil {
t.Error(err)
}
}()
err = client.AddEventListener(listener)
if err != nil {
t.Errorf("Failed to add event listener: %s", err)
}
timeout := time.After(1 * time.Second)
for i := 0; i < 9; i++ {
select {
case msg := <-listener:
t.Logf("%d: Received: %v", i, msg)
if !reflect.DeepEqual(msg, wantResponse[i]) {
t.Fatalf("%d: wanted: %#v\n got: %#v", i, wantResponse[i], msg)
}
case <-timeout:
t.Fatalf("%s timed out waiting on events", testName)
}
}
}
func TestEventListenerReAdding(t *testing.T) {
endChan := make(chan bool)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
<-endChan
}))
client, err := NewClient(server.URL)
if err != nil {
t.Errorf("Failed to create client: %s", err)
}
listener := make(chan *APIEvents, 10)
if err := client.AddEventListener(listener); err != nil {
t.Errorf("Failed to add event listener: %s", err)
}
// Make sure eventHijack() is started with the current eventMonitoringState.
time.Sleep(10 * time.Millisecond)
if err := client.RemoveEventListener(listener); err != nil {
t.Errorf("Failed to remove event listener: %s", err)
}
if err := client.AddEventListener(listener); err != nil {
t.Errorf("Failed to add event listener: %s", err)
}
endChan <- true
// Give the goroutine of the first eventHijack() time to handle the EOF.
time.Sleep(10 * time.Millisecond)
}

View File

@@ -0,0 +1,138 @@
// Copyright 2013 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker_test
import (
"archive/tar"
"bytes"
"fmt"
"log"
"time"
"github.com/fsouza/go-dockerclient"
)
func ExampleClient_AttachToContainer() {
client, err := docker.NewClient("http://localhost:4243")
if err != nil {
log.Fatal(err)
}
client.SkipServerVersionCheck = true
// Reading logs from container a84849 and sending them to buf.
var buf bytes.Buffer
err = client.AttachToContainer(docker.AttachToContainerOptions{
Container: "a84849",
OutputStream: &buf,
Logs: true,
Stdout: true,
Stderr: true,
})
if err != nil {
log.Fatal(err)
}
log.Println(buf.String())
buf.Reset()
err = client.AttachToContainer(docker.AttachToContainerOptions{
Container: "a84849",
OutputStream: &buf,
Stdout: true,
Stream: true,
})
if err != nil {
log.Fatal(err)
}
log.Println(buf.String())
}
func ExampleClient_BuildImage() {
client, err := docker.NewClient("http://localhost:4243")
if err != nil {
log.Fatal(err)
}
t := time.Now()
inputbuf, outputbuf := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
tr := tar.NewWriter(inputbuf)
tr.WriteHeader(&tar.Header{Name: "Dockerfile", Size: 10, ModTime: t, AccessTime: t, ChangeTime: t})
tr.Write([]byte("FROM base\n"))
tr.Close()
opts := docker.BuildImageOptions{
Name: "test",
InputStream: inputbuf,
OutputStream: outputbuf,
}
if err := client.BuildImage(opts); err != nil {
log.Fatal(err)
}
}
func ExampleClient_AddEventListener() {
client, err := docker.NewClient("http://localhost:4243")
if err != nil {
log.Fatal(err)
}
listener := make(chan *docker.APIEvents)
err = client.AddEventListener(listener)
if err != nil {
log.Fatal(err)
}
defer func() {
err = client.RemoveEventListener(listener)
if err != nil {
log.Fatal(err)
}
}()
timeout := time.After(1 * time.Second)
for {
select {
case msg := <-listener:
log.Println(msg)
case <-timeout:
return
}
}
}
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)
}
}

209
vendor/github.com/fsouza/go-dockerclient/exec.go generated vendored Normal file
View File

@@ -0,0 +1,209 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"golang.org/x/net/context"
)
// Exec is the type representing a `docker exec` instance and containing the
// instance ID
type Exec struct {
ID string `json:"Id,omitempty" yaml:"Id,omitempty"`
}
// CreateExecOptions specify parameters to the CreateExecContainer function.
//
// See https://goo.gl/60TeBP for more details
type CreateExecOptions struct {
AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"`
AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"`
AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"`
Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"`
Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"`
Context context.Context `json:"-"`
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"`
}
// CreateExec sets up an exec instance in a running container `id`, returning the exec
// instance, or an error in case of failure.
//
// See https://goo.gl/60TeBP for more details
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchContainer{ID: opts.Container}
}
return nil, err
}
defer resp.Body.Close()
var exec Exec
if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
return nil, err
}
return &exec, nil
}
// StartExecOptions specify parameters to the StartExecContainer function.
//
// See https://goo.gl/1EeDWi for more details
type StartExecOptions struct {
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
ErrorStream io.Writer `qs:"-"`
Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty" toml:"Detach,omitempty"`
Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"`
// Use raw terminal? Usually true when the container contains a TTY.
RawTerminal bool `qs:"-"`
// If set, after a successful connect, a sentinel will be sent and then the
// client will block on receive before continuing.
//
// It must be an unbuffered channel. Using a buffered channel can lead
// to unexpected behavior.
Success chan struct{} `json:"-"`
Context context.Context `json:"-"`
}
// StartExec starts a previously set up exec instance id. If opts.Detach is
// true, it returns after starting the exec command. Otherwise, it sets up an
// interactive session with the exec command.
//
// See https://goo.gl/1EeDWi for more details
func (c *Client) StartExec(id string, opts StartExecOptions) error {
cw, err := c.StartExecNonBlocking(id, opts)
if err != nil {
return err
}
if cw != nil {
return cw.Wait()
}
return nil
}
// StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is
// true, it returns after starting the exec command. Otherwise, it sets up an
// interactive session with the exec command.
//
// See https://goo.gl/1EeDWi for more details
func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) {
if id == "" {
return nil, &NoSuchExec{ID: id}
}
path := fmt.Sprintf("/exec/%s/start", id)
if opts.Detach {
resp, err := c.do("POST", path, doOptions{data: opts, context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchExec{ID: id}
}
return nil, err
}
defer resp.Body.Close()
return nil, nil
}
return c.hijack("POST", path, hijackOptions{
success: opts.Success,
setRawTerminal: opts.RawTerminal,
in: opts.InputStream,
stdout: opts.OutputStream,
stderr: opts.ErrorStream,
data: opts,
})
}
// ResizeExecTTY resizes the tty session used by the exec command id. This API
// is valid only if Tty was specified as part of creating and starting the exec
// command.
//
// See https://goo.gl/Mo5bxx for more details
func (c *Client) ResizeExecTTY(id string, height, width int) error {
params := make(url.Values)
params.Set("h", strconv.Itoa(height))
params.Set("w", strconv.Itoa(width))
path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
resp, err := c.do("POST", path, doOptions{})
if err != nil {
return err
}
resp.Body.Close()
return nil
}
// ExecProcessConfig is a type describing the command associated to a Exec
// instance. It's used in the ExecInspect type.
type ExecProcessConfig struct {
User string `json:"user,omitempty" yaml:"user,omitempty" toml:"user,omitempty"`
Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty" toml:"privileged,omitempty"`
Tty bool `json:"tty,omitempty" yaml:"tty,omitempty" toml:"tty,omitempty"`
EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty" toml:"entrypoint,omitempty"`
Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty" toml:"arguments,omitempty"`
}
// ExecInspect is a type with details about a exec instance, including the
// exit code if the command has finished running. It's returned by a api
// call to /exec/(id)/json
//
// See https://goo.gl/ctMUiW for more details
type ExecInspect struct {
ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"`
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"`
Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"`
OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"`
OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty" toml:"OpenStderr,omitempty"`
OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty" toml:"OpenStdout,omitempty"`
ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"`
ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"`
DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"`
CanRemove bool `json:"CanRemove,omitempty" yaml:"CanRemove,omitempty" toml:"CanRemove,omitempty"`
}
// InspectExec returns low-level information about the exec command id.
//
// See https://goo.gl/ctMUiW for more details
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
path := fmt.Sprintf("/exec/%s/json", id)
resp, err := c.do("GET", path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchExec{ID: id}
}
return nil, err
}
defer resp.Body.Close()
var exec ExecInspect
if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil {
return nil, err
}
return &exec, nil
}
// NoSuchExec is the error returned when a given exec instance does not exist.
type NoSuchExec struct {
ID string
}
func (err *NoSuchExec) Error() string {
return "No such exec instance: " + err.ID
}

179
vendor/github.com/fsouza/go-dockerclient/exec_test.go generated vendored Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"strings"
"testing"
)
func TestExecCreate(t *testing.T) {
jsonContainer := `{"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"}`
var expected struct{ ID string }
err := json.Unmarshal([]byte(jsonContainer), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
client := newTestClient(fakeRT)
config := CreateExecOptions{
Container: "test",
AttachStdin: true,
AttachStdout: true,
AttachStderr: false,
Tty: false,
Cmd: []string{"touch", "/tmp/file"},
User: "a-user",
}
execObj, err := client.CreateExec(config)
if err != nil {
t.Fatal(err)
}
expectedID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
if execObj.ID != expectedID {
t.Errorf("ExecCreate: wrong ID. Want %q. Got %q.", expectedID, execObj.ID)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("ExecCreate: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/containers/test/exec"))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
var gotBody struct{ ID string }
err = json.NewDecoder(req.Body).Decode(&gotBody)
if err != nil {
t.Fatal(err)
}
}
func TestExecStartDetached(t *testing.T) {
execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
fakeRT := &FakeRoundTripper{status: http.StatusOK}
client := newTestClient(fakeRT)
config := StartExecOptions{
Detach: true,
}
err := client.StartExec(execID, config)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("ExecStart: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/exec/" + execID + "/start"))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
t.Log(req.Body)
var gotBody struct{ Detach bool }
err = json.NewDecoder(req.Body).Decode(&gotBody)
if err != nil {
t.Fatal(err)
}
if !gotBody.Detach {
t.Fatal("Expected Detach in StartExecOptions to be true")
}
}
func TestExecStartAndAttach(t *testing.T) {
var reader = strings.NewReader("send value")
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte{1, 0, 0, 0, 0, 0, 0, 5})
w.Write([]byte("hello"))
}))
defer server.Close()
client, _ := NewClient(server.URL)
client.SkipServerVersionCheck = true
var stdout, stderr bytes.Buffer
success := make(chan struct{})
execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
opts := StartExecOptions{
OutputStream: &stdout,
ErrorStream: &stderr,
InputStream: reader,
RawTerminal: true,
Success: success,
}
go func() {
if err := client.StartExec(execID, opts); err != nil {
t.Error(err)
}
}()
<-success
}
func TestExecResize(t *testing.T) {
execID := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
fakeRT := &FakeRoundTripper{status: http.StatusOK}
client := newTestClient(fakeRT)
err := client.ResizeExecTTY(execID, 10, 20)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("ExecStart: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/exec/" + execID + "/resize?h=10&w=20"))
if gotPath := req.URL.RequestURI(); gotPath != expectedURL.RequestURI() {
t.Errorf("ExecCreate: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
}
func TestExecInspect(t *testing.T) {
jsonExec := `{
"CanRemove": false,
"ContainerID": "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126",
"DetachKeys": "",
"ExitCode": 2,
"ID": "f33bbfb39f5b142420f4759b2348913bd4a8d1a6d7fd56499cb41a1bb91d7b3b",
"OpenStderr": true,
"OpenStdin": true,
"OpenStdout": true,
"ProcessConfig": {
"arguments": [
"-c",
"exit 2"
],
"entrypoint": "sh",
"privileged": false,
"tty": true,
"user": "1000"
},
"Running": false
}`
var expected ExecInspect
err := json.Unmarshal([]byte(jsonExec), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonExec, status: http.StatusOK}
client := newTestClient(fakeRT)
expectedID := "b53ee82b53a40c7dca428523e34f741f3abc51d9f297a14ff874bf761b995126"
execObj, err := client.InspectExec(expectedID)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*execObj, expected) {
t.Errorf("ExecInspect: Expected %#v. Got %#v.", expected, *execObj)
}
req := fakeRT.requests[0]
if req.Method != "GET" {
t.Errorf("ExecInspect: wrong HTTP method. Want %q. Got %q.", "GET", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/exec/" + expectedID + "/json"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("ExecInspect: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
}

719
vendor/github.com/fsouza/go-dockerclient/image.go generated vendored Normal file
View File

@@ -0,0 +1,719 @@
// Copyright 2013 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
"golang.org/x/net/context"
)
// APIImages represent an image returned in the ListImages call.
type APIImages struct {
ID string `json:"Id" yaml:"Id" toml:"Id"`
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty" toml:"ParentId,omitempty"`
RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
}
// RootFS represents the underlying layers used by an image
type RootFS struct {
Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"`
Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty" toml:"Layers,omitempty"`
}
// Image is the type representing a docker image and its various properties
type Image struct {
ID string `json:"Id" yaml:"Id" toml:"Id"`
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"`
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty" toml:"Parent,omitempty"`
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"`
Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"`
Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"`
ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty" toml:"ContainerConfig,omitempty"`
DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty" toml:"DockerVersion,omitempty"`
Author string `json:"Author,omitempty" yaml:"Author,omitempty" toml:"Author,omitempty"`
Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"`
Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"`
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"`
RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"`
RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"`
OS string `json:"Os,omitempty" yaml:"Os,omitempty" toml:"Os,omitempty"`
}
// ImagePre012 serves the same purpose as the Image type except that it is for
// earlier versions of the Docker API (pre-012 to be specific)
type ImagePre012 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:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
Size int64 `json:"size,omitempty"`
}
var (
// ErrNoSuchImage is the error returned when the image does not exist.
ErrNoSuchImage = errors.New("no such image")
// ErrMissingRepo is the error returned when the remote repository is
// missing.
ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'")
// ErrMissingOutputStream is the error returned when no output stream
// is provided to some calls, like BuildImage.
ErrMissingOutputStream = errors.New("missing output stream")
// ErrMultipleContexts is the error returned when both a ContextDir and
// InputStream are provided in BuildImageOptions
ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream")
// ErrMustSpecifyNames is the error rreturned when the Names field on
// ExportImagesOptions is nil or empty
ErrMustSpecifyNames = errors.New("must specify at least one name to export")
)
// ListImagesOptions specify parameters to the ListImages function.
//
// See https://goo.gl/BVzauZ for more details.
type ListImagesOptions struct {
Filters map[string][]string
All bool
Digests bool
Filter string
Context context.Context
}
// ListImages returns the list of available images in the server.
//
// See https://goo.gl/BVzauZ for more details.
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
path := "/images/json?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var images []APIImages
if err := json.NewDecoder(resp.Body).Decode(&images); err != nil {
return nil, err
}
return images, nil
}
// ImageHistory represent a layer in an image's history returned by the
// ImageHistory call.
type ImageHistory struct {
ID string `json:"Id" yaml:"Id" toml:"Id"`
Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty" toml:"Tags,omitempty"`
Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"`
CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"`
Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"`
}
// ImageHistory returns the history of the image by its name or ID.
//
// See https://goo.gl/fYtxQa for more details.
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
resp, err := c.do("GET", "/images/"+name+"/history", doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchImage
}
return nil, err
}
defer resp.Body.Close()
var history []ImageHistory
if err := json.NewDecoder(resp.Body).Decode(&history); err != nil {
return nil, err
}
return history, nil
}
// RemoveImage removes an image by its name or ID.
//
// See https://goo.gl/Vd2Pck for more details.
func (c *Client) RemoveImage(name string) error {
resp, err := c.do("DELETE", "/images/"+name, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return ErrNoSuchImage
}
return err
}
resp.Body.Close()
return nil
}
// RemoveImageOptions present the set of options available for removing an image
// from a registry.
//
// See https://goo.gl/Vd2Pck for more details.
type RemoveImageOptions struct {
Force bool `qs:"force"`
NoPrune bool `qs:"noprune"`
Context context.Context
}
// RemoveImageExtended removes an image by its name or ID.
// Extra params can be passed, see RemoveImageOptions
//
// See https://goo.gl/Vd2Pck for more details.
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
resp, err := c.do("DELETE", uri, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return ErrNoSuchImage
}
return err
}
resp.Body.Close()
return nil
}
// InspectImage returns an image by its name or ID.
//
// See https://goo.gl/ncLTG8 for more details.
func (c *Client) InspectImage(name string) (*Image, error) {
resp, err := c.do("GET", "/images/"+name+"/json", doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchImage
}
return nil, err
}
defer resp.Body.Close()
var image Image
// if the caller elected to skip checking the server's version, assume it's the latest
if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
if err := json.NewDecoder(resp.Body).Decode(&image); err != nil {
return nil, err
}
} else {
var imagePre012 ImagePre012
if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil {
return nil, err
}
image.ID = imagePre012.ID
image.Parent = imagePre012.Parent
image.Comment = imagePre012.Comment
image.Created = imagePre012.Created
image.Container = imagePre012.Container
image.ContainerConfig = imagePre012.ContainerConfig
image.DockerVersion = imagePre012.DockerVersion
image.Author = imagePre012.Author
image.Config = imagePre012.Config
image.Architecture = imagePre012.Architecture
image.Size = imagePre012.Size
}
return &image, nil
}
// PushImageOptions represents options to use in the PushImage method.
//
// See https://goo.gl/BZemGg for more details.
type PushImageOptions struct {
// Name of the image
Name string
// Tag of the image
Tag string
// Registry server to push the image
Registry string
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
Context context.Context
}
// PushImage pushes an image to a remote registry, logging progress to w.
//
// An empty instance of AuthConfiguration may be used for unauthenticated
// pushes.
//
// See https://goo.gl/BZemGg for more details.
func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error {
if opts.Name == "" {
return ErrNoSuchImage
}
headers, err := headersWithAuth(auth)
if err != nil {
return err
}
name := opts.Name
opts.Name = ""
path := "/images/" + name + "/push?" + queryString(&opts)
return c.stream("POST", path, streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
context: opts.Context,
})
}
// PullImageOptions present the set of options available for pulling an image
// from a registry.
//
// See https://goo.gl/qkoSsn for more details.
type PullImageOptions struct {
Repository string `qs:"fromImage"`
Tag string
// Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21
// and Docker Engine < 1.9
// This parameter was removed in Docker Engine 1.11
Registry string
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
Context context.Context
}
// PullImage pulls an image from a remote registry, logging progress to
// opts.OutputStream.
//
// See https://goo.gl/qkoSsn for more details.
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
if opts.Repository == "" {
return ErrNoSuchImage
}
headers, err := headersWithAuth(auth)
if err != nil {
return err
}
if opts.Tag == "" && strings.Contains(opts.Repository, "@") {
parts := strings.SplitN(opts.Repository, "@", 2)
opts.Repository = parts[0]
opts.Tag = parts[1]
}
return c.createImage(queryString(&opts), headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
}
func (c *Client) createImage(qs string, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error {
path := "/images/create?" + qs
return c.stream("POST", path, streamOptions{
setRawTerminal: true,
headers: headers,
in: in,
stdout: w,
rawJSONStream: rawJSONStream,
inactivityTimeout: timeout,
context: context,
})
}
// LoadImageOptions represents the options for LoadImage Docker API Call
//
// See https://goo.gl/rEsBV3 for more details.
type LoadImageOptions struct {
InputStream io.Reader
OutputStream io.Writer
Context context.Context
}
// LoadImage imports a tarball docker image
//
// See https://goo.gl/rEsBV3 for more details.
func (c *Client) LoadImage(opts LoadImageOptions) error {
return c.stream("POST", "/images/load", streamOptions{
setRawTerminal: true,
in: opts.InputStream,
stdout: opts.OutputStream,
context: opts.Context,
})
}
// ExportImageOptions represent the options for ExportImage Docker API call.
//
// See https://goo.gl/AuySaA for more details.
type ExportImageOptions struct {
Name string
OutputStream io.Writer
InactivityTimeout time.Duration
Context context.Context
}
// ExportImage exports an image (as a tar file) into the stream.
//
// See https://goo.gl/AuySaA for more details.
func (c *Client) ExportImage(opts ExportImageOptions) error {
return c.stream("GET", fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
context: opts.Context,
})
}
// ExportImagesOptions represent the options for ExportImages Docker API call
//
// See https://goo.gl/N9XlDn for more details.
type ExportImagesOptions struct {
Names []string
OutputStream io.Writer `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
Context context.Context
}
// ExportImages exports one or more images (as a tar file) into the stream
//
// See https://goo.gl/N9XlDn for more details.
func (c *Client) ExportImages(opts ExportImagesOptions) error {
if opts.Names == nil || len(opts.Names) == 0 {
return ErrMustSpecifyNames
}
return c.stream("GET", "/images/get?"+queryString(&opts), streamOptions{
setRawTerminal: true,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
})
}
// ImportImageOptions present the set of informations available for importing
// an image from a source file or the stdin.
//
// See https://goo.gl/qkoSsn for more details.
type ImportImageOptions struct {
Repository string `qs:"repo"`
Source string `qs:"fromSrc"`
Tag string `qs:"tag"`
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
RawJSONStream bool `qs:"-"`
InactivityTimeout time.Duration `qs:"-"`
Context context.Context
}
// ImportImage imports an image from a url, a file or stdin
//
// See https://goo.gl/qkoSsn for more details.
func (c *Client) ImportImage(opts ImportImageOptions) error {
if opts.Repository == "" {
return ErrNoSuchImage
}
if opts.Source != "-" {
opts.InputStream = nil
}
if opts.Source != "-" && !isURL(opts.Source) {
f, err := os.Open(opts.Source)
if err != nil {
return err
}
opts.InputStream = f
opts.Source = "-"
}
return c.createImage(queryString(&opts), nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context)
}
// 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
// https://goo.gl/4nYHwV.
type BuildImageOptions struct {
Name string `qs:"t"`
Dockerfile string `qs:"dockerfile"`
NoCache bool `qs:"nocache"`
CacheFrom []string `qs:"-"`
SuppressOutput bool `qs:"q"`
Pull bool `qs:"pull"`
RmTmpContainer bool `qs:"rm"`
ForceRmTmpContainer bool `qs:"forcerm"`
RawJSONStream bool `qs:"-"`
Memory int64 `qs:"memory"`
Memswap int64 `qs:"memswap"`
CPUShares int64 `qs:"cpushares"`
CPUQuota int64 `qs:"cpuquota"`
CPUPeriod int64 `qs:"cpuperiod"`
CPUSetCPUs string `qs:"cpusetcpus"`
Labels map[string]string `qs:"labels"`
InputStream io.Reader `qs:"-"`
OutputStream io.Writer `qs:"-"`
Remote string `qs:"remote"`
Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header
AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
ContextDir string `qs:"-"`
Ulimits []ULimit `qs:"-"`
BuildArgs []BuildArg `qs:"-"`
NetworkMode string `qs:"networkmode"`
InactivityTimeout time.Duration `qs:"-"`
CgroupParent string `qs:"cgroupparent"`
Context context.Context
}
// BuildArg represents arguments that can be passed to the image when building
// it from a Dockerfile.
//
// For more details about the Docker building process, see
// https://goo.gl/4nYHwV.
type BuildArg struct {
Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"`
Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"`
}
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
// stream.
//
// See https://goo.gl/4nYHwV for more details.
func (c *Client) BuildImage(opts BuildImageOptions) error {
if opts.OutputStream == nil {
return ErrMissingOutputStream
}
headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs))
if err != nil {
return err
}
if opts.Remote != "" && opts.Name == "" {
opts.Name = opts.Remote
}
if opts.InputStream != nil || opts.ContextDir != "" {
headers["Content-Type"] = "application/tar"
} else if opts.Remote == "" {
return ErrMissingRepo
}
if opts.ContextDir != "" {
if opts.InputStream != nil {
return ErrMultipleContexts
}
var err error
if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil {
return err
}
}
qs := queryString(&opts)
if c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion125) && len(opts.CacheFrom) > 0 {
if b, err := json.Marshal(opts.CacheFrom); err == nil {
item := url.Values(map[string][]string{})
item.Add("cachefrom", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
}
}
if len(opts.Ulimits) > 0 {
if b, err := json.Marshal(opts.Ulimits); err == nil {
item := url.Values(map[string][]string{})
item.Add("ulimits", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
}
}
if len(opts.BuildArgs) > 0 {
v := make(map[string]string)
for _, arg := range opts.BuildArgs {
v[arg.Name] = arg.Value
}
if b, err := json.Marshal(v); err == nil {
item := url.Values(map[string][]string{})
item.Add("buildargs", string(b))
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
}
}
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
setRawTerminal: true,
rawJSONStream: opts.RawJSONStream,
headers: headers,
in: opts.InputStream,
stdout: opts.OutputStream,
inactivityTimeout: opts.InactivityTimeout,
context: opts.Context,
})
}
func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) interface{} {
if c.serverAPIVersion == nil {
c.checkAPIVersion()
}
if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) {
return AuthConfigurations119(authConfigs.Configs)
}
return authConfigs
}
// TagImageOptions present the set of options to tag an image.
//
// See https://goo.gl/prHrvo for more details.
type TagImageOptions struct {
Repo string
Tag string
Force bool
Context context.Context
}
// TagImage adds a tag to the image identified by the given name.
//
// See https://goo.gl/prHrvo for more details.
func (c *Client) TagImage(name string, opts TagImageOptions) error {
if name == "" {
return ErrNoSuchImage
}
resp, err := c.do("POST", "/images/"+name+"/tag?"+queryString(&opts), doOptions{
context: opts.Context,
})
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return ErrNoSuchImage
}
return err
}
func isURL(u string) bool {
p, err := url.Parse(u)
if err != nil {
return false
}
return p.Scheme == "http" || p.Scheme == "https"
}
func headersWithAuth(auths ...interface{}) (map[string]string, error) {
var headers = make(map[string]string)
for _, auth := range auths {
switch auth.(type) {
case AuthConfiguration:
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
return nil, err
}
headers["X-Registry-Auth"] = base64.URLEncoding.EncodeToString(buf.Bytes())
case AuthConfigurations, AuthConfigurations119:
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(auth); err != nil {
return nil, err
}
headers["X-Registry-Config"] = base64.URLEncoding.EncodeToString(buf.Bytes())
}
}
return headers, nil
}
// APIImageSearch reflect the result of a search on the Docker Hub.
//
// See https://goo.gl/KLO9IZ for more details.
type APIImageSearch struct {
Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"`
IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty" toml:"is_official,omitempty"`
IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty" toml:"is_automated,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,omitempty"`
StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty" toml:"star_count,omitempty"`
}
// SearchImages search the docker hub with a specific given term.
//
// See https://goo.gl/KLO9IZ for more details.
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
resp, err := c.do("GET", "/images/search?term="+term, doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var searchResult []APIImageSearch
if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
return nil, err
}
return searchResult, nil
}
// SearchImagesEx search the docker hub with a specific given term and authentication.
//
// See https://goo.gl/KLO9IZ for more details.
func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) {
headers, err := headersWithAuth(auth)
if err != nil {
return nil, err
}
resp, err := c.do("GET", "/images/search?term="+term, doOptions{
headers: headers,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var searchResult []APIImageSearch
if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil {
return nil, err
}
return searchResult, nil
}
// PruneImagesOptions specify parameters to the PruneImages function.
//
// See https://goo.gl/qfZlbZ for more details.
type PruneImagesOptions struct {
Filters map[string][]string
Context context.Context
}
// PruneImagesResults specify results from the PruneImages function.
//
// See https://goo.gl/qfZlbZ for more details.
type PruneImagesResults struct {
ImagesDeleted []struct{ Untagged, Deleted string }
SpaceReclaimed int64
}
// PruneImages deletes images which are unused.
//
// See https://goo.gl/qfZlbZ for more details.
func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) {
path := "/images/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var results PruneImagesResults
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
return nil, err
}
return &results, nil
}

1134
vendor/github.com/fsouza/go-dockerclient/image_test.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,94 @@
// Copyright 2015 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.
// +build docker_integration
package docker
import (
"bytes"
"os"
"testing"
)
var dockerEndpoint string
func init() {
dockerEndpoint = os.Getenv("DOCKER_HOST")
if dockerEndpoint == "" {
dockerEndpoint = "unix:///var/run/docker.sock"
}
}
func TestIntegrationPullCreateStartLogs(t *testing.T) {
imageName := pullImage(t)
client := getClient()
hostConfig := HostConfig{PublishAllPorts: true}
createOpts := CreateContainerOptions{
Config: &Config{
Image: imageName,
Cmd: []string{"cat", "/home/gopher/file.txt"},
User: "gopher",
},
HostConfig: &hostConfig,
}
container, err := client.CreateContainer(createOpts)
if err != nil {
t.Fatal(err)
}
err = client.StartContainer(container.ID, &hostConfig)
if err != nil {
t.Fatal(err)
}
status, err := client.WaitContainer(container.ID)
if err != nil {
t.Error(err)
}
if status != 0 {
t.Errorf("WaitContainer(%q): wrong status. Want 0. Got %d", container.ID, status)
}
var stdout, stderr bytes.Buffer
logsOpts := LogsOptions{
Container: container.ID,
OutputStream: &stdout,
ErrorStream: &stderr,
Stdout: true,
Stderr: true,
}
err = client.Logs(logsOpts)
if err != nil {
t.Error(err)
}
if stderr.String() != "" {
t.Errorf("Got unexpected stderr from logs: %q", stderr.String())
}
expected := `Welcome to reality, wake up and rejoice
Welcome to reality, you've made the right choice
Welcome to reality, and let them hear your voice, shout it out!
`
if stdout.String() != expected {
t.Errorf("Got wrong stdout from logs.\nWant:\n%#v.\n\nGot:\n%#v.", expected, stdout.String())
}
}
func pullImage(t *testing.T) string {
imageName := "fsouza/go-dockerclient-integration:latest"
var buf bytes.Buffer
pullOpts := PullImageOptions{
Repository: imageName,
OutputStream: &buf,
}
client := getClient()
err := client.PullImage(pullOpts, AuthConfiguration{})
if err != nil {
t.Logf("Pull output: %s", buf.String())
t.Fatal(err)
}
return imageName
}
func getClient() *Client {
client, _ := NewClient(dockerEndpoint)
return client
}

181
vendor/github.com/fsouza/go-dockerclient/misc.go generated vendored Normal file
View File

@@ -0,0 +1,181 @@
// Copyright 2013 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net"
"strings"
"github.com/docker/docker/api/types/swarm"
)
// Version returns version information about the docker server.
//
// See https://goo.gl/mU7yje for more details.
func (c *Client) Version() (*Env, error) {
resp, err := c.do("GET", "/version", doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var env Env
if err := env.Decode(resp.Body); err != nil {
return nil, err
}
return &env, nil
}
// DockerInfo contains information about the Docker server
//
// See https://goo.gl/bHUoz9 for more details.
type DockerInfo struct {
ID string
Containers int
ContainersRunning int
ContainersPaused int
ContainersStopped int
Images int
Driver string
DriverStatus [][2]string
SystemStatus [][2]string
Plugins PluginsInfo
MemoryLimit bool
SwapLimit bool
KernelMemory bool
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool
CPUSet bool
IPv4Forwarding bool
BridgeNfIptables bool
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
Debug bool
OomKillDisable bool
ExperimentalBuild bool
NFd int
NGoroutines int
SystemTime string
ExecutionDriver string
LoggingDriver string
CgroupDriver string
NEventsListener int
KernelVersion string
OperatingSystem string
OSType string
Architecture string
IndexServerAddress string
RegistryConfig *ServiceConfig
NCPU int
MemTotal int64
DockerRootDir string
HTTPProxy string `json:"HttpProxy"`
HTTPSProxy string `json:"HttpsProxy"`
NoProxy string
Name string
Labels []string
ServerVersion string
ClusterStore string
ClusterAdvertise string
Isolation string
InitBinary string
DefaultRuntime string
LiveRestoreEnabled bool
Swarm swarm.Info
}
// PluginsInfo is a struct with the plugins registered with the docker daemon
//
// for more information, see: https://goo.gl/bHUoz9
type PluginsInfo struct {
// List of Volume plugins registered
Volume []string
// List of Network plugins registered
Network []string
// List of Authorization plugins registered
Authorization []string
}
// ServiceConfig stores daemon registry services configuration.
//
// for more information, see: https://goo.gl/7iFFDz
type ServiceConfig struct {
InsecureRegistryCIDRs []*NetIPNet
IndexConfigs map[string]*IndexInfo
Mirrors []string
}
// NetIPNet is the net.IPNet type, which can be marshalled and
// unmarshalled to JSON.
//
// for more information, see: https://goo.gl/7iFFDz
type NetIPNet net.IPNet
// MarshalJSON returns the JSON representation of the IPNet.
//
func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
return json.Marshal((*net.IPNet)(ipnet).String())
}
// UnmarshalJSON sets the IPNet from a byte array of JSON.
//
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
var ipnetStr string
if err = json.Unmarshal(b, &ipnetStr); err == nil {
var cidr *net.IPNet
if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
*ipnet = NetIPNet(*cidr)
}
}
return
}
// IndexInfo contains information about a registry.
//
// for more information, see: https://goo.gl/7iFFDz
type IndexInfo struct {
Name string
Mirrors []string
Secure bool
Official bool
}
// Info returns system-wide information about the Docker server.
//
// See https://goo.gl/ElTHi2 for more details.
func (c *Client) Info() (*DockerInfo, error) {
resp, err := c.do("GET", "/info", doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var info DockerInfo
if err := json.NewDecoder(resp.Body).Decode(&info); 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. It ignores the digest when it is
// present.
//
// Some examples:
//
// localhost.localdomain:5000/samalba/hipache:latest -> localhost.localdomain:5000/samalba/hipache, latest
// localhost.localdomain:5000/samalba/hipache -> localhost.localdomain:5000/samalba/hipache, ""
// busybox:latest@sha256:4a731fb46adc5cefe3ae374a8b6020fc1b6ad667a279647766e9a3cd89f6fa92 -> busybox, latest
func ParseRepositoryTag(repoTag string) (repository string, tag string) {
parts := strings.SplitN(repoTag, "@", 2)
repoTag = parts[0]
n := strings.LastIndex(repoTag, ":")
if n < 0 {
return repoTag, ""
}
if tag := repoTag[n+1:]; !strings.Contains(tag, "/") {
return repoTag[:n], tag
}
return repoTag, ""
}

193
vendor/github.com/fsouza/go-dockerclient/misc_test.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
// Copyright 2013 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"net"
"net/http"
"net/url"
"reflect"
"testing"
)
type DockerVersion struct {
Version string
GitCommit string
GoVersion string
}
func TestVersion(t *testing.T) {
body := `{
"Version":"0.2.2",
"GitCommit":"5a2a5cc+CHANGES",
"GoVersion":"go1.0.3"
}`
fakeRT := FakeRoundTripper{message: body, status: http.StatusOK}
client := newTestClient(&fakeRT)
expected := DockerVersion{
Version: "0.2.2",
GitCommit: "5a2a5cc+CHANGES",
GoVersion: "go1.0.3",
}
version, err := client.Version()
if err != nil {
t.Fatal(err)
}
if result := version.Get("Version"); result != expected.Version {
t.Errorf("Version(): Wrong result. Want %#v. Got %#v.", expected.Version, version.Get("Version"))
}
if result := version.Get("GitCommit"); result != expected.GitCommit {
t.Errorf("GitCommit(): Wrong result. Want %#v. Got %#v.", expected.GitCommit, version.Get("GitCommit"))
}
if result := version.Get("GoVersion"); result != expected.GoVersion {
t.Errorf("GoVersion(): Wrong result. Want %#v. Got %#v.", expected.GoVersion, version.Get("GoVersion"))
}
req := fakeRT.requests[0]
if req.Method != "GET" {
t.Errorf("Version(): wrong request method. Want GET. Got %s.", req.Method)
}
u, _ := url.Parse(client.getURL("/version"))
if req.URL.Path != u.Path {
t.Errorf("Version(): wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
}
func TestVersionError(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "internal error", status: http.StatusInternalServerError}
client := newTestClient(fakeRT)
version, err := client.Version()
if version != nil {
t.Errorf("Version(): expected <nil> value, got %#v.", version)
}
if err == nil {
t.Error("Version(): unexpected <nil> error")
}
}
func TestInfo(t *testing.T) {
body := `{
"Containers":11,
"Images":16,
"Debug":false,
"NFd":11,
"NGoroutines":21,
"MemoryLimit":true,
"SwapLimit":false,
"RegistryConfig":{
"InsecureRegistryCIDRs":["127.0.0.0/8"],
"IndexConfigs":{
"docker.io":{
"Name":"docker.io",
"Mirrors":null,
"Secure":true,
"Official":true
}
},
"Mirrors":null
}
}`
fakeRT := FakeRoundTripper{message: body, status: http.StatusOK}
client := newTestClient(&fakeRT)
expected := &DockerInfo{
Containers: 11,
Images: 16,
Debug: false,
NFd: 11,
NGoroutines: 21,
MemoryLimit: true,
SwapLimit: false,
RegistryConfig: &ServiceConfig{
InsecureRegistryCIDRs: []*NetIPNet{
{
Mask: net.CIDRMask(8, 32),
IP: net.ParseIP("127.0.0.0").To4(),
},
},
IndexConfigs: map[string]*IndexInfo{
"docker.io": {
Name: "docker.io",
Secure: true,
Official: true,
},
},
},
}
info, err := client.Info()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(expected, info) {
t.Errorf("Info(): Wrong result.\nWant %#v.\nGot %#v.", expected, info)
}
req := fakeRT.requests[0]
if req.Method != "GET" {
t.Errorf("Info(): Wrong HTTP method. Want GET. Got %s.", req.Method)
}
u, _ := url.Parse(client.getURL("/info"))
if req.URL.Path != u.Path {
t.Errorf("Info(): Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
}
func TestInfoError(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "internal error", status: http.StatusInternalServerError}
client := newTestClient(fakeRT)
version, err := client.Info()
if version != nil {
t.Errorf("Info(): expected <nil> value, got %#v.", version)
}
if err == nil {
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",
},
{
"busybox@sha256:4a731fb46adc5cefe3ae374a8b6020fc1b6ad667a279647766e9a3cd89f6fa92",
"busybox",
"",
},
{
"localhost.localdomain:5000/samalba/hipache:v1@sha256:4a731fb46adc5cefe3ae374a8b6020fc1b6ad667a279647766e9a3cd89f6fa92",
"localhost.localdomain:5000/samalba/hipache",
"v1",
},
}
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)
}
}
}

321
vendor/github.com/fsouza/go-dockerclient/network.go generated vendored Normal file
View File

@@ -0,0 +1,321 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"golang.org/x/net/context"
)
// ErrNetworkAlreadyExists is the error returned by CreateNetwork when the
// network already exists.
var ErrNetworkAlreadyExists = errors.New("network already exists")
// Network represents a network.
//
// See https://goo.gl/6GugX3 for more details.
type Network struct {
Name string
ID string `json:"Id"`
Scope string
Driver string
IPAM IPAMOptions
Containers map[string]Endpoint
Options map[string]string
Internal bool
EnableIPv6 bool `json:"EnableIPv6"`
Labels map[string]string
}
// Endpoint contains network resources allocated and used for a container in a network
//
// See https://goo.gl/6GugX3 for more details.
type Endpoint struct {
Name string
ID string `json:"EndpointID"`
MacAddress string
IPv4Address string
IPv6Address string
}
// ListNetworks returns all networks.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) ListNetworks() ([]Network, error) {
resp, err := c.do("GET", "/networks", doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var networks []Network
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
return nil, err
}
return networks, nil
}
// NetworkFilterOpts is an aggregation of key=value that Docker
// uses to filter networks
type NetworkFilterOpts map[string]map[string]bool
// FilteredListNetworks returns all networks with the filters applied
//
// See goo.gl/zd2mx4 for more details.
func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error) {
params, err := json.Marshal(opts)
if err != nil {
return nil, err
}
path := "/networks?filters=" + string(params)
resp, err := c.do("GET", path, doOptions{})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var networks []Network
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
return nil, err
}
return networks, nil
}
// NetworkInfo returns information about a network by its ID.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) NetworkInfo(id string) (*Network, error) {
path := "/networks/" + id
resp, err := c.do("GET", path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchNetwork{ID: id}
}
return nil, err
}
defer resp.Body.Close()
var network Network
if err := json.NewDecoder(resp.Body).Decode(&network); err != nil {
return nil, err
}
return &network, nil
}
// CreateNetworkOptions specify parameters to the CreateNetwork function and
// (for now) is the expected body of the "create network" http request message
//
// See https://goo.gl/6GugX3 for more details.
type CreateNetworkOptions struct {
Name string `json:"Name" yaml:"Name" toml:"Name"`
Driver string `json:"Driver" yaml:"Driver" toml:"Driver"`
IPAM IPAMOptions `json:"IPAM" yaml:"IPAM" toml:"IPAM"`
Options map[string]interface{} `json:"Options" yaml:"Options" toml:"Options"`
Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"`
CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"`
Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"`
EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"`
Context context.Context `json:"-"`
}
// IPAMOptions controls IP Address Management when creating a network
//
// See https://goo.gl/T8kRVH for more details.
type IPAMOptions struct {
Driver string `json:"Driver" yaml:"Driver" toml:"Driver"`
Config []IPAMConfig `json:"Config" yaml:"Config" toml:"Config"`
}
// IPAMConfig represents IPAM configurations
//
// See https://goo.gl/T8kRVH for more details.
type IPAMConfig struct {
Subnet string `json:",omitempty"`
IPRange string `json:",omitempty"`
Gateway string `json:",omitempty"`
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
}
// CreateNetwork creates a new network, returning the network instance,
// or an error in case of failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) {
resp, err := c.do(
"POST",
"/networks/create",
doOptions{
data: opts,
context: opts.Context,
},
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
type createNetworkResponse struct {
ID string
}
var (
network Network
cnr createNetworkResponse
)
if err := json.NewDecoder(resp.Body).Decode(&cnr); err != nil {
return nil, err
}
network.Name = opts.Name
network.ID = cnr.ID
network.Driver = opts.Driver
return &network, nil
}
// RemoveNetwork removes a network or returns an error in case of failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) RemoveNetwork(id string) error {
resp, err := c.do("DELETE", "/networks/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetwork{ID: id}
}
return err
}
resp.Body.Close()
return nil
}
// NetworkConnectionOptions specify parameters to the ConnectNetwork and
// DisconnectNetwork function.
//
// See https://goo.gl/RV7BJU for more details.
type NetworkConnectionOptions struct {
Container string
// EndpointConfig is only applicable to the ConnectNetwork call
EndpointConfig *EndpointConfig `json:"EndpointConfig,omitempty"`
// Force is only applicable to the DisconnectNetwork call
Force bool
Context context.Context `json:"-"`
}
// EndpointConfig stores network endpoint details
//
// See https://goo.gl/RV7BJU for more details.
type EndpointConfig struct {
IPAMConfig *EndpointIPAMConfig `json:"IPAMConfig,omitempty" yaml:"IPAMConfig,omitempty" toml:"IPAMConfig,omitempty"`
Links []string `json:"Links,omitempty" yaml:"Links,omitempty" toml:"Links,omitempty"`
Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"`
NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"`
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"`
Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"`
IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"`
IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"`
IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"`
GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"`
GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"`
MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"`
}
// EndpointIPAMConfig represents IPAM configurations for an
// endpoint
//
// See https://goo.gl/RV7BJU for more details.
type EndpointIPAMConfig struct {
IPv4Address string `json:",omitempty"`
IPv6Address string `json:",omitempty"`
}
// ConnectNetwork adds a container to a network or returns an error in case of
// failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
resp, err := c.do("POST", "/networks/"+id+"/connect", doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
}
return err
}
resp.Body.Close()
return nil
}
// DisconnectNetwork removes a container from a network or returns an error in
// case of failure.
//
// See https://goo.gl/6GugX3 for more details.
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
resp, err := c.do("POST", "/networks/"+id+"/disconnect", doOptions{data: opts})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container}
}
return err
}
resp.Body.Close()
return nil
}
// PruneNetworksOptions specify parameters to the PruneNetworks function.
//
// See https://goo.gl/kX0S9h for more details.
type PruneNetworksOptions struct {
Filters map[string][]string
Context context.Context
}
// PruneNetworksResults specify results from the PruneNetworks function.
//
// See https://goo.gl/kX0S9h for more details.
type PruneNetworksResults struct {
NetworksDeleted []string
}
// PruneNetworks deletes networks which are unused.
//
// See https://goo.gl/kX0S9h for more details.
func (c *Client) PruneNetworks(opts PruneNetworksOptions) (*PruneNetworksResults, error) {
path := "/networks/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var results PruneNetworksResults
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
return nil, err
}
return &results, nil
}
// NoSuchNetwork is the error returned when a given network does not exist.
type NoSuchNetwork struct {
ID string
}
func (err *NoSuchNetwork) Error() string {
return fmt.Sprintf("No such network: %s", err.ID)
}
// NoSuchNetworkOrContainer is the error returned when a given network or
// container does not exist.
type NoSuchNetworkOrContainer struct {
NetworkID string
ContainerID string
}
func (err *NoSuchNetworkOrContainer) Error() string {
return fmt.Sprintf("No such network (%s) or container (%s)", err.NetworkID, err.ContainerID)
}

View File

@@ -0,0 +1,266 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"bytes"
"encoding/json"
"net/http"
"net/url"
"reflect"
"testing"
)
func TestListNetworks(t *testing.T) {
jsonNetworks := `[
{
"ID": "8dfafdbc3a40",
"Name": "blah",
"Type": "bridge",
"Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}]
},
{
"ID": "9fb1e39c",
"Name": "foo",
"Type": "bridge",
"Endpoints":[{"ID": "c080be979dda", "Name": "lllll2222", "Network": "9fb1e39c"}]
}
]`
var expected []Network
err := json.Unmarshal([]byte(jsonNetworks), &expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: jsonNetworks, status: http.StatusOK})
containers, err := client.ListNetworks()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(containers, expected) {
t.Errorf("ListNetworks: Expected %#v. Got %#v.", expected, containers)
}
}
func TestFilteredListNetworks(t *testing.T) {
jsonNetworks := `[
{
"ID": "9fb1e39c",
"Name": "foo",
"Type": "bridge",
"Endpoints":[{"ID": "c080be979dda", "Name": "lllll2222", "Network": "9fb1e39c"}]
}
]`
var expected []Network
err := json.Unmarshal([]byte(jsonNetworks), &expected)
if err != nil {
t.Fatal(err)
}
wantQuery := "filters={\"name\":{\"blah\":true}}"
fakeRT := &FakeRoundTripper{message: jsonNetworks, status: http.StatusOK}
client := newTestClient(fakeRT)
opts := NetworkFilterOpts{
"name": map[string]bool{"blah": true},
}
containers, err := client.FilteredListNetworks(opts)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(containers, expected) {
t.Errorf("ListNetworks: Expected %#v. Got %#v.", expected, containers)
}
query := fakeRT.requests[0].URL.RawQuery
if query != wantQuery {
t.Errorf("FilteredListNetworks: wrong query\nWant %q\nGot %q", wantQuery, query)
}
}
func TestNetworkInfo(t *testing.T) {
jsonNetwork := `{
"ID": "8dfafdbc3a40",
"Name": "blah",
"Type": "bridge",
"Endpoints":[{"ID": "918c11c8288a", "Name": "dsafdsaf", "Network": "8dfafdbc3a40"}]
}`
var expected Network
err := json.Unmarshal([]byte(jsonNetwork), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonNetwork, status: http.StatusOK}
client := newTestClient(fakeRT)
id := "8dfafdbc3a40"
network, err := client.NetworkInfo(id)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*network, expected) {
t.Errorf("NetworkInfo(%q): Expected %#v. Got %#v.", id, expected, network)
}
expectedURL, _ := url.Parse(client.getURL("/networks/8dfafdbc3a40"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("NetworkInfo(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestNetworkCreate(t *testing.T) {
jsonID := `{"ID": "8dfafdbc3a40"}`
jsonNetwork := `{
"ID": "8dfafdbc3a40",
"Name": "foobar",
"Driver": "bridge"
}`
var expected Network
err := json.Unmarshal([]byte(jsonNetwork), &expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: jsonID, status: http.StatusOK})
opts := CreateNetworkOptions{Name: "foobar", Driver: "bridge"}
network, err := client.CreateNetwork(opts)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*network, expected) {
t.Errorf("CreateNetwork: Expected %#v. Got %#v.", expected, network)
}
}
func TestNetworkRemove(t *testing.T) {
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
err := client.RemoveNetwork(id)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "DELETE"
if req.Method != expectedMethod {
t.Errorf("RemoveNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id))
if req.URL.Path != u.Path {
t.Errorf("RemoveNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkConnect(t *testing.T) {
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
opts := NetworkConnectionOptions{Container: "foobar"}
err := client.ConnectNetwork(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("ConnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id + "/connect"))
if req.URL.Path != u.Path {
t.Errorf("ConnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkConnectWithEndpoint(t *testing.T) {
wantJSON := `{"Container":"foobar","EndpointConfig":{"IPAMConfig":{"IPv4Address":"8.8.8.8"},"Links":null,"Aliases":null},"Force":false}`
var wantObj NetworkConnectionOptions
json.NewDecoder(bytes.NewBuffer([]byte(wantJSON))).Decode(&wantObj)
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
opts := NetworkConnectionOptions{
Container: "foobar",
EndpointConfig: &EndpointConfig{
IPAMConfig: &EndpointIPAMConfig{
IPv4Address: "8.8.8.8",
},
},
}
err := client.ConnectNetwork(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("ConnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id + "/connect"))
if req.URL.Path != u.Path {
t.Errorf("ConnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
var in NetworkConnectionOptions
if err := json.NewDecoder(req.Body).Decode(&in); err != nil {
t.Errorf("ConnectNetwork: error parsing JSON data sent: %q", err)
}
if !reflect.DeepEqual(in, wantObj) {
t.Errorf("ConnectNetwork: wanted %#v send, got: %#v", wantObj, in)
}
}
func TestNetworkConnectNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
opts := NetworkConnectionOptions{Container: "foobar"}
err := client.ConnectNetwork("8dfafdbc3a40", opts)
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
t.Errorf("ConnectNetwork: wrong error type: %s.", serr)
}
}
func TestNetworkDisconnect(t *testing.T) {
id := "8dfafdbc3a40"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
opts := NetworkConnectionOptions{Container: "foobar"}
err := client.DisconnectNetwork(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("DisconnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/networks/" + id + "/disconnect"))
if req.URL.Path != u.Path {
t.Errorf("DisconnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
}
}
func TestNetworkDisconnectNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
opts := NetworkConnectionOptions{Container: "foobar"}
err := client.DisconnectNetwork("8dfafdbc3a40", opts)
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
t.Errorf("DisconnectNetwork: wrong error type: %s.", serr)
}
}
func TestPruneNetworks(t *testing.T) {
results := `{
"NetworksDeleted": [
"a", "b", "c"
]
}`
expected := &PruneNetworksResults{}
err := json.Unmarshal([]byte(results), expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: results, status: http.StatusOK})
got, err := client.PruneNetworks(PruneNetworksOptions{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("PruneNetworks: Expected %#v. Got %#v.", expected, got)
}
}

130
vendor/github.com/fsouza/go-dockerclient/node.go generated vendored Normal file
View File

@@ -0,0 +1,130 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"strconv"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
// NoSuchNode is the error returned when a given node does not exist.
type NoSuchNode struct {
ID string
Err error
}
func (err *NoSuchNode) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return "No such node: " + err.ID
}
// ListNodesOptions specify parameters to the ListNodes function.
//
// See http://goo.gl/3K4GwU for more details.
type ListNodesOptions struct {
Filters map[string][]string
Context context.Context
}
// ListNodes returns a slice of nodes matching the given criteria.
//
// See http://goo.gl/3K4GwU for more details.
func (c *Client) ListNodes(opts ListNodesOptions) ([]swarm.Node, error) {
path := "/nodes?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var nodes []swarm.Node
if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil {
return nil, err
}
return nodes, nil
}
// InspectNode returns information about a node by its ID.
//
// See http://goo.gl/WjkTOk for more details.
func (c *Client) InspectNode(id string) (*swarm.Node, error) {
resp, err := c.do("GET", "/nodes/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchNode{ID: id}
}
return nil, err
}
defer resp.Body.Close()
var node swarm.Node
if err := json.NewDecoder(resp.Body).Decode(&node); err != nil {
return nil, err
}
return &node, nil
}
// UpdateNodeOptions specify parameters to the NodeUpdate function.
//
// See http://goo.gl/VPBFgA for more details.
type UpdateNodeOptions struct {
swarm.NodeSpec
Version uint64
Context context.Context
}
// UpdateNode updates a node.
//
// See http://goo.gl/VPBFgA for more details.
func (c *Client) UpdateNode(id string, opts UpdateNodeOptions) error {
params := make(url.Values)
params.Set("version", strconv.FormatUint(opts.Version, 10))
path := "/nodes/" + id + "/update?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
context: opts.Context,
forceJSON: true,
data: opts.NodeSpec,
})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNode{ID: id}
}
return err
}
resp.Body.Close()
return nil
}
// RemoveNodeOptions specify parameters to the RemoveNode function.
//
// See http://goo.gl/0SNvYg for more details.
type RemoveNodeOptions struct {
ID string
Force bool
Context context.Context
}
// RemoveNode removes a node.
//
// See http://goo.gl/0SNvYg for more details.
func (c *Client) RemoveNode(opts RemoveNodeOptions) error {
params := make(url.Values)
params.Set("force", strconv.FormatBool(opts.Force))
path := "/nodes/" + opts.ID + "?" + params.Encode()
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchNode{ID: opts.ID}
}
return err
}
resp.Body.Close()
return nil
}

254
vendor/github.com/fsouza/go-dockerclient/node_test.go generated vendored Normal file
View File

@@ -0,0 +1,254 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"reflect"
"testing"
"github.com/docker/docker/api/types/swarm"
)
func TestListNodes(t *testing.T) {
jsonNodes := `[
{
"ID": "24ifsmvkjbyhk",
"Version": {
"Index": 8
},
"CreatedAt": "2016-06-07T20:31:11.853781916Z",
"UpdatedAt": "2016-06-07T20:31:11.999868824Z",
"Spec": {
"Name": "my-node",
"Role": "manager",
"Availability": "active",
"Labels": {
"foo": "bar"
}
},
"Description": {
"Hostname": "bf3067039e47",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 4000000000,
"MemoryBytes": 8272408576
},
"Engine": {
"EngineVersion": "1.12.0-dev",
"Labels": {
"foo": "bar"
},
"Plugins": [
{
"Type": "Volume",
"Name": "local"
},
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
}
]
}
},
"Status": {
"State": "ready"
},
"ManagerStatus": {
"Leader": true,
"Reachability": "reachable",
"Addr": "172.17.0.2:2377"
}
}
]`
var expected []swarm.Node
err := json.Unmarshal([]byte(jsonNodes), &expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: jsonNodes, status: http.StatusOK})
nodes, err := client.ListNodes(ListNodesOptions{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(nodes, expected) {
t.Errorf("ListNodes: Expected %#v. Got %#v.", expected, nodes)
}
}
func TestInspectNode(t *testing.T) {
jsonNode := `{
"ID": "24ifsmvkjbyhk",
"Version": {
"Index": 8
},
"CreatedAt": "2016-06-07T20:31:11.853781916Z",
"UpdatedAt": "2016-06-07T20:31:11.999868824Z",
"Spec": {
"Name": "my-node",
"Role": "manager",
"Availability": "active",
"Labels": {
"foo": "bar"
}
},
"Description": {
"Hostname": "bf3067039e47",
"Platform": {
"Architecture": "x86_64",
"OS": "linux"
},
"Resources": {
"NanoCPUs": 4000000000,
"MemoryBytes": 8272408576
},
"Engine": {
"EngineVersion": "1.12.0-dev",
"Labels": {
"foo": "bar"
},
"Plugins": [
{
"Type": "Volume",
"Name": "local"
},
{
"Type": "Network",
"Name": "bridge"
},
{
"Type": "Network",
"Name": "null"
},
{
"Type": "Network",
"Name": "overlay"
}
]
}
},
"Status": {
"State": "ready"
},
"ManagerStatus": {
"Leader": true,
"Reachability": "reachable",
"Addr": "172.17.0.2:2377"
}
}`
var expected swarm.Node
err := json.Unmarshal([]byte(jsonNode), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonNode, status: http.StatusOK}
client := newTestClient(fakeRT)
id := "24ifsmvkjbyhk"
node, err := client.InspectNode(id)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*node, expected) {
t.Errorf("InspectNode(%q): Expected %#v. Got %#v.", id, expected, node)
}
expectedURL, _ := url.Parse(client.getURL("/nodes/24ifsmvkjbyhk"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("InspectNode(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestInspectNodeNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
node, err := client.InspectNode("notfound")
if node != nil {
t.Errorf("InspectNode: Expected <nil> task, got %#v", node)
}
expected := &NoSuchNode{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("InspectNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}
func TestUpdateNode(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
opts := UpdateNodeOptions{}
err := client.UpdateNode(id, opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("UpdateNode: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/nodes/" + id + "/update"))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("UpdateNode: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
expectedContentType := "application/json"
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
t.Errorf("UpdateNode: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
}
var out UpdateNodeOptions
if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(out, opts) {
t.Errorf("UpdateNode: wrong body, got: %#v, want %#v", out, opts)
}
}
func TestUpdateNodeNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
err := client.UpdateNode("notfound", UpdateNodeOptions{})
expected := &NoSuchNode{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("UpdateNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}
func TestRemoveNode(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
err := client.RemoveNode(RemoveNodeOptions{ID: id})
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "DELETE" {
t.Errorf("RemoveNode(%q): wrong HTTP method. Want %q. Got %q.", id, "DELETE", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/nodes/" + id))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("RemoveNode(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestRemoveNodeNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such node", status: http.StatusNotFound})
err := client.RemoveNode(RemoveNodeOptions{ID: "notfound"})
expected := &NoSuchNode{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("RemoveNode: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}

169
vendor/github.com/fsouza/go-dockerclient/service.go generated vendored Normal file
View File

@@ -0,0 +1,169 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"strconv"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
// NoSuchService is the error returned when a given service does not exist.
type NoSuchService struct {
ID string
Err error
}
func (err *NoSuchService) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return "No such service: " + err.ID
}
// CreateServiceOptions specify parameters to the CreateService function.
//
// See https://goo.gl/KrVjHz for more details.
type CreateServiceOptions struct {
Auth AuthConfiguration `qs:"-"`
swarm.ServiceSpec
Context context.Context
}
// CreateService creates a new service, returning the service instance
// or an error in case of failure.
//
// See https://goo.gl/KrVjHz for more details.
func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error) {
headers, err := headersWithAuth(opts.Auth)
if err != nil {
return nil, err
}
path := "/services/create?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
context: opts.Context,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var service swarm.Service
if err := json.NewDecoder(resp.Body).Decode(&service); err != nil {
return nil, err
}
return &service, nil
}
// RemoveServiceOptions encapsulates options to remove a service.
//
// See https://goo.gl/Tqrtya for more details.
type RemoveServiceOptions struct {
ID string `qs:"-"`
Context context.Context
}
// RemoveService removes a service, returning an error in case of failure.
//
// See https://goo.gl/Tqrtya for more details.
func (c *Client) RemoveService(opts RemoveServiceOptions) error {
path := "/services/" + opts.ID
resp, err := c.do("DELETE", path, doOptions{context: opts.Context})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchService{ID: opts.ID}
}
return err
}
resp.Body.Close()
return nil
}
// UpdateServiceOptions specify parameters to the UpdateService function.
//
// See https://goo.gl/wu3MmS for more details.
type UpdateServiceOptions struct {
Auth AuthConfiguration `qs:"-"`
swarm.ServiceSpec
Context context.Context
Version uint64
}
// UpdateService updates the service at ID with the options
//
// See https://goo.gl/wu3MmS for more details.
func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error {
headers, err := headersWithAuth(opts.Auth)
if err != nil {
return err
}
params := make(url.Values)
params.Set("version", strconv.FormatUint(opts.Version, 10))
resp, err := c.do("POST", "/services/"+id+"/update?"+params.Encode(), doOptions{
headers: headers,
data: opts.ServiceSpec,
forceJSON: true,
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return &NoSuchService{ID: id}
}
return err
}
defer resp.Body.Close()
return nil
}
// InspectService returns information about a service by its ID.
//
// See https://goo.gl/dHmr75 for more details.
func (c *Client) InspectService(id string) (*swarm.Service, error) {
path := "/services/" + id
resp, err := c.do("GET", path, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchService{ID: id}
}
return nil, err
}
defer resp.Body.Close()
var service swarm.Service
if err := json.NewDecoder(resp.Body).Decode(&service); err != nil {
return nil, err
}
return &service, nil
}
// ListServicesOptions specify parameters to the ListServices function.
//
// See https://goo.gl/DwvNMd for more details.
type ListServicesOptions struct {
Filters map[string][]string
Context context.Context
}
// ListServices returns a slice of services matching the given criteria.
//
// See https://goo.gl/DwvNMd for more details.
func (c *Client) ListServices(opts ListServicesOptions) ([]swarm.Service, error) {
path := "/services?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var services []swarm.Service
if err := json.NewDecoder(resp.Body).Decode(&services); err != nil {
return nil, err
}
return services, nil
}

View File

@@ -0,0 +1,404 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"reflect"
"testing"
"encoding/base64"
"github.com/docker/docker/api/types/swarm"
"strings"
)
func TestCreateService(t *testing.T) {
result := `{
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
}`
var expected swarm.Service
err := json.Unmarshal([]byte(result), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: result, status: http.StatusOK}
client := newTestClient(fakeRT)
opts := CreateServiceOptions{}
service, err := client.CreateService(opts)
if err != nil {
t.Fatal(err)
}
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
if service.ID != id {
t.Errorf("CreateServce: wrong ID. Want %q. Got %q.", id, service.ID)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("CreateService: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/services/create"))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("CreateService: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
var gotBody Config
err = json.NewDecoder(req.Body).Decode(&gotBody)
if err != nil {
t.Fatal(err)
}
auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth"))
if err != nil {
t.Errorf("CreateService: caught error decoding auth. %#v", err.Error())
}
if strings.TrimSpace(string(auth)) != "{}" {
t.Errorf("CreateService: wrong body. Want %q. Got %q.",
base64.URLEncoding.EncodeToString([]byte("{}")), req.Header.Get("X-Registry-Auth"))
}
}
func TestCreateServiceWithAuthentication(t *testing.T) {
result := `{
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
}`
var expected swarm.Service
err := json.Unmarshal([]byte(result), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: result, status: http.StatusOK}
client := newTestClient(fakeRT)
opts := CreateServiceOptions{}
opts.Auth = AuthConfiguration{
Username: "gopher",
Password: "gopher123",
Email: "gopher@tsuru.io",
}
service, err := client.CreateService(opts)
if err != nil {
t.Fatal(err)
}
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
if service.ID != id {
t.Errorf("CreateServce: wrong ID. Want %q. Got %q.", id, service.ID)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("CreateService: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/services/create"))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("CreateService: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
}
var gotBody Config
err = json.NewDecoder(req.Body).Decode(&gotBody)
if err != nil {
t.Fatal(err)
}
var gotAuth AuthConfiguration
auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth"))
if err != nil {
t.Errorf("CreateService: caught error decoding auth. %#v", err.Error())
}
err = json.Unmarshal(auth, &gotAuth)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(gotAuth, opts.Auth) {
t.Errorf("CreateService: wrong auth configuration. Want %#v. Got %#v.", opts.Auth, gotAuth)
}
}
func TestRemoveService(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
opts := RemoveServiceOptions{ID: id}
err := client.RemoveService(opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "DELETE" {
t.Errorf("RemoveService(%q): wrong HTTP method. Want %q. Got %q.", id, "DELETE", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/services/" + id))
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
t.Errorf("RemoveService(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestRemoveServiceNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such service", status: http.StatusNotFound})
err := client.RemoveService(RemoveServiceOptions{ID: "a2334"})
expected := &NoSuchService{ID: "a2334"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("RemoveService: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}
func TestUpdateService(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
update := UpdateServiceOptions{Version: 23}
err := client.UpdateService(id, update)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("UpdateService: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/services/" + id + "/update?version=23"))
if gotURI := req.URL.RequestURI(); gotURI != expectedURL.RequestURI() {
t.Errorf("UpdateService: Wrong path in request. Want %q. Got %q.", expectedURL.RequestURI(), gotURI)
}
expectedContentType := "application/json"
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
t.Errorf("UpdateService: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
}
var out UpdateServiceOptions
if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
t.Fatal(err)
}
update.Version = 0
if !reflect.DeepEqual(out, update) {
t.Errorf("UpdateService: wrong body\ngot %#v\nwant %#v", out, update)
}
}
func TestUpdateServiceWithAuthentication(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
update := UpdateServiceOptions{Version: 23}
update.Auth = AuthConfiguration{
Username: "gopher",
Password: "gopher123",
Email: "gopher@tsuru.io",
}
err := client.UpdateService(id, update)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
if req.Method != "POST" {
t.Errorf("UpdateService: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
}
expectedURL, _ := url.Parse(client.getURL("/services/" + id + "/update?version=23"))
if gotURI := req.URL.RequestURI(); gotURI != expectedURL.RequestURI() {
t.Errorf("UpdateService: Wrong path in request. Want %q. Got %q.", expectedURL.RequestURI(), gotURI)
}
expectedContentType := "application/json"
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
t.Errorf("UpdateService: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
}
var out UpdateServiceOptions
if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
t.Fatal(err)
}
var updateAuth AuthConfiguration
auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth"))
if err != nil {
t.Errorf("UpdateService: caught error decoding auth. %#v", err.Error())
}
err = json.Unmarshal(auth, &updateAuth)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(updateAuth, update.Auth) {
t.Errorf("UpdateService: wrong auth configuration. Want %#v. Got %#v", update.Auth, updateAuth)
}
}
func TestUpdateServiceNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such service", status: http.StatusNotFound})
update := UpdateServiceOptions{}
err := client.UpdateService("notfound", update)
expected := &NoSuchService{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("UpdateService: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}
func TestInspectServiceNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such service", status: http.StatusNotFound})
service, err := client.InspectService("notfound")
if service != nil {
t.Errorf("InspectService: Expected <nil> service, got %#v", service)
}
expected := &NoSuchService{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("InspectService: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}
func TestInspectService(t *testing.T) {
jsonService := `{
"ID": "ak7w3gjqoa3kuz8xcpnyy0pvl",
"Version": {
"Index": 95
},
"CreatedAt": "2016-06-07T21:10:20.269723157Z",
"UpdatedAt": "2016-06-07T21:10:20.276301259Z",
"Spec": {
"Name": "redis",
"Task": {
"ContainerSpec": {
"Image": "redis"
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "ANY"
},
"Placement": {}
},
"Mode": {
"Replicated": {
"Replicas": 1
}
},
"UpdateConfig": {
"Parallelism": 1
},
"EndpointSpec": {
"Mode": "VIP",
"Ingress": "PUBLICPORT",
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379
}
]
}
},
"Endpoint": {
"Spec": {},
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379,
"PublicPort": 30001
}
],
"VirtualIPs": [
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.4/16"
}
]
}
}`
var expected swarm.Service
err := json.Unmarshal([]byte(jsonService), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonService, status: http.StatusOK}
client := newTestClient(fakeRT)
id := "ak7w3gjqoa3kuz8xcpnyy0pvl"
service, err := client.InspectService(id)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*service, expected) {
t.Errorf("InspectService(%q): Expected %#v. Got %#v.", id, expected, service)
}
expectedURL, _ := url.Parse(client.getURL("/services/ak7w3gjqoa3kuz8xcpnyy0pvl"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("InspectService(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestListServices(t *testing.T) {
jsonServices := `[
{
"ID": "9mnpnzenvg8p8tdbtq4wvbkcz",
"Version": {
"Index": 19
},
"CreatedAt": "2016-06-07T21:05:51.880065305Z",
"UpdatedAt": "2016-06-07T21:07:29.962229872Z",
"Spec": {
"Name": "hopeful_cori",
"TaskTemplate": {
"ContainerSpec": {
"Image": "redis"
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "ANY"
},
"Placement": {}
},
"Mode": {
"Replicated": {
"Replicas": 1
}
},
"UpdateConfig": {
"Parallelism": 1
},
"EndpointSpec": {
"Mode": "VIP",
"Ingress": "PUBLICPORT",
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379
}
]
}
},
"Endpoint": {
"Spec": {},
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379,
"PublicPort": 30000
}
],
"VirtualIPs": [
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.2/16"
},
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.3/16"
}
]
}
}
]`
var expected []swarm.Service
err := json.Unmarshal([]byte(jsonServices), &expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: jsonServices, status: http.StatusOK})
services, err := client.ListServices(ListServicesOptions{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(services, expected) {
t.Errorf("ListServices: Expected %#v. Got %#v.", expected, services)
}
}

49
vendor/github.com/fsouza/go-dockerclient/signal.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
// Signal represents a signal that can be send to the container on
// KillContainer call.
type Signal int
// These values represent all signals available on Linux, where containers will
// be running.
const (
SIGABRT = Signal(0x6)
SIGALRM = Signal(0xe)
SIGBUS = Signal(0x7)
SIGCHLD = Signal(0x11)
SIGCLD = Signal(0x11)
SIGCONT = Signal(0x12)
SIGFPE = Signal(0x8)
SIGHUP = Signal(0x1)
SIGILL = Signal(0x4)
SIGINT = Signal(0x2)
SIGIO = Signal(0x1d)
SIGIOT = Signal(0x6)
SIGKILL = Signal(0x9)
SIGPIPE = Signal(0xd)
SIGPOLL = Signal(0x1d)
SIGPROF = Signal(0x1b)
SIGPWR = Signal(0x1e)
SIGQUIT = Signal(0x3)
SIGSEGV = Signal(0xb)
SIGSTKFLT = Signal(0x10)
SIGSTOP = Signal(0x13)
SIGSYS = Signal(0x1f)
SIGTERM = Signal(0xf)
SIGTRAP = Signal(0x5)
SIGTSTP = Signal(0x14)
SIGTTIN = Signal(0x15)
SIGTTOU = Signal(0x16)
SIGUNUSED = Signal(0x1f)
SIGURG = Signal(0x17)
SIGUSR1 = Signal(0xa)
SIGUSR2 = Signal(0xc)
SIGVTALRM = Signal(0x1a)
SIGWINCH = Signal(0x1c)
SIGXCPU = Signal(0x18)
SIGXFSZ = Signal(0x19)
)

156
vendor/github.com/fsouza/go-dockerclient/swarm.go generated vendored Normal file
View File

@@ -0,0 +1,156 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"errors"
"net/http"
"net/url"
"strconv"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
var (
// ErrNodeAlreadyInSwarm is the error returned by InitSwarm and JoinSwarm
// when the node is already part of a Swarm.
ErrNodeAlreadyInSwarm = errors.New("node already in a Swarm")
// ErrNodeNotInSwarm is the error returned by LeaveSwarm and UpdateSwarm
// when the node is not part of a Swarm.
ErrNodeNotInSwarm = errors.New("node is not in a Swarm")
)
// InitSwarmOptions specify parameters to the InitSwarm function.
// See https://goo.gl/hzkgWu for more details.
type InitSwarmOptions struct {
swarm.InitRequest
Context context.Context
}
// InitSwarm initializes a new Swarm and returns the node ID.
// See https://goo.gl/ZWyG1M for more details.
func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) {
path := "/swarm/init"
resp, err := c.do("POST", path, doOptions{
data: opts.InitRequest,
forceJSON: true,
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
return "", ErrNodeAlreadyInSwarm
}
return "", err
}
defer resp.Body.Close()
var response string
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return "", err
}
return response, nil
}
// JoinSwarmOptions specify parameters to the JoinSwarm function.
// See https://goo.gl/TdhJWU for more details.
type JoinSwarmOptions struct {
swarm.JoinRequest
Context context.Context
}
// JoinSwarm joins an existing Swarm.
// See https://goo.gl/N59IP1 for more details.
func (c *Client) JoinSwarm(opts JoinSwarmOptions) error {
path := "/swarm/join"
resp, err := c.do("POST", path, doOptions{
data: opts.JoinRequest,
forceJSON: true,
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
return ErrNodeAlreadyInSwarm
}
}
resp.Body.Close()
return err
}
// LeaveSwarmOptions specify parameters to the LeaveSwarm function.
// See https://goo.gl/UWDlLg for more details.
type LeaveSwarmOptions struct {
Force bool
Context context.Context
}
// LeaveSwarm leaves a Swarm.
// See https://goo.gl/FTX1aD for more details.
func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error {
params := make(url.Values)
params.Set("force", strconv.FormatBool(opts.Force))
path := "/swarm/leave?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
return ErrNodeNotInSwarm
}
}
resp.Body.Close()
return err
}
// UpdateSwarmOptions specify parameters to the UpdateSwarm function.
// See https://goo.gl/vFbq36 for more details.
type UpdateSwarmOptions struct {
Version int
RotateWorkerToken bool
RotateManagerToken bool
Swarm swarm.Spec
Context context.Context
}
// UpdateSwarm updates a Swarm.
// See https://goo.gl/iJFnsw for more details.
func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error {
params := make(url.Values)
params.Set("version", strconv.Itoa(opts.Version))
params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken))
params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken))
path := "/swarm/update?" + params.Encode()
resp, err := c.do("POST", path, doOptions{
data: opts.Swarm,
forceJSON: true,
context: opts.Context,
})
if err != nil {
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
return ErrNodeNotInSwarm
}
}
resp.Body.Close()
return err
}
// InspectSwarm inspects a Swarm.
// See https://goo.gl/MFwgX9 for more details.
func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) {
response := swarm.Swarm{}
resp, err := c.do("GET", "/swarm", doOptions{
context: ctx,
})
if err != nil {
if e, ok := err.(*Error); ok && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) {
return response, ErrNodeNotInSwarm
}
return response, err
}
defer resp.Body.Close()
err = json.NewDecoder(resp.Body).Decode(&response)
return response, err
}

200
vendor/github.com/fsouza/go-dockerclient/swarm_test.go generated vendored Normal file
View File

@@ -0,0 +1,200 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"net/http"
"net/url"
"reflect"
"testing"
"github.com/docker/docker/api/types/swarm"
)
func TestInitSwarm(t *testing.T) {
fakeRT := &FakeRoundTripper{message: `"body"`, status: http.StatusOK}
client := newTestClient(fakeRT)
response, err := client.InitSwarm(InitSwarmOptions{})
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("InitSwarm: Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/swarm/init"))
if req.URL.Path != u.Path {
t.Errorf("InitSwarm: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
expected := "body"
if response != expected {
t.Errorf("InitSwarm: Wrong response. Want %q. Got %q.", expected, response)
}
}
func TestInitSwarmAlreadyInSwarm(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusNotAcceptable})
_, err := client.InitSwarm(InitSwarmOptions{})
if err != ErrNodeAlreadyInSwarm {
t.Errorf("InitSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeAlreadyInSwarm, err)
}
client = newTestClient(&FakeRoundTripper{message: "", status: http.StatusServiceUnavailable})
_, err = client.InitSwarm(InitSwarmOptions{})
if err != ErrNodeAlreadyInSwarm {
t.Errorf("InitSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeAlreadyInSwarm, err)
}
}
func TestJoinSwarm(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
err := client.JoinSwarm(JoinSwarmOptions{})
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("JoinSwarm: Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/swarm/join"))
if req.URL.Path != u.Path {
t.Errorf("JoinSwarm: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
}
func TestJoinSwarmAlreadyInSwarm(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusNotAcceptable})
err := client.JoinSwarm(JoinSwarmOptions{})
if err != ErrNodeAlreadyInSwarm {
t.Errorf("JoinSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeAlreadyInSwarm, err)
}
client = newTestClient(&FakeRoundTripper{message: "", status: http.StatusServiceUnavailable})
err = client.JoinSwarm(JoinSwarmOptions{})
if err != ErrNodeAlreadyInSwarm {
t.Errorf("JoinSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeAlreadyInSwarm, err)
}
}
func TestLeaveSwarm(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
var testData = []struct {
force bool
expectedURI string
}{
{false, "/swarm/leave?force=false"},
{true, "/swarm/leave?force=true"},
}
for i, tt := range testData {
err := client.LeaveSwarm(LeaveSwarmOptions{Force: tt.force})
if err != nil {
t.Fatal(err)
}
expectedMethod := "POST"
req := fakeRT.requests[i]
if req.Method != expectedMethod {
t.Errorf("LeaveSwarm: Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
expected, _ := url.Parse(client.getURL(tt.expectedURI))
if req.URL.String() != expected.String() {
t.Errorf("LeaveSwarm: Wrong request string. Want %q. Got %q.", expected.String(), req.URL.String())
}
}
}
func TestLeaveSwarmNotInSwarm(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusNotAcceptable})
err := client.LeaveSwarm(LeaveSwarmOptions{})
if err != ErrNodeNotInSwarm {
t.Errorf("LeaveSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
client = newTestClient(&FakeRoundTripper{message: "", status: http.StatusServiceUnavailable})
err = client.LeaveSwarm(LeaveSwarmOptions{})
if err != ErrNodeNotInSwarm {
t.Errorf("LeaveSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
}
func TestUpdateSwarm(t *testing.T) {
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
client := newTestClient(fakeRT)
opts := UpdateSwarmOptions{
Version: 10,
RotateManagerToken: true,
RotateWorkerToken: false,
}
err := client.UpdateSwarm(opts)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("UpdateSwarm: Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
expectedPath := "/swarm/update"
if req.URL.Path != expectedPath {
t.Errorf("UpdateSwarm: Wrong request path. Want %q. Got %q.", expectedPath, req.URL.Path)
}
expected := map[string][]string{
"version": {"10"},
"rotateManagerToken": {"true"},
"rotateWorkerToken": {"false"},
}
got := map[string][]string(req.URL.Query())
if !reflect.DeepEqual(got, expected) {
t.Errorf("UpdateSwarm: Wrong request query. Want %v. Got %v", expected, got)
}
}
func TestUpdateSwarmNotInSwarm(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusNotAcceptable})
err := client.UpdateSwarm(UpdateSwarmOptions{})
if err != ErrNodeNotInSwarm {
t.Errorf("UpdateSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
client = newTestClient(&FakeRoundTripper{message: "", status: http.StatusServiceUnavailable})
err = client.UpdateSwarm(UpdateSwarmOptions{})
if err != ErrNodeNotInSwarm {
t.Errorf("UpdateSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
}
func TestInspectSwarm(t *testing.T) {
fakeRT := &FakeRoundTripper{message: `{"ID": "123"}`, status: http.StatusOK}
client := newTestClient(fakeRT)
response, err := client.InspectSwarm(nil)
if err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "GET"
if req.Method != expectedMethod {
t.Errorf("InspectSwarm: Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/swarm"))
if req.URL.Path != u.Path {
t.Errorf("InspectSwarm: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
expected := swarm.Swarm{ClusterInfo: swarm.ClusterInfo{ID: "123"}}
if !reflect.DeepEqual(expected, response) {
t.Errorf("InspectSwarm: Wrong response. Want %#v. Got %#v.", expected, response)
}
}
func TestInspectSwarmNotInSwarm(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "", status: http.StatusNotAcceptable})
_, err := client.InspectSwarm(nil)
if err != ErrNodeNotInSwarm {
t.Errorf("InspectSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
client = newTestClient(&FakeRoundTripper{message: "", status: http.StatusServiceUnavailable})
_, err = client.InspectSwarm(nil)
if err != ErrNodeNotInSwarm {
t.Errorf("InspectSwarm: Wrong error type. Want %#v. Got %#v", ErrNodeNotInSwarm, err)
}
}

117
vendor/github.com/fsouza/go-dockerclient/tar.go generated vendored Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2014 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/fileutils"
)
func createTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) {
excludes, err := parseDockerignore(srcPath)
if err != nil {
return nil, err
}
includes := []string{"."}
// If .dockerignore mentions .dockerignore or the Dockerfile
// then make sure we send both files over to the daemon
// because Dockerfile is, obviously, needed no matter what, and
// .dockerignore is needed to know if either one needs to be
// removed. The deamon will remove them for us, if needed, after it
// parses the Dockerfile.
//
// https://github.com/docker/docker/issues/8330
//
forceIncludeFiles := []string{".dockerignore", dockerfilePath}
for _, includeFile := range forceIncludeFiles {
if includeFile == "" {
continue
}
keepThem, err := fileutils.Matches(includeFile, excludes)
if err != nil {
return nil, fmt.Errorf("cannot match .dockerfile: '%s', error: %s", includeFile, err)
}
if keepThem {
includes = append(includes, includeFile)
}
}
if err := validateContextDirectory(srcPath, excludes); err != nil {
return nil, err
}
tarOpts := &archive.TarOptions{
ExcludePatterns: excludes,
IncludeFiles: includes,
Compression: archive.Uncompressed,
NoLchown: true,
}
return archive.TarWithOptions(srcPath, tarOpts)
}
// validateContextDirectory checks if all the contents of the directory
// can be read and returns an error if some files can't be read.
// Symlinks which point to non-existing files don't trigger an error
func validateContextDirectory(srcPath string, excludes []string) error {
return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error {
// skip this directory/file if it's not in the path, it won't get added to the context
if relFilePath, relErr := filepath.Rel(srcPath, filePath); relErr != nil {
return relErr
} else if skip, matchErr := fileutils.Matches(relFilePath, excludes); matchErr != nil {
return matchErr
} else if skip {
if f.IsDir() {
return filepath.SkipDir
}
return nil
}
if err != nil {
if os.IsPermission(err) {
return fmt.Errorf("can't stat '%s'", filePath)
}
if os.IsNotExist(err) {
return nil
}
return err
}
// skip checking if symlinks point to non-existing files, such symlinks can be useful
// also skip named pipes, because they hanging on open
if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 {
return nil
}
if !f.IsDir() {
currentFile, err := os.Open(filePath)
if err != nil && os.IsPermission(err) {
return fmt.Errorf("no permission to read from '%s'", filePath)
}
currentFile.Close()
}
return nil
})
}
func parseDockerignore(root string) ([]string, error) {
var excludes []string
ignore, err := ioutil.ReadFile(path.Join(root, ".dockerignore"))
if err != nil && !os.IsNotExist(err) {
return excludes, fmt.Errorf("error reading .dockerignore: '%s'", err)
}
excludes = strings.Split(string(ignore), "\n")
return excludes, nil
}

70
vendor/github.com/fsouza/go-dockerclient/task.go generated vendored Normal file
View File

@@ -0,0 +1,70 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"github.com/docker/docker/api/types/swarm"
"golang.org/x/net/context"
)
// NoSuchTask is the error returned when a given task does not exist.
type NoSuchTask struct {
ID string
Err error
}
func (err *NoSuchTask) Error() string {
if err.Err != nil {
return err.Err.Error()
}
return "No such task: " + err.ID
}
// ListTasksOptions specify parameters to the ListTasks function.
//
// See http://goo.gl/rByLzw for more details.
type ListTasksOptions struct {
Filters map[string][]string
Context context.Context
}
// ListTasks returns a slice of tasks matching the given criteria.
//
// See http://goo.gl/rByLzw for more details.
func (c *Client) ListTasks(opts ListTasksOptions) ([]swarm.Task, error) {
path := "/tasks?" + queryString(opts)
resp, err := c.do("GET", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var tasks []swarm.Task
if err := json.NewDecoder(resp.Body).Decode(&tasks); err != nil {
return nil, err
}
return tasks, nil
}
// InspectTask returns information about a task by its ID.
//
// See http://goo.gl/kyziuq for more details.
func (c *Client) InspectTask(id string) (*swarm.Task, error) {
resp, err := c.do("GET", "/tasks/"+id, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, &NoSuchTask{ID: id}
}
return nil, err
}
defer resp.Body.Close()
var task swarm.Task
if err := json.NewDecoder(resp.Body).Decode(&task); err != nil {
return nil, err
}
return &task, nil
}

373
vendor/github.com/fsouza/go-dockerclient/task_test.go generated vendored Normal file
View File

@@ -0,0 +1,373 @@
// Copyright 2016 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"reflect"
"testing"
"github.com/docker/docker/api/types/swarm"
)
func TestListTasks(t *testing.T) {
jsonTasks := `[
{
"ID": "0kzzo1i0y4jz6027t0k7aezc7",
"Version": {
"Index": 71
},
"CreatedAt": "2016-06-07T21:07:31.171892745Z",
"UpdatedAt": "2016-06-07T21:07:31.376370513Z",
"Name": "hopeful_cori",
"Spec": {
"ContainerSpec": {
"Image": "redis"
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "ANY"
},
"Placement": {}
},
"ServiceID": "9mnpnzenvg8p8tdbtq4wvbkcz",
"Instance": 1,
"NodeID": "24ifsmvkjbyhk",
"ServiceAnnotations": {},
"Status": {
"Timestamp": "2016-06-07T21:07:31.290032978Z",
"State": "FAILED",
"Message": "execution failed",
"ContainerStatus": {}
},
"DesiredState": "SHUTDOWN",
"NetworksAttachments": [
{
"Network": {
"ID": "4qvuz4ko70xaltuqbt8956gd1",
"Version": {
"Index": 18
},
"CreatedAt": "2016-06-07T20:31:11.912919752Z",
"UpdatedAt": "2016-06-07T21:07:29.955277358Z",
"Spec": {
"Name": "ingress",
"Labels": {
"com.docker.swarm.internal": "true"
},
"DriverConfiguration": {},
"IPAM": {
"Driver": {},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"DriverState": {
"Name": "overlay",
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "256"
}
},
"IPAM": {
"Driver": {
"Name": "default"
},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"Addresses": [
"10.255.0.10/16"
]
}
],
"Endpoint": {
"Spec": {},
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379,
"PublicPort": 30000
}
],
"VirtualIPs": [
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.2/16"
},
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.3/16"
}
]
}
},
{
"ID": "1yljwbmlr8er2waf8orvqpwms",
"Version": {
"Index": 30
},
"CreatedAt": "2016-06-07T21:07:30.019104782Z",
"UpdatedAt": "2016-06-07T21:07:30.231958098Z",
"Name": "hopeful_cori",
"Spec": {
"ContainerSpec": {
"Image": "redis"
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "ANY"
},
"Placement": {}
},
"ServiceID": "9mnpnzenvg8p8tdbtq4wvbkcz",
"Instance": 1,
"NodeID": "24ifsmvkjbyhk",
"ServiceAnnotations": {},
"Status": {
"Timestamp": "2016-06-07T21:07:30.202183143Z",
"State": "FAILED",
"Message": "execution failed",
"ContainerStatus": {}
},
"DesiredState": "SHUTDOWN",
"NetworksAttachments": [
{
"Network": {
"ID": "4qvuz4ko70xaltuqbt8956gd1",
"Version": {
"Index": 18
},
"CreatedAt": "2016-06-07T20:31:11.912919752Z",
"UpdatedAt": "2016-06-07T21:07:29.955277358Z",
"Spec": {
"Name": "ingress",
"Labels": {
"com.docker.swarm.internal": "true"
},
"DriverConfiguration": {},
"IPAM": {
"Driver": {},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"DriverState": {
"Name": "overlay",
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "256"
}
},
"IPAM": {
"Driver": {
"Name": "default"
},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"Addresses": [
"10.255.0.5/16"
]
}
],
"Endpoint": {
"Spec": {},
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379,
"PublicPort": 30000
}
],
"VirtualIPs": [
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.2/16"
},
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.3/16"
}
]
}
}
]`
var expected []swarm.Task
err := json.Unmarshal([]byte(jsonTasks), &expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: jsonTasks, status: http.StatusOK})
tasks, err := client.ListTasks(ListTasksOptions{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tasks, expected) {
t.Errorf("ListTasks: Expected %#v. Got %#v.", expected, tasks)
}
}
func TestInspectTask(t *testing.T) {
jsonTask := `{
"ID": "0kzzo1i0y4jz6027t0k7aezc7",
"Version": {
"Index": 71
},
"CreatedAt": "2016-06-07T21:07:31.171892745Z",
"UpdatedAt": "2016-06-07T21:07:31.376370513Z",
"Name": "hopeful_cori",
"Spec": {
"ContainerSpec": {
"Image": "redis"
},
"Resources": {
"Limits": {},
"Reservations": {}
},
"RestartPolicy": {
"Condition": "ANY"
},
"Placement": {}
},
"ServiceID": "9mnpnzenvg8p8tdbtq4wvbkcz",
"Instance": 1,
"NodeID": "24ifsmvkjbyhk",
"ServiceAnnotations": {},
"Status": {
"Timestamp": "2016-06-07T21:07:31.290032978Z",
"State": "FAILED",
"Message": "execution failed",
"ContainerStatus": {}
},
"DesiredState": "SHUTDOWN",
"NetworksAttachments": [
{
"Network": {
"ID": "4qvuz4ko70xaltuqbt8956gd1",
"Version": {
"Index": 18
},
"CreatedAt": "2016-06-07T20:31:11.912919752Z",
"UpdatedAt": "2016-06-07T21:07:29.955277358Z",
"Spec": {
"Name": "ingress",
"Labels": {
"com.docker.swarm.internal": "true"
},
"DriverConfiguration": {},
"IPAM": {
"Driver": {},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"DriverState": {
"Name": "overlay",
"Options": {
"com.docker.network.driver.overlay.vxlanid_list": "256"
}
},
"IPAM": {
"Driver": {
"Name": "default"
},
"Configs": [
{
"Family": "UNKNOWN",
"Subnet": "10.255.0.0/16"
}
]
}
},
"Addresses": [
"10.255.0.10/16"
]
}
],
"Endpoint": {
"Spec": {},
"ExposedPorts": [
{
"Protocol": "tcp",
"Port": 6379,
"PublicPort": 30000
}
],
"VirtualIPs": [
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.2/16"
},
{
"NetworkID": "4qvuz4ko70xaltuqbt8956gd1",
"Addr": "10.255.0.3/16"
}
]
}
}`
var expected swarm.Task
err := json.Unmarshal([]byte(jsonTask), &expected)
if err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: jsonTask, status: http.StatusOK}
client := newTestClient(fakeRT)
id := "0kzzo1i0y4jz6027t0k7aezc7"
task, err := client.InspectTask(id)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*task, expected) {
t.Errorf("InspectTask(%q): Expected %#v. Got %#v.", id, expected, task)
}
expectedURL, _ := url.Parse(client.getURL("/tasks/0kzzo1i0y4jz6027t0k7aezc7"))
if gotPath := fakeRT.requests[0].URL.Path; gotPath != expectedURL.Path {
t.Errorf("InspectTask(%q): Wrong path in request. Want %q. Got %q.", id, expectedURL.Path, gotPath)
}
}
func TestInspectTaskNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such task", status: http.StatusNotFound})
task, err := client.InspectTask("notfound")
if task != nil {
t.Errorf("InspectTask: Expected <nil> task, got %#v", task)
}
expected := &NoSuchTask{ID: "notfound"}
if !reflect.DeepEqual(err, expected) {
t.Errorf("InspectTask: Wrong error returned. Want %#v. Got %#v.", expected, err)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,685 @@
// Copyright 2016 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 (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"strings"
"time"
"github.com/docker/docker/api/types/swarm"
"github.com/fsouza/go-dockerclient"
"github.com/gorilla/mux"
)
type swarmServer struct {
srv *DockerServer
mux *mux.Router
listener net.Listener
}
func newSwarmServer(srv *DockerServer, bind string) (*swarmServer, error) {
listener, err := net.Listen("tcp", bind)
if err != nil {
return nil, err
}
router := mux.NewRouter()
router.Path("/internal/updatenodes").Methods("POST").HandlerFunc(srv.handlerWrapper(srv.internalUpdateNodes))
server := &swarmServer{
listener: listener,
mux: router,
srv: srv,
}
go http.Serve(listener, router)
return server, nil
}
func (s *swarmServer) URL() string {
if s.listener == nil {
return ""
}
return "http://" + s.listener.Addr().String() + "/"
}
// MutateTask changes a task, returning an error if the given id does not match
// to any task in the server.
func (s *DockerServer) MutateTask(id string, newTask swarm.Task) error {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
for i, task := range s.tasks {
if task.ID == id {
s.tasks[i] = &newTask
return nil
}
}
return errors.New("task not found")
}
func (s *DockerServer) swarmInit(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm != nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
var req swarm.InitRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil && err != io.EOF {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
node, err := s.initSwarmNode(req.ListenAddr, req.AdvertiseAddr)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
node.ManagerStatus.Leader = true
err = s.runNodeOperation(s.swarmServer.URL(), nodeOperation{
Op: "add",
Node: node,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
s.swarm = &swarm.Swarm{
JoinTokens: swarm.JoinTokens{
Manager: s.generateID(),
Worker: s.generateID(),
},
}
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(s.nodeID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (s *DockerServer) swarmInspect(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
} else {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(s.swarm)
}
}
func (s *DockerServer) swarmJoin(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm != nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
var req swarm.JoinRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if len(req.RemoteAddrs) == 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
node, err := s.initSwarmNode(req.ListenAddr, req.AdvertiseAddr)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
s.swarm = &swarm.Swarm{
JoinTokens: swarm.JoinTokens{
Manager: s.generateID(),
Worker: s.generateID(),
},
}
s.swarmMut.Unlock()
err = s.runNodeOperation(fmt.Sprintf("http://%s", req.RemoteAddrs[0]), nodeOperation{
Op: "add",
Node: node,
forceLock: true,
})
s.swarmMut.Lock()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func (s *DockerServer) swarmLeave(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
} else {
s.swarmServer.listener.Close()
s.swarm = nil
s.nodes = nil
s.swarmServer = nil
s.nodeID = ""
w.WriteHeader(http.StatusOK)
}
}
func (s *DockerServer) containerForService(srv *swarm.Service, name string) *docker.Container {
hostConfig := docker.HostConfig{}
dockerConfig := docker.Config{
Entrypoint: srv.Spec.TaskTemplate.ContainerSpec.Command,
Cmd: srv.Spec.TaskTemplate.ContainerSpec.Args,
Env: srv.Spec.TaskTemplate.ContainerSpec.Env,
}
return &docker.Container{
ID: s.generateID(),
Name: name,
Image: srv.Spec.TaskTemplate.ContainerSpec.Image,
Created: time.Now(),
Config: &dockerConfig,
HostConfig: &hostConfig,
State: docker.State{
Running: true,
StartedAt: time.Now(),
Pid: rand.Int() % 50000,
ExitCode: 0,
},
}
}
func (s *DockerServer) serviceCreate(w http.ResponseWriter, r *http.Request) {
var config swarm.ServiceSpec
defer r.Body.Close()
err := json.NewDecoder(r.Body).Decode(&config)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
s.cMut.Lock()
defer s.cMut.Unlock()
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if len(s.nodes) == 0 || s.swarm == nil {
http.Error(w, "no swarm nodes available", http.StatusNotAcceptable)
return
}
if config.Name == "" {
config.Name = s.generateID()
}
for _, s := range s.services {
if s.Spec.Name == config.Name {
http.Error(w, "there's already a service with this name", http.StatusConflict)
return
}
}
service := swarm.Service{
ID: s.generateID(),
Spec: config,
}
s.setServiceEndpoint(&service)
s.addTasks(&service, false)
s.services = append(s.services, &service)
err = s.runNodeOperation(s.swarmServer.URL(), nodeOperation{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(service)
}
func (s *DockerServer) setServiceEndpoint(service *swarm.Service) {
if service.Spec.EndpointSpec == nil {
return
}
service.Endpoint = swarm.Endpoint{
Spec: *service.Spec.EndpointSpec,
}
for _, port := range service.Spec.EndpointSpec.Ports {
if port.PublishedPort == 0 {
port.PublishedPort = uint32(30000 + s.servicePorts)
s.servicePorts++
}
service.Endpoint.Ports = append(service.Endpoint.Ports, port)
}
}
func (s *DockerServer) addTasks(service *swarm.Service, update bool) {
containerCount := 1
if service.Spec.Mode.Global != nil {
containerCount = len(s.nodes)
} else if repl := service.Spec.Mode.Replicated; repl != nil {
if repl.Replicas != nil {
containerCount = int(*repl.Replicas)
}
}
for i := 0; i < containerCount; i++ {
name := fmt.Sprintf("%s-%d", service.Spec.Name, i)
if update {
name = fmt.Sprintf("%s-%d-updated", service.Spec.Name, i)
}
container := s.containerForService(service, name)
chosenNode := s.nodes[s.nodeRR]
s.nodeRR = (s.nodeRR + 1) % len(s.nodes)
task := swarm.Task{
ID: s.generateID(),
ServiceID: service.ID,
NodeID: chosenNode.ID,
Status: swarm.TaskStatus{
State: swarm.TaskStateReady,
ContainerStatus: swarm.ContainerStatus{
ContainerID: container.ID,
},
},
DesiredState: swarm.TaskStateReady,
Spec: service.Spec.TaskTemplate,
}
s.tasks = append(s.tasks, &task)
s.containers = append(s.containers, container)
s.notify(container)
}
}
func (s *DockerServer) serviceInspect(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
for _, srv := range s.services {
if srv.ID == id || srv.Spec.Name == id {
json.NewEncoder(w).Encode(srv)
return
}
}
http.Error(w, "service not found", http.StatusNotFound)
}
func (s *DockerServer) taskInspect(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
for _, task := range s.tasks {
if task.ID == id {
json.NewEncoder(w).Encode(task)
return
}
}
http.Error(w, "task not found", http.StatusNotFound)
}
func (s *DockerServer) serviceList(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
filtersRaw := r.FormValue("filters")
var filters map[string][]string
json.Unmarshal([]byte(filtersRaw), &filters)
if filters == nil {
json.NewEncoder(w).Encode(s.services)
return
}
var ret []*swarm.Service
for i, srv := range s.services {
if inFilter(filters["id"], srv.ID) &&
inFilter(filters["name"], srv.Spec.Name) {
ret = append(ret, s.services[i])
}
}
json.NewEncoder(w).Encode(ret)
}
func (s *DockerServer) taskList(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
filtersRaw := r.FormValue("filters")
var filters map[string][]string
json.Unmarshal([]byte(filtersRaw), &filters)
if filters == nil {
json.NewEncoder(w).Encode(s.tasks)
return
}
var ret []*swarm.Task
for i, task := range s.tasks {
var srv *swarm.Service
for _, srv = range s.services {
if task.ServiceID == srv.ID {
break
}
}
if srv == nil {
http.Error(w, "service not found", http.StatusNotFound)
return
}
if inFilter(filters["id"], task.ID) &&
(inFilter(filters["service"], task.ServiceID) ||
inFilter(filters["service"], srv.Spec.Annotations.Name)) &&
inFilter(filters["node"], task.NodeID) &&
inFilter(filters["desired-state"], string(task.DesiredState)) &&
inLabelFilter(filters["label"], srv.Spec.Annotations.Labels) {
ret = append(ret, s.tasks[i])
}
}
json.NewEncoder(w).Encode(ret)
}
func inLabelFilter(list []string, labels map[string]string) bool {
if len(list) == 0 {
return true
}
for _, item := range list {
parts := strings.Split(item, "=")
key := parts[0]
if val, ok := labels[key]; ok {
if len(parts) > 1 && val != parts[1] {
continue
}
return true
}
}
return false
}
func inFilter(list []string, wanted string) bool {
if len(list) == 0 {
return true
}
for _, item := range list {
if item == wanted {
return true
}
}
return false
}
func (s *DockerServer) serviceDelete(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
s.cMut.Lock()
defer s.cMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
var i int
var toDelete *swarm.Service
for i = range s.services {
if s.services[i].ID == id || s.services[i].Spec.Name == id {
toDelete = s.services[i]
break
}
}
if toDelete == nil {
http.Error(w, "service not found", http.StatusNotFound)
return
}
s.services[i] = s.services[len(s.services)-1]
s.services = s.services[:len(s.services)-1]
for i := 0; i < len(s.tasks); i++ {
if s.tasks[i].ServiceID == toDelete.ID {
_, contIdx, _ := s.findContainerWithLock(s.tasks[i].Status.ContainerStatus.ContainerID, false)
if contIdx != -1 {
s.containers = append(s.containers[:contIdx], s.containers[contIdx+1:]...)
}
s.tasks = append(s.tasks[:i], s.tasks[i+1:]...)
i--
}
}
err := s.runNodeOperation(s.swarmServer.URL(), nodeOperation{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *DockerServer) serviceUpdate(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
s.cMut.Lock()
defer s.cMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
var toUpdate *swarm.Service
for i := range s.services {
if s.services[i].ID == id || s.services[i].Spec.Name == id {
toUpdate = s.services[i]
break
}
}
if toUpdate == nil {
http.Error(w, "service not found", http.StatusNotFound)
return
}
var newSpec swarm.ServiceSpec
err := json.NewDecoder(r.Body).Decode(&newSpec)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
toUpdate.Spec = newSpec
s.setServiceEndpoint(toUpdate)
for i := 0; i < len(s.tasks); i++ {
if s.tasks[i].ServiceID != toUpdate.ID {
continue
}
_, contIdx, _ := s.findContainerWithLock(s.tasks[i].Status.ContainerStatus.ContainerID, false)
if contIdx != -1 {
s.containers = append(s.containers[:contIdx], s.containers[contIdx+1:]...)
}
s.tasks = append(s.tasks[:i], s.tasks[i+1:]...)
i--
}
s.addTasks(toUpdate, true)
err = s.runNodeOperation(s.swarmServer.URL(), nodeOperation{})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *DockerServer) nodeUpdate(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
var n *swarm.Node
for i := range s.nodes {
if s.nodes[i].ID == id {
n = &s.nodes[i]
break
}
}
if n == nil {
w.WriteHeader(http.StatusNotFound)
return
}
var spec swarm.NodeSpec
err := json.NewDecoder(r.Body).Decode(&spec)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
n.Spec = spec
err = s.runNodeOperation(s.swarmServer.URL(), nodeOperation{
Op: "update",
Node: *n,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *DockerServer) nodeDelete(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
err := s.runNodeOperation(s.swarmServer.URL(), nodeOperation{
Op: "delete",
Node: swarm.Node{
ID: id,
},
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (s *DockerServer) nodeInspect(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
id := mux.Vars(r)["id"]
for _, n := range s.nodes {
if n.ID == id {
err := json.NewEncoder(w).Encode(n)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return
}
}
w.WriteHeader(http.StatusNotFound)
}
func (s *DockerServer) nodeList(w http.ResponseWriter, r *http.Request) {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
if s.swarm == nil {
w.WriteHeader(http.StatusNotAcceptable)
return
}
err := json.NewEncoder(w).Encode(s.nodes)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
type nodeOperation struct {
Op string
Node swarm.Node
Tasks []*swarm.Task
Services []*swarm.Service
forceLock bool
}
func (s *DockerServer) runNodeOperation(dst string, nodeOp nodeOperation) error {
data, err := json.Marshal(nodeOp)
if err != nil {
return err
}
url := fmt.Sprintf("%s/internal/updatenodes", strings.TrimRight(dst, "/"))
if nodeOp.forceLock {
url += "?forcelock=1"
}
rsp, err := http.Post(url, "application/json", bytes.NewReader(data))
if err != nil {
return err
}
if rsp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status code in updatenodes: %d", rsp.StatusCode)
}
return json.NewDecoder(rsp.Body).Decode(&s.nodes)
}
func (s *DockerServer) internalUpdateNodes(w http.ResponseWriter, r *http.Request) {
propagate := r.URL.Query().Get("propagate") != "0"
if !propagate || r.URL.Query().Get("forcelock") != "" {
s.swarmMut.Lock()
defer s.swarmMut.Unlock()
}
data, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var nodeOp nodeOperation
err = json.Unmarshal(data, &nodeOp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
switch nodeOp.Op {
case "add":
s.nodes = append(s.nodes, nodeOp.Node)
case "update":
for i, n := range s.nodes {
if n.ID == nodeOp.Node.ID {
s.nodes[i] = nodeOp.Node
break
}
}
case "delete":
for i, n := range s.nodes {
if n.ID == nodeOp.Node.ID {
s.nodes = append(s.nodes[:i], s.nodes[i+1:]...)
break
}
}
}
if propagate {
nodeOp.Services = s.services
nodeOp.Tasks = s.tasks
data, _ = json.Marshal(nodeOp)
for _, node := range s.nodes {
if s.nodeID == node.ID {
continue
}
url := fmt.Sprintf("http://%s/internal/updatenodes?propagate=0", node.ManagerStatus.Addr)
_, err = http.Post(url, "application/json", bytes.NewReader(data))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
}
if nodeOp.Services != nil {
s.services = nodeOp.Services
}
if nodeOp.Tasks != nil {
s.tasks = nodeOp.Tasks
}
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(s.nodes)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

File diff suppressed because it is too large Load Diff

118
vendor/github.com/fsouza/go-dockerclient/tls.go generated vendored Normal file
View File

@@ -0,0 +1,118 @@
// 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.
//
// The content is borrowed from Docker's own source code to provide a simple
// tls based dialer
package docker
import (
"crypto/tls"
"errors"
"net"
"strings"
"time"
)
type tlsClientCon struct {
*tls.Conn
rawConn net.Conn
}
func (c *tlsClientCon) CloseWrite() error {
// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
// on its underlying connection.
if cwc, ok := c.rawConn.(interface {
CloseWrite() error
}); ok {
return cwc.CloseWrite()
}
return nil
}
func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
// We want the Timeout and Deadline values from dialer to cover the
// whole process: TCP connection and TLS handshake. This means that we
// also need to start our own timers now.
timeout := dialer.Timeout
if !dialer.Deadline.IsZero() {
deadlineTimeout := dialer.Deadline.Sub(time.Now())
if timeout == 0 || deadlineTimeout < timeout {
timeout = deadlineTimeout
}
}
var errChannel chan error
if timeout != 0 {
errChannel = make(chan error, 2)
time.AfterFunc(timeout, func() {
errChannel <- errors.New("")
})
}
rawConn, err := dialer.Dial(network, addr)
if err != nil {
return nil, err
}
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
hostname := addr[:colonPos]
// If no ServerName is set, infer the ServerName
// from the hostname we're connecting to.
if config.ServerName == "" {
// Make a copy to avoid polluting argument or default.
config = copyTLSConfig(config)
config.ServerName = hostname
}
conn := tls.Client(rawConn, config)
if timeout == 0 {
err = conn.Handshake()
} else {
go func() {
errChannel <- conn.Handshake()
}()
err = <-errChannel
}
if err != nil {
rawConn.Close()
return nil, err
}
// This is Docker difference with standard's crypto/tls package: returned a
// wrapper which holds both the TLS and raw connections.
return &tlsClientCon{conn, rawConn}, nil
}
// this exists to silent an error message in go vet
func copyTLSConfig(cfg *tls.Config) *tls.Config {
return &tls.Config{
Certificates: cfg.Certificates,
CipherSuites: cfg.CipherSuites,
ClientAuth: cfg.ClientAuth,
ClientCAs: cfg.ClientCAs,
ClientSessionCache: cfg.ClientSessionCache,
CurvePreferences: cfg.CurvePreferences,
InsecureSkipVerify: cfg.InsecureSkipVerify,
MaxVersion: cfg.MaxVersion,
MinVersion: cfg.MinVersion,
NameToCertificate: cfg.NameToCertificate,
NextProtos: cfg.NextProtos,
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
Rand: cfg.Rand,
RootCAs: cfg.RootCAs,
ServerName: cfg.ServerName,
SessionTicketKey: cfg.SessionTicketKey,
SessionTicketsDisabled: cfg.SessionTicketsDisabled,
}
}

View File

@@ -0,0 +1,19 @@
#!/bin/bash -x
# Copyright 2016 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.
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
sudo stop docker || true
sudo rm -rf /var/lib/docker
sudo rm -f `which docker`
set -e
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty testing" | sudo tee -a /etc/apt/sources.list.d/docker.list
sudo apt-get update
sudo apt-get install docker-engine=${DOCKER_VERSION}-0~ubuntu-$(lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
sudo start docker || true
fi

View File

@@ -0,0 +1,11 @@
#!/bin/bash -ex
# Copyright 2016 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.
make test
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
make integration
fi

171
vendor/github.com/fsouza/go-dockerclient/volume.go generated vendored Normal file
View File

@@ -0,0 +1,171 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"errors"
"net/http"
"golang.org/x/net/context"
)
var (
// ErrNoSuchVolume is the error returned when the volume does not exist.
ErrNoSuchVolume = errors.New("no such volume")
// ErrVolumeInUse is the error returned when the volume requested to be removed is still in use.
ErrVolumeInUse = errors.New("volume in use and cannot be removed")
)
// Volume represents a volume.
//
// See https://goo.gl/FZA4BK for more details.
type Volume struct {
Name string `json:"Name" yaml:"Name" toml:"Name"`
Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"`
Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty" toml:"Mountpoint,omitempty"`
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"`
}
// ListVolumesOptions specify parameters to the ListVolumes function.
//
// See https://goo.gl/FZA4BK for more details.
type ListVolumesOptions struct {
Filters map[string][]string
Context context.Context
}
// ListVolumes returns a list of available volumes in the server.
//
// See https://goo.gl/FZA4BK for more details.
func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) {
resp, err := c.do("GET", "/volumes?"+queryString(opts), doOptions{
context: opts.Context,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
m := make(map[string]interface{})
if err = json.NewDecoder(resp.Body).Decode(&m); err != nil {
return nil, err
}
var volumes []Volume
volumesJSON, ok := m["Volumes"]
if !ok {
return volumes, nil
}
data, err := json.Marshal(volumesJSON)
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, &volumes); err != nil {
return nil, err
}
return volumes, nil
}
// CreateVolumeOptions specify parameters to the CreateVolume function.
//
// See https://goo.gl/pBUbZ9 for more details.
type CreateVolumeOptions struct {
Name string
Driver string
DriverOpts map[string]string
Context context.Context `json:"-"`
Labels map[string]string
}
// CreateVolume creates a volume on the server.
//
// See https://goo.gl/pBUbZ9 for more details.
func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) {
resp, err := c.do("POST", "/volumes/create", doOptions{
data: opts,
context: opts.Context,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var volume Volume
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
return nil, err
}
return &volume, nil
}
// InspectVolume returns a volume by its name.
//
// See https://goo.gl/0g9A6i for more details.
func (c *Client) InspectVolume(name string) (*Volume, error) {
resp, err := c.do("GET", "/volumes/"+name, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok && e.Status == http.StatusNotFound {
return nil, ErrNoSuchVolume
}
return nil, err
}
defer resp.Body.Close()
var volume Volume
if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil {
return nil, err
}
return &volume, nil
}
// RemoveVolume removes a volume by its name.
//
// See https://goo.gl/79GNQz for more details.
func (c *Client) RemoveVolume(name string) error {
resp, err := c.do("DELETE", "/volumes/"+name, doOptions{})
if err != nil {
if e, ok := err.(*Error); ok {
if e.Status == http.StatusNotFound {
return ErrNoSuchVolume
}
if e.Status == http.StatusConflict {
return ErrVolumeInUse
}
}
return nil
}
defer resp.Body.Close()
return nil
}
// PruneVolumesOptions specify parameters to the PruneVolumes function.
//
// See https://goo.gl/pFN1Hj for more details.
type PruneVolumesOptions struct {
Filters map[string][]string
Context context.Context
}
// PruneVolumesResults specify results from the PruneVolumes function.
//
// See https://goo.gl/pFN1Hj for more details.
type PruneVolumesResults struct {
VolumesDeleted []string
SpaceReclaimed int64
}
// PruneVolumes deletes volumes which are unused.
//
// See https://goo.gl/pFN1Hj for more details.
func (c *Client) PruneVolumes(opts PruneVolumesOptions) (*PruneVolumesResults, error) {
path := "/volumes/prune?" + queryString(opts)
resp, err := c.do("POST", path, doOptions{context: opts.Context})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var results PruneVolumesResults
if err := json.NewDecoder(resp.Body).Decode(&results); err != nil {
return nil, err
}
return &results, nil
}

165
vendor/github.com/fsouza/go-dockerclient/volume_test.go generated vendored Normal file
View File

@@ -0,0 +1,165 @@
// Copyright 2015 go-dockerclient authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docker
import (
"encoding/json"
"net/http"
"net/url"
"reflect"
"testing"
)
func TestListVolumes(t *testing.T) {
volumesData := `[
{
"Name": "tardis",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/tardis"
},
{
"Name": "foo",
"Driver": "bar",
"Mountpoint": "/var/lib/docker/volumes/bar"
}
]`
body := `{ "Volumes": ` + volumesData + ` }`
var expected []Volume
if err := json.Unmarshal([]byte(volumesData), &expected); err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
volumes, err := client.ListVolumes(ListVolumesOptions{})
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(volumes, expected) {
t.Errorf("ListVolumes: Wrong return value. Want %#v. Got %#v.", expected, volumes)
}
}
func TestCreateVolume(t *testing.T) {
body := `{
"Name": "tardis",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/tardis"
}`
var expected Volume
if err := json.Unmarshal([]byte(body), &expected); err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK}
client := newTestClient(fakeRT)
volume, err := client.CreateVolume(
CreateVolumeOptions{
Name: "tardis",
Driver: "local",
DriverOpts: map[string]string{
"foo": "bar",
},
},
)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(volume, &expected) {
t.Errorf("CreateVolume: Wrong return value. Want %#v. Got %#v.", expected, volume)
}
req := fakeRT.requests[0]
expectedMethod := "POST"
if req.Method != expectedMethod {
t.Errorf("CreateVolume(): Wrong HTTP method. Want %s. Got %s.", expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/volumes/create"))
if req.URL.Path != u.Path {
t.Errorf("CreateVolume(): Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
}
}
func TestInspectVolume(t *testing.T) {
body := `{
"Name": "tardis",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/tardis"
}`
var expected Volume
if err := json.Unmarshal([]byte(body), &expected); err != nil {
t.Fatal(err)
}
fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK}
client := newTestClient(fakeRT)
name := "tardis"
volume, err := client.InspectVolume(name)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(volume, &expected) {
t.Errorf("InspectVolume: Wrong return value. Want %#v. Got %#v.", expected, volume)
}
req := fakeRT.requests[0]
expectedMethod := "GET"
if req.Method != expectedMethod {
t.Errorf("InspectVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/volumes/" + name))
if req.URL.Path != u.Path {
t.Errorf("CreateVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
}
}
func TestRemoveVolume(t *testing.T) {
name := "test"
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
client := newTestClient(fakeRT)
if err := client.RemoveVolume(name); err != nil {
t.Fatal(err)
}
req := fakeRT.requests[0]
expectedMethod := "DELETE"
if req.Method != expectedMethod {
t.Errorf("RemoveVolume(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
}
u, _ := url.Parse(client.getURL("/volumes/" + name))
if req.URL.Path != u.Path {
t.Errorf("RemoveVolume(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
}
}
func TestRemoveVolumeNotFound(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "no such volume", status: http.StatusNotFound})
if err := client.RemoveVolume("test:"); err != ErrNoSuchVolume {
t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrNoSuchVolume, err)
}
}
func TestRemoveVolumeInUse(t *testing.T) {
client := newTestClient(&FakeRoundTripper{message: "volume in use and cannot be removed", status: http.StatusConflict})
if err := client.RemoveVolume("test:"); err != ErrVolumeInUse {
t.Errorf("RemoveVolume: wrong error. Want %#v. Got %#v.", ErrVolumeInUse, err)
}
}
func TestPruneVolumes(t *testing.T) {
results := `{
"VolumesDeleted": [
"a", "b", "c"
],
"SpaceReclaimed": 123
}`
expected := &PruneVolumesResults{}
err := json.Unmarshal([]byte(results), expected)
if err != nil {
t.Fatal(err)
}
client := newTestClient(&FakeRoundTripper{message: results, status: http.StatusOK})
got, err := client.PruneVolumes(PruneVolumesOptions{})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(got, expected) {
t.Errorf("PruneContainers: Expected %#v. Got %#v.", expected, got)
}
}