mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
add functions/vendor files
This commit is contained in:
2
vendor/github.com/fsouza/go-dockerclient/.gitignore
generated
vendored
Normal file
2
vendor/github.com/fsouza/go-dockerclient/.gitignore
generated
vendored
Normal 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
32
vendor/github.com/fsouza/go-dockerclient/.travis.yml
generated
vendored
Normal 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
171
vendor/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
Normal 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
|
||||
6
vendor/github.com/fsouza/go-dockerclient/DOCKER-LICENSE
generated
vendored
Normal file
6
vendor/github.com/fsouza/go-dockerclient/DOCKER-LICENSE
generated
vendored
Normal 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
22
vendor/github.com/fsouza/go-dockerclient/LICENSE
generated
vendored
Normal 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
41
vendor/github.com/fsouza/go-dockerclient/Makefile
generated
vendored
Normal 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 ./...
|
||||
112
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
Normal file
112
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
# go-dockerclient
|
||||
|
||||
[](https://travis-ci.org/fsouza/go-dockerclient)
|
||||
[](https://ci.appveyor.com/project/fsouza/go-dockerclient)
|
||||
[](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
20
vendor/github.com/fsouza/go-dockerclient/appveyor.yml
generated
vendored
Normal 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
182
vendor/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
Normal 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
162
vendor/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
Normal 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
162
vendor/github.com/fsouza/go-dockerclient/build_test.go
generated
vendored
Normal 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
43
vendor/github.com/fsouza/go-dockerclient/change.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package 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)
|
||||
}
|
||||
24
vendor/github.com/fsouza/go-dockerclient/change_test.go
generated
vendored
Normal file
24
vendor/github.com/fsouza/go-dockerclient/change_test.go
generated
vendored
Normal 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
1035
vendor/github.com/fsouza/go-dockerclient/client.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
134
vendor/github.com/fsouza/go-dockerclient/client_stress_test.go
generated
vendored
Normal file
134
vendor/github.com/fsouza/go-dockerclient/client_stress_test.go
generated
vendored
Normal 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
815
vendor/github.com/fsouza/go-dockerclient/client_test.go
generated
vendored
Normal 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"`
|
||||
}
|
||||
32
vendor/github.com/fsouza/go-dockerclient/client_unix.go
generated
vendored
Normal file
32
vendor/github.com/fsouza/go-dockerclient/client_unix.go
generated
vendored
Normal 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}
|
||||
}
|
||||
68
vendor/github.com/fsouza/go-dockerclient/client_unix_test.go
generated
vendored
Normal file
68
vendor/github.com/fsouza/go-dockerclient/client_unix_test.go
generated
vendored
Normal 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
|
||||
}
|
||||
46
vendor/github.com/fsouza/go-dockerclient/client_windows.go
generated
vendored
Normal file
46
vendor/github.com/fsouza/go-dockerclient/client_windows.go
generated
vendored
Normal 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}
|
||||
}
|
||||
44
vendor/github.com/fsouza/go-dockerclient/client_windows_test.go
generated
vendored
Normal file
44
vendor/github.com/fsouza/go-dockerclient/client_windows_test.go
generated
vendored
Normal 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
1547
vendor/github.com/fsouza/go-dockerclient/container.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2686
vendor/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
Normal file
2686
vendor/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
108
vendor/github.com/fsouza/go-dockerclient/container_unix_test.go
generated
vendored
Normal file
108
vendor/github.com/fsouza/go-dockerclient/container_unix_test.go
generated
vendored
Normal 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
168
vendor/github.com/fsouza/go-dockerclient/env.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2014 Docker authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the DOCKER-LICENSE file.
|
||||
|
||||
package docker
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Env represents a list of key-pair represented in the form KEY=VALUE.
|
||||
type Env []string
|
||||
|
||||
// Get returns the string value of the given key.
|
||||
func (env *Env) Get(key string) (value string) {
|
||||
return env.Map()[key]
|
||||
}
|
||||
|
||||
// Exists checks whether the given key is defined in the internal Env
|
||||
// representation.
|
||||
func (env *Env) Exists(key string) bool {
|
||||
_, exists := env.Map()[key]
|
||||
return exists
|
||||
}
|
||||
|
||||
// GetBool returns a boolean representation of the given key. The key is false
|
||||
// whenever its value if 0, no, false, none or an empty string. Any other value
|
||||
// will be interpreted as true.
|
||||
func (env *Env) GetBool(key string) (value bool) {
|
||||
s := strings.ToLower(strings.Trim(env.Get(key), " \t"))
|
||||
if s == "" || s == "0" || s == "no" || s == "false" || s == "none" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SetBool defines a boolean value to the given key.
|
||||
func (env *Env) SetBool(key string, value bool) {
|
||||
if value {
|
||||
env.Set(key, "1")
|
||||
} else {
|
||||
env.Set(key, "0")
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt returns the value of the provided key, converted to int.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt(key string) int {
|
||||
return int(env.GetInt64(key))
|
||||
}
|
||||
|
||||
// SetInt defines an integer value to the given key.
|
||||
func (env *Env) SetInt(key string, value int) {
|
||||
env.Set(key, strconv.Itoa(value))
|
||||
}
|
||||
|
||||
// GetInt64 returns the value of the provided key, converted to int64.
|
||||
//
|
||||
// It the value cannot be represented as an integer, it returns -1.
|
||||
func (env *Env) GetInt64(key string) int64 {
|
||||
s := strings.Trim(env.Get(key), " \t")
|
||||
val, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// SetInt64 defines an integer (64-bit wide) value to the given key.
|
||||
func (env *Env) SetInt64(key string, value int64) {
|
||||
env.Set(key, strconv.FormatInt(value, 10))
|
||||
}
|
||||
|
||||
// GetJSON unmarshals the value of the provided key in the provided iface.
|
||||
//
|
||||
// iface is a value that can be provided to the json.Unmarshal function.
|
||||
func (env *Env) GetJSON(key string, iface interface{}) error {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal([]byte(sval), iface)
|
||||
}
|
||||
|
||||
// SetJSON marshals the given value to JSON format and stores it using the
|
||||
// provided key.
|
||||
func (env *Env) SetJSON(key string, value interface{}) error {
|
||||
sval, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env.Set(key, string(sval))
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetList returns a list of strings matching the provided key. It handles the
|
||||
// list as a JSON representation of a list of strings.
|
||||
//
|
||||
// If the given key matches to a single string, it will return a list
|
||||
// containing only the value that matches the key.
|
||||
func (env *Env) GetList(key string) []string {
|
||||
sval := env.Get(key)
|
||||
if sval == "" {
|
||||
return nil
|
||||
}
|
||||
var l []string
|
||||
if err := json.Unmarshal([]byte(sval), &l); err != nil {
|
||||
l = append(l, sval)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// SetList stores the given list in the provided key, after serializing it to
|
||||
// JSON format.
|
||||
func (env *Env) SetList(key string, value []string) error {
|
||||
return env.SetJSON(key, value)
|
||||
}
|
||||
|
||||
// Set defines the value of a key to the given string.
|
||||
func (env *Env) Set(key, value string) {
|
||||
*env = append(*env, key+"="+value)
|
||||
}
|
||||
|
||||
// Decode decodes `src` as a json dictionary, and adds each decoded key-value
|
||||
// pair to the environment.
|
||||
//
|
||||
// If `src` cannot be decoded as a json dictionary, an error is returned.
|
||||
func (env *Env) Decode(src io.Reader) error {
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(src).Decode(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range m {
|
||||
env.SetAuto(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetAuto will try to define the Set* method to call based on the given value.
|
||||
func (env *Env) SetAuto(key string, value interface{}) {
|
||||
if fval, ok := value.(float64); ok {
|
||||
env.SetInt64(key, int64(fval))
|
||||
} else if sval, ok := value.(string); ok {
|
||||
env.Set(key, sval)
|
||||
} else if val, err := json.Marshal(value); err == nil {
|
||||
env.Set(key, string(val))
|
||||
} else {
|
||||
env.Set(key, fmt.Sprintf("%v", value))
|
||||
}
|
||||
}
|
||||
|
||||
// Map returns the map representation of the env.
|
||||
func (env *Env) Map() map[string]string {
|
||||
if len(*env) == 0 {
|
||||
return nil
|
||||
}
|
||||
m := make(map[string]string)
|
||||
for _, kv := range *env {
|
||||
parts := strings.SplitN(kv, "=", 2)
|
||||
m[parts[0]] = parts[1]
|
||||
}
|
||||
return m
|
||||
}
|
||||
351
vendor/github.com/fsouza/go-dockerclient/env_test.go
generated
vendored
Normal file
351
vendor/github.com/fsouza/go-dockerclient/env_test.go
generated
vendored
Normal 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
391
vendor/github.com/fsouza/go-dockerclient/event.go
generated
vendored
Normal 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
291
vendor/github.com/fsouza/go-dockerclient/event_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
138
vendor/github.com/fsouza/go-dockerclient/example_test.go
generated
vendored
Normal file
138
vendor/github.com/fsouza/go-dockerclient/example_test.go
generated
vendored
Normal 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
209
vendor/github.com/fsouza/go-dockerclient/exec.go
generated
vendored
Normal 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
179
vendor/github.com/fsouza/go-dockerclient/exec_test.go
generated
vendored
Normal 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
719
vendor/github.com/fsouza/go-dockerclient/image.go
generated
vendored
Normal 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
1134
vendor/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
94
vendor/github.com/fsouza/go-dockerclient/integration_test.go
generated
vendored
Normal file
94
vendor/github.com/fsouza/go-dockerclient/integration_test.go
generated
vendored
Normal 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
181
vendor/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
Normal 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
193
vendor/github.com/fsouza/go-dockerclient/misc_test.go
generated
vendored
Normal 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
321
vendor/github.com/fsouza/go-dockerclient/network.go
generated
vendored
Normal 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)
|
||||
}
|
||||
266
vendor/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
Normal file
266
vendor/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
Normal 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
130
vendor/github.com/fsouza/go-dockerclient/node.go
generated
vendored
Normal 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
254
vendor/github.com/fsouza/go-dockerclient/node_test.go
generated
vendored
Normal 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
169
vendor/github.com/fsouza/go-dockerclient/service.go
generated
vendored
Normal 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
|
||||
}
|
||||
404
vendor/github.com/fsouza/go-dockerclient/service_test.go
generated
vendored
Normal file
404
vendor/github.com/fsouza/go-dockerclient/service_test.go
generated
vendored
Normal 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
49
vendor/github.com/fsouza/go-dockerclient/signal.go
generated
vendored
Normal 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
156
vendor/github.com/fsouza/go-dockerclient/swarm.go
generated
vendored
Normal 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
200
vendor/github.com/fsouza/go-dockerclient/swarm_test.go
generated
vendored
Normal 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
117
vendor/github.com/fsouza/go-dockerclient/tar.go
generated
vendored
Normal 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
70
vendor/github.com/fsouza/go-dockerclient/task.go
generated
vendored
Normal 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
373
vendor/github.com/fsouza/go-dockerclient/task_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
1568
vendor/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
Normal file
1568
vendor/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2451
vendor/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
Normal file
2451
vendor/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
685
vendor/github.com/fsouza/go-dockerclient/testing/swarm.go
generated
vendored
Normal file
685
vendor/github.com/fsouza/go-dockerclient/testing/swarm.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
1363
vendor/github.com/fsouza/go-dockerclient/testing/swarm_test.go
generated
vendored
Normal file
1363
vendor/github.com/fsouza/go-dockerclient/testing/swarm_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
118
vendor/github.com/fsouza/go-dockerclient/tls.go
generated
vendored
Normal file
118
vendor/github.com/fsouza/go-dockerclient/tls.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
||||
19
vendor/github.com/fsouza/go-dockerclient/travis-scripts/install-docker.bash
generated
vendored
Executable file
19
vendor/github.com/fsouza/go-dockerclient/travis-scripts/install-docker.bash
generated
vendored
Executable 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
|
||||
11
vendor/github.com/fsouza/go-dockerclient/travis-scripts/run-tests.bash
generated
vendored
Executable file
11
vendor/github.com/fsouza/go-dockerclient/travis-scripts/run-tests.bash
generated
vendored
Executable 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
171
vendor/github.com/fsouza/go-dockerclient/volume.go
generated
vendored
Normal 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
165
vendor/github.com/fsouza/go-dockerclient/volume_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user