mirror of https://github.com/k3s-io/k3s
bump(github.com/fsouza/go-dockerclient): 17d39bcb22e8103ba6d1c0cb2530c6434cb870a3
parent
1db1894850
commit
fb63370451
|
@ -179,7 +179,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||
"Rev": "d19717788084716e4adff0515be6289aa04bec46"
|
||||
"Rev": "17d39bcb22e8103ba6d1c0cb2530c6434cb870a3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.2.2
|
||||
- 1.3.1
|
||||
- 1.4
|
||||
- tip
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
# This is the official list of go-dockerclient authors for copyright purposes.
|
||||
|
||||
Adam Bell-Hanssen <adamb@aller.no>
|
||||
Aldrin Leal <aldrin@leal.eng.br>
|
||||
Andreas Jaekle <andreas@jaekle.net>
|
||||
Andrews Medina <andrewsmedina@gmail.com>
|
||||
Artem Sidorenko <artem@2realities.com>
|
||||
Andy Goldstein <andy.goldstein@redhat.com>
|
||||
Ben Marini <ben@remind101.com>
|
||||
Ben McCann <benmccann.com>
|
||||
Brian Lalor <blalor@bravo5.org>
|
||||
Burke Libbey <burke@libbey.me>
|
||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||
cheneydeng <cheneydeng@qq.com>
|
||||
CMGS <ilskdw@gmail.com>
|
||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||
Darren Shepherd <darren@rancher.com>
|
||||
David Huie <dahuie@gmail.com>
|
||||
Dawn Chen <dawnchen@google.com>
|
||||
Ed <edrocksit@gmail.com>
|
||||
Eric Anderson <anderson@copperegg.com>
|
||||
Fabio Rehm <fgrehm@gmail.com>
|
||||
Fatih Arslan <ftharsln@gmail.com>
|
||||
Fatih Arslan <ftharsln@gmail.com>
|
||||
Flavia Missi <flaviamissi@gmail.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
||||
Jari Kolehmainen <jari.kolehmainen@digia.com>
|
||||
Jason Wilder <jwilder@litl.com>
|
||||
Jawher Moussa <jawher.moussa@gmail.com>
|
||||
|
@ -30,19 +36,26 @@ Johan Euphrosine <proppy@google.com>
|
|||
Kamil Domanski <kamil@domanski.co>
|
||||
Karan Misra <kidoman@gmail.com>
|
||||
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
|
||||
liron-l <levinlir@gmail.com>
|
||||
Lucas Clemente <lucas@clemente.io>
|
||||
Lucas Weiblen <lucasweiblen@gmail.com>
|
||||
Mantas Matelis <mmatelis@coursera.org>
|
||||
Martin Sweeney <martin@sweeney.io>
|
||||
Máximo Cuadros Ortiz <mcuadros@gmail.com>
|
||||
Michal Fojtik <mfojtik@redhat.com>
|
||||
Mike Dillon <mike.dillon@synctree.com>
|
||||
Mrunal Patel <mrunalp@gmail.com>
|
||||
Nick Ethier <ncethier@gmail.com>
|
||||
Omeid Matten <public@omeid.me>
|
||||
Paul Morie <pmorie@gmail.com>
|
||||
Paul Weil <pweil@redhat.com>
|
||||
Peter Jihoon Kim <raingrove@gmail.com>
|
||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||
Rafe Colton <rafael.colton@gmail.com>
|
||||
Rob Miller <rob@kalistra.com>
|
||||
Robert Williamson <williamson.robert@gmail.com>
|
||||
Salvador Gironès <salvadorgirones@gmail.com>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Simon Eskildsen <sirup@sirupsen.com>
|
||||
Simon Menke <simon.menke@gmail.com>
|
||||
Skolos <skolos@gopherlab.com>
|
||||
|
@ -51,5 +64,7 @@ Sridhar Ratnakumar <sridharr@activestate.com>
|
|||
Summer Mousa <smousa@zenoss.com>
|
||||
Tarsis Azevedo <tarsis@corp.globo.com>
|
||||
Tim Schindler <tim@catalyst-zero.com>
|
||||
Vincenzo Prignano <vincenzo.prignano@gmail.com>
|
||||
Wiliam Souza <wiliamsouza83@gmail.com>
|
||||
Ye Yin <eyniy@qq.com>
|
||||
Yuriy Bogdanov <chinsay@gmail.com>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2014, go-dockerclient authors
|
||||
Copyright (c) 2015, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
[![Build Status](https://drone.io/github.com/fsouza/go-dockerclient/status.png)](https://drone.io/github.com/fsouza/go-dockerclient/latest)
|
||||
[![Build Status](https://travis-ci.org/fsouza/go-dockerclient.png)](https://travis-ci.org/fsouza/go-dockerclient)
|
||||
|
||||
[![GoDoc](http://godoc.org/github.com/fsouza/go-dockerclient?status.png)](http://godoc.org/github.com/fsouza/go-dockerclient)
|
||||
[![GoDoc](https://godoc.org/github.com/fsouza/go-dockerclient?status.png)](https://godoc.org/github.com/fsouza/go-dockerclient)
|
||||
|
||||
This package presents a client for the Docker remote API.
|
||||
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/API/).
|
||||
|
||||
For more details, check the [remote API documentation](http://docs.docker.io/en/latest/reference/api/docker_remote_api/).
|
||||
For more details, check the [remote API documentation](http://docs.docker.com/en/latest/reference/api/docker_remote_api/).
|
||||
|
||||
## Example
|
||||
|
||||
|
@ -16,6 +17,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
|
@ -34,6 +36,32 @@ func main() {
|
|||
}
|
||||
```
|
||||
|
||||
## Using with Boot2Docker
|
||||
|
||||
Boot2Docker runs Docker with TLS enabled. In order to instantiate the client you should use NewTLSClient, passing the endpoint and path for key and certificates as parameters.
|
||||
|
||||
For more details about TLS support in Boot2Docker, please refer to [TLS support](https://github.com/boot2docker/boot2docker#tls-support) on Boot2Docker's readme.
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
You can run the tests with:
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
// 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"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// dockerConfig represents a registry authentation configuration from the
|
||||
// .dockercfg file.
|
||||
type dockerConfig struct {
|
||||
Auth string `json:"auth"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from the
|
||||
// ~/.dockercfg file.
|
||||
func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) {
|
||||
p := path.Join(os.Getenv("HOME"), ".dockercfg")
|
||||
r, err := os.Open(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAuthConfigurations(r)
|
||||
}
|
||||
|
||||
// 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
|
||||
var confs map[string]dockerConfig
|
||||
if err := json.NewDecoder(r).Decode(&confs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth, err := authConfigs(confs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return auth, 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.Split(string(data), ":")
|
||||
c.Configs[reg] = AuthConfiguration{
|
||||
Email: conf.Email,
|
||||
Username: userpass[0],
|
||||
Password: userpass[1],
|
||||
ServerAddress: reg,
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
}
|
37
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// 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"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthConfig(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
|
||||
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, "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)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -34,7 +34,7 @@ var (
|
|||
// ErrConnectionRefused is returned when the client cannot connect to the given endpoint.
|
||||
ErrConnectionRefused = errors.New("cannot connect to Docker endpoint")
|
||||
|
||||
apiVersion1_12, _ = NewAPIVersion("1.12")
|
||||
apiVersion112, _ = NewAPIVersion("1.12")
|
||||
)
|
||||
|
||||
// APIVersion is an internal representation of a version of the Remote API.
|
||||
|
@ -143,7 +143,7 @@ func NewClient(endpoint string) (*Client, error) {
|
|||
// server endpoint, key and certificates . It will use the latest remote API version
|
||||
// available in the server.
|
||||
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
|
||||
client, err := NewVersionnedTLSClient(endpoint, cert, key, ca, "")
|
||||
client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
|
|||
// NewVersionedClient returns a Client instance ready for communication with
|
||||
// the given server endpoint, using a specific remote API version.
|
||||
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
|
||||
u, err := parseEndpoint(endpoint)
|
||||
u, err := parseEndpoint(endpoint, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -174,10 +174,15 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
|
|||
}, nil
|
||||
}
|
||||
|
||||
// NewVersionnedTLSClient returns a Client instance ready for TLS communications with the givens
|
||||
// server endpoint, key and certificates, using a specific remote API version.
|
||||
// NewVersionnedTLSClient has been DEPRECATED, please use NewVersionedTLSClient.
|
||||
func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
|
||||
u, err := parseEndpoint(endpoint)
|
||||
return NewVersionedTLSClient(endpoint, cert, key, ca, apiVersionString)
|
||||
}
|
||||
|
||||
// NewVersionedTLSClient returns a Client instance ready for TLS communications with the givens
|
||||
// server endpoint, key and certificates, using a specific remote API version.
|
||||
func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
|
||||
u, err := parseEndpoint(endpoint, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -247,7 +252,7 @@ func (c *Client) checkAPIVersion() error {
|
|||
// See http://goo.gl/stJENm for more details.
|
||||
func (c *Client) Ping() error {
|
||||
path := "/_ping"
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
body, status, err := c.do("GET", path, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -258,7 +263,7 @@ func (c *Client) Ping() error {
|
|||
}
|
||||
|
||||
func (c *Client) getServerAPIVersionString() (version string, err error) {
|
||||
body, status, err := c.do("GET", "/version", nil)
|
||||
body, status, err := c.do("GET", "/version", nil, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -274,9 +279,9 @@ func (c *Client) getServerAPIVersionString() (version string, err error) {
|
|||
return version, nil
|
||||
}
|
||||
|
||||
func (c *Client) do(method, path string, data interface{}) ([]byte, int, error) {
|
||||
func (c *Client) do(method, path string, data interface{}, forceJSON bool) ([]byte, int, error) {
|
||||
var params io.Reader
|
||||
if data != nil {
|
||||
if data != nil || forceJSON {
|
||||
buf, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
|
@ -599,11 +604,14 @@ func (e *Error) Error() string {
|
|||
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
|
||||
}
|
||||
|
||||
func parseEndpoint(endpoint string) (*url.URL, error) {
|
||||
func parseEndpoint(endpoint string, tls bool) (*url.URL, error) {
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidEndpoint
|
||||
}
|
||||
if tls {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
if u.Scheme == "tcp" {
|
||||
_, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -93,7 +93,7 @@ func TestNewTLSVersionedClient(t *testing.T) {
|
|||
keyPath := "testing/data/key.pem"
|
||||
caPath := "testing/data/ca.pem"
|
||||
endpoint := "https://localhost:4243"
|
||||
client, err := NewVersionnedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
||||
client, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func TestNewTLSVersionedClientInvalidCA(t *testing.T) {
|
|||
keyPath := "testing/data/key.pem"
|
||||
caPath := "testing/data/key.pem"
|
||||
endpoint := "https://localhost:4243"
|
||||
_, err := NewVersionnedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
||||
_, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
||||
if err == nil {
|
||||
t.Errorf("Expected invalid ca at %s", caPath)
|
||||
}
|
||||
|
@ -136,14 +136,15 @@ func TestNewClientInvalidEndpoint(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNewTLSClient2376(t *testing.T) {
|
||||
func TestNewTLSClient(t *testing.T) {
|
||||
var tests = []struct {
|
||||
endpoint string
|
||||
expected string
|
||||
}{
|
||||
{"tcp://localhost:2376", "https"},
|
||||
{"tcp://localhost:2375", "http"},
|
||||
{"tcp://localhost:4000", "http"},
|
||||
{"tcp://localhost:2375", "https"},
|
||||
{"tcp://localhost:4000", "https"},
|
||||
{"http://localhost:4000", "https"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -56,7 +56,7 @@ type APIContainers struct {
|
|||
// See http://goo.gl/6Y4Gz7 for more details.
|
||||
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
||||
path := "/containers/json?" + queryString(opts)
|
||||
body, _, err := c.do("GET", path, nil)
|
||||
body, _, err := c.do("GET", path, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ func parsePort(rawPort string) (int, error) {
|
|||
}
|
||||
|
||||
// Config is the list of configuration options used when creating a container.
|
||||
// Config does not the options that are specific to starting a container on a
|
||||
// Config does not contain the options that are specific to starting a container on a
|
||||
// given host. Those are contained in HostConfig
|
||||
type Config struct {
|
||||
Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"`
|
||||
|
@ -193,6 +193,26 @@ type Config struct {
|
|||
WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"`
|
||||
Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty"`
|
||||
NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty"`
|
||||
SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty"`
|
||||
OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
|
||||
}
|
||||
|
||||
// LogConfig defines the log driver type and the configuration for it.
|
||||
type LogConfig struct {
|
||||
Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
|
||||
Config map[string]string `json:"Config,omitempty" yaml:"Config,omitempty"`
|
||||
}
|
||||
|
||||
// SwarmNode containers information about which Swarm node the container is on
|
||||
type SwarmNode struct {
|
||||
ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
|
||||
IP string `json:"IP,omitempty" yaml:"IP,omitempty"`
|
||||
Addr string `json:"Addr,omitempty" yaml:"Addr,omitempty"`
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
|
||||
CPUs int64 `json:"CPUs,omitempty" yaml:"CPUs,omitempty"`
|
||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
|
||||
}
|
||||
|
||||
// Container is the type encompasing everything about a container - its config,
|
||||
|
@ -209,6 +229,8 @@ type Container struct {
|
|||
State State `json:"State,omitempty" yaml:"State,omitempty"`
|
||||
Image string `json:"Image,omitempty" yaml:"Image,omitempty"`
|
||||
|
||||
Node *SwarmNode `json:"Node,omitempty" yaml:"Node,omitempty"`
|
||||
|
||||
NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
|
||||
|
||||
SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty"`
|
||||
|
@ -221,6 +243,28 @@ type Container struct {
|
|||
Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty"`
|
||||
VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"`
|
||||
HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"`
|
||||
ExecIDs []string `json:"ExecIDs,omitempty" yaml:"ExecIDs,omitempty"`
|
||||
|
||||
AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"`
|
||||
}
|
||||
|
||||
// RenameContainerOptions specify parameters to the RenameContainer function.
|
||||
//
|
||||
// See http://goo.gl/L00hoj for more details.
|
||||
type RenameContainerOptions struct {
|
||||
// ID of container to rename
|
||||
ID string `qs:"-"`
|
||||
|
||||
// New name
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
}
|
||||
|
||||
// RenameContainer updates and existing containers name
|
||||
//
|
||||
// See http://goo.gl/L00hoj for more details.
|
||||
func (c *Client) RenameContainer(opts RenameContainerOptions) error {
|
||||
_, _, err := c.do("POST", fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), nil, false)
|
||||
return err
|
||||
}
|
||||
|
||||
// InspectContainer returns information about a container by its ID.
|
||||
|
@ -228,7 +272,7 @@ type Container struct {
|
|||
// See http://goo.gl/CxVuJ5 for more details.
|
||||
func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||
path := "/containers/" + id + "/json"
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
body, status, err := c.do("GET", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -248,7 +292,7 @@ func (c *Client) InspectContainer(id string) (*Container, error) {
|
|||
// See http://goo.gl/QkW9sH for more details.
|
||||
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||
path := "/containers/" + id + "/changes"
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
body, status, err := c.do("GET", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -284,7 +328,7 @@ func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error
|
|||
}{
|
||||
opts.Config,
|
||||
opts.HostConfig,
|
||||
})
|
||||
}, false)
|
||||
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
|
@ -341,6 +385,14 @@ func NeverRestart() RestartPolicy {
|
|||
return RestartPolicy{Name: "no"}
|
||||
}
|
||||
|
||||
// Device represents a device mapping between the Docker host and the
|
||||
// container.
|
||||
type Device struct {
|
||||
PathOnHost string `json:"PathOnHost,omitempty" yaml:"PathOnHost,omitempty"`
|
||||
PathInContainer string `json:"PathInContainer,omitempty" yaml:"PathInContainer,omitempty"`
|
||||
CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"`
|
||||
}
|
||||
|
||||
// HostConfig contains the container options related to starting a container on
|
||||
// a given host
|
||||
type HostConfig struct {
|
||||
|
@ -359,20 +411,22 @@ type HostConfig struct {
|
|||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||
}
|
||||
|
||||
// StartContainer starts a container, returning an error in case of failure.
|
||||
//
|
||||
// See http://goo.gl/iM5GYs for more details.
|
||||
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||
if hostConfig == nil {
|
||||
hostConfig = &HostConfig{}
|
||||
}
|
||||
path := "/containers/" + id + "/start"
|
||||
_, status, err := c.do("POST", path, hostConfig)
|
||||
_, status, err := c.do("POST", path, hostConfig, true)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
return &NoSuchContainer{ID: id, Err: err}
|
||||
}
|
||||
if status == http.StatusNotModified {
|
||||
return &ContainerAlreadyRunning{ID: id}
|
||||
|
@ -389,7 +443,7 @@ func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
|||
// See http://goo.gl/EbcpXt for more details.
|
||||
func (c *Client) StopContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
_, status, err := c.do("POST", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -408,7 +462,7 @@ func (c *Client) StopContainer(id string, timeout uint) error {
|
|||
// See http://goo.gl/VOzR2n for more details.
|
||||
func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
_, status, err := c.do("POST", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -423,7 +477,7 @@ func (c *Client) RestartContainer(id string, timeout uint) error {
|
|||
// See http://goo.gl/AM5t42 for more details.
|
||||
func (c *Client) PauseContainer(id string) error {
|
||||
path := fmt.Sprintf("/containers/%s/pause", id)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
_, status, err := c.do("POST", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -438,7 +492,7 @@ func (c *Client) PauseContainer(id string) error {
|
|||
// See http://goo.gl/eBrNSL for more details.
|
||||
func (c *Client) UnpauseContainer(id string) error {
|
||||
path := fmt.Sprintf("/containers/%s/unpause", id)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
_, status, err := c.do("POST", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -467,7 +521,7 @@ func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) {
|
|||
args = fmt.Sprintf("?ps_args=%s", psArgs)
|
||||
}
|
||||
path := fmt.Sprintf("/containers/%s/top%s", id, args)
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
body, status, err := c.do("GET", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return result, &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -499,7 +553,7 @@ type KillContainerOptions struct {
|
|||
// See http://goo.gl/TFkECx for more details.
|
||||
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
||||
_, status, err := c.do("POST", path, nil)
|
||||
_, status, err := c.do("POST", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: opts.ID}
|
||||
}
|
||||
|
@ -530,7 +584,7 @@ type RemoveContainerOptions struct {
|
|||
// See http://goo.gl/ZB83ji for more details.
|
||||
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
||||
_, status, err := c.do("DELETE", path, nil)
|
||||
_, status, err := c.do("DELETE", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: opts.ID}
|
||||
}
|
||||
|
@ -559,7 +613,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
|||
return &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
url := fmt.Sprintf("/containers/%s/copy", opts.Container)
|
||||
body, status, err := c.do("POST", url, opts)
|
||||
body, status, err := c.do("POST", url, opts, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
|
@ -575,7 +629,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
|||
//
|
||||
// See http://goo.gl/J88DHU for more details.
|
||||
func (c *Client) WaitContainer(id string) (int, error) {
|
||||
body, status, err := c.do("POST", "/containers/"+id+"/wait", nil)
|
||||
body, status, err := c.do("POST", "/containers/"+id+"/wait", nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return 0, &NoSuchContainer{ID: id}
|
||||
}
|
||||
|
@ -607,7 +661,7 @@ type CommitContainerOptions struct {
|
|||
// See http://goo.gl/Jn8pe8 for more details.
|
||||
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||
path := "/commit?" + queryString(opts)
|
||||
body, status, err := c.do("POST", path, opts.Run)
|
||||
body, status, err := c.do("POST", path, opts.Run, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
|
@ -706,7 +760,7 @@ func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
|||
params := make(url.Values)
|
||||
params.Set("h", strconv.Itoa(height))
|
||||
params.Set("w", strconv.Itoa(width))
|
||||
_, _, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), nil)
|
||||
_, _, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), nil, false)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -734,9 +788,13 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
|||
// NoSuchContainer is the error returned when a given container does not exist.
|
||||
type NoSuchContainer struct {
|
||||
ID string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (err *NoSuchContainer) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
}
|
||||
return "No such container: " + err.ID
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -7,6 +7,7 @@ package docker
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -154,6 +155,7 @@ func TestListContainersFailure(t *testing.T) {
|
|||
func TestInspectContainer(t *testing.T) {
|
||||
jsonContainer := `{
|
||||
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
|
||||
"AppArmorProfile": "Profile",
|
||||
"Created": "2013-05-07T14:51:42.087658+02:00",
|
||||
"Path": "date",
|
||||
"Args": [],
|
||||
|
@ -175,7 +177,10 @@ func TestInspectContainer(t *testing.T) {
|
|||
],
|
||||
"Image": "base",
|
||||
"Volumes": {},
|
||||
"VolumesFrom": ""
|
||||
"VolumesFrom": "",
|
||||
"SecurityOpt": [
|
||||
"label:user:USER"
|
||||
]
|
||||
},
|
||||
"State": {
|
||||
"Running": false,
|
||||
|
@ -184,6 +189,21 @@ func TestInspectContainer(t *testing.T) {
|
|||
"StartedAt": "2013-05-07T14:51:42.087658+02:00",
|
||||
"Ghost": false
|
||||
},
|
||||
"Node": {
|
||||
"ID": "4I4E:QR4I:Z733:QEZK:5X44:Q4T7:W2DD:JRDY:KB2O:PODO:Z5SR:XRB6",
|
||||
"IP": "192.168.99.105",
|
||||
"Addra": "192.168.99.105:2376",
|
||||
"Name": "node-01",
|
||||
"Cpus": 4,
|
||||
"Memory": 1048436736,
|
||||
"Labels": {
|
||||
"executiondriver": "native-0.2",
|
||||
"kernelversion": "3.18.5-tinycore64",
|
||||
"operatingsystem": "Boot2Docker 1.5.0 (TCL 5.4); master : a66bce5 - Tue Feb 10 23:31:27 UTC 2015",
|
||||
"provider": "virtualbox",
|
||||
"storagedriver": "aufs"
|
||||
}
|
||||
},
|
||||
"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
"NetworkSettings": {
|
||||
"IpAddress": "",
|
||||
|
@ -511,12 +531,17 @@ func TestStartContainerNilHostConfig(t *testing.T) {
|
|||
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
|
||||
t.Errorf("StartContainer(%q): Wrong content-type in request. Want %q. Got %q.", id, expectedContentType, contentType)
|
||||
}
|
||||
var buf [4]byte
|
||||
req.Body.Read(buf[:])
|
||||
if string(buf[:]) != "null" {
|
||||
t.Errorf("Startcontainer(%q): Wrong body. Want null. Got %s", buf[:])
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartContainerNotFound(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
|
||||
err := client.StartContainer("a2344", &HostConfig{})
|
||||
expected := &NoSuchContainer{ID: "a2344"}
|
||||
expected := &NoSuchContainer{ID: "a2344", Err: err.(*NoSuchContainer).Err}
|
||||
if !reflect.DeepEqual(err, expected) {
|
||||
t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
|
||||
}
|
||||
|
@ -1287,6 +1312,14 @@ func TestNoSuchContainerError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNoSuchContainerErrorMessage(t *testing.T) {
|
||||
var err = &NoSuchContainer{ID: "i345", Err: errors.New("some advanced error info")}
|
||||
expected := "some advanced error info"
|
||||
if got := err.Error(); got != expected {
|
||||
t.Errorf("NoSuchContainer: wrong message. Want %q. Got %q.", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExportContainer(t *testing.T) {
|
||||
content := "exported container tar content"
|
||||
out := stdoutMock{bytes.NewBufferString(content)}
|
||||
|
@ -1311,7 +1344,7 @@ func TestExportContainerViaUnixSocket(t *testing.T) {
|
|||
tempSocket := tempfile("export_socket")
|
||||
defer os.Remove(tempSocket)
|
||||
endpoint := "unix://" + tempSocket
|
||||
u, _ := parseEndpoint(endpoint)
|
||||
u, _ := parseEndpoint(endpoint, false)
|
||||
client := Client{
|
||||
HTTPClient: http.DefaultClient,
|
||||
endpoint: endpoint,
|
||||
|
@ -1522,3 +1555,26 @@ func TestTopContainerWithPsArgs(t *testing.T) {
|
|||
t.Errorf("TopContainer: Expected URI to have %q. Got %q.", expectedURI, fakeRT.requests[0].URL.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameContainer(t *testing.T) {
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
opts := RenameContainerOptions{ID: "something_old", Name: "something_new"}
|
||||
err := client.RenameContainer(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
if req.Method != "POST" {
|
||||
t.Errorf("RenameContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
|
||||
}
|
||||
expectedURL, _ := url.Parse(client.getURL("/containers/something_old/rename?name=something_new"))
|
||||
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
|
||||
t.Errorf("RenameContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
|
||||
}
|
||||
expectedValues := expectedURL.Query()["name"]
|
||||
actualValues := req.URL.Query()["name"]
|
||||
if len(actualValues) != 1 || expectedValues[0] != actualValues[0] {
|
||||
t.Errorf("RenameContainer: Wrong params in request. Want %q. Got %q.", expectedValues, actualValues)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -182,8 +182,8 @@ func (eventState *eventMonitoringState) monitorEvents(c *Client) {
|
|||
eventState.terminate()
|
||||
return
|
||||
}
|
||||
eventState.updateLastSeen(ev)
|
||||
go eventState.sendEvent(ev)
|
||||
go eventState.updateLastSeen(ev)
|
||||
case err = <-eventState.errC:
|
||||
if err == ErrNoListeners {
|
||||
eventState.terminate()
|
||||
|
@ -227,7 +227,7 @@ func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
|
|||
eventState.Add(1)
|
||||
defer eventState.Done()
|
||||
if eventState.isEnabled() {
|
||||
if eventState.noListeners() {
|
||||
if len(eventState.listeners) == 0 {
|
||||
eventState.errC <- ErrNoListeners
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -90,7 +90,7 @@ type ExecInspect struct {
|
|||
// See http://goo.gl/8izrzI for more details
|
||||
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
||||
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
|
||||
body, status, err := c.do("POST", path, opts)
|
||||
body, status, err := c.do("POST", path, opts, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchContainer{ID: opts.Container}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func (c *Client) StartExec(id string, opts StartExecOptions) error {
|
|||
path := fmt.Sprintf("/exec/%s/start", id)
|
||||
|
||||
if opts.Detach {
|
||||
_, status, err := c.do("POST", path, opts)
|
||||
_, status, err := c.do("POST", path, opts, false)
|
||||
if status == http.StatusNotFound {
|
||||
return &NoSuchExec{ID: id}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
|||
params.Set("w", strconv.Itoa(width))
|
||||
|
||||
path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
|
||||
_, _, err := c.do("POST", path, nil)
|
||||
_, _, err := c.do("POST", path, nil, false)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
|||
// See http://goo.gl/ypQULN for more details
|
||||
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
||||
path := fmt.Sprintf("/exec/%s/json", id)
|
||||
body, status, err := c.do("GET", path, nil)
|
||||
body, status, err := c.do("GET", path, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, &NoSuchExec{ID: id}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -99,7 +99,7 @@ var (
|
|||
// See http://goo.gl/2rOLFF for more details.
|
||||
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||
path := "/images/json?" + queryString(opts)
|
||||
body, _, err := c.do("GET", path, nil)
|
||||
body, _, err := c.do("GET", path, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
|||
//
|
||||
// See http://goo.gl/2oJmNs for more details.
|
||||
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/history", nil)
|
||||
body, status, err := c.do("GET", "/images/"+name+"/history", nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
|
@ -134,7 +134,29 @@ func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
|||
//
|
||||
// See http://goo.gl/znj0wM for more details.
|
||||
func (c *Client) RemoveImage(name string) error {
|
||||
_, status, err := c.do("DELETE", "/images/"+name, nil)
|
||||
_, status, err := c.do("DELETE", "/images/"+name, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// RemoveImageOptions present the set of options available for removing an image
|
||||
// from a registry.
|
||||
//
|
||||
// See http://goo.gl/6V48bF for more details.
|
||||
type RemoveImageOptions struct {
|
||||
Force bool `qs:"force"`
|
||||
NoPrune bool `qs:"noprune"`
|
||||
}
|
||||
|
||||
// RemoveImageExtended removes an image by its name or ID.
|
||||
// Extra params can be passed, see RemoveImageOptions
|
||||
//
|
||||
// See http://goo.gl/znj0wM for more details.
|
||||
func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error {
|
||||
uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts))
|
||||
_, status, err := c.do("DELETE", uri, nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
|
@ -145,7 +167,7 @@ func (c *Client) RemoveImage(name string) error {
|
|||
//
|
||||
// See http://goo.gl/Q112NY for more details.
|
||||
func (c *Client) InspectImage(name string) (*Image, error) {
|
||||
body, status, err := c.do("GET", "/images/"+name+"/json", nil)
|
||||
body, status, err := c.do("GET", "/images/"+name+"/json", nil, false)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrNoSuchImage
|
||||
}
|
||||
|
@ -156,7 +178,7 @@ func (c *Client) InspectImage(name string) (*Image, error) {
|
|||
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(apiVersion1_12) {
|
||||
if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) {
|
||||
err = json.Unmarshal(body, &image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -201,21 +223,6 @@ type PushImageOptions struct {
|
|||
RawJSONStream bool `qs:"-"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// PushImage pushes an image to a remote registry, logging progress to w.
|
||||
//
|
||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||
|
@ -245,7 +252,7 @@ type PullImageOptions struct {
|
|||
RawJSONStream bool `qs:"-"`
|
||||
}
|
||||
|
||||
// PullImage pulls an image from a remote registry, logging progress to w.
|
||||
// PullImage pulls an image from a remote registry, logging progress to opts.OutputStream.
|
||||
//
|
||||
// See http://goo.gl/ACyYNS for more details.
|
||||
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||
|
@ -333,6 +340,7 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
|
|||
// http://goo.gl/tlPXPu.
|
||||
type BuildImageOptions struct {
|
||||
Name string `qs:"t"`
|
||||
Dockerfile string `qs:"dockerfile"`
|
||||
NoCache bool `qs:"nocache"`
|
||||
SuppressOutput bool `qs:"q"`
|
||||
RmTmpContainer bool `qs:"rm"`
|
||||
|
@ -395,7 +403,8 @@ func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
|||
return ErrNoSuchImage
|
||||
}
|
||||
_, status, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s",
|
||||
queryString(&opts)), nil)
|
||||
queryString(&opts)), nil, false)
|
||||
|
||||
if status == http.StatusNotFound {
|
||||
return ErrNoSuchImage
|
||||
}
|
||||
|
@ -445,7 +454,7 @@ type APIImageSearch struct {
|
|||
//
|
||||
// See http://goo.gl/xI5lLZ for more details.
|
||||
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
|
||||
body, _, err := c.do("GET", "/images/search?term="+term, nil)
|
||||
body, _, err := c.do("GET", "/images/search?term="+term, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
func newTestClient(rt *FakeRoundTripper) Client {
|
||||
endpoint := "http://localhost:4243"
|
||||
u, _ := parseEndpoint("http://localhost:4243")
|
||||
u, _ := parseEndpoint("http://localhost:4243", false)
|
||||
client := Client{
|
||||
HTTPClient: &http.Client{Transport: rt},
|
||||
endpoint: endpoint,
|
||||
|
@ -208,6 +208,29 @@ func TestRemoveImageNotFound(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRemoveImageExtended(t *testing.T) {
|
||||
name := "test"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
client := newTestClient(fakeRT)
|
||||
err := client.RemoveImageExtended(name, RemoveImageOptions{Force: true, NoPrune: true})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
expectedMethod := "DELETE"
|
||||
if req.Method != expectedMethod {
|
||||
t.Errorf("RemoveImage(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
|
||||
}
|
||||
u, _ := url.Parse(client.getURL("/images/" + name))
|
||||
if req.URL.Path != u.Path {
|
||||
t.Errorf("RemoveImage(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
|
||||
}
|
||||
expectedQuery := "force=1&noprune=1"
|
||||
if query := req.URL.Query().Encode(); query != expectedQuery {
|
||||
t.Errorf("PushImage: Wrong query string. Want %q. Got %q.", expectedQuery, query)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInspectImage(t *testing.T) {
|
||||
body := `{
|
||||
"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
//
|
||||
// See http://goo.gl/BOZrF5 for more details.
|
||||
func (c *Client) Version() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/version", nil)
|
||||
body, _, err := c.do("GET", "/version", nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ func (c *Client) Version() (*Env, error) {
|
|||
//
|
||||
// See http://goo.gl/wmqZsW for more details.
|
||||
func (c *Client) Info() (*Env, error) {
|
||||
body, _, err := c.do("GET", "/info", nil)
|
||||
body, _, err := c.do("GET", "/info", nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -35,6 +35,7 @@ import (
|
|||
type DockerServer struct {
|
||||
containers []*docker.Container
|
||||
execs []*docker.ExecInspect
|
||||
execMut sync.RWMutex
|
||||
cMut sync.RWMutex
|
||||
images []docker.Image
|
||||
iMut sync.RWMutex
|
||||
|
@ -43,6 +44,8 @@ type DockerServer struct {
|
|||
mux *mux.Router
|
||||
hook func(*http.Request)
|
||||
failures map[string]string
|
||||
multiFailures []map[string]string
|
||||
execCallbacks map[string]func()
|
||||
customHandlers map[string]http.Handler
|
||||
handlerMutex sync.RWMutex
|
||||
cChan chan<- *docker.Container
|
||||
|
@ -69,6 +72,7 @@ func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*h
|
|||
imgIDs: make(map[string]string),
|
||||
hook: hook,
|
||||
failures: make(map[string]string),
|
||||
execCallbacks: make(map[string]func()),
|
||||
customHandlers: make(map[string]http.Handler),
|
||||
cChan: containerChan,
|
||||
}
|
||||
|
@ -89,6 +93,7 @@ func (s *DockerServer) buildMuxer() {
|
|||
s.mux.Path("/containers/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.listContainers))
|
||||
s.mux.Path("/containers/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createContainer))
|
||||
s.mux.Path("/containers/{id:.*}/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectContainer))
|
||||
s.mux.Path("/containers/{id:.*}/rename").Methods("POST").HandlerFunc(s.handlerWrapper(s.renameContainer))
|
||||
s.mux.Path("/containers/{id:.*}/top").Methods("GET").HandlerFunc(s.handlerWrapper(s.topContainer))
|
||||
s.mux.Path("/containers/{id:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startContainer))
|
||||
s.mux.Path("/containers/{id:.*}/kill").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer))
|
||||
|
@ -115,17 +120,59 @@ func (s *DockerServer) buildMuxer() {
|
|||
s.mux.Path("/images/{id:.*}/get").Methods("GET").HandlerFunc(s.handlerWrapper(s.getImage))
|
||||
}
|
||||
|
||||
// SetHook changes the hook function used by the server.
|
||||
//
|
||||
// The hook function is a function called on every request.
|
||||
func (s *DockerServer) SetHook(hook func(*http.Request)) {
|
||||
s.hook = hook
|
||||
}
|
||||
|
||||
// PrepareExec adds a callback to a container exec in the fake server.
|
||||
//
|
||||
// This function will be called whenever the given exec id is started, and the
|
||||
// given exec id will remain in the "Running" start while the function is
|
||||
// running, so it's useful for emulating an exec that runs for two seconds, for
|
||||
// example:
|
||||
//
|
||||
// opts := docker.CreateExecOptions{
|
||||
// AttachStdin: true,
|
||||
// AttachStdout: true,
|
||||
// AttachStderr: true,
|
||||
// Tty: true,
|
||||
// Cmd: []string{"/bin/bash", "-l"},
|
||||
// }
|
||||
// // Client points to a fake server.
|
||||
// exec, err := client.CreateExec(opts)
|
||||
// // handle error
|
||||
// server.PrepareExec(exec.ID, func() {time.Sleep(2 * time.Second)})
|
||||
// err = client.StartExec(exec.ID, docker.StartExecOptions{Tty: true}) // will block for 2 seconds
|
||||
// // handle error
|
||||
func (s *DockerServer) PrepareExec(id string, callback func()) {
|
||||
s.execCallbacks[id] = callback
|
||||
}
|
||||
|
||||
// PrepareFailure adds a new expected failure based on a URL regexp it receives
|
||||
// an id for the failure.
|
||||
func (s *DockerServer) PrepareFailure(id string, urlRegexp string) {
|
||||
s.failures[id] = urlRegexp
|
||||
}
|
||||
|
||||
// PrepareMultiFailures enqueues a new expected failure based on a URL regexp
|
||||
// it receives an id for the failure.
|
||||
func (s *DockerServer) PrepareMultiFailures(id string, urlRegexp string) {
|
||||
s.multiFailures = append(s.multiFailures, map[string]string{"error": id, "url": urlRegexp})
|
||||
}
|
||||
|
||||
// ResetFailure removes an expected failure identified by the given id.
|
||||
func (s *DockerServer) ResetFailure(id string) {
|
||||
delete(s.failures, id)
|
||||
}
|
||||
|
||||
// ResetMultiFailures removes all enqueued failures.
|
||||
func (s *DockerServer) ResetMultiFailures() {
|
||||
s.multiFailures = []map[string]string{}
|
||||
}
|
||||
|
||||
// CustomHandler registers a custom handler for a specific path.
|
||||
//
|
||||
// For example:
|
||||
|
@ -170,10 +217,12 @@ func (s *DockerServer) URL() string {
|
|||
func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.handlerMutex.RLock()
|
||||
defer s.handlerMutex.RUnlock()
|
||||
if handler, ok := s.customHandlers[r.URL.Path]; ok {
|
||||
for re, handler := range s.customHandlers {
|
||||
if m, _ := regexp.MatchString(re, r.URL.Path); m {
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
s.mux.ServeHTTP(w, r)
|
||||
if s.hook != nil {
|
||||
s.hook(r)
|
||||
|
@ -200,6 +249,19 @@ func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)
|
|||
http.Error(w, errorID, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
for i, failure := range s.multiFailures {
|
||||
matched, err := regexp.MatchString(failure["url"], r.URL.Path)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
http.Error(w, failure["error"], http.StatusBadRequest)
|
||||
s.multiFailures = append(s.multiFailures[:i], s.multiFailures[i+1:]...)
|
||||
return
|
||||
}
|
||||
f(w, r)
|
||||
}
|
||||
}
|
||||
|
@ -336,6 +398,23 @@ func (s *DockerServer) generateID() string {
|
|||
return fmt.Sprintf("%x", buf)
|
||||
}
|
||||
|
||||
func (s *DockerServer) renameContainer(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
container, index, err := s.findContainer(id)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
copy := *container
|
||||
copy.Name = r.URL.Query().Get("name")
|
||||
s.cMut.Lock()
|
||||
defer s.cMut.Unlock()
|
||||
if s.containers[index].ID == copy.ID {
|
||||
s.containers[index] = ©
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *DockerServer) inspectContainer(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
container, _, err := s.findContainer(id)
|
||||
|
@ -524,9 +603,13 @@ func (s *DockerServer) commitContainer(w http.ResponseWriter, r *http.Request) {
|
|||
Config: config,
|
||||
}
|
||||
repository := r.URL.Query().Get("repo")
|
||||
tag := r.URL.Query().Get("tag")
|
||||
s.iMut.Lock()
|
||||
s.images = append(s.images, image)
|
||||
if repository != "" {
|
||||
if tag != "" {
|
||||
repository += ":" + tag
|
||||
}
|
||||
s.imgIDs[repository] = image.ID
|
||||
}
|
||||
s.iMut.Unlock()
|
||||
|
@ -582,20 +665,28 @@ func (s *DockerServer) buildImage(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (s *DockerServer) pullImage(w http.ResponseWriter, r *http.Request) {
|
||||
repository := r.URL.Query().Get("fromImage")
|
||||
fromImageName := r.URL.Query().Get("fromImage")
|
||||
tag := r.URL.Query().Get("tag")
|
||||
image := docker.Image{
|
||||
ID: s.generateID(),
|
||||
}
|
||||
s.iMut.Lock()
|
||||
s.images = append(s.images, image)
|
||||
if repository != "" {
|
||||
s.imgIDs[repository] = image.ID
|
||||
if fromImageName != "" {
|
||||
if tag != "" {
|
||||
fromImageName = fmt.Sprintf("%s:%s", fromImageName, tag)
|
||||
}
|
||||
s.imgIDs[fromImageName] = image.ID
|
||||
}
|
||||
s.iMut.Unlock()
|
||||
}
|
||||
|
||||
func (s *DockerServer) pushImage(w http.ResponseWriter, r *http.Request) {
|
||||
name := mux.Vars(r)["name"]
|
||||
tag := r.URL.Query().Get("tag")
|
||||
if tag != "" {
|
||||
name += ":" + tag
|
||||
}
|
||||
s.iMut.RLock()
|
||||
if _, ok := s.imgIDs[name]; !ok {
|
||||
s.iMut.RUnlock()
|
||||
|
@ -619,6 +710,10 @@ func (s *DockerServer) tagImage(w http.ResponseWriter, r *http.Request) {
|
|||
s.iMut.Lock()
|
||||
defer s.iMut.Unlock()
|
||||
newRepo := r.URL.Query().Get("repo")
|
||||
newTag := r.URL.Query().Get("tag")
|
||||
if newTag != "" {
|
||||
newRepo += ":" + newTag
|
||||
}
|
||||
s.imgIDs[newRepo] = s.imgIDs[name]
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
@ -722,7 +817,6 @@ func (s *DockerServer) loadImage(w http.ResponseWriter, r *http.Request) {
|
|||
func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/tar")
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -733,7 +827,7 @@ func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Reques
|
|||
return
|
||||
}
|
||||
exec := docker.ExecInspect{
|
||||
ID: "id-exec-created-by-test",
|
||||
ID: s.generateID(),
|
||||
Container: *container,
|
||||
}
|
||||
var params docker.CreateExecOptions
|
||||
|
@ -748,44 +842,63 @@ func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Reques
|
|||
exec.ProcessConfig.Arguments = params.Cmd[1:]
|
||||
}
|
||||
}
|
||||
s.execMut.Lock()
|
||||
s.execs = append(s.execs, &exec)
|
||||
s.execMut.Unlock()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]string{"Id": exec.ID})
|
||||
|
||||
}
|
||||
|
||||
func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
for _, exec := range s.execs {
|
||||
if exec.ID == id {
|
||||
if exec, err := s.getExec(id); err == nil {
|
||||
s.execMut.Lock()
|
||||
exec.Running = true
|
||||
s.execMut.Unlock()
|
||||
if callback, ok := s.execCallbacks[id]; ok {
|
||||
callback()
|
||||
delete(s.execCallbacks, id)
|
||||
} else if callback, ok := s.execCallbacks["*"]; ok {
|
||||
callback()
|
||||
delete(s.execCallbacks, "*")
|
||||
}
|
||||
s.execMut.Lock()
|
||||
exec.Running = false
|
||||
s.execMut.Unlock()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *DockerServer) resizeExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
for _, exec := range s.execs {
|
||||
if exec.ID == id {
|
||||
if _, err := s.getExec(id); err == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *DockerServer) inspectExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||
id := mux.Vars(r)["id"]
|
||||
for _, exec := range s.execs {
|
||||
if exec.ID == id {
|
||||
if exec, err := s.getExec(id); err == nil {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(exec)
|
||||
return
|
||||
}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *DockerServer) getExec(id string) (*docker.ExecInspect, error) {
|
||||
s.execMut.RLock()
|
||||
defer s.execMut.RUnlock()
|
||||
for _, exec := range s.execs {
|
||||
if exec.ID == id {
|
||||
return exec, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("exec not found")
|
||||
}
|
||||
|
|
305
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
305
Godeps/_workspace/src/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
||||
// 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.
|
||||
|
||||
|
@ -82,6 +82,19 @@ func TestHandleWithHook(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSetHook(t *testing.T) {
|
||||
var called bool
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
defer server.Stop()
|
||||
server.SetHook(func(*http.Request) { called = true })
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if !called {
|
||||
t.Error("ServeHTTP did not call the hook function.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomHandler(t *testing.T) {
|
||||
var called bool
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
|
@ -101,6 +114,25 @@ func TestCustomHandler(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCustomHandlerRegexp(t *testing.T) {
|
||||
var called bool
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
addContainers(server, 2)
|
||||
server.CustomHandler("/containers/.*/json", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
fmt.Fprint(w, "Hello world")
|
||||
}))
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/containers/.*/json?all=1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if !called {
|
||||
t.Error("Did not call the custom handler")
|
||||
}
|
||||
if got := recorder.Body.String(); got != "Hello world" {
|
||||
t.Errorf("Wrong output for custom handler: want %q. Got %q.", "Hello world", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListContainers(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 2)
|
||||
|
@ -223,6 +255,35 @@ func TestCreateContainerImageNotFound(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRenameContainer(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 2)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
newName := server.containers[0].Name + "abc"
|
||||
path := fmt.Sprintf("/containers/%s/rename?name=%s", server.containers[0].ID, newName)
|
||||
request, _ := http.NewRequest("POST", path, nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusNoContent {
|
||||
t.Errorf("RenameContainer: wrong status. Want %d. Got %d.", http.StatusNoContent, recorder.Code)
|
||||
}
|
||||
container := server.containers[0]
|
||||
if container.Name != newName {
|
||||
t.Errorf("RenameContainer: did not rename the container. Want %q. Got %q.", newName, container.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameContainerNotFound(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("POST", "/containers/blabla/rename?name=something", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusNotFound {
|
||||
t.Errorf("RenameContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitContainer(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 2)
|
||||
|
@ -277,6 +338,27 @@ func TestCommitContainerComplete(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestCommitContainerWithTag(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
server.imgIDs = make(map[string]string)
|
||||
addContainers(&server, 2)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
queryString := "container=" + server.containers[0].ID + "&repo=tsuru/python&tag=v1"
|
||||
request, _ := http.NewRequest("POST", "/commit?"+queryString, nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
image := server.images[0]
|
||||
if image.Parent != server.containers[0].Image {
|
||||
t.Errorf("CommitContainer: wrong parent image. Want %q. Got %q.", server.containers[0].Image, image.Parent)
|
||||
}
|
||||
if image.Container != server.containers[0].ID {
|
||||
t.Errorf("CommitContainer: wrong container. Want %q. Got %q.", server.containers[0].ID, image.Container)
|
||||
}
|
||||
if id := server.imgIDs["tsuru/python:v1"]; id != image.ID {
|
||||
t.Errorf("CommitContainer: wrong ID saved for repository. Want %q. Got %q.", image.ID, id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitContainerInvalidRun(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
addContainers(&server, 1)
|
||||
|
@ -767,6 +849,23 @@ func TestPullImage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPullImageWithTag(t *testing.T) {
|
||||
server := DockerServer{imgIDs: make(map[string]string)}
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("POST", "/images/create?fromImage=base&tag=tag", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Errorf("PullImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
if len(server.images) != 1 {
|
||||
t.Errorf("PullImage: Want 1 image. Got %d.", len(server.images))
|
||||
}
|
||||
if _, ok := server.imgIDs["base:tag"]; !ok {
|
||||
t.Error("PullImage: Repository should not be empty.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushImage(t *testing.T) {
|
||||
server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}}
|
||||
server.buildMuxer()
|
||||
|
@ -778,6 +877,17 @@ func TestPushImage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPushImageWithTag(t *testing.T) {
|
||||
server := DockerServer{imgIDs: map[string]string{"tsuru/python:v1": "a123"}}
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("POST", "/images/tsuru/python/push?tag=v1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Errorf("PushImage: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPushImageNotFound(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
server.buildMuxer()
|
||||
|
@ -803,6 +913,20 @@ func TestTagImage(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTagImageWithRepoAndTag(t *testing.T) {
|
||||
server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}}
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("POST", "/images/tsuru/python/tag?repo=tsuru/new-python&tag=v1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusCreated {
|
||||
t.Errorf("TagImage: wrong status. Want %d. Got %d.", http.StatusCreated, recorder.Code)
|
||||
}
|
||||
if server.imgIDs["tsuru/python"] != server.imgIDs["tsuru/new-python:v1"] {
|
||||
t.Errorf("TagImage: did not tag the image")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagImageNotFound(t *testing.T) {
|
||||
server := DockerServer{}
|
||||
server.buildMuxer()
|
||||
|
@ -986,6 +1110,41 @@ func TestPrepareFailure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPrepareMultiFailures(t *testing.T) {
|
||||
server := DockerServer{multiFailures: []map[string]string{}}
|
||||
server.buildMuxer()
|
||||
errorID := "multi error"
|
||||
server.PrepareMultiFailures(errorID, "containers/json")
|
||||
server.PrepareMultiFailures(errorID, "containers/json")
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusBadRequest {
|
||||
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||
}
|
||||
if recorder.Body.String() != errorID+"\n" {
|
||||
t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String())
|
||||
}
|
||||
recorder = httptest.NewRecorder()
|
||||
request, _ = http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusBadRequest {
|
||||
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusBadRequest, recorder.Code)
|
||||
}
|
||||
if recorder.Body.String() != errorID+"\n" {
|
||||
t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String())
|
||||
}
|
||||
recorder = httptest.NewRecorder()
|
||||
request, _ = http.NewRequest("GET", "/containers/json?all=1", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Errorf("PrepareFailure: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
if recorder.Body.String() == errorID+"\n" {
|
||||
t.Errorf("PrepareFailure: wrong message. Want %s. Got %s.", errorID, recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveFailure(t *testing.T) {
|
||||
server := DockerServer{failures: make(map[string]string)}
|
||||
server.buildMuxer()
|
||||
|
@ -1006,6 +1165,21 @@ func TestRemoveFailure(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestResetMultiFailures(t *testing.T) {
|
||||
server := DockerServer{multiFailures: []map[string]string{}}
|
||||
server.buildMuxer()
|
||||
errorID := "multi error"
|
||||
server.PrepareMultiFailures(errorID, "containers/json")
|
||||
server.PrepareMultiFailures(errorID, "containers/json")
|
||||
if len(server.multiFailures) != 2 {
|
||||
t.Errorf("PrepareMultiFailures: error adding multi failures.")
|
||||
}
|
||||
server.ResetMultiFailures()
|
||||
if len(server.multiFailures) != 0 {
|
||||
t.Errorf("ResetMultiFailures: error reseting multi failures.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMutateContainer(t *testing.T) {
|
||||
server := DockerServer{failures: make(map[string]string)}
|
||||
server.buildMuxer()
|
||||
|
@ -1169,3 +1343,132 @@ func TestInspectExecContainer(t *testing.T) {
|
|||
t.Errorf("InspectContainer: wrong value. Want:\n%#v\nGot:\n%#v\n", expected, got2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartExecContainer(t *testing.T) {
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
addContainers(server, 1)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
body := `{"Cmd": ["bash", "-c", "ls"]}`
|
||||
path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID)
|
||||
request, _ := http.NewRequest("POST", path, strings.NewReader(body))
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
var exec docker.Exec
|
||||
err := json.NewDecoder(recorder.Body).Decode(&exec)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unleash := make(chan bool)
|
||||
server.PrepareExec(exec.ID, func() {
|
||||
<-unleash
|
||||
})
|
||||
codes := make(chan int, 1)
|
||||
sent := make(chan bool)
|
||||
go func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
path := fmt.Sprintf("/exec/%s/start", exec.ID)
|
||||
body := `{"Tty":true}`
|
||||
request, _ := http.NewRequest("POST", path, strings.NewReader(body))
|
||||
close(sent)
|
||||
server.ServeHTTP(recorder, request)
|
||||
codes <- recorder.Code
|
||||
}()
|
||||
<-sent
|
||||
execInfo, err := waitExec(server.URL(), exec.ID, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !execInfo.Running {
|
||||
t.Error("StartExec: expected exec to be running, but it's not running")
|
||||
}
|
||||
close(unleash)
|
||||
if code := <-codes; code != http.StatusOK {
|
||||
t.Errorf("StartExec: wrong status. Want %d. Got %d.", http.StatusOK, code)
|
||||
}
|
||||
execInfo, err = waitExec(server.URL(), exec.ID, false, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if execInfo.Running {
|
||||
t.Error("StartExec: expected exec to be not running after start returns, but it's running")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartExecContainerWildcardCallback(t *testing.T) {
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
addContainers(server, 1)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
body := `{"Cmd": ["bash", "-c", "ls"]}`
|
||||
path := fmt.Sprintf("/containers/%s/exec", server.containers[0].ID)
|
||||
request, _ := http.NewRequest("POST", path, strings.NewReader(body))
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("CreateExec: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
unleash := make(chan bool)
|
||||
server.PrepareExec("*", func() {
|
||||
<-unleash
|
||||
})
|
||||
var exec docker.Exec
|
||||
err := json.NewDecoder(recorder.Body).Decode(&exec)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := make(chan int, 1)
|
||||
sent := make(chan bool)
|
||||
go func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
path := fmt.Sprintf("/exec/%s/start", exec.ID)
|
||||
body := `{"Tty":true}`
|
||||
request, _ := http.NewRequest("POST", path, strings.NewReader(body))
|
||||
close(sent)
|
||||
server.ServeHTTP(recorder, request)
|
||||
codes <- recorder.Code
|
||||
}()
|
||||
<-sent
|
||||
execInfo, err := waitExec(server.URL(), exec.ID, true, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !execInfo.Running {
|
||||
t.Error("StartExec: expected exec to be running, but it's not running")
|
||||
}
|
||||
close(unleash)
|
||||
if code := <-codes; code != http.StatusOK {
|
||||
t.Errorf("StartExec: wrong status. Want %d. Got %d.", http.StatusOK, code)
|
||||
}
|
||||
execInfo, err = waitExec(server.URL(), exec.ID, false, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if execInfo.Running {
|
||||
t.Error("StartExec: expected exec to be not running after start returns, but it's running")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartExecContainerNotFound(t *testing.T) {
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
addContainers(server, 1)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
body := `{"Tty":true}`
|
||||
request, _ := http.NewRequest("POST", "/exec/something-wat/start", strings.NewReader(body))
|
||||
server.ServeHTTP(recorder, request)
|
||||
}
|
||||
|
||||
func waitExec(url, execID string, running bool, maxTry int) (*docker.ExecInspect, error) {
|
||||
client, err := docker.NewClient(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exec, err := client.InspectExec(execID)
|
||||
for i := 0; i < maxTry && exec.Running != running && err == nil; i++ {
|
||||
time.Sleep(100e6)
|
||||
exec, err = client.InspectExec(exec.ID)
|
||||
}
|
||||
return exec, err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue