584 lines
14 KiB
Go
584 lines
14 KiB
Go
// Copyright 2014 go-dockerclient authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package docker
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ListContainersOptions specify parameters to the ListContainers function.
|
|
//
|
|
// See http://goo.gl/QpCnDN for more details.
|
|
type ListContainersOptions struct {
|
|
All bool
|
|
Size bool
|
|
Limit int
|
|
Since string
|
|
Before string
|
|
}
|
|
|
|
type APIPort struct {
|
|
PrivatePort int64
|
|
PublicPort int64
|
|
Type string
|
|
IP string
|
|
}
|
|
|
|
// APIContainers represents a container.
|
|
//
|
|
// See http://goo.gl/QeFH7U for more details.
|
|
type APIContainers struct {
|
|
ID string `json:"Id"`
|
|
Image string
|
|
Command string
|
|
Created int64
|
|
Status string
|
|
Ports []APIPort
|
|
SizeRw int64
|
|
SizeRootFs int64
|
|
Names []string
|
|
}
|
|
|
|
// ListContainers returns a slice of containers matching the given criteria.
|
|
//
|
|
// See http://goo.gl/QpCnDN for more details.
|
|
func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) {
|
|
path := "/containers/json?" + queryString(opts)
|
|
body, _, err := c.do("GET", path, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var containers []APIContainers
|
|
err = json.Unmarshal(body, &containers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return containers, nil
|
|
}
|
|
|
|
// Port represents the port number and the protocol, in the form
|
|
// <number>/<protocol>. For example: 80/tcp.
|
|
type Port string
|
|
|
|
// Port returns the number of the port.
|
|
func (p Port) Port() string {
|
|
return strings.Split(string(p), "/")[0]
|
|
}
|
|
|
|
// Proto returns the name of the protocol.
|
|
func (p Port) Proto() string {
|
|
parts := strings.Split(string(p), "/")
|
|
if len(parts) == 1 {
|
|
return "tcp"
|
|
}
|
|
return parts[1]
|
|
}
|
|
|
|
// State represents the state of a container.
|
|
type State struct {
|
|
sync.RWMutex
|
|
Running bool
|
|
Pid int
|
|
ExitCode int
|
|
StartedAt time.Time
|
|
FinishedAt time.Time
|
|
Ghost bool
|
|
}
|
|
|
|
// String returns the string representation of a state.
|
|
func (s *State) String() string {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
if s.Running {
|
|
if s.Ghost {
|
|
return "Ghost"
|
|
}
|
|
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
|
|
}
|
|
return fmt.Sprintf("Exit %d", s.ExitCode)
|
|
}
|
|
|
|
type PortBinding struct {
|
|
HostIp string
|
|
HostPort string
|
|
}
|
|
|
|
type PortMapping map[string]string
|
|
|
|
type NetworkSettings struct {
|
|
IPAddress string
|
|
IPPrefixLen int
|
|
Gateway string
|
|
Bridge string
|
|
PortMapping map[string]PortMapping
|
|
Ports map[Port][]PortBinding
|
|
}
|
|
|
|
func (settings *NetworkSettings) PortMappingAPI() []APIPort {
|
|
var mapping []APIPort
|
|
for port, bindings := range settings.Ports {
|
|
p, _ := parsePort(port.Port())
|
|
if len(bindings) == 0 {
|
|
mapping = append(mapping, APIPort{
|
|
PublicPort: int64(p),
|
|
Type: port.Proto(),
|
|
})
|
|
continue
|
|
}
|
|
for _, binding := range bindings {
|
|
p, _ := parsePort(port.Port())
|
|
h, _ := parsePort(binding.HostPort)
|
|
mapping = append(mapping, APIPort{
|
|
PrivatePort: int64(p),
|
|
PublicPort: int64(h),
|
|
Type: port.Proto(),
|
|
IP: binding.HostIp,
|
|
})
|
|
}
|
|
}
|
|
return mapping
|
|
}
|
|
|
|
func parsePort(rawPort string) (int, error) {
|
|
port, err := strconv.ParseUint(rawPort, 10, 16)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return int(port), nil
|
|
}
|
|
|
|
type Config struct {
|
|
Hostname string
|
|
Domainname string
|
|
User string
|
|
Memory int64
|
|
MemorySwap int64
|
|
CpuShares int64
|
|
AttachStdin bool
|
|
AttachStdout bool
|
|
AttachStderr bool
|
|
PortSpecs []string
|
|
ExposedPorts map[Port]struct{}
|
|
Tty bool
|
|
OpenStdin bool
|
|
StdinOnce bool
|
|
Env []string
|
|
Cmd []string
|
|
Dns []string // For Docker API v1.9 and below only
|
|
Image string
|
|
Volumes map[string]struct{}
|
|
VolumesFrom string
|
|
WorkingDir string
|
|
Entrypoint []string
|
|
NetworkDisabled bool
|
|
}
|
|
|
|
type Container struct {
|
|
ID string
|
|
|
|
Created time.Time
|
|
|
|
Path string
|
|
Args []string
|
|
|
|
Config *Config
|
|
State State
|
|
Image string
|
|
|
|
NetworkSettings *NetworkSettings
|
|
|
|
SysInitPath string
|
|
ResolvConfPath string
|
|
HostnamePath string
|
|
HostsPath string
|
|
Name string
|
|
Driver string
|
|
|
|
Volumes map[string]string
|
|
VolumesRW map[string]bool
|
|
HostConfig *HostConfig
|
|
}
|
|
|
|
// InspectContainer returns information about a container by its ID.
|
|
//
|
|
// See http://goo.gl/2o52Sx for more details.
|
|
func (c *Client) InspectContainer(id string) (*Container, error) {
|
|
path := "/containers/" + id + "/json"
|
|
body, status, err := c.do("GET", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return nil, &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var container Container
|
|
err = json.Unmarshal(body, &container)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &container, nil
|
|
}
|
|
|
|
// ContainerChanges returns changes in the filesystem of the given container.
|
|
//
|
|
// See http://goo.gl/DpGyzK for more details.
|
|
func (c *Client) ContainerChanges(id string) ([]Change, error) {
|
|
path := "/containers/" + id + "/changes"
|
|
body, status, err := c.do("GET", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return nil, &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var changes []Change
|
|
err = json.Unmarshal(body, &changes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return changes, nil
|
|
}
|
|
|
|
// CreateContainerOptions specify parameters to the CreateContainer function.
|
|
//
|
|
// See http://goo.gl/WPPYtB for more details.
|
|
type CreateContainerOptions struct {
|
|
Name string
|
|
Config *Config `qs:"-"`
|
|
}
|
|
|
|
// CreateContainer creates a new container, returning the container instance,
|
|
// or an error in case of failure.
|
|
//
|
|
// See http://goo.gl/tjihUc for more details.
|
|
func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) {
|
|
path := "/containers/create?" + queryString(opts)
|
|
body, status, err := c.do("POST", path, opts.Config)
|
|
if status == http.StatusNotFound {
|
|
return nil, ErrNoSuchImage
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var container Container
|
|
err = json.Unmarshal(body, &container)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
container.Name = opts.Name
|
|
|
|
return &container, nil
|
|
}
|
|
|
|
type KeyValuePair struct {
|
|
Key string
|
|
Value string
|
|
}
|
|
|
|
type HostConfig struct {
|
|
Binds []string
|
|
ContainerIDFile string
|
|
LxcConf []KeyValuePair
|
|
Privileged bool
|
|
PortBindings map[Port][]PortBinding
|
|
Links []string
|
|
PublishAllPorts bool
|
|
Dns []string // For Docker API v1.10 and above only
|
|
}
|
|
|
|
// StartContainer starts a container, returning an errror in case of failure.
|
|
//
|
|
// See http://goo.gl/y5GZlE for more details.
|
|
func (c *Client) StartContainer(id string, hostConfig *HostConfig) error {
|
|
if hostConfig == nil {
|
|
hostConfig = &HostConfig{}
|
|
}
|
|
path := "/containers/" + id + "/start"
|
|
_, status, err := c.do("POST", path, hostConfig)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StopContainer stops a container, killing it after the given timeout (in
|
|
// seconds).
|
|
//
|
|
// See http://goo.gl/X2mj8t for more details.
|
|
func (c *Client) StopContainer(id string, timeout uint) error {
|
|
path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout)
|
|
_, status, err := c.do("POST", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RestartContainer stops a container, killing it after the given timeout (in
|
|
// seconds), during the stop process.
|
|
//
|
|
// See http://goo.gl/zms73Z for more details.
|
|
func (c *Client) RestartContainer(id string, timeout uint) error {
|
|
path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout)
|
|
_, status, err := c.do("POST", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// KillContainerOptions represents the set of options that can be used in a
|
|
// call to KillContainer.
|
|
type KillContainerOptions struct {
|
|
// The ID of the container.
|
|
ID string `qs:"-"`
|
|
|
|
// The signal to send to the container. When omitted, Docker server
|
|
// will assume SIGKILL.
|
|
Signal Signal
|
|
}
|
|
|
|
// KillContainer kills a container, returning an error in case of failure.
|
|
//
|
|
// See http://goo.gl/DPbbBy for more details.
|
|
func (c *Client) KillContainer(opts KillContainerOptions) error {
|
|
path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts)
|
|
_, status, err := c.do("POST", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: opts.ID}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveContainerOptions encapsulates options to remove a container.
|
|
type RemoveContainerOptions struct {
|
|
// The ID of the container.
|
|
ID string `qs:"-"`
|
|
|
|
// A flag that indicates whether Docker should remove the volumes
|
|
// associated to the container.
|
|
RemoveVolumes bool `qs:"v"`
|
|
|
|
// A flag that indicates whether Docker should remove the container
|
|
// even if it is currently running.
|
|
Force bool
|
|
}
|
|
|
|
// RemoveContainer removes a container, returning an error in case of failure.
|
|
//
|
|
// See http://goo.gl/PBvGdU for more details.
|
|
func (c *Client) RemoveContainer(opts RemoveContainerOptions) error {
|
|
path := "/containers/" + opts.ID + "?" + queryString(opts)
|
|
_, status, err := c.do("DELETE", path, nil)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: opts.ID}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CopyFromContainerOptions is the set of options that can be used when copying
|
|
// files or folders from a container.
|
|
//
|
|
// See http://goo.gl/mnxRMl for more details.
|
|
type CopyFromContainerOptions struct {
|
|
OutputStream io.Writer `json:"-"`
|
|
Container string `json:"-"`
|
|
Resource string
|
|
}
|
|
|
|
// CopyFromContainer copy files or folders from a container, using a given
|
|
// resource.
|
|
//
|
|
// See http://goo.gl/mnxRMl for more details.
|
|
func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error {
|
|
if opts.Container == "" {
|
|
return &NoSuchContainer{ID: opts.Container}
|
|
}
|
|
url := fmt.Sprintf("/containers/%s/copy", opts.Container)
|
|
body, status, err := c.do("POST", url, opts)
|
|
if status == http.StatusNotFound {
|
|
return &NoSuchContainer{ID: opts.Container}
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
io.Copy(opts.OutputStream, bytes.NewBuffer(body))
|
|
return nil
|
|
}
|
|
|
|
// WaitContainer blocks until the given container stops, return the exit code
|
|
// of the container status.
|
|
//
|
|
// See http://goo.gl/gnHJL2 for more details.
|
|
func (c *Client) WaitContainer(id string) (int, error) {
|
|
body, status, err := c.do("POST", "/containers/"+id+"/wait", nil)
|
|
if status == http.StatusNotFound {
|
|
return 0, &NoSuchContainer{ID: id}
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var r struct{ StatusCode int }
|
|
err = json.Unmarshal(body, &r)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return r.StatusCode, nil
|
|
}
|
|
|
|
// CommitContainerOptions aggregates parameters to the CommitContainer method.
|
|
//
|
|
// See http://goo.gl/628gxm for more details.
|
|
type CommitContainerOptions struct {
|
|
Container string
|
|
Repository string `qs:"repo"`
|
|
Tag string
|
|
Message string `qs:"m"`
|
|
Author string
|
|
Run *Config `qs:"-"`
|
|
}
|
|
|
|
type Image struct {
|
|
ID string `json:"id"`
|
|
Parent string `json:"parent,omitempty"`
|
|
Comment string `json:"comment,omitempty"`
|
|
Created time.Time `json:"created"`
|
|
Container string `json:"container,omitempty"`
|
|
ContainerConfig Config `json:"container_config,omitempty"`
|
|
DockerVersion string `json:"docker_version,omitempty"`
|
|
Author string `json:"author,omitempty"`
|
|
Config *Config `json:"config,omitempty"`
|
|
Architecture string `json:"architecture,omitempty"`
|
|
Size int64
|
|
}
|
|
|
|
// CommitContainer creates a new image from a container's changes.
|
|
//
|
|
// See http://goo.gl/628gxm for more details.
|
|
func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) {
|
|
path := "/commit?" + queryString(opts)
|
|
body, status, err := c.do("POST", path, opts.Run)
|
|
if status == http.StatusNotFound {
|
|
return nil, &NoSuchContainer{ID: opts.Container}
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var image Image
|
|
err = json.Unmarshal(body, &image)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &image, nil
|
|
}
|
|
|
|
// AttachToContainerOptions is the set of options that can be used when
|
|
// attaching to a container.
|
|
//
|
|
// See http://goo.gl/oPzcqH for more details.
|
|
type AttachToContainerOptions struct {
|
|
Container string `qs:"-"`
|
|
InputStream io.Reader `qs:"-"`
|
|
OutputStream io.Writer `qs:"-"`
|
|
ErrorStream io.Writer `qs:"-"`
|
|
|
|
// Get container logs, sending it to OutputStream.
|
|
Logs bool
|
|
|
|
// Stream the response?
|
|
Stream bool
|
|
|
|
// Attach to stdin, and use InputFile.
|
|
Stdin bool
|
|
|
|
// Attach to stdout, and use OutputStream.
|
|
Stdout bool
|
|
|
|
// Attach to stderr, and use ErrorStream.
|
|
Stderr bool
|
|
|
|
// If set, after a successful connect, a sentinel will be sent and then the
|
|
// client will block on receive before continuing.
|
|
//
|
|
// It must be an unbuffered channel. Using a buffered channel can lead
|
|
// to unexpected behavior.
|
|
Success chan struct{}
|
|
}
|
|
|
|
// AttachToContainer attaches to a container, using the given options.
|
|
//
|
|
// See http://goo.gl/oPzcqH for more details.
|
|
func (c *Client) AttachToContainer(opts AttachToContainerOptions) error {
|
|
if opts.Container == "" {
|
|
return &NoSuchContainer{ID: opts.Container}
|
|
}
|
|
path := "/containers/" + opts.Container + "/attach?" + queryString(opts)
|
|
return c.hijack("POST", path, opts.Success, opts.InputStream, opts.ErrorStream, opts.OutputStream)
|
|
}
|
|
|
|
// ResizeContainerTTY resizes the terminal to the given height and width.
|
|
func (c *Client) ResizeContainerTTY(id string, height, width int) error {
|
|
params := make(url.Values)
|
|
params.Set("h", strconv.Itoa(height))
|
|
params.Set("w", strconv.Itoa(width))
|
|
_, _, err := c.do("POST", "/containers/"+id+"/resize?"+params.Encode(), nil)
|
|
return err
|
|
}
|
|
|
|
// ExportContainerOptions is the set of parameters to the ExportContainer
|
|
// method.
|
|
//
|
|
// See http://goo.gl/Lqk0FZ for more details.
|
|
type ExportContainerOptions struct {
|
|
ID string
|
|
OutputStream io.Writer
|
|
}
|
|
|
|
// ExportContainer export the contents of container id as tar archive
|
|
// and prints the exported contents to stdout.
|
|
//
|
|
// See http://goo.gl/Lqk0FZ for more details.
|
|
func (c *Client) ExportContainer(opts ExportContainerOptions) error {
|
|
if opts.ID == "" {
|
|
return NoSuchContainer{ID: opts.ID}
|
|
}
|
|
url := fmt.Sprintf("/containers/%s/export", opts.ID)
|
|
return c.stream("GET", url, nil, nil, opts.OutputStream)
|
|
}
|
|
|
|
// NoSuchContainer is the error returned when a given container does not exist.
|
|
type NoSuchContainer struct {
|
|
ID string
|
|
}
|
|
|
|
func (err NoSuchContainer) Error() string {
|
|
return "No such container: " + err.ID
|
|
}
|