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",
|
"ImportPath": "github.com/fsouza/go-dockerclient",
|
||||||
"Rev": "d19717788084716e4adff0515be6289aa04bec46"
|
"Rev": "17d39bcb22e8103ba6d1c0cb2530c6434cb870a3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/garyburd/redigo/internal",
|
"ImportPath": "github.com/garyburd/redigo/internal",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.2.2
|
|
||||||
- 1.3.1
|
- 1.3.1
|
||||||
- 1.4
|
- 1.4
|
||||||
- tip
|
- tip
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
# This is the official list of go-dockerclient authors for copyright purposes.
|
# This is the official list of go-dockerclient authors for copyright purposes.
|
||||||
|
|
||||||
|
Adam Bell-Hanssen <adamb@aller.no>
|
||||||
Aldrin Leal <aldrin@leal.eng.br>
|
Aldrin Leal <aldrin@leal.eng.br>
|
||||||
Andreas Jaekle <andreas@jaekle.net>
|
Andreas Jaekle <andreas@jaekle.net>
|
||||||
Andrews Medina <andrewsmedina@gmail.com>
|
Andrews Medina <andrewsmedina@gmail.com>
|
||||||
Artem Sidorenko <artem@2realities.com>
|
Artem Sidorenko <artem@2realities.com>
|
||||||
Andy Goldstein <andy.goldstein@redhat.com>
|
Andy Goldstein <andy.goldstein@redhat.com>
|
||||||
|
Ben Marini <ben@remind101.com>
|
||||||
Ben McCann <benmccann.com>
|
Ben McCann <benmccann.com>
|
||||||
|
Brian Lalor <blalor@bravo5.org>
|
||||||
|
Burke Libbey <burke@libbey.me>
|
||||||
Carlos Diaz-Padron <cpadron@mozilla.com>
|
Carlos Diaz-Padron <cpadron@mozilla.com>
|
||||||
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
Cezar Sa Espinola <cezar.sa@corp.globo.com>
|
||||||
Cheah Chu Yeow <chuyeow@gmail.com>
|
Cheah Chu Yeow <chuyeow@gmail.com>
|
||||||
cheneydeng <cheneydeng@qq.com>
|
cheneydeng <cheneydeng@qq.com>
|
||||||
CMGS <ilskdw@gmail.com>
|
CMGS <ilskdw@gmail.com>
|
||||||
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
Daniel, Dao Quang Minh <dqminh89@gmail.com>
|
||||||
|
Darren Shepherd <darren@rancher.com>
|
||||||
David Huie <dahuie@gmail.com>
|
David Huie <dahuie@gmail.com>
|
||||||
Dawn Chen <dawnchen@google.com>
|
Dawn Chen <dawnchen@google.com>
|
||||||
Ed <edrocksit@gmail.com>
|
Ed <edrocksit@gmail.com>
|
||||||
Eric Anderson <anderson@copperegg.com>
|
Eric Anderson <anderson@copperegg.com>
|
||||||
Fabio Rehm <fgrehm@gmail.com>
|
Fabio Rehm <fgrehm@gmail.com>
|
||||||
Fatih Arslan <ftharsln@gmail.com>
|
Fatih Arslan <ftharsln@gmail.com>
|
||||||
Flavia Missi <flaviamissi@gmail.com>
|
Flavia Missi <flaviamissi@gmail.com>
|
||||||
Francisco Souza <f@souza.cc>
|
Francisco Souza <f@souza.cc>
|
||||||
|
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
||||||
Jari Kolehmainen <jari.kolehmainen@digia.com>
|
Jari Kolehmainen <jari.kolehmainen@digia.com>
|
||||||
Jason Wilder <jwilder@litl.com>
|
Jason Wilder <jwilder@litl.com>
|
||||||
Jawher Moussa <jawher.moussa@gmail.com>
|
Jawher Moussa <jawher.moussa@gmail.com>
|
||||||
|
@ -30,19 +36,26 @@ Johan Euphrosine <proppy@google.com>
|
||||||
Kamil Domanski <kamil@domanski.co>
|
Kamil Domanski <kamil@domanski.co>
|
||||||
Karan Misra <kidoman@gmail.com>
|
Karan Misra <kidoman@gmail.com>
|
||||||
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
|
Kim, Hirokuni <hirokuni.kim@kvh.co.jp>
|
||||||
|
liron-l <levinlir@gmail.com>
|
||||||
Lucas Clemente <lucas@clemente.io>
|
Lucas Clemente <lucas@clemente.io>
|
||||||
|
Lucas Weiblen <lucasweiblen@gmail.com>
|
||||||
|
Mantas Matelis <mmatelis@coursera.org>
|
||||||
Martin Sweeney <martin@sweeney.io>
|
Martin Sweeney <martin@sweeney.io>
|
||||||
Máximo Cuadros Ortiz <mcuadros@gmail.com>
|
Máximo Cuadros Ortiz <mcuadros@gmail.com>
|
||||||
|
Michal Fojtik <mfojtik@redhat.com>
|
||||||
Mike Dillon <mike.dillon@synctree.com>
|
Mike Dillon <mike.dillon@synctree.com>
|
||||||
Mrunal Patel <mrunalp@gmail.com>
|
Mrunal Patel <mrunalp@gmail.com>
|
||||||
|
Nick Ethier <ncethier@gmail.com>
|
||||||
Omeid Matten <public@omeid.me>
|
Omeid Matten <public@omeid.me>
|
||||||
Paul Morie <pmorie@gmail.com>
|
Paul Morie <pmorie@gmail.com>
|
||||||
|
Paul Weil <pweil@redhat.com>
|
||||||
Peter Jihoon Kim <raingrove@gmail.com>
|
Peter Jihoon Kim <raingrove@gmail.com>
|
||||||
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||||
Rafe Colton <rafael.colton@gmail.com>
|
Rafe Colton <rafael.colton@gmail.com>
|
||||||
Rob Miller <rob@kalistra.com>
|
Rob Miller <rob@kalistra.com>
|
||||||
Robert Williamson <williamson.robert@gmail.com>
|
Robert Williamson <williamson.robert@gmail.com>
|
||||||
Salvador Gironès <salvadorgirones@gmail.com>
|
Salvador Gironès <salvadorgirones@gmail.com>
|
||||||
|
Sam Rijs <srijs@airpost.net>
|
||||||
Simon Eskildsen <sirup@sirupsen.com>
|
Simon Eskildsen <sirup@sirupsen.com>
|
||||||
Simon Menke <simon.menke@gmail.com>
|
Simon Menke <simon.menke@gmail.com>
|
||||||
Skolos <skolos@gopherlab.com>
|
Skolos <skolos@gopherlab.com>
|
||||||
|
@ -51,5 +64,7 @@ Sridhar Ratnakumar <sridharr@activestate.com>
|
||||||
Summer Mousa <smousa@zenoss.com>
|
Summer Mousa <smousa@zenoss.com>
|
||||||
Tarsis Azevedo <tarsis@corp.globo.com>
|
Tarsis Azevedo <tarsis@corp.globo.com>
|
||||||
Tim Schindler <tim@catalyst-zero.com>
|
Tim Schindler <tim@catalyst-zero.com>
|
||||||
|
Vincenzo Prignano <vincenzo.prignano@gmail.com>
|
||||||
Wiliam Souza <wiliamsouza83@gmail.com>
|
Wiliam Souza <wiliamsouza83@gmail.com>
|
||||||
Ye Yin <eyniy@qq.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.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
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://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)
|
[![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
|
## Example
|
||||||
|
|
||||||
|
@ -15,22 +16,49 @@ For more details, check the [remote API documentation](http://docs.docker.io/en/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fsouza/go-dockerclient"
|
|
||||||
|
"github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
endpoint := "unix:///var/run/docker.sock"
|
endpoint := "unix:///var/run/docker.sock"
|
||||||
client, _ := docker.NewClient(endpoint)
|
client, _ := docker.NewClient(endpoint)
|
||||||
imgs, _ := client.ListImages(docker.ListImagesOptions{All: false})
|
imgs, _ := client.ListImages(docker.ListImagesOptions{All: false})
|
||||||
for _, img := range imgs {
|
for _, img := range imgs {
|
||||||
fmt.Println("ID: ", img.ID)
|
fmt.Println("ID: ", img.ID)
|
||||||
fmt.Println("RepoTags: ", img.RepoTags)
|
fmt.Println("RepoTags: ", img.RepoTags)
|
||||||
fmt.Println("Created: ", img.Created)
|
fmt.Println("Created: ", img.Created)
|
||||||
fmt.Println("Size: ", img.Size)
|
fmt.Println("Size: ", img.Size)
|
||||||
fmt.Println("VirtualSize: ", img.VirtualSize)
|
fmt.Println("VirtualSize: ", img.VirtualSize)
|
||||||
fmt.Println("ParentId: ", img.ParentID)
|
fmt.Println("ParentId: ", img.ParentID)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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 is returned when the client cannot connect to the given endpoint.
|
||||||
ErrConnectionRefused = errors.New("cannot connect to Docker 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.
|
// 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
|
// server endpoint, key and certificates . It will use the latest remote API version
|
||||||
// available in the server.
|
// available in the server.
|
||||||
func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// NewVersionedClient returns a Client instance ready for communication with
|
||||||
// the given server endpoint, using a specific remote API version.
|
// the given server endpoint, using a specific remote API version.
|
||||||
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
|
func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) {
|
||||||
u, err := parseEndpoint(endpoint)
|
u, err := parseEndpoint(endpoint, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -174,10 +174,15 @@ func NewVersionedClient(endpoint string, apiVersionString string) (*Client, erro
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVersionnedTLSClient returns a Client instance ready for TLS communications with the givens
|
// NewVersionnedTLSClient has been DEPRECATED, please use NewVersionedTLSClient.
|
||||||
// server endpoint, key and certificates, using a specific remote API version.
|
|
||||||
func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -247,7 +252,7 @@ func (c *Client) checkAPIVersion() error {
|
||||||
// See http://goo.gl/stJENm for more details.
|
// See http://goo.gl/stJENm for more details.
|
||||||
func (c *Client) Ping() error {
|
func (c *Client) Ping() error {
|
||||||
path := "/_ping"
|
path := "/_ping"
|
||||||
body, status, err := c.do("GET", path, nil)
|
body, status, err := c.do("GET", path, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -258,7 +263,7 @@ func (c *Client) Ping() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) getServerAPIVersionString() (version string, err 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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -274,9 +279,9 @@ func (c *Client) getServerAPIVersionString() (version string, err error) {
|
||||||
return version, nil
|
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
|
var params io.Reader
|
||||||
if data != nil {
|
if data != nil || forceJSON {
|
||||||
buf, err := json.Marshal(data)
|
buf, err := json.Marshal(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
|
@ -599,11 +604,14 @@ func (e *Error) Error() string {
|
||||||
return fmt.Sprintf("API error (%d): %s", e.Status, e.Message)
|
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)
|
u, err := url.Parse(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrInvalidEndpoint
|
return nil, ErrInvalidEndpoint
|
||||||
}
|
}
|
||||||
|
if tls {
|
||||||
|
u.Scheme = "https"
|
||||||
|
}
|
||||||
if u.Scheme == "tcp" {
|
if u.Scheme == "tcp" {
|
||||||
_, port, err := net.SplitHostPort(u.Host)
|
_, port, err := net.SplitHostPort(u.Host)
|
||||||
if err != nil {
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func TestNewTLSVersionedClient(t *testing.T) {
|
||||||
keyPath := "testing/data/key.pem"
|
keyPath := "testing/data/key.pem"
|
||||||
caPath := "testing/data/ca.pem"
|
caPath := "testing/data/ca.pem"
|
||||||
endpoint := "https://localhost:4243"
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ func TestNewTLSVersionedClientInvalidCA(t *testing.T) {
|
||||||
keyPath := "testing/data/key.pem"
|
keyPath := "testing/data/key.pem"
|
||||||
caPath := "testing/data/key.pem"
|
caPath := "testing/data/key.pem"
|
||||||
endpoint := "https://localhost:4243"
|
endpoint := "https://localhost:4243"
|
||||||
_, err := NewVersionnedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
_, err := NewVersionedTLSClient(endpoint, certPath, keyPath, caPath, "1.14")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected invalid ca at %s", caPath)
|
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 {
|
var tests = []struct {
|
||||||
endpoint string
|
endpoint string
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{"tcp://localhost:2376", "https"},
|
{"tcp://localhost:2376", "https"},
|
||||||
{"tcp://localhost:2375", "http"},
|
{"tcp://localhost:2375", "https"},
|
||||||
{"tcp://localhost:4000", "http"},
|
{"tcp://localhost:4000", "https"},
|
||||||
|
{"http://localhost:4000", "https"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ type APIContainers struct {
|
||||||
// See http://goo.gl/6Y4Gz7 for more details.
|
// See http://goo.gl/6Y4Gz7 for more details.
|
||||||
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
||||||
path := "/containers/json?" + queryString(opts)
|
path := "/containers/json?" + queryString(opts)
|
||||||
body, _, err := c.do("GET", path, nil)
|
body, _, err := c.do("GET", path, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 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
|
// given host. Those are contained in HostConfig
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"`
|
Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty"`
|
||||||
|
@ -193,6 +193,26 @@ type Config struct {
|
||||||
WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"`
|
WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty"`
|
||||||
Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty"`
|
Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty"`
|
||||||
NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,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,
|
// 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"`
|
State State `json:"State,omitempty" yaml:"State,omitempty"`
|
||||||
Image string `json:"Image,omitempty" yaml:"Image,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"`
|
NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
|
||||||
|
|
||||||
SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,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"`
|
Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty"`
|
||||||
VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"`
|
VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty"`
|
||||||
HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,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.
|
// InspectContainer returns information about a container by its ID.
|
||||||
|
@ -228,7 +272,7 @@ type Container struct {
|
||||||
// See http://goo.gl/CxVuJ5 for more details.
|
// See http://goo.gl/CxVuJ5 for more details.
|
||||||
func (c *Client) InspectContainer(id string) (*Container, error) {
|
func (c *Client) InspectContainer(id string) (*Container, error) {
|
||||||
path := "/containers/" + id + "/json"
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, &NoSuchContainer{ID: id}
|
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.
|
// See http://goo.gl/QkW9sH for more details.
|
||||||
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
||||||
path := "/containers/" + id + "/changes"
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, &NoSuchContainer{ID: id}
|
return nil, &NoSuchContainer{ID: id}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +328,7 @@ func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error
|
||||||
}{
|
}{
|
||||||
opts.Config,
|
opts.Config,
|
||||||
opts.HostConfig,
|
opts.HostConfig,
|
||||||
})
|
}, false)
|
||||||
|
|
||||||
if status == http.StatusNotFound {
|
if status == http.StatusNotFound {
|
||||||
return nil, ErrNoSuchImage
|
return nil, ErrNoSuchImage
|
||||||
|
@ -341,6 +385,14 @@ func NeverRestart() RestartPolicy {
|
||||||
return RestartPolicy{Name: "no"}
|
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
|
// HostConfig contains the container options related to starting a container on
|
||||||
// a given host
|
// a given host
|
||||||
type HostConfig struct {
|
type HostConfig struct {
|
||||||
|
@ -359,20 +411,22 @@ type HostConfig struct {
|
||||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,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"`
|
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.
|
// StartContainer starts a container, returning an error in case of failure.
|
||||||
//
|
//
|
||||||
// See http://goo.gl/iM5GYs for more details.
|
// See http://goo.gl/iM5GYs for more details.
|
||||||
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
||||||
if hostConfig == nil {
|
|
||||||
hostConfig = &HostConfig{}
|
|
||||||
}
|
|
||||||
path := "/containers/" + id + "/start"
|
path := "/containers/" + id + "/start"
|
||||||
_, status, err := c.do("POST", path, hostConfig)
|
_, status, err := c.do("POST", path, hostConfig, true)
|
||||||
if status == http.StatusNotFound {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: id}
|
return &NoSuchContainer{ID: id, Err: err}
|
||||||
}
|
}
|
||||||
if status == http.StatusNotModified {
|
if status == http.StatusNotModified {
|
||||||
return &ContainerAlreadyRunning{ID: id}
|
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.
|
// See http://goo.gl/EbcpXt for more details.
|
||||||
func (c *Client) StopContainer(id string, timeout uint) error {
|
func (c *Client) StopContainer(id string, timeout uint) error {
|
||||||
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: id}
|
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.
|
// See http://goo.gl/VOzR2n for more details.
|
||||||
func (c *Client) RestartContainer(id string, timeout uint) error {
|
func (c *Client) RestartContainer(id string, timeout uint) error {
|
||||||
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: id}
|
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.
|
// See http://goo.gl/AM5t42 for more details.
|
||||||
func (c *Client) PauseContainer(id string) error {
|
func (c *Client) PauseContainer(id string) error {
|
||||||
path := fmt.Sprintf("/containers/%s/pause", id)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: id}
|
return &NoSuchContainer{ID: id}
|
||||||
}
|
}
|
||||||
|
@ -438,7 +492,7 @@ func (c *Client) PauseContainer(id string) error {
|
||||||
// See http://goo.gl/eBrNSL for more details.
|
// See http://goo.gl/eBrNSL for more details.
|
||||||
func (c *Client) UnpauseContainer(id string) error {
|
func (c *Client) UnpauseContainer(id string) error {
|
||||||
path := fmt.Sprintf("/containers/%s/unpause", id)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: id}
|
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)
|
args = fmt.Sprintf("?ps_args=%s", psArgs)
|
||||||
}
|
}
|
||||||
path := fmt.Sprintf("/containers/%s/top%s", id, args)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return result, &NoSuchContainer{ID: id}
|
return result, &NoSuchContainer{ID: id}
|
||||||
}
|
}
|
||||||
|
@ -499,7 +553,7 @@ type KillContainerOptions struct {
|
||||||
// See http://goo.gl/TFkECx for more details.
|
// See http://goo.gl/TFkECx for more details.
|
||||||
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
||||||
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: opts.ID}
|
return &NoSuchContainer{ID: opts.ID}
|
||||||
}
|
}
|
||||||
|
@ -530,7 +584,7 @@ type RemoveContainerOptions struct {
|
||||||
// See http://goo.gl/ZB83ji for more details.
|
// See http://goo.gl/ZB83ji for more details.
|
||||||
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
||||||
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: opts.ID}
|
return &NoSuchContainer{ID: opts.ID}
|
||||||
}
|
}
|
||||||
|
@ -559,7 +613,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||||
return &NoSuchContainer{ID: opts.Container}
|
return &NoSuchContainer{ID: opts.Container}
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("/containers/%s/copy", 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 {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchContainer{ID: opts.Container}
|
return &NoSuchContainer{ID: opts.Container}
|
||||||
}
|
}
|
||||||
|
@ -575,7 +629,7 @@ func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/J88DHU for more details.
|
// See http://goo.gl/J88DHU for more details.
|
||||||
func (c *Client) WaitContainer(id string) (int, error) {
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return 0, &NoSuchContainer{ID: id}
|
return 0, &NoSuchContainer{ID: id}
|
||||||
}
|
}
|
||||||
|
@ -607,7 +661,7 @@ type CommitContainerOptions struct {
|
||||||
// See http://goo.gl/Jn8pe8 for more details.
|
// See http://goo.gl/Jn8pe8 for more details.
|
||||||
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
||||||
path := "/commit?" + queryString(opts)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, &NoSuchContainer{ID: opts.Container}
|
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 := make(url.Values)
|
||||||
params.Set("h", strconv.Itoa(height))
|
params.Set("h", strconv.Itoa(height))
|
||||||
params.Set("w", strconv.Itoa(width))
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,10 +787,14 @@ func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
||||||
|
|
||||||
// NoSuchContainer is the error returned when a given container does not exist.
|
// NoSuchContainer is the error returned when a given container does not exist.
|
||||||
type NoSuchContainer struct {
|
type NoSuchContainer struct {
|
||||||
ID string
|
ID string
|
||||||
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *NoSuchContainer) Error() string {
|
func (err *NoSuchContainer) Error() string {
|
||||||
|
if err.Err != nil {
|
||||||
|
return err.Err.Error()
|
||||||
|
}
|
||||||
return "No such container: " + err.ID
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package docker
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -154,6 +155,7 @@ func TestListContainersFailure(t *testing.T) {
|
||||||
func TestInspectContainer(t *testing.T) {
|
func TestInspectContainer(t *testing.T) {
|
||||||
jsonContainer := `{
|
jsonContainer := `{
|
||||||
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
|
"Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
|
||||||
|
"AppArmorProfile": "Profile",
|
||||||
"Created": "2013-05-07T14:51:42.087658+02:00",
|
"Created": "2013-05-07T14:51:42.087658+02:00",
|
||||||
"Path": "date",
|
"Path": "date",
|
||||||
"Args": [],
|
"Args": [],
|
||||||
|
@ -175,7 +177,10 @@ func TestInspectContainer(t *testing.T) {
|
||||||
],
|
],
|
||||||
"Image": "base",
|
"Image": "base",
|
||||||
"Volumes": {},
|
"Volumes": {},
|
||||||
"VolumesFrom": ""
|
"VolumesFrom": "",
|
||||||
|
"SecurityOpt": [
|
||||||
|
"label:user:USER"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"State": {
|
"State": {
|
||||||
"Running": false,
|
"Running": false,
|
||||||
|
@ -184,6 +189,21 @@ func TestInspectContainer(t *testing.T) {
|
||||||
"StartedAt": "2013-05-07T14:51:42.087658+02:00",
|
"StartedAt": "2013-05-07T14:51:42.087658+02:00",
|
||||||
"Ghost": false
|
"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",
|
"Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||||||
"NetworkSettings": {
|
"NetworkSettings": {
|
||||||
"IpAddress": "",
|
"IpAddress": "",
|
||||||
|
@ -511,12 +531,17 @@ func TestStartContainerNilHostConfig(t *testing.T) {
|
||||||
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
|
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)
|
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) {
|
func TestStartContainerNotFound(t *testing.T) {
|
||||||
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
|
client := newTestClient(&FakeRoundTripper{message: "no such container", status: http.StatusNotFound})
|
||||||
err := client.StartContainer("a2344", &HostConfig{})
|
err := client.StartContainer("a2344", &HostConfig{})
|
||||||
expected := &NoSuchContainer{ID: "a2344"}
|
expected := &NoSuchContainer{ID: "a2344", Err: err.(*NoSuchContainer).Err}
|
||||||
if !reflect.DeepEqual(err, expected) {
|
if !reflect.DeepEqual(err, expected) {
|
||||||
t.Errorf("StartContainer: Wrong error returned. Want %#v. Got %#v.", expected, err)
|
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) {
|
func TestExportContainer(t *testing.T) {
|
||||||
content := "exported container tar content"
|
content := "exported container tar content"
|
||||||
out := stdoutMock{bytes.NewBufferString(content)}
|
out := stdoutMock{bytes.NewBufferString(content)}
|
||||||
|
@ -1311,7 +1344,7 @@ func TestExportContainerViaUnixSocket(t *testing.T) {
|
||||||
tempSocket := tempfile("export_socket")
|
tempSocket := tempfile("export_socket")
|
||||||
defer os.Remove(tempSocket)
|
defer os.Remove(tempSocket)
|
||||||
endpoint := "unix://" + tempSocket
|
endpoint := "unix://" + tempSocket
|
||||||
u, _ := parseEndpoint(endpoint)
|
u, _ := parseEndpoint(endpoint, false)
|
||||||
client := Client{
|
client := Client{
|
||||||
HTTPClient: http.DefaultClient,
|
HTTPClient: http.DefaultClient,
|
||||||
endpoint: endpoint,
|
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())
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -182,8 +182,8 @@ func (eventState *eventMonitoringState) monitorEvents(c *Client) {
|
||||||
eventState.terminate()
|
eventState.terminate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
eventState.updateLastSeen(ev)
|
||||||
go eventState.sendEvent(ev)
|
go eventState.sendEvent(ev)
|
||||||
go eventState.updateLastSeen(ev)
|
|
||||||
case err = <-eventState.errC:
|
case err = <-eventState.errC:
|
||||||
if err == ErrNoListeners {
|
if err == ErrNoListeners {
|
||||||
eventState.terminate()
|
eventState.terminate()
|
||||||
|
@ -227,7 +227,7 @@ func (eventState *eventMonitoringState) sendEvent(event *APIEvents) {
|
||||||
eventState.Add(1)
|
eventState.Add(1)
|
||||||
defer eventState.Done()
|
defer eventState.Done()
|
||||||
if eventState.isEnabled() {
|
if eventState.isEnabled() {
|
||||||
if eventState.noListeners() {
|
if len(eventState.listeners) == 0 {
|
||||||
eventState.errC <- ErrNoListeners
|
eventState.errC <- ErrNoListeners
|
||||||
return
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ type ExecInspect struct {
|
||||||
// See http://goo.gl/8izrzI for more details
|
// See http://goo.gl/8izrzI for more details
|
||||||
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) {
|
||||||
path := fmt.Sprintf("/containers/%s/exec", opts.Container)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, &NoSuchContainer{ID: opts.Container}
|
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)
|
path := fmt.Sprintf("/exec/%s/start", id)
|
||||||
|
|
||||||
if opts.Detach {
|
if opts.Detach {
|
||||||
_, status, err := c.do("POST", path, opts)
|
_, status, err := c.do("POST", path, opts, false)
|
||||||
if status == http.StatusNotFound {
|
if status == http.StatusNotFound {
|
||||||
return &NoSuchExec{ID: id}
|
return &NoSuchExec{ID: id}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||||
params.Set("w", strconv.Itoa(width))
|
params.Set("w", strconv.Itoa(width))
|
||||||
|
|
||||||
path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode())
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ func (c *Client) ResizeExecTTY(id string, height, width int) error {
|
||||||
// See http://goo.gl/ypQULN for more details
|
// See http://goo.gl/ypQULN for more details
|
||||||
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
func (c *Client) InspectExec(id string) (*ExecInspect, error) {
|
||||||
path := fmt.Sprintf("/exec/%s/json", id)
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, &NoSuchExec{ID: id}
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ var (
|
||||||
// See http://goo.gl/2rOLFF for more details.
|
// See http://goo.gl/2rOLFF for more details.
|
||||||
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||||
path := "/images/json?" + queryString(opts)
|
path := "/images/json?" + queryString(opts)
|
||||||
body, _, err := c.do("GET", path, nil)
|
body, _, err := c.do("GET", path, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/2oJmNs for more details.
|
// See http://goo.gl/2oJmNs for more details.
|
||||||
func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, ErrNoSuchImage
|
return nil, ErrNoSuchImage
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,29 @@ func (c *Client) ImageHistory(name string) ([]ImageHistory, error) {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/znj0wM for more details.
|
// See http://goo.gl/znj0wM for more details.
|
||||||
func (c *Client) RemoveImage(name string) error {
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return ErrNoSuchImage
|
return ErrNoSuchImage
|
||||||
}
|
}
|
||||||
|
@ -145,7 +167,7 @@ func (c *Client) RemoveImage(name string) error {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/Q112NY for more details.
|
// See http://goo.gl/Q112NY for more details.
|
||||||
func (c *Client) InspectImage(name string) (*Image, error) {
|
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 {
|
if status == http.StatusNotFound {
|
||||||
return nil, ErrNoSuchImage
|
return nil, ErrNoSuchImage
|
||||||
}
|
}
|
||||||
|
@ -156,7 +178,7 @@ func (c *Client) InspectImage(name string) (*Image, error) {
|
||||||
var image Image
|
var image Image
|
||||||
|
|
||||||
// if the caller elected to skip checking the server's version, assume it's the latest
|
// 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)
|
err = json.Unmarshal(body, &image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -201,21 +223,6 @@ type PushImageOptions struct {
|
||||||
RawJSONStream bool `qs:"-"`
|
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.
|
// PushImage pushes an image to a remote registry, logging progress to w.
|
||||||
//
|
//
|
||||||
// An empty instance of AuthConfiguration may be used for unauthenticated
|
// An empty instance of AuthConfiguration may be used for unauthenticated
|
||||||
|
@ -245,7 +252,7 @@ type PullImageOptions struct {
|
||||||
RawJSONStream bool `qs:"-"`
|
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.
|
// See http://goo.gl/ACyYNS for more details.
|
||||||
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error {
|
||||||
|
@ -333,6 +340,7 @@ func (c *Client) ImportImage(opts ImportImageOptions) error {
|
||||||
// http://goo.gl/tlPXPu.
|
// http://goo.gl/tlPXPu.
|
||||||
type BuildImageOptions struct {
|
type BuildImageOptions struct {
|
||||||
Name string `qs:"t"`
|
Name string `qs:"t"`
|
||||||
|
Dockerfile string `qs:"dockerfile"`
|
||||||
NoCache bool `qs:"nocache"`
|
NoCache bool `qs:"nocache"`
|
||||||
SuppressOutput bool `qs:"q"`
|
SuppressOutput bool `qs:"q"`
|
||||||
RmTmpContainer bool `qs:"rm"`
|
RmTmpContainer bool `qs:"rm"`
|
||||||
|
@ -395,7 +403,8 @@ func (c *Client) TagImage(name string, opts TagImageOptions) error {
|
||||||
return ErrNoSuchImage
|
return ErrNoSuchImage
|
||||||
}
|
}
|
||||||
_, status, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s",
|
_, status, err := c.do("POST", fmt.Sprintf("/images/"+name+"/tag?%s",
|
||||||
queryString(&opts)), nil)
|
queryString(&opts)), nil, false)
|
||||||
|
|
||||||
if status == http.StatusNotFound {
|
if status == http.StatusNotFound {
|
||||||
return ErrNoSuchImage
|
return ErrNoSuchImage
|
||||||
}
|
}
|
||||||
|
@ -445,7 +454,7 @@ type APIImageSearch struct {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/xI5lLZ for more details.
|
// See http://goo.gl/xI5lLZ for more details.
|
||||||
func (c *Client) SearchImages(term string) ([]APIImageSearch, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
func newTestClient(rt *FakeRoundTripper) Client {
|
func newTestClient(rt *FakeRoundTripper) Client {
|
||||||
endpoint := "http://localhost:4243"
|
endpoint := "http://localhost:4243"
|
||||||
u, _ := parseEndpoint("http://localhost:4243")
|
u, _ := parseEndpoint("http://localhost:4243", false)
|
||||||
client := Client{
|
client := Client{
|
||||||
HTTPClient: &http.Client{Transport: rt},
|
HTTPClient: &http.Client{Transport: rt},
|
||||||
endpoint: endpoint,
|
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) {
|
func TestInspectImage(t *testing.T) {
|
||||||
body := `{
|
body := `{
|
||||||
"id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
"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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import (
|
||||||
//
|
//
|
||||||
// See http://goo.gl/BOZrF5 for more details.
|
// See http://goo.gl/BOZrF5 for more details.
|
||||||
func (c *Client) Version() (*Env, error) {
|
func (c *Client) Version() (*Env, error) {
|
||||||
body, _, err := c.do("GET", "/version", nil)
|
body, _, err := c.do("GET", "/version", nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func (c *Client) Version() (*Env, error) {
|
||||||
//
|
//
|
||||||
// See http://goo.gl/wmqZsW for more details.
|
// See http://goo.gl/wmqZsW for more details.
|
||||||
func (c *Client) Info() (*Env, error) {
|
func (c *Client) Info() (*Env, error) {
|
||||||
body, _, err := c.do("GET", "/info", nil)
|
body, _, err := c.do("GET", "/info", nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import (
|
||||||
type DockerServer struct {
|
type DockerServer struct {
|
||||||
containers []*docker.Container
|
containers []*docker.Container
|
||||||
execs []*docker.ExecInspect
|
execs []*docker.ExecInspect
|
||||||
|
execMut sync.RWMutex
|
||||||
cMut sync.RWMutex
|
cMut sync.RWMutex
|
||||||
images []docker.Image
|
images []docker.Image
|
||||||
iMut sync.RWMutex
|
iMut sync.RWMutex
|
||||||
|
@ -43,6 +44,8 @@ type DockerServer struct {
|
||||||
mux *mux.Router
|
mux *mux.Router
|
||||||
hook func(*http.Request)
|
hook func(*http.Request)
|
||||||
failures map[string]string
|
failures map[string]string
|
||||||
|
multiFailures []map[string]string
|
||||||
|
execCallbacks map[string]func()
|
||||||
customHandlers map[string]http.Handler
|
customHandlers map[string]http.Handler
|
||||||
handlerMutex sync.RWMutex
|
handlerMutex sync.RWMutex
|
||||||
cChan chan<- *docker.Container
|
cChan chan<- *docker.Container
|
||||||
|
@ -69,6 +72,7 @@ func NewServer(bind string, containerChan chan<- *docker.Container, hook func(*h
|
||||||
imgIDs: make(map[string]string),
|
imgIDs: make(map[string]string),
|
||||||
hook: hook,
|
hook: hook,
|
||||||
failures: make(map[string]string),
|
failures: make(map[string]string),
|
||||||
|
execCallbacks: make(map[string]func()),
|
||||||
customHandlers: make(map[string]http.Handler),
|
customHandlers: make(map[string]http.Handler),
|
||||||
cChan: containerChan,
|
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/json").Methods("GET").HandlerFunc(s.handlerWrapper(s.listContainers))
|
||||||
s.mux.Path("/containers/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createContainer))
|
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:.*}/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:.*}/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:.*}/start").Methods("POST").HandlerFunc(s.handlerWrapper(s.startContainer))
|
||||||
s.mux.Path("/containers/{id:.*}/kill").Methods("POST").HandlerFunc(s.handlerWrapper(s.stopContainer))
|
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))
|
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
|
// PrepareFailure adds a new expected failure based on a URL regexp it receives
|
||||||
// an id for the failure.
|
// an id for the failure.
|
||||||
func (s *DockerServer) PrepareFailure(id string, urlRegexp string) {
|
func (s *DockerServer) PrepareFailure(id string, urlRegexp string) {
|
||||||
s.failures[id] = urlRegexp
|
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.
|
// ResetFailure removes an expected failure identified by the given id.
|
||||||
func (s *DockerServer) ResetFailure(id string) {
|
func (s *DockerServer) ResetFailure(id string) {
|
||||||
delete(s.failures, id)
|
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.
|
// CustomHandler registers a custom handler for a specific path.
|
||||||
//
|
//
|
||||||
// For example:
|
// For example:
|
||||||
|
@ -170,9 +217,11 @@ func (s *DockerServer) URL() string {
|
||||||
func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
s.handlerMutex.RLock()
|
s.handlerMutex.RLock()
|
||||||
defer s.handlerMutex.RUnlock()
|
defer s.handlerMutex.RUnlock()
|
||||||
if handler, ok := s.customHandlers[r.URL.Path]; ok {
|
for re, handler := range s.customHandlers {
|
||||||
handler.ServeHTTP(w, r)
|
if m, _ := regexp.MatchString(re, r.URL.Path); m {
|
||||||
return
|
handler.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.mux.ServeHTTP(w, r)
|
s.mux.ServeHTTP(w, r)
|
||||||
if s.hook != nil {
|
if s.hook != nil {
|
||||||
|
@ -200,6 +249,19 @@ func (s *DockerServer) handlerWrapper(f func(http.ResponseWriter, *http.Request)
|
||||||
http.Error(w, errorID, http.StatusBadRequest)
|
http.Error(w, errorID, http.StatusBadRequest)
|
||||||
return
|
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)
|
f(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,6 +398,23 @@ func (s *DockerServer) generateID() string {
|
||||||
return fmt.Sprintf("%x", buf)
|
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) {
|
func (s *DockerServer) inspectContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
container, _, err := s.findContainer(id)
|
container, _, err := s.findContainer(id)
|
||||||
|
@ -524,9 +603,13 @@ func (s *DockerServer) commitContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
Config: config,
|
Config: config,
|
||||||
}
|
}
|
||||||
repository := r.URL.Query().Get("repo")
|
repository := r.URL.Query().Get("repo")
|
||||||
|
tag := r.URL.Query().Get("tag")
|
||||||
s.iMut.Lock()
|
s.iMut.Lock()
|
||||||
s.images = append(s.images, image)
|
s.images = append(s.images, image)
|
||||||
if repository != "" {
|
if repository != "" {
|
||||||
|
if tag != "" {
|
||||||
|
repository += ":" + tag
|
||||||
|
}
|
||||||
s.imgIDs[repository] = image.ID
|
s.imgIDs[repository] = image.ID
|
||||||
}
|
}
|
||||||
s.iMut.Unlock()
|
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) {
|
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{
|
image := docker.Image{
|
||||||
ID: s.generateID(),
|
ID: s.generateID(),
|
||||||
}
|
}
|
||||||
s.iMut.Lock()
|
s.iMut.Lock()
|
||||||
s.images = append(s.images, image)
|
s.images = append(s.images, image)
|
||||||
if repository != "" {
|
if fromImageName != "" {
|
||||||
s.imgIDs[repository] = image.ID
|
if tag != "" {
|
||||||
|
fromImageName = fmt.Sprintf("%s:%s", fromImageName, tag)
|
||||||
|
}
|
||||||
|
s.imgIDs[fromImageName] = image.ID
|
||||||
}
|
}
|
||||||
s.iMut.Unlock()
|
s.iMut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) pushImage(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) pushImage(w http.ResponseWriter, r *http.Request) {
|
||||||
name := mux.Vars(r)["name"]
|
name := mux.Vars(r)["name"]
|
||||||
|
tag := r.URL.Query().Get("tag")
|
||||||
|
if tag != "" {
|
||||||
|
name += ":" + tag
|
||||||
|
}
|
||||||
s.iMut.RLock()
|
s.iMut.RLock()
|
||||||
if _, ok := s.imgIDs[name]; !ok {
|
if _, ok := s.imgIDs[name]; !ok {
|
||||||
s.iMut.RUnlock()
|
s.iMut.RUnlock()
|
||||||
|
@ -619,6 +710,10 @@ func (s *DockerServer) tagImage(w http.ResponseWriter, r *http.Request) {
|
||||||
s.iMut.Lock()
|
s.iMut.Lock()
|
||||||
defer s.iMut.Unlock()
|
defer s.iMut.Unlock()
|
||||||
newRepo := r.URL.Query().Get("repo")
|
newRepo := r.URL.Query().Get("repo")
|
||||||
|
newTag := r.URL.Query().Get("tag")
|
||||||
|
if newTag != "" {
|
||||||
|
newRepo += ":" + newTag
|
||||||
|
}
|
||||||
s.imgIDs[newRepo] = s.imgIDs[name]
|
s.imgIDs[newRepo] = s.imgIDs[name]
|
||||||
w.WriteHeader(http.StatusCreated)
|
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) {
|
func (s *DockerServer) getImage(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set("Content-Type", "application/tar")
|
w.Header().Set("Content-Type", "application/tar")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Request) {
|
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
|
return
|
||||||
}
|
}
|
||||||
exec := docker.ExecInspect{
|
exec := docker.ExecInspect{
|
||||||
ID: "id-exec-created-by-test",
|
ID: s.generateID(),
|
||||||
Container: *container,
|
Container: *container,
|
||||||
}
|
}
|
||||||
var params docker.CreateExecOptions
|
var params docker.CreateExecOptions
|
||||||
|
@ -748,44 +842,63 @@ func (s *DockerServer) createExecContainer(w http.ResponseWriter, r *http.Reques
|
||||||
exec.ProcessConfig.Arguments = params.Cmd[1:]
|
exec.ProcessConfig.Arguments = params.Cmd[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.execMut.Lock()
|
||||||
s.execs = append(s.execs, &exec)
|
s.execs = append(s.execs, &exec)
|
||||||
|
s.execMut.Unlock()
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(map[string]string{"Id": exec.ID})
|
json.NewEncoder(w).Encode(map[string]string{"Id": exec.ID})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) startExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
for _, exec := range s.execs {
|
if exec, err := s.getExec(id); err == nil {
|
||||||
if exec.ID == id {
|
s.execMut.Lock()
|
||||||
w.WriteHeader(http.StatusOK)
|
exec.Running = true
|
||||||
return
|
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)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) resizeExecContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) resizeExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
for _, exec := range s.execs {
|
if _, err := s.getExec(id); err == nil {
|
||||||
if exec.ID == id {
|
w.WriteHeader(http.StatusOK)
|
||||||
w.WriteHeader(http.StatusOK)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerServer) inspectExecContainer(w http.ResponseWriter, r *http.Request) {
|
func (s *DockerServer) inspectExecContainer(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
for _, exec := range s.execs {
|
if exec, err := s.getExec(id); err == nil {
|
||||||
if exec.ID == id {
|
w.WriteHeader(http.StatusOK)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Header().Set("Content-Type", "application/json")
|
json.NewEncoder(w).Encode(exec)
|
||||||
json.NewEncoder(w).Encode(exec)
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNotFound)
|
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
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// 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) {
|
func TestCustomHandler(t *testing.T) {
|
||||||
var called bool
|
var called bool
|
||||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
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) {
|
func TestListContainers(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 2)
|
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) {
|
func TestCommitContainer(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 2)
|
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) {
|
func TestCommitContainerInvalidRun(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
addContainers(&server, 1)
|
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) {
|
func TestPushImage(t *testing.T) {
|
||||||
server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}}
|
server := DockerServer{imgIDs: map[string]string{"tsuru/python": "a123"}}
|
||||||
server.buildMuxer()
|
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) {
|
func TestPushImageNotFound(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
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) {
|
func TestTagImageNotFound(t *testing.T) {
|
||||||
server := DockerServer{}
|
server := DockerServer{}
|
||||||
server.buildMuxer()
|
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) {
|
func TestRemoveFailure(t *testing.T) {
|
||||||
server := DockerServer{failures: make(map[string]string)}
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
server.buildMuxer()
|
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) {
|
func TestMutateContainer(t *testing.T) {
|
||||||
server := DockerServer{failures: make(map[string]string)}
|
server := DockerServer{failures: make(map[string]string)}
|
||||||
server.buildMuxer()
|
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)
|
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