mirror of
https://github.com/moby/moby.git
synced 2025-08-01 05:47:11 +03:00
Remove types and lib packages from the engine.
They live in github.com/docker/engine-api now. Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
@ -1,142 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client is the API client that performs all operations
|
|
||||||
// against a docker server.
|
|
||||||
type Client struct {
|
|
||||||
// proto holds the client protocol i.e. unix.
|
|
||||||
proto string
|
|
||||||
// addr holds the client address.
|
|
||||||
addr string
|
|
||||||
// basePath holds the path to prepend to the requests
|
|
||||||
basePath string
|
|
||||||
// scheme holds the scheme of the client i.e. https.
|
|
||||||
scheme string
|
|
||||||
// tlsConfig holds the tls configuration to use in hijacked requests.
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
// httpClient holds the client transport instance. Exported to keep the old code running.
|
|
||||||
httpClient *http.Client
|
|
||||||
// version of the server to talk to.
|
|
||||||
version string
|
|
||||||
// custom http headers configured by users
|
|
||||||
customHTTPHeaders map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEnvClient initializes a new API client based on environment variables.
|
|
||||||
// Use DOCKER_HOST to set the url to the docker server.
|
|
||||||
// Use DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest.
|
|
||||||
// Use DOCKER_CERT_PATH to load the tls certificates from.
|
|
||||||
// Use DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
|
|
||||||
func NewEnvClient() (*Client, error) {
|
|
||||||
var transport *http.Transport
|
|
||||||
if dockerCertPath := os.Getenv("DOCKER_CERT_PATH"); dockerCertPath != "" {
|
|
||||||
tlsc := &tls.Config{}
|
|
||||||
|
|
||||||
cert, err := tls.LoadX509KeyPair(filepath.Join(dockerCertPath, "cert.pem"), filepath.Join(dockerCertPath, "key.pem"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsc.Certificates = append(tlsc.Certificates, cert)
|
|
||||||
tlsc.InsecureSkipVerify = os.Getenv("DOCKER_TLS_VERIFY") == ""
|
|
||||||
transport = &http.Transport{
|
|
||||||
TLSClientConfig: tlsc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewClient(os.Getenv("DOCKER_HOST"), os.Getenv("DOCKER_API_VERSION"), transport, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient initializes a new API client for the given host and API version.
|
|
||||||
// It won't send any version information if the version number is empty.
|
|
||||||
// It uses the transport to create a new http client.
|
|
||||||
// It also initializes the custom http headers to add to each request.
|
|
||||||
func NewClient(host string, version string, transport *http.Transport, httpHeaders map[string]string) (*Client, error) {
|
|
||||||
var (
|
|
||||||
basePath string
|
|
||||||
tlsConfig *tls.Config
|
|
||||||
scheme = "http"
|
|
||||||
protoAddrParts = strings.SplitN(host, "://", 2)
|
|
||||||
proto, addr = protoAddrParts[0], protoAddrParts[1]
|
|
||||||
)
|
|
||||||
|
|
||||||
if proto == "tcp" {
|
|
||||||
parsed, err := url.Parse("tcp://" + addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addr = parsed.Host
|
|
||||||
basePath = parsed.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
transport = configureTransport(transport, proto, addr)
|
|
||||||
if transport.TLSClientConfig != nil {
|
|
||||||
scheme = "https"
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Client{
|
|
||||||
proto: proto,
|
|
||||||
addr: addr,
|
|
||||||
basePath: basePath,
|
|
||||||
scheme: scheme,
|
|
||||||
tlsConfig: tlsConfig,
|
|
||||||
httpClient: &http.Client{Transport: transport},
|
|
||||||
version: version,
|
|
||||||
customHTTPHeaders: httpHeaders,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAPIPath returns the versioned request path to call the api.
|
|
||||||
// It appends the query parameters to the path if they are not empty.
|
|
||||||
func (cli *Client) getAPIPath(p string, query url.Values) string {
|
|
||||||
var apiPath string
|
|
||||||
if cli.version != "" {
|
|
||||||
v := strings.TrimPrefix(cli.version, "v")
|
|
||||||
apiPath = fmt.Sprintf("%s/v%s%s", cli.basePath, v, p)
|
|
||||||
} else {
|
|
||||||
apiPath = fmt.Sprintf("%s%s", cli.basePath, p)
|
|
||||||
}
|
|
||||||
if len(query) > 0 {
|
|
||||||
apiPath += "?" + query.Encode()
|
|
||||||
}
|
|
||||||
return apiPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientVersion returns the version string associated with this
|
|
||||||
// instance of the Client. Note that this value can be changed
|
|
||||||
// via the DOCKER_API_VERSION env var.
|
|
||||||
func (cli *Client) ClientVersion() string {
|
|
||||||
return cli.version
|
|
||||||
}
|
|
||||||
|
|
||||||
func configureTransport(tr *http.Transport, proto, addr string) *http.Transport {
|
|
||||||
if tr == nil {
|
|
||||||
tr = &http.Transport{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Why 32? See https://github.com/docker/docker/pull/8035.
|
|
||||||
timeout := 32 * time.Second
|
|
||||||
if proto == "unix" {
|
|
||||||
// No need for compression in local communications.
|
|
||||||
tr.DisableCompression = true
|
|
||||||
tr.Dial = func(_, _ string) (net.Conn, error) {
|
|
||||||
return net.DialTimeout(proto, addr, timeout)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tr.Proxy = http.ProxyFromEnvironment
|
|
||||||
tr.Dial = (&net.Dialer{Timeout: timeout}).Dial
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetAPIPath(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
v string
|
|
||||||
p string
|
|
||||||
q url.Values
|
|
||||||
e string
|
|
||||||
}{
|
|
||||||
{"", "/containers/json", nil, "/containers/json"},
|
|
||||||
{"", "/containers/json", url.Values{}, "/containers/json"},
|
|
||||||
{"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"},
|
|
||||||
{"1.22", "/containers/json", nil, "/v1.22/containers/json"},
|
|
||||||
{"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
|
|
||||||
{"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
|
|
||||||
{"v1.22", "/containers/json", nil, "/v1.22/containers/json"},
|
|
||||||
{"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
|
|
||||||
{"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cs := range cases {
|
|
||||||
c, err := NewClient("unix:///var/run/docker.sock", cs.v, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
g := c.getAPIPath(cs.p, cs.q)
|
|
||||||
if g != cs.e {
|
|
||||||
t.Fatalf("Expected %s, got %s", cs.e, g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerAttach attaches a connection to a container in the server.
|
|
||||||
// It returns a types.HijackedConnection with the hijacked connection
|
|
||||||
// and the a reader to get output. It's up to the called to close
|
|
||||||
// the hijacked connection by calling types.HijackedResponse.Close.
|
|
||||||
func (cli *Client) ContainerAttach(options types.ContainerAttachOptions) (types.HijackedResponse, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
if options.Stream {
|
|
||||||
query.Set("stream", "1")
|
|
||||||
}
|
|
||||||
if options.Stdin {
|
|
||||||
query.Set("stdin", "1")
|
|
||||||
}
|
|
||||||
if options.Stdout {
|
|
||||||
query.Set("stdout", "1")
|
|
||||||
}
|
|
||||||
if options.Stderr {
|
|
||||||
query.Set("stderr", "1")
|
|
||||||
}
|
|
||||||
if options.DetachKeys != "" {
|
|
||||||
query.Set("detachKeys", options.DetachKeys)
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := map[string][]string{"Content-Type": {"text/plain"}}
|
|
||||||
return cli.postHijacked("/containers/"+options.ContainerID+"/attach", query, nil, headers)
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerCommit applies changes into a container and creates a new tagged image.
|
|
||||||
func (cli *Client) ContainerCommit(options types.ContainerCommitOptions) (types.ContainerCommitResponse, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("container", options.ContainerID)
|
|
||||||
query.Set("repo", options.RepositoryName)
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
query.Set("comment", options.Comment)
|
|
||||||
query.Set("author", options.Author)
|
|
||||||
for _, change := range options.Changes {
|
|
||||||
query.Add("changes", change)
|
|
||||||
}
|
|
||||||
if options.Pause != true {
|
|
||||||
query.Set("pause", "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
var response types.ContainerCommitResponse
|
|
||||||
resp, err := cli.post("/commit", query, options.Config, nil)
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(resp.body).Decode(&response); err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
type configWrapper struct {
|
|
||||||
*container.Config
|
|
||||||
HostConfig *container.HostConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerCreate creates a new container based in the given configuration.
|
|
||||||
// It can be associated with a name, but it's not mandatory.
|
|
||||||
func (cli *Client) ContainerCreate(config *container.Config, hostConfig *container.HostConfig, containerName string) (types.ContainerCreateResponse, error) {
|
|
||||||
var response types.ContainerCreateResponse
|
|
||||||
query := url.Values{}
|
|
||||||
if containerName != "" {
|
|
||||||
query.Set("name", containerName)
|
|
||||||
}
|
|
||||||
|
|
||||||
body := configWrapper{
|
|
||||||
Config: config,
|
|
||||||
HostConfig: hostConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
serverResp, err := cli.post("/containers/create", query, body, nil)
|
|
||||||
if err != nil {
|
|
||||||
if serverResp != nil && serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
|
|
||||||
return response, imageNotFoundError{config.Image}
|
|
||||||
}
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverResp.statusCode == 404 && strings.Contains(err.Error(), config.Image) {
|
|
||||||
return response, imageNotFoundError{config.Image}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(serverResp.body).Decode(&response); err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return response, nil
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerInspect returns the container information.
|
|
||||||
func (cli *Client) ContainerInspect(containerID string) (types.ContainerJSON, error) {
|
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
|
||||||
return types.ContainerJSON{}, containerNotFoundError{containerID}
|
|
||||||
}
|
|
||||||
return types.ContainerJSON{}, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
var response types.ContainerJSON
|
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerInspectWithRaw returns the container information and it's raw representation.
|
|
||||||
func (cli *Client) ContainerInspectWithRaw(containerID string, getSize bool) (types.ContainerJSON, []byte, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
if getSize {
|
|
||||||
query.Set("size", "1")
|
|
||||||
}
|
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/json", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
|
||||||
return types.ContainerJSON{}, nil, containerNotFoundError{containerID}
|
|
||||||
}
|
|
||||||
return types.ContainerJSON{}, nil, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(serverResp.body)
|
|
||||||
if err != nil {
|
|
||||||
return types.ContainerJSON{}, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response types.ContainerJSON
|
|
||||||
rdr := bytes.NewReader(body)
|
|
||||||
err = json.NewDecoder(rdr).Decode(&response)
|
|
||||||
return response, body, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) containerInspectWithResponse(containerID string, query url.Values) (types.ContainerJSON, *serverResponse, error) {
|
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/json", nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.ContainerJSON{}, serverResp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response types.ContainerJSON
|
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&response)
|
|
||||||
return response, serverResp, err
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerList returns the list of containers in the docker host.
|
|
||||||
func (cli *Client) ContainerList(options types.ContainerListOptions) ([]types.Container, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
|
|
||||||
if options.All {
|
|
||||||
query.Set("all", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Limit != -1 {
|
|
||||||
query.Set("limit", strconv.Itoa(options.Limit))
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Since != "" {
|
|
||||||
query.Set("since", options.Since)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Before != "" {
|
|
||||||
query.Set("before", options.Before)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Size {
|
|
||||||
query.Set("size", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Filter.Len() > 0 {
|
|
||||||
filterJSON, err := filters.ToParam(options.Filter)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Set("filters", filterJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.get("/containers/json", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
var containers []types.Container
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&containers)
|
|
||||||
return containers, err
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerRemove kills and removes a container from the docker host.
|
|
||||||
func (cli *Client) ContainerRemove(options types.ContainerRemoveOptions) error {
|
|
||||||
query := url.Values{}
|
|
||||||
if options.RemoveVolumes {
|
|
||||||
query.Set("v", "1")
|
|
||||||
}
|
|
||||||
if options.RemoveLinks {
|
|
||||||
query.Set("link", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Force {
|
|
||||||
query.Set("force", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.delete("/containers/"+options.ContainerID, query, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import "net/url"
|
|
||||||
|
|
||||||
// ContainerRename changes the name of a given container.
|
|
||||||
func (cli *Client) ContainerRename(containerID, newContainerName string) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("name", newContainerName)
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/rename", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerRestart stops and starts a container again.
|
|
||||||
// It makes the daemon to wait for the container to be up again for
|
|
||||||
// a specific amount of time, given the timeout.
|
|
||||||
func (cli *Client) ContainerRestart(containerID string, timeout int) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("t", strconv.Itoa(timeout))
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/restart", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
// ContainerStart sends a request to the docker daemon to start a container.
|
|
||||||
func (cli *Client) ContainerStart(containerID string) error {
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/start", nil, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerStats returns near realtime stats for a given container.
|
|
||||||
// It's up to the caller to close the io.ReadCloser returned.
|
|
||||||
func (cli *Client) ContainerStats(containerID string, stream bool) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("stream", "0")
|
|
||||||
if stream {
|
|
||||||
query.Set("stream", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.get("/containers/"+containerID+"/stats", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, err
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerStop stops a container without terminating the process.
|
|
||||||
// The process is blocked until the container stops or the timeout expires.
|
|
||||||
func (cli *Client) ContainerStop(containerID string, timeout int) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("t", strconv.Itoa(timeout))
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/stop", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerTop shows process information from within a container.
|
|
||||||
func (cli *Client) ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error) {
|
|
||||||
var response types.ContainerProcessList
|
|
||||||
query := url.Values{}
|
|
||||||
if len(arguments) > 0 {
|
|
||||||
query.Set("ps_args", strings.Join(arguments, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.get("/containers/"+containerID+"/top", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
// ContainerUnpause resumes the process execution within a container
|
|
||||||
func (cli *Client) ContainerUnpause(containerID string) error {
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/unpause", nil, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerUpdate updates resources of a container
|
|
||||||
func (cli *Client) ContainerUpdate(containerID string, hostConfig container.HostConfig) error {
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/update", nil, hostConfig, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerStatPath returns Stat information about a path inside the container filesystem.
|
|
||||||
func (cli *Client) ContainerStatPath(containerID, path string) (types.ContainerPathStat, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("path", filepath.ToSlash(path)) // Normalize the paths used in the API.
|
|
||||||
|
|
||||||
urlStr := fmt.Sprintf("/containers/%s/archive", containerID)
|
|
||||||
response, err := cli.head(urlStr, query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.ContainerPathStat{}, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(response)
|
|
||||||
return getContainerPathStatFromHeader(response.header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyToContainer copies content into the container filesystem.
|
|
||||||
func (cli *Client) CopyToContainer(options types.CopyToContainerOptions) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("path", filepath.ToSlash(options.Path)) // Normalize the paths used in the API.
|
|
||||||
// Do not allow for an existing directory to be overwritten by a non-directory and vice versa.
|
|
||||||
if !options.AllowOverwriteDirWithFile {
|
|
||||||
query.Set("noOverwriteDirNonDir", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := fmt.Sprintf("/containers/%s/archive", options.ContainerID)
|
|
||||||
|
|
||||||
response, err := cli.putRaw(path, query, options.Content, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(response)
|
|
||||||
|
|
||||||
if response.statusCode != http.StatusOK {
|
|
||||||
return fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFromContainer get the content from the container and return it as a Reader
|
|
||||||
// to manipulate it in the host. It's up to the caller to close the reader.
|
|
||||||
func (cli *Client) CopyFromContainer(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
|
|
||||||
query := make(url.Values, 1)
|
|
||||||
query.Set("path", filepath.ToSlash(srcPath)) // Normalize the paths used in the API.
|
|
||||||
|
|
||||||
apiPath := fmt.Sprintf("/containers/%s/archive", containerID)
|
|
||||||
response, err := cli.get(apiPath, query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, types.ContainerPathStat{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.statusCode != http.StatusOK {
|
|
||||||
return nil, types.ContainerPathStat{}, fmt.Errorf("unexpected status code from daemon: %d", response.statusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to get the copy behavior right, we need to know information
|
|
||||||
// about both the source and the destination. The response headers include
|
|
||||||
// stat info about the source that we can use in deciding exactly how to
|
|
||||||
// copy it locally. Along with the stat info about the local destination,
|
|
||||||
// we have everything we need to handle the multiple possibilities there
|
|
||||||
// can be when copying a file/dir from one location to another file/dir.
|
|
||||||
stat, err := getContainerPathStatFromHeader(response.header)
|
|
||||||
if err != nil {
|
|
||||||
return nil, stat, fmt.Errorf("unable to get resource stat from response: %s", err)
|
|
||||||
}
|
|
||||||
return response.body, stat, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContainerPathStatFromHeader(header http.Header) (types.ContainerPathStat, error) {
|
|
||||||
var stat types.ContainerPathStat
|
|
||||||
|
|
||||||
encodedStat := header.Get("X-Docker-Container-Path-Stat")
|
|
||||||
statDecoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(encodedStat))
|
|
||||||
|
|
||||||
err := json.NewDecoder(statDecoder).Decode(&stat)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("unable to decode container path stat header: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return stat, err
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerDiff shows differences in a container filesystem since it was started.
|
|
||||||
func (cli *Client) ContainerDiff(containerID string) ([]types.ContainerChange, error) {
|
|
||||||
var changes []types.ContainerChange
|
|
||||||
|
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/changes", url.Values{}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return changes, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(serverResp.body).Decode(&changes); err != nil {
|
|
||||||
return changes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return changes, nil
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrConnectionFailed is a error raised when the connection between the client and the server failed.
|
|
||||||
var ErrConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
|
|
||||||
|
|
||||||
// imageNotFoundError implements an error returned when an image is not in the docker host.
|
|
||||||
type imageNotFoundError struct {
|
|
||||||
imageID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of an imageNotFoundError
|
|
||||||
func (i imageNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such image: %s", i.imageID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrImageNotFound returns true if the error is caused
|
|
||||||
// when an image is not found in the docker host.
|
|
||||||
func IsErrImageNotFound(err error) bool {
|
|
||||||
_, ok := err.(imageNotFoundError)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// containerNotFoundError implements an error returned when a container is not in the docker host.
|
|
||||||
type containerNotFoundError struct {
|
|
||||||
containerID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of an containerNotFoundError
|
|
||||||
func (e containerNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such container: %s", e.containerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrContainerNotFound returns true if the error is caused
|
|
||||||
// when a container is not found in the docker host.
|
|
||||||
func IsErrContainerNotFound(err error) bool {
|
|
||||||
_, ok := err.(containerNotFoundError)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// networkNotFoundError implements an error returned when a network is not in the docker host.
|
|
||||||
type networkNotFoundError struct {
|
|
||||||
networkID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of an networkNotFoundError
|
|
||||||
func (e networkNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such network: %s", e.networkID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrNetworkNotFound returns true if the error is caused
|
|
||||||
// when a network is not found in the docker host.
|
|
||||||
func IsErrNetworkNotFound(err error) bool {
|
|
||||||
_, ok := err.(networkNotFoundError)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// volumeNotFoundError implements an error returned when a volume is not in the docker host.
|
|
||||||
type volumeNotFoundError struct {
|
|
||||||
volumeID string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of an networkNotFoundError
|
|
||||||
func (e volumeNotFoundError) Error() string {
|
|
||||||
return fmt.Sprintf("Error: No such volume: %s", e.volumeID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrVolumeNotFound returns true if the error is caused
|
|
||||||
// when a volume is not found in the docker host.
|
|
||||||
func IsErrVolumeNotFound(err error) bool {
|
|
||||||
_, ok := err.(networkNotFoundError)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// unauthorizedError represents an authorization error in a remote registry.
|
|
||||||
type unauthorizedError struct {
|
|
||||||
cause error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns a string representation of an unauthorizedError
|
|
||||||
func (u unauthorizedError) Error() string {
|
|
||||||
return u.cause.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrUnauthorized returns true if the error is caused
|
|
||||||
// when an the remote registry authentication fails
|
|
||||||
func IsErrUnauthorized(err error) bool {
|
|
||||||
_, ok := err.(unauthorizedError)
|
|
||||||
return ok
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
timetypes "github.com/docker/engine-api/types/time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Events returns a stream of events in the daemon in a ReadCloser.
|
|
||||||
// It's up to the caller to close the stream.
|
|
||||||
func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
ref := time.Now()
|
|
||||||
|
|
||||||
if options.Since != "" {
|
|
||||||
ts, err := timetypes.GetTimestamp(options.Since, ref)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query.Set("since", ts)
|
|
||||||
}
|
|
||||||
if options.Until != "" {
|
|
||||||
ts, err := timetypes.GetTimestamp(options.Until, ref)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query.Set("until", ts)
|
|
||||||
}
|
|
||||||
if options.Filters.Len() > 0 {
|
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query.Set("filters", filterJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverResponse, err := cli.get("/events", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return serverResponse.body, nil
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerExecCreate creates a new exec configuration to run an exec process.
|
|
||||||
func (cli *Client) ContainerExecCreate(config types.ExecConfig) (types.ContainerExecCreateResponse, error) {
|
|
||||||
var response types.ContainerExecCreateResponse
|
|
||||||
resp, err := cli.post("/containers/"+config.Container+"/exec", nil, config, nil)
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecStart starts an exec process already create in the docker host.
|
|
||||||
func (cli *Client) ContainerExecStart(execID string, config types.ExecStartCheck) error {
|
|
||||||
resp, err := cli.post("/exec/"+execID+"/start", nil, config, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecAttach attaches a connection to an exec process in the server.
|
|
||||||
// It returns a types.HijackedConnection with the hijacked connection
|
|
||||||
// and the a reader to get output. It's up to the called to close
|
|
||||||
// the hijacked connection by calling types.HijackedResponse.Close.
|
|
||||||
func (cli *Client) ContainerExecAttach(execID string, config types.ExecConfig) (types.HijackedResponse, error) {
|
|
||||||
headers := map[string][]string{"Content-Type": {"application/json"}}
|
|
||||||
return cli.postHijacked("/exec/"+execID+"/start", nil, config, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecInspect returns information about a specific exec process on the docker host.
|
|
||||||
func (cli *Client) ContainerExecInspect(execID string) (types.ContainerExecInspect, error) {
|
|
||||||
var response types.ContainerExecInspect
|
|
||||||
resp, err := cli.get("/exec/"+execID+"/json", nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerExport retrieves the raw contents of a container
|
|
||||||
// and returns them as a io.ReadCloser. It's up to the caller
|
|
||||||
// to close the stream.
|
|
||||||
func (cli *Client) ContainerExport(containerID string) (io.ReadCloser, error) {
|
|
||||||
serverResp, err := cli.get("/containers/"+containerID+"/export", url.Values{}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return serverResp.body, nil
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// tlsClientCon holds tls information and a dialed connection.
|
|
||||||
type tlsClientCon struct {
|
|
||||||
*tls.Conn
|
|
||||||
rawConn net.Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tlsClientCon) CloseWrite() error {
|
|
||||||
// Go standard tls.Conn doesn't provide the CloseWrite() method so we do it
|
|
||||||
// on its underlying connection.
|
|
||||||
if conn, ok := c.rawConn.(types.CloseWriter); ok {
|
|
||||||
return conn.CloseWrite()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// postHijacked sends a POST request and hijacks the connection.
|
|
||||||
func (cli *Client) postHijacked(path string, query url.Values, body interface{}, headers map[string][]string) (types.HijackedResponse, error) {
|
|
||||||
bodyEncoded, err := encodeData(body)
|
|
||||||
if err != nil {
|
|
||||||
return types.HijackedResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := cli.newRequest("POST", path, query, bodyEncoded, headers)
|
|
||||||
if err != nil {
|
|
||||||
return types.HijackedResponse{}, err
|
|
||||||
}
|
|
||||||
req.Host = cli.addr
|
|
||||||
|
|
||||||
req.Header.Set("Connection", "Upgrade")
|
|
||||||
req.Header.Set("Upgrade", "tcp")
|
|
||||||
|
|
||||||
conn, err := dial(cli.proto, cli.addr, cli.tlsConfig)
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "connection refused") {
|
|
||||||
return types.HijackedResponse{}, fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?")
|
|
||||||
}
|
|
||||||
return types.HijackedResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we set up a TCP connection for hijack, there could be long periods
|
|
||||||
// of inactivity (a long running command with no output) that in certain
|
|
||||||
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
|
||||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
|
||||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
|
||||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
|
||||||
tcpConn.SetKeepAlive(true)
|
|
||||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientconn := httputil.NewClientConn(conn, nil)
|
|
||||||
defer clientconn.Close()
|
|
||||||
|
|
||||||
// Server hijacks the connection, error 'connection closed' expected
|
|
||||||
clientconn.Do(req)
|
|
||||||
|
|
||||||
rwc, br := clientconn.Hijack()
|
|
||||||
|
|
||||||
return types.HijackedResponse{Conn: rwc, Reader: br}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func tlsDial(network, addr string, config *tls.Config) (net.Conn, error) {
|
|
||||||
return tlsDialWithDialer(new(net.Dialer), network, addr, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to copy Go's implementation of tls.Dial (pkg/cryptor/tls/tls.go) in
|
|
||||||
// order to return our custom tlsClientCon struct which holds both the tls.Conn
|
|
||||||
// object _and_ its underlying raw connection. The rationale for this is that
|
|
||||||
// we need to be able to close the write end of the connection when attaching,
|
|
||||||
// which tls.Conn does not provide.
|
|
||||||
func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) {
|
|
||||||
// We want the Timeout and Deadline values from dialer to cover the
|
|
||||||
// whole process: TCP connection and TLS handshake. This means that we
|
|
||||||
// also need to start our own timers now.
|
|
||||||
timeout := dialer.Timeout
|
|
||||||
|
|
||||||
if !dialer.Deadline.IsZero() {
|
|
||||||
deadlineTimeout := dialer.Deadline.Sub(time.Now())
|
|
||||||
if timeout == 0 || deadlineTimeout < timeout {
|
|
||||||
timeout = deadlineTimeout
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var errChannel chan error
|
|
||||||
|
|
||||||
if timeout != 0 {
|
|
||||||
errChannel = make(chan error, 2)
|
|
||||||
time.AfterFunc(timeout, func() {
|
|
||||||
errChannel <- errors.New("")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rawConn, err := dialer.Dial(network, addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// When we set up a TCP connection for hijack, there could be long periods
|
|
||||||
// of inactivity (a long running command with no output) that in certain
|
|
||||||
// network setups may cause ECONNTIMEOUT, leaving the client in an unknown
|
|
||||||
// state. Setting TCP KeepAlive on the socket connection will prohibit
|
|
||||||
// ECONNTIMEOUT unless the socket connection truly is broken
|
|
||||||
if tcpConn, ok := rawConn.(*net.TCPConn); ok {
|
|
||||||
tcpConn.SetKeepAlive(true)
|
|
||||||
tcpConn.SetKeepAlivePeriod(30 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
colonPos := strings.LastIndex(addr, ":")
|
|
||||||
if colonPos == -1 {
|
|
||||||
colonPos = len(addr)
|
|
||||||
}
|
|
||||||
hostname := addr[:colonPos]
|
|
||||||
|
|
||||||
// If no ServerName is set, infer the ServerName
|
|
||||||
// from the hostname we're connecting to.
|
|
||||||
if config.ServerName == "" {
|
|
||||||
// Make a copy to avoid polluting argument or default.
|
|
||||||
c := *config
|
|
||||||
c.ServerName = hostname
|
|
||||||
config = &c
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := tls.Client(rawConn, config)
|
|
||||||
|
|
||||||
if timeout == 0 {
|
|
||||||
err = conn.Handshake()
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
errChannel <- conn.Handshake()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = <-errChannel
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
rawConn.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is Docker difference with standard's crypto/tls package: returned a
|
|
||||||
// wrapper which holds both the TLS and raw connections.
|
|
||||||
return &tlsClientCon{conn, rawConn}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dial(proto, addr string, tlsConfig *tls.Config) (net.Conn, error) {
|
|
||||||
if tlsConfig != nil && proto != "unix" {
|
|
||||||
// Notice this isn't Go standard's tls.Dial function
|
|
||||||
return tlsDial(proto, addr, tlsConfig)
|
|
||||||
}
|
|
||||||
return net.Dial(proto, addr)
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageHistory returns the changes in an image in history format.
|
|
||||||
func (cli *Client) ImageHistory(imageID string) ([]types.ImageHistory, error) {
|
|
||||||
var history []types.ImageHistory
|
|
||||||
serverResp, err := cli.get("/images/"+imageID+"/history", url.Values{}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return history, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(serverResp.body).Decode(&history); err != nil {
|
|
||||||
return history, err
|
|
||||||
}
|
|
||||||
return history, nil
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
)
|
|
||||||
|
|
||||||
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
|
|
||||||
|
|
||||||
// ImageBuild sends request to the daemon to build images.
|
|
||||||
// The Body in the response implement an io.ReadCloser and it's up to the caller to
|
|
||||||
// close it.
|
|
||||||
func (cli *Client) ImageBuild(options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
|
||||||
query, err := imageBuildOptionsToQuery(options)
|
|
||||||
if err != nil {
|
|
||||||
return types.ImageBuildResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := http.Header(make(map[string][]string))
|
|
||||||
buf, err := json.Marshal(options.AuthConfigs)
|
|
||||||
if err != nil {
|
|
||||||
return types.ImageBuildResponse{}, err
|
|
||||||
}
|
|
||||||
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
|
|
||||||
headers.Set("Content-Type", "application/tar")
|
|
||||||
|
|
||||||
serverResp, err := cli.postRaw("/build", query, options.Context, headers)
|
|
||||||
if err != nil {
|
|
||||||
return types.ImageBuildResponse{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
osType := getDockerOS(serverResp.header.Get("Server"))
|
|
||||||
|
|
||||||
return types.ImageBuildResponse{
|
|
||||||
Body: serverResp.body,
|
|
||||||
OSType: osType,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, error) {
|
|
||||||
query := url.Values{
|
|
||||||
"t": options.Tags,
|
|
||||||
}
|
|
||||||
if options.SuppressOutput {
|
|
||||||
query.Set("q", "1")
|
|
||||||
}
|
|
||||||
if options.RemoteContext != "" {
|
|
||||||
query.Set("remote", options.RemoteContext)
|
|
||||||
}
|
|
||||||
if options.NoCache {
|
|
||||||
query.Set("nocache", "1")
|
|
||||||
}
|
|
||||||
if options.Remove {
|
|
||||||
query.Set("rm", "1")
|
|
||||||
} else {
|
|
||||||
query.Set("rm", "0")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.ForceRemove {
|
|
||||||
query.Set("forcerm", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.PullParent {
|
|
||||||
query.Set("pull", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !container.IsolationLevel.IsDefault(options.IsolationLevel) {
|
|
||||||
query.Set("isolation", string(options.IsolationLevel))
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Set("cpusetcpus", options.CPUSetCPUs)
|
|
||||||
query.Set("cpusetmems", options.CPUSetMems)
|
|
||||||
query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
|
|
||||||
query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
|
|
||||||
query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
|
|
||||||
query.Set("memory", strconv.FormatInt(options.Memory, 10))
|
|
||||||
query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
|
|
||||||
query.Set("cgroupparent", options.CgroupParent)
|
|
||||||
query.Set("shmsize", strconv.FormatInt(options.ShmSize, 10))
|
|
||||||
query.Set("dockerfile", options.Dockerfile)
|
|
||||||
|
|
||||||
ulimitsJSON, err := json.Marshal(options.Ulimits)
|
|
||||||
if err != nil {
|
|
||||||
return query, err
|
|
||||||
}
|
|
||||||
query.Set("ulimits", string(ulimitsJSON))
|
|
||||||
|
|
||||||
buildArgsJSON, err := json.Marshal(options.BuildArgs)
|
|
||||||
if err != nil {
|
|
||||||
return query, err
|
|
||||||
}
|
|
||||||
query.Set("buildargs", string(buildArgsJSON))
|
|
||||||
|
|
||||||
return query, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDockerOS(serverHeader string) string {
|
|
||||||
var osType string
|
|
||||||
matches := headerRegexp.FindStringSubmatch(serverHeader)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
osType = matches[1]
|
|
||||||
}
|
|
||||||
return osType
|
|
||||||
}
|
|
||||||
|
|
||||||
// convertKVStringsToMap converts ["key=value"] to {"key":"value"}
|
|
||||||
func convertKVStringsToMap(values []string) map[string]string {
|
|
||||||
result := make(map[string]string, len(values))
|
|
||||||
for _, value := range values {
|
|
||||||
kv := strings.SplitN(value, "=", 2)
|
|
||||||
if len(kv) == 1 {
|
|
||||||
result[kv[0]] = ""
|
|
||||||
} else {
|
|
||||||
result[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestGetDockerOS(t *testing.T) {
|
|
||||||
cases := map[string]string{
|
|
||||||
"Docker/v1.22 (linux)": "linux",
|
|
||||||
"Docker/v1.22 (windows)": "windows",
|
|
||||||
"Foo/v1.22 (bar)": "",
|
|
||||||
}
|
|
||||||
for header, os := range cases {
|
|
||||||
g := getDockerOS(header)
|
|
||||||
if g != os {
|
|
||||||
t.Fatalf("Expected %s, got %s", os, g)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageCreate creates a new image based in the parent options.
|
|
||||||
// It returns the JSON content in the response body.
|
|
||||||
func (cli *Client) ImageCreate(options types.ImageCreateOptions) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("fromImage", options.Parent)
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
resp, err := cli.tryImageCreate(query, options.RegistryAuth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) tryImageCreate(query url.Values, registryAuth string) (*serverResponse, error) {
|
|
||||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
|
||||||
return cli.post("/images/create", query, nil, headers)
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageImport creates a new image based in the source options.
|
|
||||||
// It returns the JSON content in the response body.
|
|
||||||
func (cli *Client) ImageImport(options types.ImageImportOptions) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("fromSrc", options.SourceName)
|
|
||||||
query.Set("repo", options.RepositoryName)
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
query.Set("message", options.Message)
|
|
||||||
for _, change := range options.Changes {
|
|
||||||
query.Add("changes", change)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.postRaw("/images/create", query, options.Source, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageInspectWithRaw returns the image information and it's raw representation.
|
|
||||||
func (cli *Client) ImageInspectWithRaw(imageID string, getSize bool) (types.ImageInspect, []byte, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
if getSize {
|
|
||||||
query.Set("size", "1")
|
|
||||||
}
|
|
||||||
serverResp, err := cli.get("/images/"+imageID+"/json", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
if serverResp.statusCode == http.StatusNotFound {
|
|
||||||
return types.ImageInspect{}, nil, imageNotFoundError{imageID}
|
|
||||||
}
|
|
||||||
return types.ImageInspect{}, nil, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(serverResp.body)
|
|
||||||
if err != nil {
|
|
||||||
return types.ImageInspect{}, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response types.ImageInspect
|
|
||||||
rdr := bytes.NewReader(body)
|
|
||||||
err = json.NewDecoder(rdr).Decode(&response)
|
|
||||||
return response, body, err
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageList returns a list of images in the docker host.
|
|
||||||
func (cli *Client) ImageList(options types.ImageListOptions) ([]types.Image, error) {
|
|
||||||
var images []types.Image
|
|
||||||
query := url.Values{}
|
|
||||||
|
|
||||||
if options.Filters.Len() > 0 {
|
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
|
||||||
if err != nil {
|
|
||||||
return images, err
|
|
||||||
}
|
|
||||||
query.Set("filters", filterJSON)
|
|
||||||
}
|
|
||||||
if options.MatchName != "" {
|
|
||||||
// FIXME rename this parameter, to not be confused with the filters flag
|
|
||||||
query.Set("filter", options.MatchName)
|
|
||||||
}
|
|
||||||
if options.All {
|
|
||||||
query.Set("all", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
serverResp, err := cli.get("/images/json", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return images, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
err = json.NewDecoder(serverResp.body).Decode(&images)
|
|
||||||
return images, err
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageLoad loads an image in the docker host from the client host.
|
|
||||||
// It's up to the caller to close the io.ReadCloser returned by
|
|
||||||
// this function.
|
|
||||||
func (cli *Client) ImageLoad(input io.Reader) (types.ImageLoadResponse, error) {
|
|
||||||
resp, err := cli.postRaw("/images/load", url.Values{}, input, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.ImageLoadResponse{}, err
|
|
||||||
}
|
|
||||||
return types.ImageLoadResponse{
|
|
||||||
Body: resp.body,
|
|
||||||
JSON: resp.header.Get("Content-Type") == "application/json",
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImagePull request the docker host to pull an image from a remote registry.
|
|
||||||
// It executes the privileged function if the operation is unauthorized
|
|
||||||
// and it tries one more time.
|
|
||||||
// It's up to the caller to handle the io.ReadCloser and close it properly.
|
|
||||||
func (cli *Client) ImagePull(options types.ImagePullOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("fromImage", options.ImageID)
|
|
||||||
if options.Tag != "" {
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.tryImageCreate(query, options.RegistryAuth)
|
|
||||||
if resp.statusCode == http.StatusUnauthorized {
|
|
||||||
newAuthHeader, privilegeErr := privilegeFunc()
|
|
||||||
if privilegeErr != nil {
|
|
||||||
return nil, privilegeErr
|
|
||||||
}
|
|
||||||
resp, err = cli.tryImageCreate(query, newAuthHeader)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImagePush request the docker host to push an image to a remote registry.
|
|
||||||
// It executes the privileged function if the operation is unauthorized
|
|
||||||
// and it tries one more time.
|
|
||||||
// It's up to the caller to handle the io.ReadCloser and close it properly.
|
|
||||||
func (cli *Client) ImagePush(options types.ImagePushOptions, privilegeFunc RequestPrivilegeFunc) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
|
|
||||||
resp, err := cli.tryImagePush(options.ImageID, query, options.RegistryAuth)
|
|
||||||
if resp.statusCode == http.StatusUnauthorized {
|
|
||||||
newAuthHeader, privilegeErr := privilegeFunc()
|
|
||||||
if privilegeErr != nil {
|
|
||||||
return nil, privilegeErr
|
|
||||||
}
|
|
||||||
resp, err = cli.tryImagePush(options.ImageID, query, newAuthHeader)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) tryImagePush(imageID string, query url.Values, registryAuth string) (*serverResponse, error) {
|
|
||||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
|
||||||
return cli.post("/images/"+imageID+"/push", query, nil, headers)
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageRemove removes an image from the docker host.
|
|
||||||
func (cli *Client) ImageRemove(options types.ImageRemoveOptions) ([]types.ImageDelete, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
|
|
||||||
if options.Force {
|
|
||||||
query.Set("force", "1")
|
|
||||||
}
|
|
||||||
if !options.PruneChildren {
|
|
||||||
query.Set("noprune", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.delete("/images/"+options.ImageID, query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
var dels []types.ImageDelete
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&dels)
|
|
||||||
return dels, err
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageSave retrieves one or more images from the docker host as a io.ReadCloser.
|
|
||||||
// It's up to the caller to store the images and close the stream.
|
|
||||||
func (cli *Client) ImageSave(imageIDs []string) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{
|
|
||||||
"names": imageIDs,
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.get("/images/get", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/registry"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageSearch makes the docker host to search by a term in a remote registry.
|
|
||||||
// The list of results is not sorted in any fashion.
|
|
||||||
func (cli *Client) ImageSearch(options types.ImageSearchOptions, privilegeFunc RequestPrivilegeFunc) ([]registry.SearchResult, error) {
|
|
||||||
var results []registry.SearchResult
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("term", options.Term)
|
|
||||||
|
|
||||||
resp, err := cli.tryImageSearch(query, options.RegistryAuth)
|
|
||||||
if resp.statusCode == http.StatusUnauthorized {
|
|
||||||
newAuthHeader, privilegeErr := privilegeFunc()
|
|
||||||
if privilegeErr != nil {
|
|
||||||
return results, privilegeErr
|
|
||||||
}
|
|
||||||
resp, err = cli.tryImageSearch(query, newAuthHeader)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&results)
|
|
||||||
return results, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) tryImageSearch(query url.Values, registryAuth string) (*serverResponse, error) {
|
|
||||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
|
||||||
return cli.get("/images/search", query, headers)
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ImageTag tags an image in the docker host
|
|
||||||
func (cli *Client) ImageTag(options types.ImageTagOptions) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("repo", options.RepositoryName)
|
|
||||||
query.Set("tag", options.Tag)
|
|
||||||
if options.Force {
|
|
||||||
query.Set("force", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.post("/images/"+options.ImageID+"/tag", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Info returns information about the docker server.
|
|
||||||
func (cli *Client) Info() (types.Info, error) {
|
|
||||||
var info types.Info
|
|
||||||
serverResp, err := cli.get("/info", url.Values{}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(serverResp)
|
|
||||||
|
|
||||||
if err := json.NewDecoder(serverResp.body).Decode(&info); err != nil {
|
|
||||||
return info, fmt.Errorf("Error reading remote info: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return info, nil
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import "net/url"
|
|
||||||
|
|
||||||
// ContainerKill terminates the container process but does not remove the container from the docker host.
|
|
||||||
func (cli *Client) ContainerKill(containerID, signal string) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("signal", signal)
|
|
||||||
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/kill", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegistryLogin authenticates the docker server with a given docker registry.
|
|
||||||
// It returns UnauthorizerError when the authentication fails.
|
|
||||||
func (cli *Client) RegistryLogin(auth types.AuthConfig) (types.AuthResponse, error) {
|
|
||||||
resp, err := cli.post("/auth", url.Values{}, auth, nil)
|
|
||||||
|
|
||||||
if resp != nil && resp.statusCode == http.StatusUnauthorized {
|
|
||||||
return types.AuthResponse{}, unauthorizedError{err}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return types.AuthResponse{}, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
var response types.AuthResponse
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&response)
|
|
||||||
return response, err
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
timetypes "github.com/docker/engine-api/types/time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerLogs returns the logs generated by a container in an io.ReadCloser.
|
|
||||||
// It's up to the caller to close the stream.
|
|
||||||
func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadCloser, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
if options.ShowStdout {
|
|
||||||
query.Set("stdout", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.ShowStderr {
|
|
||||||
query.Set("stderr", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Since != "" {
|
|
||||||
ts, err := timetypes.GetTimestamp(options.Since, time.Now())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query.Set("since", ts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Timestamps {
|
|
||||||
query.Set("timestamps", "1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Follow {
|
|
||||||
query.Set("follow", "1")
|
|
||||||
}
|
|
||||||
query.Set("tail", options.Tail)
|
|
||||||
|
|
||||||
resp, err := cli.get("/containers/"+options.ContainerID+"/logs", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.body, nil
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NetworkCreate creates a new network in the docker host.
|
|
||||||
func (cli *Client) NetworkCreate(options types.NetworkCreate) (types.NetworkCreateResponse, error) {
|
|
||||||
var response types.NetworkCreateResponse
|
|
||||||
serverResp, err := cli.post("/networks/create", nil, options, nil)
|
|
||||||
if err != nil {
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
json.NewDecoder(serverResp.body).Decode(&response)
|
|
||||||
ensureReaderClosed(serverResp)
|
|
||||||
return response, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkRemove removes an existent network from the docker host.
|
|
||||||
func (cli *Client) NetworkRemove(networkID string) error {
|
|
||||||
resp, err := cli.delete("/networks/"+networkID, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkConnect connects a container to an existent network in the docker host.
|
|
||||||
func (cli *Client) NetworkConnect(networkID, containerID string) error {
|
|
||||||
nc := types.NetworkConnect{Container: containerID}
|
|
||||||
resp, err := cli.post("/networks/"+networkID+"/connect", nil, nc, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkDisconnect disconnects a container from an existent network in the docker host.
|
|
||||||
func (cli *Client) NetworkDisconnect(networkID, containerID string) error {
|
|
||||||
nc := types.NetworkConnect{Container: containerID}
|
|
||||||
resp, err := cli.post("/networks/"+networkID+"/disconnect", nil, nc, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkList returns the list of networks configured in the docker host.
|
|
||||||
func (cli *Client) NetworkList(options types.NetworkListOptions) ([]types.NetworkResource, error) {
|
|
||||||
query := url.Values{}
|
|
||||||
if options.Filters.Len() > 0 {
|
|
||||||
filterJSON, err := filters.ToParam(options.Filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
query.Set("filters", filterJSON)
|
|
||||||
}
|
|
||||||
var networkResources []types.NetworkResource
|
|
||||||
resp, err := cli.get("/networks", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return networkResources, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&networkResources)
|
|
||||||
return networkResources, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkInspect returns the information for a specific network configured in the docker host.
|
|
||||||
func (cli *Client) NetworkInspect(networkID string) (types.NetworkResource, error) {
|
|
||||||
var networkResource types.NetworkResource
|
|
||||||
resp, err := cli.get("/networks/"+networkID, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
if resp.statusCode == http.StatusNotFound {
|
|
||||||
return networkResource, networkNotFoundError{networkID}
|
|
||||||
}
|
|
||||||
return networkResource, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&networkResource)
|
|
||||||
return networkResource, err
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
// ContainerPause pauses the main process of a given container without terminating it.
|
|
||||||
func (cli *Client) ContainerPause(containerID string) error {
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/pause", nil, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
// RequestPrivilegeFunc is a function interface that
|
|
||||||
// clients can supply to retry operations after
|
|
||||||
// getting an authorization error.
|
|
||||||
// This function returns the registry authentication
|
|
||||||
// header value in base 64 format, or an error
|
|
||||||
// if the privilege request fails.
|
|
||||||
type RequestPrivilegeFunc func() (string, error)
|
|
@ -1,176 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// serverResponse is a wrapper for http API responses.
|
|
||||||
type serverResponse struct {
|
|
||||||
body io.ReadCloser
|
|
||||||
header http.Header
|
|
||||||
statusCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
// head sends an http request to the docker API using the method HEAD.
|
|
||||||
func (cli *Client) head(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendRequest("HEAD", path, query, nil, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get sends an http request to the docker API using the method GET.
|
|
||||||
func (cli *Client) get(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendRequest("GET", path, query, nil, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// post sends an http request to the docker API using the method POST.
|
|
||||||
func (cli *Client) post(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendRequest("POST", path, query, body, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// postRaw sends the raw input to the docker API using the method POST.
|
|
||||||
func (cli *Client) postRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendClientRequest("POST", path, query, body, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// put sends an http request to the docker API using the method PUT.
|
|
||||||
func (cli *Client) put(path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendRequest("PUT", path, query, body, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// putRaw sends the raw input to the docker API using the method PUT.
|
|
||||||
func (cli *Client) putRaw(path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendClientRequest("PUT", path, query, body, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete sends an http request to the docker API using the method DELETE.
|
|
||||||
func (cli *Client) delete(path string, query url.Values, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
return cli.sendRequest("DELETE", path, query, nil, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) sendRequest(method, path string, query url.Values, body interface{}, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
params, err := encodeData(body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if body != nil {
|
|
||||||
if headers == nil {
|
|
||||||
headers = make(map[string][]string)
|
|
||||||
}
|
|
||||||
headers["Content-Type"] = []string{"application/json"}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli.sendClientRequest(method, path, query, params, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) sendClientRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*serverResponse, error) {
|
|
||||||
serverResp := &serverResponse{
|
|
||||||
body: nil,
|
|
||||||
statusCode: -1,
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedPayload := (method == "POST" || method == "PUT")
|
|
||||||
if expectedPayload && body == nil {
|
|
||||||
body = bytes.NewReader([]byte{})
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := cli.newRequest(method, path, query, body, headers)
|
|
||||||
req.URL.Host = cli.addr
|
|
||||||
req.URL.Scheme = cli.scheme
|
|
||||||
|
|
||||||
if expectedPayload && req.Header.Get("Content-Type") == "" {
|
|
||||||
req.Header.Set("Content-Type", "text/plain")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := cli.httpClient.Do(req)
|
|
||||||
if resp != nil {
|
|
||||||
serverResp.statusCode = resp.StatusCode
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if isTimeout(err) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
|
||||||
return serverResp, ErrConnectionFailed
|
|
||||||
}
|
|
||||||
|
|
||||||
if cli.scheme == "http" && strings.Contains(err.Error(), "malformed HTTP response") {
|
|
||||||
return serverResp, fmt.Errorf("%v.\n* Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
|
||||||
}
|
|
||||||
if cli.scheme == "https" && strings.Contains(err.Error(), "remote error: bad certificate") {
|
|
||||||
return serverResp, fmt.Errorf("The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return serverResp, fmt.Errorf("An error occurred trying to connect: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverResp.statusCode < 200 || serverResp.statusCode >= 400 {
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return serverResp, err
|
|
||||||
}
|
|
||||||
if len(body) == 0 {
|
|
||||||
return serverResp, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(serverResp.statusCode), req.URL)
|
|
||||||
}
|
|
||||||
return serverResp, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
|
|
||||||
}
|
|
||||||
|
|
||||||
serverResp.body = resp.Body
|
|
||||||
serverResp.header = resp.Header
|
|
||||||
return serverResp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) newRequest(method, path string, query url.Values, body io.Reader, headers map[string][]string) (*http.Request, error) {
|
|
||||||
apiPath := cli.getAPIPath(path, query)
|
|
||||||
req, err := http.NewRequest(method, apiPath, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add CLI Config's HTTP Headers BEFORE we set the Docker headers
|
|
||||||
// then the user can't change OUR headers
|
|
||||||
for k, v := range cli.customHTTPHeaders {
|
|
||||||
req.Header.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if headers != nil {
|
|
||||||
for k, v := range headers {
|
|
||||||
req.Header[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeData(data interface{}) (*bytes.Buffer, error) {
|
|
||||||
params := bytes.NewBuffer(nil)
|
|
||||||
if data != nil {
|
|
||||||
if err := json.NewEncoder(params).Encode(data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureReaderClosed(response *serverResponse) {
|
|
||||||
if response != nil && response.body != nil {
|
|
||||||
response.body.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isTimeout(err error) bool {
|
|
||||||
type timeout interface {
|
|
||||||
Timeout() bool
|
|
||||||
}
|
|
||||||
e := err
|
|
||||||
switch urlErr := err.(type) {
|
|
||||||
case *url.Error:
|
|
||||||
e = urlErr.Err
|
|
||||||
}
|
|
||||||
t, ok := e.(timeout)
|
|
||||||
return ok && t.Timeout()
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerResize changes the size of the tty for a container.
|
|
||||||
func (cli *Client) ContainerResize(options types.ResizeOptions) error {
|
|
||||||
return cli.resize("/containers/"+options.ID, options.Height, options.Width)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecResize changes the size of the tty for an exec process running inside a container.
|
|
||||||
func (cli *Client) ContainerExecResize(options types.ResizeOptions) error {
|
|
||||||
return cli.resize("/exec/"+options.ID, options.Height, options.Width)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) resize(basePath string, height, width int) error {
|
|
||||||
query := url.Values{}
|
|
||||||
query.Set("h", strconv.Itoa(height))
|
|
||||||
query.Set("w", strconv.Itoa(width))
|
|
||||||
|
|
||||||
resp, err := cli.post(basePath+"/resize", query, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ServerVersion returns information of the docker client and server host.
|
|
||||||
func (cli *Client) ServerVersion() (types.Version, error) {
|
|
||||||
resp, err := cli.get("/version", nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.Version{}, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
var server types.Version
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&server)
|
|
||||||
return server, err
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolumeList returns the volumes configured in the docker host.
|
|
||||||
func (cli *Client) VolumeList(filter filters.Args) (types.VolumesListResponse, error) {
|
|
||||||
var volumes types.VolumesListResponse
|
|
||||||
query := url.Values{}
|
|
||||||
|
|
||||||
if filter.Len() > 0 {
|
|
||||||
filterJSON, err := filters.ToParam(filter)
|
|
||||||
if err != nil {
|
|
||||||
return volumes, err
|
|
||||||
}
|
|
||||||
query.Set("filters", filterJSON)
|
|
||||||
}
|
|
||||||
resp, err := cli.get("/volumes", query, nil)
|
|
||||||
if err != nil {
|
|
||||||
return volumes, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&volumes)
|
|
||||||
return volumes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeInspect returns the information about a specific volume in the docker host.
|
|
||||||
func (cli *Client) VolumeInspect(volumeID string) (types.Volume, error) {
|
|
||||||
var volume types.Volume
|
|
||||||
resp, err := cli.get("/volumes/"+volumeID, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
if resp.statusCode == http.StatusNotFound {
|
|
||||||
return volume, volumeNotFoundError{volumeID}
|
|
||||||
}
|
|
||||||
return volume, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&volume)
|
|
||||||
return volume, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeCreate creates a volume in the docker host.
|
|
||||||
func (cli *Client) VolumeCreate(options types.VolumeCreateRequest) (types.Volume, error) {
|
|
||||||
var volume types.Volume
|
|
||||||
resp, err := cli.post("/volumes/create", nil, options, nil)
|
|
||||||
if err != nil {
|
|
||||||
return volume, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
err = json.NewDecoder(resp.body).Decode(&volume)
|
|
||||||
return volume, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeRemove removes a volume from the docker host.
|
|
||||||
func (cli *Client) VolumeRemove(volumeID string) error {
|
|
||||||
resp, err := cli.delete("/volumes/"+volumeID, nil, nil)
|
|
||||||
ensureReaderClosed(resp)
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerWait pauses execution util a container is exits.
|
|
||||||
// It returns the API status code as response of its readiness.
|
|
||||||
func (cli *Client) ContainerWait(containerID string) (int, error) {
|
|
||||||
resp, err := cli.post("/containers/"+containerID+"/wait", nil, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
defer ensureReaderClosed(resp)
|
|
||||||
|
|
||||||
var res types.ContainerWaitResponse
|
|
||||||
if err := json.NewDecoder(resp.body).Decode(&res); err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.StatusCode, nil
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
// AuthConfig contains authorization information for connecting to a Registry
|
|
||||||
type AuthConfig struct {
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
Auth string `json:"auth"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
ServerAddress string `json:"serveraddress,omitempty"`
|
|
||||||
RegistryToken string `json:"registrytoken,omitempty"`
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package blkiodev
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// WeightDevice is a structure that hold device:weight pair
|
|
||||||
type WeightDevice struct {
|
|
||||||
Path string
|
|
||||||
Weight uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WeightDevice) String() string {
|
|
||||||
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThrottleDevice is a structure that hold device:rate_per_second pair
|
|
||||||
type ThrottleDevice struct {
|
|
||||||
Path string
|
|
||||||
Rate uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *ThrottleDevice) String() string {
|
|
||||||
return fmt.Sprintf("%s:%d", t.Path, t.Rate)
|
|
||||||
}
|
|
@ -1,245 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
"github.com/docker/engine-api/types/filters"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerAttachOptions holds parameters to attach to a container.
|
|
||||||
type ContainerAttachOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
Stream bool
|
|
||||||
Stdin bool
|
|
||||||
Stdout bool
|
|
||||||
Stderr bool
|
|
||||||
DetachKeys string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerCommitOptions holds parameters to commit changes into a container.
|
|
||||||
type ContainerCommitOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
RepositoryName string
|
|
||||||
Tag string
|
|
||||||
Comment string
|
|
||||||
Author string
|
|
||||||
Changes []string
|
|
||||||
Pause bool
|
|
||||||
Config *container.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecInspect holds information returned by exec inspect.
|
|
||||||
type ContainerExecInspect struct {
|
|
||||||
ExecID string
|
|
||||||
ContainerID string
|
|
||||||
Running bool
|
|
||||||
ExitCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerListOptions holds parameters to list containers with.
|
|
||||||
type ContainerListOptions struct {
|
|
||||||
Quiet bool
|
|
||||||
Size bool
|
|
||||||
All bool
|
|
||||||
Latest bool
|
|
||||||
Since string
|
|
||||||
Before string
|
|
||||||
Limit int
|
|
||||||
Filter filters.Args
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerLogsOptions holds parameters to filter logs with.
|
|
||||||
type ContainerLogsOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
ShowStdout bool
|
|
||||||
ShowStderr bool
|
|
||||||
Since string
|
|
||||||
Timestamps bool
|
|
||||||
Follow bool
|
|
||||||
Tail string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerRemoveOptions holds parameters to remove containers.
|
|
||||||
type ContainerRemoveOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
RemoveVolumes bool
|
|
||||||
RemoveLinks bool
|
|
||||||
Force bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyToContainerOptions holds information
|
|
||||||
// about files to copy into a container
|
|
||||||
type CopyToContainerOptions struct {
|
|
||||||
ContainerID string
|
|
||||||
Path string
|
|
||||||
Content io.Reader
|
|
||||||
AllowOverwriteDirWithFile bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// EventsOptions hold parameters to filter events with.
|
|
||||||
type EventsOptions struct {
|
|
||||||
Since string
|
|
||||||
Until string
|
|
||||||
Filters filters.Args
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkListOptions holds parameters to filter the list of networks with.
|
|
||||||
type NetworkListOptions struct {
|
|
||||||
Filters filters.Args
|
|
||||||
}
|
|
||||||
|
|
||||||
// HijackedResponse holds connection information for a hijacked request.
|
|
||||||
type HijackedResponse struct {
|
|
||||||
Conn net.Conn
|
|
||||||
Reader *bufio.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the hijacked connection and reader.
|
|
||||||
func (h *HijackedResponse) Close() {
|
|
||||||
h.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseWriter is an interface that implement structs
|
|
||||||
// that close input streams to prevent from writing.
|
|
||||||
type CloseWriter interface {
|
|
||||||
CloseWrite() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseWrite closes a readWriter for writing.
|
|
||||||
func (h *HijackedResponse) CloseWrite() error {
|
|
||||||
if conn, ok := h.Conn.(CloseWriter); ok {
|
|
||||||
return conn.CloseWrite()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageBuildOptions holds the information
|
|
||||||
// necessary to build images.
|
|
||||||
type ImageBuildOptions struct {
|
|
||||||
Tags []string
|
|
||||||
SuppressOutput bool
|
|
||||||
RemoteContext string
|
|
||||||
NoCache bool
|
|
||||||
Remove bool
|
|
||||||
ForceRemove bool
|
|
||||||
PullParent bool
|
|
||||||
IsolationLevel container.IsolationLevel
|
|
||||||
CPUSetCPUs string
|
|
||||||
CPUSetMems string
|
|
||||||
CPUShares int64
|
|
||||||
CPUQuota int64
|
|
||||||
CPUPeriod int64
|
|
||||||
Memory int64
|
|
||||||
MemorySwap int64
|
|
||||||
CgroupParent string
|
|
||||||
ShmSize int64
|
|
||||||
Dockerfile string
|
|
||||||
Ulimits []*units.Ulimit
|
|
||||||
BuildArgs map[string]string
|
|
||||||
AuthConfigs map[string]AuthConfig
|
|
||||||
Context io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageBuildResponse holds information
|
|
||||||
// returned by a server after building
|
|
||||||
// an image.
|
|
||||||
type ImageBuildResponse struct {
|
|
||||||
Body io.ReadCloser
|
|
||||||
OSType string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageCreateOptions holds information to create images.
|
|
||||||
type ImageCreateOptions struct {
|
|
||||||
// Parent is the image to create this image from
|
|
||||||
Parent string
|
|
||||||
// Tag is the name to tag this image
|
|
||||||
Tag string
|
|
||||||
// RegistryAuth is the base64 encoded credentials for this server
|
|
||||||
RegistryAuth string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageImportOptions holds information to import images from the client host.
|
|
||||||
type ImageImportOptions struct {
|
|
||||||
// Source is the data to send to the server to create this image from
|
|
||||||
Source io.Reader
|
|
||||||
// Source is the name of the source to import this image from
|
|
||||||
SourceName string
|
|
||||||
// RepositoryName is the name of the repository to import this image
|
|
||||||
RepositoryName string
|
|
||||||
// Message is the message to tag the image with
|
|
||||||
Message string
|
|
||||||
// Tag is the name to tag this image
|
|
||||||
Tag string
|
|
||||||
// Changes are the raw changes to apply to the image
|
|
||||||
Changes []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageListOptions holds parameters to filter the list of images with.
|
|
||||||
type ImageListOptions struct {
|
|
||||||
MatchName string
|
|
||||||
All bool
|
|
||||||
Filters filters.Args
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageLoadResponse returns information to the client about a load process.
|
|
||||||
type ImageLoadResponse struct {
|
|
||||||
Body io.ReadCloser
|
|
||||||
JSON bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImagePullOptions holds information to pull images.
|
|
||||||
type ImagePullOptions struct {
|
|
||||||
ImageID string
|
|
||||||
Tag string
|
|
||||||
// RegistryAuth is the base64 encoded credentials for this server
|
|
||||||
RegistryAuth string
|
|
||||||
}
|
|
||||||
|
|
||||||
//ImagePushOptions holds information to push images.
|
|
||||||
type ImagePushOptions ImagePullOptions
|
|
||||||
|
|
||||||
// ImageRemoveOptions holds parameters to remove images.
|
|
||||||
type ImageRemoveOptions struct {
|
|
||||||
ImageID string
|
|
||||||
Force bool
|
|
||||||
PruneChildren bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageSearchOptions holds parameters to search images with.
|
|
||||||
type ImageSearchOptions struct {
|
|
||||||
Term string
|
|
||||||
RegistryAuth string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageTagOptions holds parameters to tag an image
|
|
||||||
type ImageTagOptions struct {
|
|
||||||
ImageID string
|
|
||||||
RepositoryName string
|
|
||||||
Tag string
|
|
||||||
Force bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResizeOptions holds parameters to resize a tty.
|
|
||||||
// It can be used to resize container ttys and
|
|
||||||
// exec process ttys too.
|
|
||||||
type ResizeOptions struct {
|
|
||||||
ID string
|
|
||||||
Height int
|
|
||||||
Width int
|
|
||||||
}
|
|
||||||
|
|
||||||
// VersionResponse holds version information for the client and the server
|
|
||||||
type VersionResponse struct {
|
|
||||||
Client *Version
|
|
||||||
Server *Version
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerOK return true when the client could connect to the docker server
|
|
||||||
// and parse the information received. It returns false otherwise.
|
|
||||||
func (v VersionResponse) ServerOK() bool {
|
|
||||||
return v.Server != nil
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import "github.com/docker/engine-api/types/container"
|
|
||||||
|
|
||||||
// configs holds structs used for internal communication between the
|
|
||||||
// frontend (such as an http server) and the backend (such as the
|
|
||||||
// docker daemon).
|
|
||||||
|
|
||||||
// ContainerCreateConfig is the parameter set to ContainerCreate()
|
|
||||||
type ContainerCreateConfig struct {
|
|
||||||
Name string
|
|
||||||
Config *container.Config
|
|
||||||
HostConfig *container.HostConfig
|
|
||||||
AdjustCPUShares bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerRmConfig holds arguments for the container remove
|
|
||||||
// operation. This struct is used to tell the backend what operations
|
|
||||||
// to perform.
|
|
||||||
type ContainerRmConfig struct {
|
|
||||||
ForceRemove, RemoveVolume, RemoveLink bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerCommitConfig contains build configs for commit operation,
|
|
||||||
// and is used when making a commit with the current state of the container.
|
|
||||||
type ContainerCommitConfig struct {
|
|
||||||
Pause bool
|
|
||||||
Repo string
|
|
||||||
Tag string
|
|
||||||
Author string
|
|
||||||
Comment string
|
|
||||||
// merge container config into commit config before commit
|
|
||||||
MergeConfigs bool
|
|
||||||
Config *container.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecConfig is a small subset of the Config struct that hold the configuration
|
|
||||||
// for the exec feature of docker.
|
|
||||||
type ExecConfig struct {
|
|
||||||
User string // User that will run the command
|
|
||||||
Privileged bool // Is the container in privileged mode
|
|
||||||
Tty bool // Attach standard streams to a tty.
|
|
||||||
Container string // Name of the container (to execute in)
|
|
||||||
AttachStdin bool // Attach the standard input, makes possible user interaction
|
|
||||||
AttachStderr bool // Attach the standard output
|
|
||||||
AttachStdout bool // Attach the standard error
|
|
||||||
Detach bool // Execute in detach mode
|
|
||||||
DetachKeys string // Escape keys for detach
|
|
||||||
Cmd []string // Execution commands and args
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/engine-api/types/strslice"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config contains the configuration data about a container.
|
|
||||||
// It should hold only portable information about the container.
|
|
||||||
// Here, "portable" means "independent from the host we are running on".
|
|
||||||
// Non-portable information *should* appear in HostConfig.
|
|
||||||
// All fields added to this struct must be marked `omitempty` to keep getting
|
|
||||||
// predictable hashes from the old `v1Compatibility` configuration.
|
|
||||||
type Config struct {
|
|
||||||
Hostname string // Hostname
|
|
||||||
Domainname string // Domainname
|
|
||||||
User string // User that will run the command(s) inside the container
|
|
||||||
AttachStdin bool // Attach the standard input, makes possible user interaction
|
|
||||||
AttachStdout bool // Attach the standard output
|
|
||||||
AttachStderr bool // Attach the standard error
|
|
||||||
ExposedPorts map[nat.Port]struct{} `json:",omitempty"` // List of exposed ports
|
|
||||||
PublishService string `json:",omitempty"` // Name of the network service exposed by the container
|
|
||||||
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
|
|
||||||
OpenStdin bool // Open stdin
|
|
||||||
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
|
|
||||||
Env []string // List of environment variable to set in the container
|
|
||||||
Cmd *strslice.StrSlice // Command to run when starting the container
|
|
||||||
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
|
||||||
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
|
|
||||||
Volumes map[string]struct{} // List of volumes (mounts) used for the container
|
|
||||||
WorkingDir string // Current directory (PWD) in the command will be launched
|
|
||||||
Entrypoint *strslice.StrSlice // Entrypoint to run when starting the container
|
|
||||||
NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
|
||||||
MacAddress string `json:",omitempty"` // Mac Address of the container
|
|
||||||
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
|
||||||
Labels map[string]string // List of labels set to this container
|
|
||||||
StopSignal string `json:",omitempty"` // Signal to stop a container
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types/blkiodev"
|
|
||||||
"github.com/docker/engine-api/types/strslice"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NetworkMode represents the container network stack.
|
|
||||||
type NetworkMode string
|
|
||||||
|
|
||||||
// IsolationLevel represents the isolation level of a container. The supported
|
|
||||||
// values are platform specific
|
|
||||||
type IsolationLevel string
|
|
||||||
|
|
||||||
// IsDefault indicates the default isolation level of a container. On Linux this
|
|
||||||
// is the native driver. On Windows, this is a Windows Server Container.
|
|
||||||
func (i IsolationLevel) IsDefault() bool {
|
|
||||||
return strings.ToLower(string(i)) == "default" || string(i) == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IpcMode represents the container ipc stack.
|
|
||||||
type IpcMode string
|
|
||||||
|
|
||||||
// IsPrivate indicates whether the container uses it's private ipc stack.
|
|
||||||
func (n IpcMode) IsPrivate() bool {
|
|
||||||
return !(n.IsHost() || n.IsContainer())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHost indicates whether the container uses the host's ipc stack.
|
|
||||||
func (n IpcMode) IsHost() bool {
|
|
||||||
return n == "host"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainer indicates whether the container uses a container's ipc stack.
|
|
||||||
func (n IpcMode) IsContainer() bool {
|
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
|
||||||
return len(parts) > 1 && parts[0] == "container"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid indicates whether the ipc stack is valid.
|
|
||||||
func (n IpcMode) Valid() bool {
|
|
||||||
parts := strings.Split(string(n), ":")
|
|
||||||
switch mode := parts[0]; mode {
|
|
||||||
case "", "host":
|
|
||||||
case "container":
|
|
||||||
if len(parts) != 2 || parts[1] == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container returns the name of the container ipc stack is going to be used.
|
|
||||||
func (n IpcMode) Container() string {
|
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
|
||||||
if len(parts) > 1 {
|
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTSMode represents the UTS namespace of the container.
|
|
||||||
type UTSMode string
|
|
||||||
|
|
||||||
// IsPrivate indicates whether the container uses it's private UTS namespace.
|
|
||||||
func (n UTSMode) IsPrivate() bool {
|
|
||||||
return !(n.IsHost())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHost indicates whether the container uses the host's UTS namespace.
|
|
||||||
func (n UTSMode) IsHost() bool {
|
|
||||||
return n == "host"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid indicates whether the UTS namespace is valid.
|
|
||||||
func (n UTSMode) Valid() bool {
|
|
||||||
parts := strings.Split(string(n), ":")
|
|
||||||
switch mode := parts[0]; mode {
|
|
||||||
case "", "host":
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// PidMode represents the pid stack of the container.
|
|
||||||
type PidMode string
|
|
||||||
|
|
||||||
// IsPrivate indicates whether the container uses it's private pid stack.
|
|
||||||
func (n PidMode) IsPrivate() bool {
|
|
||||||
return !(n.IsHost())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHost indicates whether the container uses the host's pid stack.
|
|
||||||
func (n PidMode) IsHost() bool {
|
|
||||||
return n == "host"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid indicates whether the pid stack is valid.
|
|
||||||
func (n PidMode) Valid() bool {
|
|
||||||
parts := strings.Split(string(n), ":")
|
|
||||||
switch mode := parts[0]; mode {
|
|
||||||
case "", "host":
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceMapping represents the device mapping between the host and the container.
|
|
||||||
type DeviceMapping struct {
|
|
||||||
PathOnHost string
|
|
||||||
PathInContainer string
|
|
||||||
CgroupPermissions string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestartPolicy represents the restart policies of the container.
|
|
||||||
type RestartPolicy struct {
|
|
||||||
Name string
|
|
||||||
MaximumRetryCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNone indicates whether the container has the "no" restart policy.
|
|
||||||
// This means the container will not automatically restart when exiting.
|
|
||||||
func (rp *RestartPolicy) IsNone() bool {
|
|
||||||
return rp.Name == "no"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsAlways indicates whether the container has the "always" restart policy.
|
|
||||||
// This means the container will automatically restart regardless of the exit status.
|
|
||||||
func (rp *RestartPolicy) IsAlways() bool {
|
|
||||||
return rp.Name == "always"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOnFailure indicates whether the container has the "on-failure" restart policy.
|
|
||||||
// This means the contain will automatically restart of exiting with a non-zero exit status.
|
|
||||||
func (rp *RestartPolicy) IsOnFailure() bool {
|
|
||||||
return rp.Name == "on-failure"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUnlessStopped indicates whether the container has the
|
|
||||||
// "unless-stopped" restart policy. This means the container will
|
|
||||||
// automatically restart unless user has put it to stopped state.
|
|
||||||
func (rp *RestartPolicy) IsUnlessStopped() bool {
|
|
||||||
return rp.Name == "unless-stopped"
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogConfig represents the logging configuration of the container.
|
|
||||||
type LogConfig struct {
|
|
||||||
Type string
|
|
||||||
Config map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resources contains container's resources (cgroups config, ulimits...)
|
|
||||||
type Resources struct {
|
|
||||||
// Applicable to all platforms
|
|
||||||
CPUShares int64 `json:"CpuShares"` // CPU shares (relative weight vs. other containers)
|
|
||||||
|
|
||||||
// Applicable to UNIX platforms
|
|
||||||
CgroupParent string // Parent cgroup.
|
|
||||||
BlkioWeight uint16 // Block IO weight (relative weight vs. other containers)
|
|
||||||
BlkioWeightDevice []*blkiodev.WeightDevice
|
|
||||||
BlkioDeviceReadBps []*blkiodev.ThrottleDevice
|
|
||||||
BlkioDeviceWriteBps []*blkiodev.ThrottleDevice
|
|
||||||
BlkioDeviceReadIOps []*blkiodev.ThrottleDevice
|
|
||||||
BlkioDeviceWriteIOps []*blkiodev.ThrottleDevice
|
|
||||||
CPUPeriod int64 `json:"CpuPeriod"` // CPU CFS (Completely Fair Scheduler) period
|
|
||||||
CPUQuota int64 `json:"CpuQuota"` // CPU CFS (Completely Fair Scheduler) quota
|
|
||||||
CpusetCpus string // CpusetCpus 0-2, 0,1
|
|
||||||
CpusetMems string // CpusetMems 0-2, 0,1
|
|
||||||
Devices []DeviceMapping // List of devices to map inside the container
|
|
||||||
KernelMemory int64 // Kernel memory limit (in bytes)
|
|
||||||
Memory int64 // Memory limit (in bytes)
|
|
||||||
MemoryReservation int64 // Memory soft limit (in bytes)
|
|
||||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to disable swap
|
|
||||||
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
|
||||||
OomKillDisable bool // Whether to disable OOM Killer or not
|
|
||||||
Ulimits []*units.Ulimit // List of ulimits to be set in the container
|
|
||||||
}
|
|
||||||
|
|
||||||
// HostConfig the non-portable Config structure of a container.
|
|
||||||
// Here, "non-portable" means "dependent of the host we are running on".
|
|
||||||
// Portable information *should* appear in Config.
|
|
||||||
type HostConfig struct {
|
|
||||||
// Applicable to all platforms
|
|
||||||
Binds []string // List of volume bindings for this container
|
|
||||||
ContainerIDFile string // File (path) where the containerId is written
|
|
||||||
LogConfig LogConfig // Configuration of the logs for this container
|
|
||||||
NetworkMode NetworkMode // Network mode to use for the container
|
|
||||||
PortBindings nat.PortMap // Port mapping between the exposed port (container) and the host
|
|
||||||
RestartPolicy RestartPolicy // Restart policy to be used for the container
|
|
||||||
VolumeDriver string // Name of the volume driver used to mount volumes
|
|
||||||
VolumesFrom []string // List of volumes to take from other container
|
|
||||||
|
|
||||||
// Applicable to UNIX platforms
|
|
||||||
CapAdd *strslice.StrSlice // List of kernel capabilities to add to the container
|
|
||||||
CapDrop *strslice.StrSlice // List of kernel capabilities to remove from the container
|
|
||||||
DNS []string `json:"Dns"` // List of DNS server to lookup
|
|
||||||
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for
|
|
||||||
DNSSearch []string `json:"DnsSearch"` // List of DNSSearch to look for
|
|
||||||
ExtraHosts []string // List of extra hosts
|
|
||||||
GroupAdd []string // List of additional groups that the container process will run as
|
|
||||||
IpcMode IpcMode // IPC namespace to use for the container
|
|
||||||
Links []string // List of links (in the name:alias form)
|
|
||||||
OomScoreAdj int // Container preference for OOM-killing
|
|
||||||
PidMode PidMode // PID namespace to use for the container
|
|
||||||
Privileged bool // Is the container in privileged mode
|
|
||||||
PublishAllPorts bool // Should docker publish all exposed port for the container
|
|
||||||
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
|
||||||
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
|
||||||
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
|
||||||
UTSMode UTSMode // UTS namespace to use for the container
|
|
||||||
ShmSize int64 // Total shm memory usage
|
|
||||||
|
|
||||||
// Applicable to Windows
|
|
||||||
ConsoleSize [2]int // Initial console size
|
|
||||||
Isolation IsolationLevel // Isolation level of the container (eg default, hyperv)
|
|
||||||
|
|
||||||
// Contains container's resources (cgroups, ulimits)
|
|
||||||
Resources
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package container
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// IsValid indicates is an isolation level is valid
|
|
||||||
func (i IsolationLevel) IsValid() bool {
|
|
||||||
return i.IsDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPrivate indicates whether container uses it's private network stack.
|
|
||||||
func (n NetworkMode) IsPrivate() bool {
|
|
||||||
return !(n.IsHost() || n.IsContainer())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDefault indicates whether container uses the default network stack.
|
|
||||||
func (n NetworkMode) IsDefault() bool {
|
|
||||||
return n == "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkName returns the name of the network stack.
|
|
||||||
func (n NetworkMode) NetworkName() string {
|
|
||||||
if n.IsBridge() {
|
|
||||||
return "bridge"
|
|
||||||
} else if n.IsHost() {
|
|
||||||
return "host"
|
|
||||||
} else if n.IsContainer() {
|
|
||||||
return "container"
|
|
||||||
} else if n.IsNone() {
|
|
||||||
return "none"
|
|
||||||
} else if n.IsDefault() {
|
|
||||||
return "default"
|
|
||||||
} else if n.IsUserDefined() {
|
|
||||||
return n.UserDefined()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsBridge indicates whether container uses the bridge network stack
|
|
||||||
func (n NetworkMode) IsBridge() bool {
|
|
||||||
return n == "bridge"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHost indicates whether container uses the host network stack.
|
|
||||||
func (n NetworkMode) IsHost() bool {
|
|
||||||
return n == "host"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsContainer indicates whether container uses a container network stack.
|
|
||||||
func (n NetworkMode) IsContainer() bool {
|
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
|
||||||
return len(parts) > 1 && parts[0] == "container"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNone indicates whether container isn't using a network stack.
|
|
||||||
func (n NetworkMode) IsNone() bool {
|
|
||||||
return n == "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectedContainer is the id of the container which network this container is connected to.
|
|
||||||
func (n NetworkMode) ConnectedContainer() string {
|
|
||||||
parts := strings.SplitN(string(n), ":", 2)
|
|
||||||
if len(parts) > 1 {
|
|
||||||
return parts[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserDefined indicates user-created network
|
|
||||||
func (n NetworkMode) IsUserDefined() bool {
|
|
||||||
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
|
|
||||||
func IsPreDefinedNetwork(network string) bool {
|
|
||||||
n := NetworkMode(network)
|
|
||||||
return n.IsBridge() || n.IsHost() || n.IsNone()
|
|
||||||
}
|
|
||||||
|
|
||||||
//UserDefined indicates user-created network
|
|
||||||
func (n NetworkMode) UserDefined() string {
|
|
||||||
if n.IsUserDefined() {
|
|
||||||
return string(n)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsDefault indicates whether container uses the default network stack.
|
|
||||||
func (n NetworkMode) IsDefault() bool {
|
|
||||||
return n == "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsHyperV indicates the use of a Hyper-V partition for isolation
|
|
||||||
func (i IsolationLevel) IsHyperV() bool {
|
|
||||||
return strings.ToLower(string(i)) == "hyperv"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsProcess indicates the use of process isolation
|
|
||||||
func (i IsolationLevel) IsProcess() bool {
|
|
||||||
return strings.ToLower(string(i)) == "process"
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid indicates is an isolation level is valid
|
|
||||||
func (i IsolationLevel) IsValid() bool {
|
|
||||||
return i.IsDefault() || i.IsHyperV() || i.IsProcess()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultDaemonNetworkMode returns the default network stack the daemon should
|
|
||||||
// use.
|
|
||||||
func DefaultDaemonNetworkMode() NetworkMode {
|
|
||||||
return NetworkMode("default")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkName returns the name of the network stack.
|
|
||||||
func (n NetworkMode) NetworkName() string {
|
|
||||||
if n.IsDefault() {
|
|
||||||
return "default"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPreDefinedNetwork indicates if a network is predefined by the daemon
|
|
||||||
func IsPreDefinedNetwork(network string) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateNetMode ensures that the various combinations of requested
|
|
||||||
// network settings are valid.
|
|
||||||
func ValidateNetMode(c *Config, hc *HostConfig) error {
|
|
||||||
// We may not be passed a host config, such as in the case of docker commit
|
|
||||||
if hc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
parts := strings.Split(string(hc.NetworkMode), ":")
|
|
||||||
switch mode := parts[0]; mode {
|
|
||||||
case "default", "none":
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("invalid --net: %s", hc.NetworkMode)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateIsolationLevel performs platform specific validation of the
|
|
||||||
// isolation level in the hostconfig structure. Windows supports 'default' (or
|
|
||||||
// blank), 'process', or 'hyperv'.
|
|
||||||
func ValidateIsolationLevel(hc *HostConfig) error {
|
|
||||||
// We may not be passed a host config, such as in the case of docker commit
|
|
||||||
if hc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !hc.Isolation.IsValid() {
|
|
||||||
return fmt.Errorf("invalid --isolation: %q. Windows supports 'default', 'process', or 'hyperv'", hc.Isolation)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
package events
|
|
||||||
|
|
||||||
const (
|
|
||||||
// ContainerEventType is the event type that containers generate
|
|
||||||
ContainerEventType = "container"
|
|
||||||
// ImageEventType is the event type that images generate
|
|
||||||
ImageEventType = "image"
|
|
||||||
// VolumeEventType is the event type that volumes generate
|
|
||||||
VolumeEventType = "volume"
|
|
||||||
// NetworkEventType is the event type that networks generate
|
|
||||||
NetworkEventType = "network"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Actor describes something that generates events,
|
|
||||||
// like a container, or a network, or a volume.
|
|
||||||
// It has a defined name and a set or attributes.
|
|
||||||
// The container attributes are its labels, other actors
|
|
||||||
// can generate these attributes from other properties.
|
|
||||||
type Actor struct {
|
|
||||||
ID string
|
|
||||||
Attributes map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message represents the information an event contains
|
|
||||||
type Message struct {
|
|
||||||
// Deprecated information from JSONMessage.
|
|
||||||
// With data only in container events.
|
|
||||||
Status string `json:"status,omitempty"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
From string `json:"from,omitempty"`
|
|
||||||
|
|
||||||
Type string
|
|
||||||
Action string
|
|
||||||
Actor Actor
|
|
||||||
|
|
||||||
Time int64 `json:"time,omitempty"`
|
|
||||||
TimeNano int64 `json:"timeNano,omitempty"`
|
|
||||||
}
|
|
@ -1,257 +0,0 @@
|
|||||||
// Package filters provides helper function to parse and handle command line
|
|
||||||
// filter, used for example in docker ps or docker images commands.
|
|
||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Args stores filter arguments as map key:{array of values}.
|
|
||||||
// It contains a aggregation of the list of arguments (which are in the form
|
|
||||||
// of -f 'key=value') based on the key, and store values for the same key
|
|
||||||
// in an slice.
|
|
||||||
// e.g given -f 'label=label1=1' -f 'label=label2=2' -f 'image.name=ubuntu'
|
|
||||||
// the args will be {'label': {'label1=1','label2=2'}, 'image.name', {'ubuntu'}}
|
|
||||||
type Args struct {
|
|
||||||
fields map[string]map[string]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewArgs initializes a new Args struct.
|
|
||||||
func NewArgs() Args {
|
|
||||||
return Args{fields: map[string]map[string]bool{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseFlag parses the argument to the filter flag. Like
|
|
||||||
//
|
|
||||||
// `docker ps -f 'created=today' -f 'image.name=ubuntu*'`
|
|
||||||
//
|
|
||||||
// If prev map is provided, then it is appended to, and returned. By default a new
|
|
||||||
// map is created.
|
|
||||||
func ParseFlag(arg string, prev Args) (Args, error) {
|
|
||||||
filters := prev
|
|
||||||
if len(arg) == 0 {
|
|
||||||
return filters, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(arg, "=") {
|
|
||||||
return filters, ErrBadFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
f := strings.SplitN(arg, "=", 2)
|
|
||||||
|
|
||||||
name := strings.ToLower(strings.TrimSpace(f[0]))
|
|
||||||
value := strings.TrimSpace(f[1])
|
|
||||||
|
|
||||||
filters.Add(name, value)
|
|
||||||
|
|
||||||
return filters, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrBadFormat is an error returned in case of bad format for a filter.
|
|
||||||
var ErrBadFormat = errors.New("bad format of filter (expected name=value)")
|
|
||||||
|
|
||||||
// ToParam packs the Args into an string for easy transport from client to server.
|
|
||||||
func ToParam(a Args) (string, error) {
|
|
||||||
// this way we don't URL encode {}, just empty space
|
|
||||||
if a.Len() == 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := json.Marshal(a.fields)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromParam unpacks the filter Args.
|
|
||||||
func FromParam(p string) (Args, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return NewArgs(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r := strings.NewReader(p)
|
|
||||||
d := json.NewDecoder(r)
|
|
||||||
|
|
||||||
m := map[string]map[string]bool{}
|
|
||||||
if err := d.Decode(&m); err != nil {
|
|
||||||
r.Seek(0, 0)
|
|
||||||
|
|
||||||
// Allow parsing old arguments in slice format.
|
|
||||||
// Because other libraries might be sending them in this format.
|
|
||||||
deprecated := map[string][]string{}
|
|
||||||
if deprecatedErr := d.Decode(&deprecated); deprecatedErr == nil {
|
|
||||||
m = deprecatedArgs(deprecated)
|
|
||||||
} else {
|
|
||||||
return NewArgs(), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Args{m}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the list of values associates with a field.
|
|
||||||
// It returns a slice of strings to keep backwards compatibility with old code.
|
|
||||||
func (filters Args) Get(field string) []string {
|
|
||||||
values := filters.fields[field]
|
|
||||||
if values == nil {
|
|
||||||
return make([]string, 0)
|
|
||||||
}
|
|
||||||
slice := make([]string, 0, len(values))
|
|
||||||
for key := range values {
|
|
||||||
slice = append(slice, key)
|
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds a new value to a filter field.
|
|
||||||
func (filters Args) Add(name, value string) {
|
|
||||||
if _, ok := filters.fields[name]; ok {
|
|
||||||
filters.fields[name][value] = true
|
|
||||||
} else {
|
|
||||||
filters.fields[name] = map[string]bool{value: true}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Del removes a value from a filter field.
|
|
||||||
func (filters Args) Del(name, value string) {
|
|
||||||
if _, ok := filters.fields[name]; ok {
|
|
||||||
delete(filters.fields[name], value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of fields in the arguments.
|
|
||||||
func (filters Args) Len() int {
|
|
||||||
return len(filters.fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchKVList returns true if the values for the specified field matches the ones
|
|
||||||
// from the sources.
|
|
||||||
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
|
||||||
// field is 'label' and sources are {'label1': '1', 'label2': '2'}
|
|
||||||
// it returns true.
|
|
||||||
func (filters Args) MatchKVList(field string, sources map[string]string) bool {
|
|
||||||
fieldValues := filters.fields[field]
|
|
||||||
|
|
||||||
//do not filter if there is no filter set or cannot determine filter
|
|
||||||
if len(fieldValues) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if sources == nil || len(sources) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for name2match := range fieldValues {
|
|
||||||
testKV := strings.SplitN(name2match, "=", 2)
|
|
||||||
|
|
||||||
v, ok := sources[testKV[0]]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(testKV) == 2 && testKV[1] != v {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match returns true if the values for the specified field matches the source string
|
|
||||||
// e.g. given Args are {'label': {'label1=1','label2=1'}, 'image.name', {'ubuntu'}},
|
|
||||||
// field is 'image.name' and source is 'ubuntu'
|
|
||||||
// it returns true.
|
|
||||||
func (filters Args) Match(field, source string) bool {
|
|
||||||
if filters.ExactMatch(field, source) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValues := filters.fields[field]
|
|
||||||
for name2match := range fieldValues {
|
|
||||||
match, err := regexp.MatchString(name2match, source)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExactMatch returns true if the source matches exactly one of the filters.
|
|
||||||
func (filters Args) ExactMatch(field, source string) bool {
|
|
||||||
fieldValues, ok := filters.fields[field]
|
|
||||||
//do not filter if there is no filter set or cannot determine filter
|
|
||||||
if !ok || len(fieldValues) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to march full name value to avoid O(N) regular expression matching
|
|
||||||
if fieldValues[source] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuzzyMatch returns true if the source matches exactly one of the filters,
|
|
||||||
// or the source has one of the filters as a prefix.
|
|
||||||
func (filters Args) FuzzyMatch(field, source string) bool {
|
|
||||||
if filters.ExactMatch(field, source) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldValues := filters.fields[field]
|
|
||||||
for prefix := range fieldValues {
|
|
||||||
if strings.HasPrefix(source, prefix) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include returns true if the name of the field to filter is in the filters.
|
|
||||||
func (filters Args) Include(field string) bool {
|
|
||||||
_, ok := filters.fields[field]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate ensures that all the fields in the filter are valid.
|
|
||||||
// It returns an error as soon as it finds an invalid field.
|
|
||||||
func (filters Args) Validate(accepted map[string]bool) error {
|
|
||||||
for name := range filters.fields {
|
|
||||||
if !accepted[name] {
|
|
||||||
return fmt.Errorf("Invalid filter '%s'", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalkValues iterates over the list of filtered values for a field.
|
|
||||||
// It stops the iteration if it finds an error and it returns that error.
|
|
||||||
func (filters Args) WalkValues(field string, op func(value string) error) error {
|
|
||||||
if _, ok := filters.fields[field]; !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for v := range filters.fields[field] {
|
|
||||||
if err := op(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func deprecatedArgs(d map[string][]string) map[string]map[string]bool {
|
|
||||||
m := map[string]map[string]bool{}
|
|
||||||
for k, v := range d {
|
|
||||||
values := map[string]bool{}
|
|
||||||
for _, vv := range v {
|
|
||||||
values[vv] = true
|
|
||||||
}
|
|
||||||
m[k] = values
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
@ -1,369 +0,0 @@
|
|||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseArgs(t *testing.T) {
|
|
||||||
// equivalent of `docker ps -f 'created=today' -f 'image.name=ubuntu*' -f 'image.name=*untu'`
|
|
||||||
flagArgs := []string{
|
|
||||||
"created=today",
|
|
||||||
"image.name=ubuntu*",
|
|
||||||
"image.name=*untu",
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
args = NewArgs()
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
for i := range flagArgs {
|
|
||||||
args, err = ParseFlag(flagArgs[i], args)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to parse %s: %s", flagArgs[i], err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(args.Get("created")) != 1 {
|
|
||||||
t.Errorf("failed to set this arg")
|
|
||||||
}
|
|
||||||
if len(args.Get("image.name")) != 2 {
|
|
||||||
t.Errorf("the args should have collapsed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseArgsEdgeCase(t *testing.T) {
|
|
||||||
var filters Args
|
|
||||||
args, err := ParseFlag("", filters)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if args.Len() != 0 {
|
|
||||||
t.Fatalf("Expected an empty Args (map), got %v", args)
|
|
||||||
}
|
|
||||||
if args, err = ParseFlag("anything", args); err == nil || err != ErrBadFormat {
|
|
||||||
t.Fatalf("Expected ErrBadFormat, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToParam(t *testing.T) {
|
|
||||||
fields := map[string]map[string]bool{
|
|
||||||
"created": {"today": true},
|
|
||||||
"image.name": {"ubuntu*": true, "*untu": true},
|
|
||||||
}
|
|
||||||
a := Args{fields: fields}
|
|
||||||
|
|
||||||
_, err := ToParam(a)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to marshal the filters: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromParam(t *testing.T) {
|
|
||||||
invalids := []string{
|
|
||||||
"anything",
|
|
||||||
"['a','list']",
|
|
||||||
"{'key': 'value'}",
|
|
||||||
`{"key": "value"}`,
|
|
||||||
}
|
|
||||||
valid := map[*Args][]string{
|
|
||||||
&Args{fields: map[string]map[string]bool{"key": {"value": true}}}: {
|
|
||||||
`{"key": ["value"]}`,
|
|
||||||
`{"key": {"value": true}}`,
|
|
||||||
},
|
|
||||||
&Args{fields: map[string]map[string]bool{"key": {"value1": true, "value2": true}}}: {
|
|
||||||
`{"key": ["value1", "value2"]}`,
|
|
||||||
`{"key": {"value1": true, "value2": true}}`,
|
|
||||||
},
|
|
||||||
&Args{fields: map[string]map[string]bool{"key1": {"value1": true}, "key2": {"value2": true}}}: {
|
|
||||||
`{"key1": ["value1"], "key2": ["value2"]}`,
|
|
||||||
`{"key1": {"value1": true}, "key2": {"value2": true}}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, invalid := range invalids {
|
|
||||||
if _, err := FromParam(invalid); err == nil {
|
|
||||||
t.Fatalf("Expected an error with %v, got nothing", invalid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for expectedArgs, matchers := range valid {
|
|
||||||
for _, json := range matchers {
|
|
||||||
args, err := FromParam(json)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if args.Len() != expectedArgs.Len() {
|
|
||||||
t.Fatalf("Expected %v, go %v", expectedArgs, args)
|
|
||||||
}
|
|
||||||
for key, expectedValues := range expectedArgs.fields {
|
|
||||||
values := args.Get(key)
|
|
||||||
|
|
||||||
if len(values) != len(expectedValues) {
|
|
||||||
t.Fatalf("Expected %v, go %v", expectedArgs, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range values {
|
|
||||||
if !expectedValues[v] {
|
|
||||||
t.Fatalf("Expected %v, go %v", expectedArgs, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmpty(t *testing.T) {
|
|
||||||
a := Args{}
|
|
||||||
v, err := ToParam(a)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to marshal the filters: %s", err)
|
|
||||||
}
|
|
||||||
v1, err := FromParam(v)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
if a.Len() != v1.Len() {
|
|
||||||
t.Errorf("these should both be empty sets")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgsMatchKVListEmptySources(t *testing.T) {
|
|
||||||
args := NewArgs()
|
|
||||||
if !args.MatchKVList("created", map[string]string{}) {
|
|
||||||
t.Fatalf("Expected true for (%v,created), got true", args)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = Args{map[string]map[string]bool{"created": {"today": true}}}
|
|
||||||
if args.MatchKVList("created", map[string]string{}) {
|
|
||||||
t.Fatalf("Expected false for (%v,created), got true", args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgsMatchKVList(t *testing.T) {
|
|
||||||
// Not empty sources
|
|
||||||
sources := map[string]string{
|
|
||||||
"key1": "value1",
|
|
||||||
"key2": "value2",
|
|
||||||
"key3": "value3",
|
|
||||||
}
|
|
||||||
|
|
||||||
matches := map[*Args]string{
|
|
||||||
&Args{}: "field",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true},
|
|
||||||
"labels": map[string]bool{"key1": true}},
|
|
||||||
}: "labels",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true},
|
|
||||||
"labels": map[string]bool{"key1=value1": true}},
|
|
||||||
}: "labels",
|
|
||||||
}
|
|
||||||
|
|
||||||
for args, field := range matches {
|
|
||||||
if args.MatchKVList(field, sources) != true {
|
|
||||||
t.Fatalf("Expected true for %v on %v, got false", sources, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
differs := map[*Args]string{
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true},
|
|
||||||
"labels": map[string]bool{"key4": true}},
|
|
||||||
}: "labels",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true},
|
|
||||||
"labels": map[string]bool{"key1=value3": true}},
|
|
||||||
}: "labels",
|
|
||||||
}
|
|
||||||
|
|
||||||
for args, field := range differs {
|
|
||||||
if args.MatchKVList(field, sources) != false {
|
|
||||||
t.Fatalf("Expected false for %v on %v, got true", sources, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgsMatch(t *testing.T) {
|
|
||||||
source := "today"
|
|
||||||
|
|
||||||
matches := map[*Args]string{
|
|
||||||
&Args{}: "field",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today": true}},
|
|
||||||
}: "today",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"to*": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"to(.*)": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"tod": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"anyting": true, "to*": true}},
|
|
||||||
}: "created",
|
|
||||||
}
|
|
||||||
|
|
||||||
for args, field := range matches {
|
|
||||||
if args.Match(field, source) != true {
|
|
||||||
t.Fatalf("Expected true for %v on %v, got false", source, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
differs := map[*Args]string{
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"tomorrow": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"to(day": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"tom(.*)": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"tom": true}},
|
|
||||||
}: "created",
|
|
||||||
&Args{map[string]map[string]bool{
|
|
||||||
"created": map[string]bool{"today1": true},
|
|
||||||
"labels": map[string]bool{"today": true}},
|
|
||||||
}: "created",
|
|
||||||
}
|
|
||||||
|
|
||||||
for args, field := range differs {
|
|
||||||
if args.Match(field, source) != false {
|
|
||||||
t.Fatalf("Expected false for %v on %v, got true", source, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAdd(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
f.Add("status", "running")
|
|
||||||
v := f.fields["status"]
|
|
||||||
if len(v) != 1 || !v["running"] {
|
|
||||||
t.Fatalf("Expected to include a running status, got %v", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Add("status", "paused")
|
|
||||||
if len(v) != 2 || !v["paused"] {
|
|
||||||
t.Fatalf("Expected to include a paused status, got %v", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDel(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
f.Add("status", "running")
|
|
||||||
f.Del("status", "running")
|
|
||||||
v := f.fields["status"]
|
|
||||||
if v["running"] {
|
|
||||||
t.Fatalf("Expected to not include a running status filter, got true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLen(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
if f.Len() != 0 {
|
|
||||||
t.Fatalf("Expected to not include any field")
|
|
||||||
}
|
|
||||||
f.Add("status", "running")
|
|
||||||
if f.Len() != 1 {
|
|
||||||
t.Fatalf("Expected to include one field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExactMatch(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
|
|
||||||
if !f.ExactMatch("status", "running") {
|
|
||||||
t.Fatalf("Expected to match `running` when there are no filters, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Add("status", "running")
|
|
||||||
f.Add("status", "pause*")
|
|
||||||
|
|
||||||
if !f.ExactMatch("status", "running") {
|
|
||||||
t.Fatalf("Expected to match `running` with one of the filters, got false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.ExactMatch("status", "paused") {
|
|
||||||
t.Fatalf("Expected to not match `paused` with one of the filters, got true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInclude(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
if f.Include("status") {
|
|
||||||
t.Fatalf("Expected to not include a status key, got true")
|
|
||||||
}
|
|
||||||
f.Add("status", "running")
|
|
||||||
if !f.Include("status") {
|
|
||||||
t.Fatalf("Expected to include a status key, got false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
f.Add("status", "running")
|
|
||||||
|
|
||||||
valid := map[string]bool{
|
|
||||||
"status": true,
|
|
||||||
"dangling": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Validate(valid); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Add("bogus", "running")
|
|
||||||
if err := f.Validate(valid); err == nil {
|
|
||||||
t.Fatalf("Expected to return an error, got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWalkValues(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
f.Add("status", "running")
|
|
||||||
f.Add("status", "paused")
|
|
||||||
|
|
||||||
f.WalkValues("status", func(value string) error {
|
|
||||||
if value != "running" && value != "paused" {
|
|
||||||
t.Fatalf("Unexpected value %s", value)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
err := f.WalkValues("status", func(value string) error {
|
|
||||||
return fmt.Errorf("return")
|
|
||||||
})
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected to get an error, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f.WalkValues("foo", func(value string) error {
|
|
||||||
return fmt.Errorf("return")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Expected to not iterate when the field doesn't exist, got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuzzyMatch(t *testing.T) {
|
|
||||||
f := NewArgs()
|
|
||||||
f.Add("container", "foo")
|
|
||||||
|
|
||||||
cases := map[string]bool{
|
|
||||||
"foo": true,
|
|
||||||
"foobar": true,
|
|
||||||
"barfoo": false,
|
|
||||||
"bar": false,
|
|
||||||
}
|
|
||||||
for source, match := range cases {
|
|
||||||
got := f.FuzzyMatch("container", source)
|
|
||||||
if got != match {
|
|
||||||
t.Fatalf("Expected %v, got %v: %s", match, got, source)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package network
|
|
||||||
|
|
||||||
// Address represents an IP address
|
|
||||||
type Address struct {
|
|
||||||
Addr string
|
|
||||||
PrefixLen int
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPAM represents IP Address Management
|
|
||||||
type IPAM struct {
|
|
||||||
Driver string
|
|
||||||
Config []IPAMConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPAMConfig represents IPAM configurations
|
|
||||||
type IPAMConfig struct {
|
|
||||||
Subnet string `json:",omitempty"`
|
|
||||||
IPRange string `json:",omitempty"`
|
|
||||||
Gateway string `json:",omitempty"`
|
|
||||||
AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EndpointSettings stores the network endpoint details
|
|
||||||
type EndpointSettings struct {
|
|
||||||
EndpointID string
|
|
||||||
Gateway string
|
|
||||||
IPAddress string
|
|
||||||
IPPrefixLen int
|
|
||||||
IPv6Gateway string
|
|
||||||
GlobalIPv6Address string
|
|
||||||
GlobalIPv6PrefixLen int
|
|
||||||
MacAddress string
|
|
||||||
}
|
|
@ -1,101 +0,0 @@
|
|||||||
package registry
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ServiceConfig stores daemon registry services configuration.
|
|
||||||
type ServiceConfig struct {
|
|
||||||
InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"`
|
|
||||||
IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"`
|
|
||||||
Mirrors []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetIPNet is the net.IPNet type, which can be marshalled and
|
|
||||||
// unmarshalled to JSON
|
|
||||||
type NetIPNet net.IPNet
|
|
||||||
|
|
||||||
// MarshalJSON returns the JSON representation of the IPNet
|
|
||||||
func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal((*net.IPNet)(ipnet).String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON sets the IPNet from a byte array of JSON
|
|
||||||
func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
|
|
||||||
var ipnetStr string
|
|
||||||
if err = json.Unmarshal(b, &ipnetStr); err == nil {
|
|
||||||
var cidr *net.IPNet
|
|
||||||
if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
|
|
||||||
*ipnet = NetIPNet(*cidr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexInfo contains information about a registry
|
|
||||||
//
|
|
||||||
// RepositoryInfo Examples:
|
|
||||||
// {
|
|
||||||
// "Index" : {
|
|
||||||
// "Name" : "docker.io",
|
|
||||||
// "Mirrors" : ["https://registry-2.docker.io/v1/", "https://registry-3.docker.io/v1/"],
|
|
||||||
// "Secure" : true,
|
|
||||||
// "Official" : true,
|
|
||||||
// },
|
|
||||||
// "RemoteName" : "library/debian",
|
|
||||||
// "LocalName" : "debian",
|
|
||||||
// "CanonicalName" : "docker.io/debian"
|
|
||||||
// "Official" : true,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// "Index" : {
|
|
||||||
// "Name" : "127.0.0.1:5000",
|
|
||||||
// "Mirrors" : [],
|
|
||||||
// "Secure" : false,
|
|
||||||
// "Official" : false,
|
|
||||||
// },
|
|
||||||
// "RemoteName" : "user/repo",
|
|
||||||
// "LocalName" : "127.0.0.1:5000/user/repo",
|
|
||||||
// "CanonicalName" : "127.0.0.1:5000/user/repo",
|
|
||||||
// "Official" : false,
|
|
||||||
// }
|
|
||||||
type IndexInfo struct {
|
|
||||||
// Name is the name of the registry, such as "docker.io"
|
|
||||||
Name string
|
|
||||||
// Mirrors is a list of mirrors, expressed as URIs
|
|
||||||
Mirrors []string
|
|
||||||
// Secure is set to false if the registry is part of the list of
|
|
||||||
// insecure registries. Insecure registries accept HTTP and/or accept
|
|
||||||
// HTTPS with certificates from unknown CAs.
|
|
||||||
Secure bool
|
|
||||||
// Official indicates whether this is an official registry
|
|
||||||
Official bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchResult describes a search result returned from a registry
|
|
||||||
type SearchResult struct {
|
|
||||||
// StarCount indicates the number of stars this repository has
|
|
||||||
StarCount int `json:"star_count"`
|
|
||||||
// IsOfficial indicates whether the result is an official repository or not
|
|
||||||
IsOfficial bool `json:"is_official"`
|
|
||||||
// Name is the name of the repository
|
|
||||||
Name string `json:"name"`
|
|
||||||
// IsOfficial indicates whether the result is trusted
|
|
||||||
IsTrusted bool `json:"is_trusted"`
|
|
||||||
// IsAutomated indicates whether the result is automated
|
|
||||||
IsAutomated bool `json:"is_automated"`
|
|
||||||
// Description is a textual description of the repository
|
|
||||||
Description string `json:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchResults lists a collection search results returned from a registry
|
|
||||||
type SearchResults struct {
|
|
||||||
// Query contains the query string that generated the search results
|
|
||||||
Query string `json:"query"`
|
|
||||||
// NumResults indicates the number of results the query returned
|
|
||||||
NumResults int `json:"num_results"`
|
|
||||||
// Results is a slice containing the actual results for the search
|
|
||||||
Results []SearchResult `json:"results"`
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
// Package types is used for API stability in the types and response to the
|
|
||||||
// consumers of the API stats endpoint.
|
|
||||||
package types
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// ThrottlingData stores CPU throttling stats of one running container
|
|
||||||
type ThrottlingData struct {
|
|
||||||
// Number of periods with throttling active
|
|
||||||
Periods uint64 `json:"periods"`
|
|
||||||
// Number of periods when the container hit its throttling limit.
|
|
||||||
ThrottledPeriods uint64 `json:"throttled_periods"`
|
|
||||||
// Aggregate time the container was throttled for in nanoseconds.
|
|
||||||
ThrottledTime uint64 `json:"throttled_time"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPUUsage stores All CPU stats aggregated since container inception.
|
|
||||||
type CPUUsage struct {
|
|
||||||
// Total CPU time consumed.
|
|
||||||
// Units: nanoseconds.
|
|
||||||
TotalUsage uint64 `json:"total_usage"`
|
|
||||||
// Total CPU time consumed per core.
|
|
||||||
// Units: nanoseconds.
|
|
||||||
PercpuUsage []uint64 `json:"percpu_usage"`
|
|
||||||
// Time spent by tasks of the cgroup in kernel mode.
|
|
||||||
// Units: nanoseconds.
|
|
||||||
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
|
||||||
// Time spent by tasks of the cgroup in user mode.
|
|
||||||
// Units: nanoseconds.
|
|
||||||
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CPUStats aggregates and wraps all CPU related info of container
|
|
||||||
type CPUStats struct {
|
|
||||||
CPUUsage CPUUsage `json:"cpu_usage"`
|
|
||||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
|
||||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MemoryStats aggregates All memory stats since container inception
|
|
||||||
type MemoryStats struct {
|
|
||||||
// current res_counter usage for memory
|
|
||||||
Usage uint64 `json:"usage"`
|
|
||||||
// maximum usage ever recorded.
|
|
||||||
MaxUsage uint64 `json:"max_usage"`
|
|
||||||
// TODO(vishh): Export these as stronger types.
|
|
||||||
// all the stats exported via memory.stat.
|
|
||||||
Stats map[string]uint64 `json:"stats"`
|
|
||||||
// number of times memory usage hits limits.
|
|
||||||
Failcnt uint64 `json:"failcnt"`
|
|
||||||
Limit uint64 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlkioStatEntry is one small entity to store a piece of Blkio stats
|
|
||||||
// TODO Windows: This can be factored out
|
|
||||||
type BlkioStatEntry struct {
|
|
||||||
Major uint64 `json:"major"`
|
|
||||||
Minor uint64 `json:"minor"`
|
|
||||||
Op string `json:"op"`
|
|
||||||
Value uint64 `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlkioStats stores All IO service stats for data read and write
|
|
||||||
// TODO Windows: This can be factored out
|
|
||||||
type BlkioStats struct {
|
|
||||||
// number of bytes transferred to and from the block device
|
|
||||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
|
||||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
|
||||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
|
||||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
|
||||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
|
||||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
|
||||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
|
||||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkStats aggregates All network stats of one container
|
|
||||||
// TODO Windows: This will require refactoring
|
|
||||||
type NetworkStats struct {
|
|
||||||
RxBytes uint64 `json:"rx_bytes"`
|
|
||||||
RxPackets uint64 `json:"rx_packets"`
|
|
||||||
RxErrors uint64 `json:"rx_errors"`
|
|
||||||
RxDropped uint64 `json:"rx_dropped"`
|
|
||||||
TxBytes uint64 `json:"tx_bytes"`
|
|
||||||
TxPackets uint64 `json:"tx_packets"`
|
|
||||||
TxErrors uint64 `json:"tx_errors"`
|
|
||||||
TxDropped uint64 `json:"tx_dropped"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stats is Ultimate struct aggregating all types of stats of one container
|
|
||||||
type Stats struct {
|
|
||||||
Read time.Time `json:"read"`
|
|
||||||
PreCPUStats CPUStats `json:"precpu_stats,omitempty"`
|
|
||||||
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
|
||||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
|
||||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatsJSON is newly used Networks
|
|
||||||
type StatsJSON struct {
|
|
||||||
Stats
|
|
||||||
|
|
||||||
// Networks request version >=1.21
|
|
||||||
Networks map[string]NetworkStats `json:"networks,omitempty"`
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
package strslice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StrSlice represents a string or an array of strings.
|
|
||||||
// We need to override the json decoder to accept both options.
|
|
||||||
type StrSlice struct {
|
|
||||||
parts []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON Marshals (or serializes) the StrSlice into the json format.
|
|
||||||
// This method is needed to implement json.Marshaller.
|
|
||||||
func (e *StrSlice) MarshalJSON() ([]byte, error) {
|
|
||||||
if e == nil {
|
|
||||||
return []byte{}, nil
|
|
||||||
}
|
|
||||||
return json.Marshal(e.Slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON decodes the byte slice whether it's a string or an array of strings.
|
|
||||||
// This method is needed to implement json.Unmarshaler.
|
|
||||||
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
|
||||||
if len(b) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p := make([]string, 0, 1)
|
|
||||||
if err := json.Unmarshal(b, &p); err != nil {
|
|
||||||
var s string
|
|
||||||
if err := json.Unmarshal(b, &s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p = append(p, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.parts = p
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of parts of the StrSlice.
|
|
||||||
func (e *StrSlice) Len() int {
|
|
||||||
if e == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return len(e.parts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice gets the parts of the StrSlice as a Slice of string.
|
|
||||||
func (e *StrSlice) Slice() []string {
|
|
||||||
if e == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return e.parts
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToString gets space separated string of all the parts.
|
|
||||||
func (e *StrSlice) ToString() string {
|
|
||||||
s := e.Slice()
|
|
||||||
if s == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.Join(s, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates an StrSlice based on the specified parts (as strings).
|
|
||||||
func New(parts ...string) *StrSlice {
|
|
||||||
return &StrSlice{parts}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
package strslice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStrSliceMarshalJSON(t *testing.T) {
|
|
||||||
strss := map[*StrSlice]string{
|
|
||||||
nil: "",
|
|
||||||
&StrSlice{}: "null",
|
|
||||||
&StrSlice{[]string{"/bin/sh", "-c", "echo"}}: `["/bin/sh","-c","echo"]`,
|
|
||||||
}
|
|
||||||
|
|
||||||
for strs, expected := range strss {
|
|
||||||
data, err := strs.MarshalJSON()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(data) != expected {
|
|
||||||
t.Fatalf("Expected %v, got %v", expected, string(data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceUnmarshalJSON(t *testing.T) {
|
|
||||||
parts := map[string][]string{
|
|
||||||
"": {"default", "values"},
|
|
||||||
"[]": {},
|
|
||||||
`["/bin/sh","-c","echo"]`: {"/bin/sh", "-c", "echo"},
|
|
||||||
}
|
|
||||||
for json, expectedParts := range parts {
|
|
||||||
strs := &StrSlice{
|
|
||||||
[]string{"default", "values"},
|
|
||||||
}
|
|
||||||
if err := strs.UnmarshalJSON([]byte(json)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actualParts := strs.Slice()
|
|
||||||
if len(actualParts) != len(expectedParts) {
|
|
||||||
t.Fatalf("Expected %v parts, got %v (%v)", len(expectedParts), len(actualParts), expectedParts)
|
|
||||||
}
|
|
||||||
for index, part := range actualParts {
|
|
||||||
if part != expectedParts[index] {
|
|
||||||
t.Fatalf("Expected %v, got %v", expectedParts, actualParts)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceUnmarshalString(t *testing.T) {
|
|
||||||
var e *StrSlice
|
|
||||||
echo, err := json.Marshal("echo")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(echo, &e); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
slice := e.Slice()
|
|
||||||
if len(slice) != 1 {
|
|
||||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
if slice[0] != "echo" {
|
|
||||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceUnmarshalSlice(t *testing.T) {
|
|
||||||
var e *StrSlice
|
|
||||||
echo, err := json.Marshal([]string{"echo"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(echo, &e); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
slice := e.Slice()
|
|
||||||
if len(slice) != 1 {
|
|
||||||
t.Fatalf("expected 1 element after unmarshal: %q", slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
if slice[0] != "echo" {
|
|
||||||
t.Fatalf("expected `echo`, got: %q", slice[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceToString(t *testing.T) {
|
|
||||||
slices := map[*StrSlice]string{
|
|
||||||
New(""): "",
|
|
||||||
New("one"): "one",
|
|
||||||
New("one", "two"): "one two",
|
|
||||||
}
|
|
||||||
for s, expected := range slices {
|
|
||||||
toString := s.ToString()
|
|
||||||
if toString != expected {
|
|
||||||
t.Fatalf("Expected %v, got %v", expected, toString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceLen(t *testing.T) {
|
|
||||||
var emptyStrSlice *StrSlice
|
|
||||||
slices := map[*StrSlice]int{
|
|
||||||
New(""): 1,
|
|
||||||
New("one"): 1,
|
|
||||||
New("one", "two"): 2,
|
|
||||||
emptyStrSlice: 0,
|
|
||||||
}
|
|
||||||
for s, expected := range slices {
|
|
||||||
if s.Len() != expected {
|
|
||||||
t.Fatalf("Expected %d, got %d", s.Len(), expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStrSliceSlice(t *testing.T) {
|
|
||||||
var emptyStrSlice *StrSlice
|
|
||||||
slices := map[*StrSlice][]string{
|
|
||||||
New("one"): {"one"},
|
|
||||||
New("one", "two"): {"one", "two"},
|
|
||||||
emptyStrSlice: nil,
|
|
||||||
}
|
|
||||||
for s, expected := range slices {
|
|
||||||
if !reflect.DeepEqual(s.Slice(), expected) {
|
|
||||||
t.Fatalf("Expected %v, got %v", s.Slice(), expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
package time
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These are additional predefined layouts for use in Time.Format and Time.Parse
|
|
||||||
// with --since and --until parameters for `docker logs` and `docker events`
|
|
||||||
const (
|
|
||||||
rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone
|
|
||||||
rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
|
|
||||||
dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00
|
|
||||||
dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetTimestamp tries to parse given string as golang duration,
|
|
||||||
// then RFC3339 time and finally as a Unix timestamp. If
|
|
||||||
// any of these were successful, it returns a Unix timestamp
|
|
||||||
// as string otherwise returns the given value back.
|
|
||||||
// In case of duration input, the returned timestamp is computed
|
|
||||||
// as the given reference time minus the amount of the duration.
|
|
||||||
func GetTimestamp(value string, reference time.Time) (string, error) {
|
|
||||||
if d, err := time.ParseDuration(value); value != "0" && err == nil {
|
|
||||||
return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var format string
|
|
||||||
var parseInLocation bool
|
|
||||||
|
|
||||||
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
|
|
||||||
parseInLocation = !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
|
|
||||||
|
|
||||||
if strings.Contains(value, ".") {
|
|
||||||
if parseInLocation {
|
|
||||||
format = rFC3339NanoLocal
|
|
||||||
} else {
|
|
||||||
format = time.RFC3339Nano
|
|
||||||
}
|
|
||||||
} else if strings.Contains(value, "T") {
|
|
||||||
// we want the number of colons in the T portion of the timestamp
|
|
||||||
tcolons := strings.Count(value, ":")
|
|
||||||
// if parseInLocation is off and we have a +/- zone offset (not Z) then
|
|
||||||
// there will be an extra colon in the input for the tz offset subtract that
|
|
||||||
// colon from the tcolons count
|
|
||||||
if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
|
|
||||||
tcolons--
|
|
||||||
}
|
|
||||||
if parseInLocation {
|
|
||||||
switch tcolons {
|
|
||||||
case 0:
|
|
||||||
format = "2006-01-02T15"
|
|
||||||
case 1:
|
|
||||||
format = "2006-01-02T15:04"
|
|
||||||
default:
|
|
||||||
format = rFC3339Local
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch tcolons {
|
|
||||||
case 0:
|
|
||||||
format = "2006-01-02T15Z07:00"
|
|
||||||
case 1:
|
|
||||||
format = "2006-01-02T15:04Z07:00"
|
|
||||||
default:
|
|
||||||
format = time.RFC3339
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if parseInLocation {
|
|
||||||
format = dateLocal
|
|
||||||
} else {
|
|
||||||
format = dateWithZone
|
|
||||||
}
|
|
||||||
|
|
||||||
var t time.Time
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if parseInLocation {
|
|
||||||
t, err = time.ParseInLocation(format, value, time.FixedZone(time.Now().Zone()))
|
|
||||||
} else {
|
|
||||||
t, err = time.Parse(format, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// if there is a `-` then its an RFC3339 like timestamp otherwise assume unixtimestamp
|
|
||||||
if strings.Contains(value, "-") {
|
|
||||||
return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
|
|
||||||
}
|
|
||||||
return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
|
|
||||||
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
|
|
||||||
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
|
|
||||||
// converted to nanoseconds. The expectation is that the seconds and
|
|
||||||
// seconds will be used to create a time variable. For example:
|
|
||||||
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
|
|
||||||
// if err == nil since := time.Unix(seconds, nanoseconds)
|
|
||||||
// returns seconds as def(aultSeconds) if value == ""
|
|
||||||
func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
|
||||||
if value == "" {
|
|
||||||
return def, 0, nil
|
|
||||||
}
|
|
||||||
sa := strings.SplitN(value, ".", 2)
|
|
||||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return s, 0, err
|
|
||||||
}
|
|
||||||
if len(sa) != 2 {
|
|
||||||
return s, 0, nil
|
|
||||||
}
|
|
||||||
n, err := strconv.ParseInt(sa[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return s, n, err
|
|
||||||
}
|
|
||||||
// should already be in nanoseconds but just in case convert n to nanoseonds
|
|
||||||
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
|
|
||||||
return s, n, nil
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package time
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetTimestamp(t *testing.T) {
|
|
||||||
now := time.Now()
|
|
||||||
cases := []struct {
|
|
||||||
in, expected string
|
|
||||||
expectedErr bool
|
|
||||||
}{
|
|
||||||
// Partial RFC3339 strings get parsed with second precision
|
|
||||||
{"2006-01-02T15:04:05.999999999+07:00", "1136189045.999999999", false},
|
|
||||||
{"2006-01-02T15:04:05.999999999Z", "1136214245.999999999", false},
|
|
||||||
{"2006-01-02T15:04:05.999999999", "1136214245.999999999", false},
|
|
||||||
{"2006-01-02T15:04:05Z", "1136214245.000000000", false},
|
|
||||||
{"2006-01-02T15:04:05", "1136214245.000000000", false},
|
|
||||||
{"2006-01-02T15:04:0Z", "", true},
|
|
||||||
{"2006-01-02T15:04:0", "", true},
|
|
||||||
{"2006-01-02T15:04Z", "1136214240.000000000", false},
|
|
||||||
{"2006-01-02T15:04+00:00", "1136214240.000000000", false},
|
|
||||||
{"2006-01-02T15:04-00:00", "1136214240.000000000", false},
|
|
||||||
{"2006-01-02T15:04", "1136214240.000000000", false},
|
|
||||||
{"2006-01-02T15:0Z", "", true},
|
|
||||||
{"2006-01-02T15:0", "", true},
|
|
||||||
{"2006-01-02T15Z", "1136214000.000000000", false},
|
|
||||||
{"2006-01-02T15+00:00", "1136214000.000000000", false},
|
|
||||||
{"2006-01-02T15-00:00", "1136214000.000000000", false},
|
|
||||||
{"2006-01-02T15", "1136214000.000000000", false},
|
|
||||||
{"2006-01-02T1Z", "1136163600.000000000", false},
|
|
||||||
{"2006-01-02T1", "1136163600.000000000", false},
|
|
||||||
{"2006-01-02TZ", "", true},
|
|
||||||
{"2006-01-02T", "", true},
|
|
||||||
{"2006-01-02+00:00", "1136160000.000000000", false},
|
|
||||||
{"2006-01-02-00:00", "1136160000.000000000", false},
|
|
||||||
{"2006-01-02-00:01", "1136160060.000000000", false},
|
|
||||||
{"2006-01-02Z", "1136160000.000000000", false},
|
|
||||||
{"2006-01-02", "1136160000.000000000", false},
|
|
||||||
{"2015-05-13T20:39:09Z", "1431549549.000000000", false},
|
|
||||||
|
|
||||||
// unix timestamps returned as is
|
|
||||||
{"1136073600", "1136073600", false},
|
|
||||||
{"1136073600.000000001", "1136073600.000000001", false},
|
|
||||||
// Durations
|
|
||||||
{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix()), false},
|
|
||||||
{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
|
|
||||||
{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
|
|
||||||
|
|
||||||
// String fallback
|
|
||||||
{"invalid", "invalid", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
o, err := GetTimestamp(c.in, now)
|
|
||||||
if o != c.expected ||
|
|
||||||
(err == nil && c.expectedErr) ||
|
|
||||||
(err != nil && !c.expectedErr) {
|
|
||||||
t.Errorf("wrong value for '%s'. expected:'%s' got:'%s' with error: `%s`", c.in, c.expected, o, err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseTimestamps(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
in string
|
|
||||||
def, expectedS, expectedN int64
|
|
||||||
expectedErr bool
|
|
||||||
}{
|
|
||||||
// unix timestamps
|
|
||||||
{"1136073600", 0, 1136073600, 0, false},
|
|
||||||
{"1136073600.000000001", 0, 1136073600, 1, false},
|
|
||||||
{"1136073600.0000000010", 0, 1136073600, 1, false},
|
|
||||||
{"1136073600.00000001", 0, 1136073600, 10, false},
|
|
||||||
{"foo.bar", 0, 0, 0, true},
|
|
||||||
{"1136073600.bar", 0, 1136073600, 0, true},
|
|
||||||
{"", -1, -1, 0, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
s, n, err := ParseTimestamps(c.in, c.def)
|
|
||||||
if s != c.expectedS ||
|
|
||||||
n != c.expectedN ||
|
|
||||||
(err == nil && c.expectedErr) ||
|
|
||||||
(err != nil && !c.expectedErr) {
|
|
||||||
t.Errorf("wrong values for input `%s` with default `%d` expected:'%d'seconds and `%d`nanosecond got:'%d'seconds and `%d`nanoseconds with error: `%s`", c.in, c.def, c.expectedS, c.expectedN, s, n, err)
|
|
||||||
t.Fail()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,424 +0,0 @@
|
|||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
"github.com/docker/engine-api/types/network"
|
|
||||||
"github.com/docker/engine-api/types/registry"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerCreateResponse contains the information returned to a client on the
|
|
||||||
// creation of a new container.
|
|
||||||
type ContainerCreateResponse struct {
|
|
||||||
// ID is the ID of the created container.
|
|
||||||
ID string `json:"Id"`
|
|
||||||
|
|
||||||
// Warnings are any warnings encountered during the creation of the container.
|
|
||||||
Warnings []string `json:"Warnings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerExecCreateResponse contains response of Remote API:
|
|
||||||
// POST "/containers/{name:.*}/exec"
|
|
||||||
type ContainerExecCreateResponse struct {
|
|
||||||
// ID is the exec ID.
|
|
||||||
ID string `json:"Id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerUpdateResponse contains response of Remote API:
|
|
||||||
// POST /containers/{name:.*}/update
|
|
||||||
type ContainerUpdateResponse struct {
|
|
||||||
// Warnings are any warnings encountered during the updating of the container.
|
|
||||||
Warnings []string `json:"Warnings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuthResponse contains response of Remote API:
|
|
||||||
// POST "/auth"
|
|
||||||
type AuthResponse struct {
|
|
||||||
// Status is the authentication status
|
|
||||||
Status string `json:"Status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerWaitResponse contains response of Remote API:
|
|
||||||
// POST "/containers/"+containerID+"/wait"
|
|
||||||
type ContainerWaitResponse struct {
|
|
||||||
// StatusCode is the status code of the wait job
|
|
||||||
StatusCode int `json:"StatusCode"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerCommitResponse contains response of Remote API:
|
|
||||||
// POST "/commit?container="+containerID
|
|
||||||
type ContainerCommitResponse struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerChange contains response of Remote API:
|
|
||||||
// GET "/containers/{name:.*}/changes"
|
|
||||||
type ContainerChange struct {
|
|
||||||
Kind int
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageHistory contains response of Remote API:
|
|
||||||
// GET "/images/{name:.*}/history"
|
|
||||||
type ImageHistory struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Created int64
|
|
||||||
CreatedBy string
|
|
||||||
Tags []string
|
|
||||||
Size int64
|
|
||||||
Comment string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageDelete contains response of Remote API:
|
|
||||||
// DELETE "/images/{name:.*}"
|
|
||||||
type ImageDelete struct {
|
|
||||||
Untagged string `json:",omitempty"`
|
|
||||||
Deleted string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image contains response of Remote API:
|
|
||||||
// GET "/images/json"
|
|
||||||
type Image struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
ParentID string `json:"ParentId"`
|
|
||||||
RepoTags []string
|
|
||||||
RepoDigests []string
|
|
||||||
Created int64
|
|
||||||
Size int64
|
|
||||||
VirtualSize int64
|
|
||||||
Labels map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GraphDriverData returns Image's graph driver config info
|
|
||||||
// when calling inspect command
|
|
||||||
type GraphDriverData struct {
|
|
||||||
Name string
|
|
||||||
Data map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ImageInspect contains response of Remote API:
|
|
||||||
// GET "/images/{name:.*}/json"
|
|
||||||
type ImageInspect struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
RepoTags []string
|
|
||||||
RepoDigests []string
|
|
||||||
Parent string
|
|
||||||
Comment string
|
|
||||||
Created string
|
|
||||||
Container string
|
|
||||||
ContainerConfig *container.Config
|
|
||||||
DockerVersion string
|
|
||||||
Author string
|
|
||||||
Config *container.Config
|
|
||||||
Architecture string
|
|
||||||
Os string
|
|
||||||
Size int64
|
|
||||||
VirtualSize int64
|
|
||||||
GraphDriver GraphDriverData
|
|
||||||
}
|
|
||||||
|
|
||||||
// Port stores open ports info of container
|
|
||||||
// e.g. {"PrivatePort": 8080, "PublicPort": 80, "Type": "tcp"}
|
|
||||||
type Port struct {
|
|
||||||
IP string `json:",omitempty"`
|
|
||||||
PrivatePort int
|
|
||||||
PublicPort int `json:",omitempty"`
|
|
||||||
Type string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Container contains response of Remote API:
|
|
||||||
// GET "/containers/json"
|
|
||||||
type Container struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Names []string
|
|
||||||
Image string
|
|
||||||
ImageID string
|
|
||||||
Command string
|
|
||||||
Created int64
|
|
||||||
Ports []Port
|
|
||||||
SizeRw int64 `json:",omitempty"`
|
|
||||||
SizeRootFs int64 `json:",omitempty"`
|
|
||||||
Labels map[string]string
|
|
||||||
Status string
|
|
||||||
HostConfig struct {
|
|
||||||
NetworkMode string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
NetworkSettings *SummaryNetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyConfig contains request body of Remote API:
|
|
||||||
// POST "/containers/"+containerID+"/copy"
|
|
||||||
type CopyConfig struct {
|
|
||||||
Resource string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerPathStat is used to encode the header from
|
|
||||||
// GET "/containers/{name:.*}/archive"
|
|
||||||
// "Name" is the file or directory name.
|
|
||||||
type ContainerPathStat struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
Mode os.FileMode `json:"mode"`
|
|
||||||
Mtime time.Time `json:"mtime"`
|
|
||||||
LinkTarget string `json:"linkTarget"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerProcessList contains response of Remote API:
|
|
||||||
// GET "/containers/{name:.*}/top"
|
|
||||||
type ContainerProcessList struct {
|
|
||||||
Processes [][]string
|
|
||||||
Titles []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version contains response of Remote API:
|
|
||||||
// GET "/version"
|
|
||||||
type Version struct {
|
|
||||||
Version string
|
|
||||||
APIVersion string `json:"ApiVersion"`
|
|
||||||
GitCommit string
|
|
||||||
GoVersion string
|
|
||||||
Os string
|
|
||||||
Arch string
|
|
||||||
KernelVersion string `json:",omitempty"`
|
|
||||||
Experimental bool `json:",omitempty"`
|
|
||||||
BuildTime string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info contains response of Remote API:
|
|
||||||
// GET "/info"
|
|
||||||
type Info struct {
|
|
||||||
ID string
|
|
||||||
Containers int
|
|
||||||
Images int
|
|
||||||
Driver string
|
|
||||||
DriverStatus [][2]string
|
|
||||||
Plugins PluginsInfo
|
|
||||||
MemoryLimit bool
|
|
||||||
SwapLimit bool
|
|
||||||
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
|
||||||
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
|
||||||
CPUShares bool
|
|
||||||
CPUSet bool
|
|
||||||
IPv4Forwarding bool
|
|
||||||
BridgeNfIptables bool
|
|
||||||
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
|
||||||
Debug bool
|
|
||||||
NFd int
|
|
||||||
OomKillDisable bool
|
|
||||||
NGoroutines int
|
|
||||||
SystemTime string
|
|
||||||
ExecutionDriver string
|
|
||||||
LoggingDriver string
|
|
||||||
NEventsListener int
|
|
||||||
KernelVersion string
|
|
||||||
OperatingSystem string
|
|
||||||
OSType string
|
|
||||||
Architecture string
|
|
||||||
IndexServerAddress string
|
|
||||||
RegistryConfig *registry.ServiceConfig
|
|
||||||
InitSha1 string
|
|
||||||
InitPath string
|
|
||||||
NCPU int
|
|
||||||
MemTotal int64
|
|
||||||
DockerRootDir string
|
|
||||||
HTTPProxy string `json:"HttpProxy"`
|
|
||||||
HTTPSProxy string `json:"HttpsProxy"`
|
|
||||||
NoProxy string
|
|
||||||
Name string
|
|
||||||
Labels []string
|
|
||||||
ExperimentalBuild bool
|
|
||||||
ServerVersion string
|
|
||||||
ClusterStore string
|
|
||||||
ClusterAdvertise string
|
|
||||||
}
|
|
||||||
|
|
||||||
// PluginsInfo is temp struct holds Plugins name
|
|
||||||
// registered with docker daemon. It used by Info struct
|
|
||||||
type PluginsInfo struct {
|
|
||||||
// List of Volume plugins registered
|
|
||||||
Volume []string
|
|
||||||
// List of Network plugins registered
|
|
||||||
Network []string
|
|
||||||
// List of Authorization plugins registered
|
|
||||||
Authorization []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExecStartCheck is a temp struct used by execStart
|
|
||||||
// Config fields is part of ExecConfig in runconfig package
|
|
||||||
type ExecStartCheck struct {
|
|
||||||
// ExecStart will first check if it's detached
|
|
||||||
Detach bool
|
|
||||||
// Check if there's a tty
|
|
||||||
Tty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerState stores container's running state
|
|
||||||
// it's part of ContainerJSONBase and will return by "inspect" command
|
|
||||||
type ContainerState struct {
|
|
||||||
Status string
|
|
||||||
Running bool
|
|
||||||
Paused bool
|
|
||||||
Restarting bool
|
|
||||||
OOMKilled bool
|
|
||||||
Dead bool
|
|
||||||
Pid int
|
|
||||||
ExitCode int
|
|
||||||
Error string
|
|
||||||
StartedAt string
|
|
||||||
FinishedAt string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerJSONBase contains response of Remote API:
|
|
||||||
// GET "/containers/{name:.*}/json"
|
|
||||||
type ContainerJSONBase struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Created string
|
|
||||||
Path string
|
|
||||||
Args []string
|
|
||||||
State *ContainerState
|
|
||||||
Image string
|
|
||||||
ResolvConfPath string
|
|
||||||
HostnamePath string
|
|
||||||
HostsPath string
|
|
||||||
LogPath string
|
|
||||||
Name string
|
|
||||||
RestartCount int
|
|
||||||
Driver string
|
|
||||||
MountLabel string
|
|
||||||
ProcessLabel string
|
|
||||||
AppArmorProfile string
|
|
||||||
ExecIDs []string
|
|
||||||
HostConfig *container.HostConfig
|
|
||||||
GraphDriver GraphDriverData
|
|
||||||
SizeRw *int64 `json:",omitempty"`
|
|
||||||
SizeRootFs *int64 `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerJSON is newly used struct along with MountPoint
|
|
||||||
type ContainerJSON struct {
|
|
||||||
*ContainerJSONBase
|
|
||||||
Mounts []MountPoint
|
|
||||||
Config *container.Config
|
|
||||||
NetworkSettings *NetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkSettings exposes the network settings in the api
|
|
||||||
type NetworkSettings struct {
|
|
||||||
NetworkSettingsBase
|
|
||||||
DefaultNetworkSettings
|
|
||||||
Networks map[string]*network.EndpointSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// SummaryNetworkSettings provides a summary of container's networks
|
|
||||||
// in /containers/json
|
|
||||||
type SummaryNetworkSettings struct {
|
|
||||||
Networks map[string]*network.EndpointSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkSettingsBase holds basic information about networks
|
|
||||||
type NetworkSettingsBase struct {
|
|
||||||
Bridge string
|
|
||||||
SandboxID string
|
|
||||||
HairpinMode bool
|
|
||||||
LinkLocalIPv6Address string
|
|
||||||
LinkLocalIPv6PrefixLen int
|
|
||||||
Ports nat.PortMap
|
|
||||||
SandboxKey string
|
|
||||||
SecondaryIPAddresses []network.Address
|
|
||||||
SecondaryIPv6Addresses []network.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultNetworkSettings holds network information
|
|
||||||
// during the 2 release deprecation period.
|
|
||||||
// It will be removed in Docker 1.11.
|
|
||||||
type DefaultNetworkSettings struct {
|
|
||||||
EndpointID string
|
|
||||||
Gateway string
|
|
||||||
GlobalIPv6Address string
|
|
||||||
GlobalIPv6PrefixLen int
|
|
||||||
IPAddress string
|
|
||||||
IPPrefixLen int
|
|
||||||
IPv6Gateway string
|
|
||||||
MacAddress string
|
|
||||||
}
|
|
||||||
|
|
||||||
// MountPoint represents a mount point configuration inside the container.
|
|
||||||
type MountPoint struct {
|
|
||||||
Name string `json:",omitempty"`
|
|
||||||
Source string
|
|
||||||
Destination string
|
|
||||||
Driver string `json:",omitempty"`
|
|
||||||
Mode string
|
|
||||||
RW bool
|
|
||||||
Propagation string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume represents the configuration of a volume for the remote API
|
|
||||||
type Volume struct {
|
|
||||||
Name string // Name is the name of the volume
|
|
||||||
Driver string // Driver is the Driver name used to create the volume
|
|
||||||
Mountpoint string // Mountpoint is the location on disk of the volume
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumesListResponse contains the response for the remote API:
|
|
||||||
// GET "/volumes"
|
|
||||||
type VolumesListResponse struct {
|
|
||||||
Volumes []*Volume // Volumes is the list of volumes being returned
|
|
||||||
Warnings []string // Warnings is a list of warnings that occurred when getting the list from the volume drivers
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeCreateRequest contains the response for the remote API:
|
|
||||||
// POST "/volumes/create"
|
|
||||||
type VolumeCreateRequest struct {
|
|
||||||
Name string // Name is the requested name of the volume
|
|
||||||
Driver string // Driver is the name of the driver that should be used to create the volume
|
|
||||||
DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkResource is the body of the "get network" http response message
|
|
||||||
type NetworkResource struct {
|
|
||||||
Name string
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Scope string
|
|
||||||
Driver string
|
|
||||||
IPAM network.IPAM
|
|
||||||
Containers map[string]EndpointResource
|
|
||||||
Options map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// EndpointResource contains network resources allocated and used for a container in a network
|
|
||||||
type EndpointResource struct {
|
|
||||||
Name string
|
|
||||||
EndpointID string
|
|
||||||
MacAddress string
|
|
||||||
IPv4Address string
|
|
||||||
IPv6Address string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkCreate is the expected body of the "create network" http request message
|
|
||||||
type NetworkCreate struct {
|
|
||||||
Name string
|
|
||||||
CheckDuplicate bool
|
|
||||||
Driver string
|
|
||||||
IPAM network.IPAM
|
|
||||||
Options map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkCreateResponse is the response message sent by the server for network create call
|
|
||||||
type NetworkCreateResponse struct {
|
|
||||||
ID string `json:"Id"`
|
|
||||||
Warning string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkConnect represents the data to be used to connect a container to the network
|
|
||||||
type NetworkConnect struct {
|
|
||||||
Container string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkDisconnect represents the data to be used to disconnect a container from the network
|
|
||||||
type NetworkDisconnect struct {
|
|
||||||
Container string
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
## Legacy API type versions
|
|
||||||
|
|
||||||
This package includes types for legacy API versions. The stable version of the API types live in `api/types/*.go`.
|
|
||||||
|
|
||||||
Consider moving a type here when you need to keep backwards compatibility in the API. This legacy types are organized by the latest API version they appear in. For instance, types in the `v1p19` package are valid for API versions below or equal `1.19`. Types in the `v1p20` package are valid for the API version `1.20`, since the versions below that will use the legacy types in `v1p19`.
|
|
||||||
|
|
||||||
### Package name conventions
|
|
||||||
|
|
||||||
The package name convention is to use `v` as a prefix for the version number and `p`(patch) as a separator. We use this nomenclature due to a few restrictions in the Go package name convention:
|
|
||||||
|
|
||||||
1. We cannot use `.` because it's interpreted by the language, think of `v1.20.CallFunction`.
|
|
||||||
2. We cannot use `_` because golint complains abount it. The code is actually valid, but it looks probably more weird: `v1_20.CallFunction`.
|
|
||||||
|
|
||||||
For instance, if you want to modify a type that was available in the version `1.21` of the API but it will have different fields in the version `1.22`, you want to create a new package under `api/types/versions/v1p21`.
|
|
@ -1,35 +0,0 @@
|
|||||||
// Package v1p19 provides specific API types for the API version 1, patch 19.
|
|
||||||
package v1p19
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
"github.com/docker/engine-api/types/versions/v1p20"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerJSON is a backcompatibility struct for APIs prior to 1.20.
|
|
||||||
// Note this is not used by the Windows daemon.
|
|
||||||
type ContainerJSON struct {
|
|
||||||
*types.ContainerJSONBase
|
|
||||||
Volumes map[string]string
|
|
||||||
VolumesRW map[string]bool
|
|
||||||
Config *ContainerConfig
|
|
||||||
NetworkSettings *v1p20.NetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerConfig is a backcompatibility struct for APIs prior to 1.20.
|
|
||||||
type ContainerConfig struct {
|
|
||||||
*container.Config
|
|
||||||
|
|
||||||
MacAddress string
|
|
||||||
NetworkDisabled bool
|
|
||||||
ExposedPorts map[nat.Port]struct{}
|
|
||||||
|
|
||||||
// backward compatibility, they now live in HostConfig
|
|
||||||
VolumeDriver string
|
|
||||||
Memory int64
|
|
||||||
MemorySwap int64
|
|
||||||
CPUShares int64 `json:"CpuShares"`
|
|
||||||
CPUSet string `json:"Cpuset"`
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// Package v1p20 provides specific API types for the API version 1, patch 20.
|
|
||||||
package v1p20
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/docker/engine-api/types"
|
|
||||||
"github.com/docker/engine-api/types/container"
|
|
||||||
"github.com/docker/go-connections/nat"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerJSON is a backcompatibility struct for the API 1.20
|
|
||||||
type ContainerJSON struct {
|
|
||||||
*types.ContainerJSONBase
|
|
||||||
Mounts []types.MountPoint
|
|
||||||
Config *ContainerConfig
|
|
||||||
NetworkSettings *NetworkSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerConfig is a backcompatibility struct used in ContainerJSON for the API 1.20
|
|
||||||
type ContainerConfig struct {
|
|
||||||
*container.Config
|
|
||||||
|
|
||||||
MacAddress string
|
|
||||||
NetworkDisabled bool
|
|
||||||
ExposedPorts map[nat.Port]struct{}
|
|
||||||
|
|
||||||
// backward compatibility, they now live in HostConfig
|
|
||||||
VolumeDriver string
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatsJSON is a backcompatibility struct used in Stats for API prior to 1.21
|
|
||||||
type StatsJSON struct {
|
|
||||||
types.Stats
|
|
||||||
Network types.NetworkStats `json:"network,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkSettings is a backward compatible struct for APIs prior to 1.21
|
|
||||||
type NetworkSettings struct {
|
|
||||||
types.NetworkSettingsBase
|
|
||||||
types.DefaultNetworkSettings
|
|
||||||
}
|
|
Reference in New Issue
Block a user