woodpecker/vendor/github.com/samalba/dockerclient/dockerclient.go
2015-09-29 17:34:44 -07:00

381 lines
9.2 KiB
Go

package dockerclient
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"sync/atomic"
"time"
)
const (
APIVersion = "v1.15"
)
var (
ErrNotFound = errors.New("Not found")
defaultTimeout = 30 * time.Second
)
type DockerClient struct {
URL *url.URL
HTTPClient *http.Client
TLSConfig *tls.Config
monitorEvents int32
}
type Error struct {
StatusCode int
Status string
msg string
}
func (e Error) Error() string {
return fmt.Sprintf("%s: %s", e.Status, e.msg)
}
func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout))
}
func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) {
u, err := url.Parse(daemonUrl)
if err != nil {
return nil, err
}
if u.Scheme == "" || u.Scheme == "tcp" {
if tlsConfig == nil {
u.Scheme = "http"
} else {
u.Scheme = "https"
}
}
httpClient := newHTTPClient(u, tlsConfig, timeout)
return &DockerClient{u, httpClient, tlsConfig, 0}, nil
}
func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
b := bytes.NewBuffer(body)
req, err := http.NewRequest(method, client.URL.String()+path, b)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
if headers != nil {
for header, value := range headers {
req.Header.Add(header, value)
}
}
resp, err := client.HTTPClient.Do(req)
if err != nil {
if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
}
return nil, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode == 404 {
return nil, ErrNotFound
}
if resp.StatusCode >= 400 {
return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
}
return data, nil
}
func (client *DockerClient) Info() (*Info, error) {
uri := fmt.Sprintf("/%s/info", APIVersion)
data, err := client.doRequest("GET", uri, nil, nil)
if err != nil {
return nil, err
}
ret := &Info{}
err = json.Unmarshal(data, &ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (client *DockerClient) ListContainers(all bool, size bool, filters string) ([]Container, error) {
argAll := 0
if all == true {
argAll = 1
}
showSize := 0
if size == true {
showSize = 1
}
uri := fmt.Sprintf("/%s/containers/json?all=%d&size=%d", APIVersion, argAll, showSize)
if filters != "" {
uri += "&filters=" + filters
}
data, err := client.doRequest("GET", uri, nil, nil)
if err != nil {
return nil, err
}
ret := []Container{}
err = json.Unmarshal(data, &ret)
if err != nil {
return nil, err
}
return ret, nil
}
func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
uri := fmt.Sprintf("/%s/containers/%s/json", APIVersion, id)
data, err := client.doRequest("GET", uri, nil, nil)
if err != nil {
return nil, err
}
info := &ContainerInfo{}
err = json.Unmarshal(data, info)
if err != nil {
return nil, err
}
return info, nil
}
func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) {
data, err := json.Marshal(config)
if err != nil {
return "", err
}
uri := fmt.Sprintf("/%s/containers/create", APIVersion)
if name != "" {
v := url.Values{}
v.Set("name", name)
uri = fmt.Sprintf("%s?%s", uri, v.Encode())
}
data, err = client.doRequest("POST", uri, data, nil)
if err != nil {
return "", err
}
result := &RespContainersCreate{}
err = json.Unmarshal(data, result)
if err != nil {
return "", err
}
return result.Id, nil
}
func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) {
v := url.Values{}
v.Add("follow", strconv.FormatBool(options.Follow))
v.Add("stdout", strconv.FormatBool(options.Stdout))
v.Add("stderr", strconv.FormatBool(options.Stderr))
v.Add("timestamps", strconv.FormatBool(options.Timestamps))
if options.Tail > 0 {
v.Add("tail", strconv.FormatInt(options.Tail, 10))
}
uri := fmt.Sprintf("/%s/containers/%s/logs?%s", APIVersion, id, v.Encode())
req, err := http.NewRequest("GET", client.URL.String()+uri, nil)
if err != nil {
return nil, err
}
req.Header.Add("Content-Type", "application/json")
resp, err := client.HTTPClient.Do(req)
if err != nil {
return nil, err
}
return resp.Body, nil
}
func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
data, err := json.Marshal(config)
if err != nil {
return err
}
uri := fmt.Sprintf("/%s/containers/%s/start", APIVersion, id)
_, err = client.doRequest("POST", uri, data, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) StopContainer(id string, timeout int) error {
uri := fmt.Sprintf("/%s/containers/%s/stop?t=%d", APIVersion, id, timeout)
_, err := client.doRequest("POST", uri, nil, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) RestartContainer(id string, timeout int) error {
uri := fmt.Sprintf("/%s/containers/%s/restart?t=%d", APIVersion, id, timeout)
_, err := client.doRequest("POST", uri, nil, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) KillContainer(id, signal string) error {
uri := fmt.Sprintf("/%s/containers/%s/kill?signal=%s", APIVersion, id, signal)
_, err := client.doRequest("POST", uri, nil, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) {
atomic.StoreInt32(&client.monitorEvents, 1)
go client.getEvents(cb, ec, args...)
}
func (client *DockerClient) getEvents(cb Callback, ec chan error, args ...interface{}) {
uri := fmt.Sprintf("%s/%s/events", client.URL.String(), APIVersion)
resp, err := client.HTTPClient.Get(uri)
if err != nil {
ec <- err
return
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
for atomic.LoadInt32(&client.monitorEvents) > 0 {
var event *Event
if err := dec.Decode(&event); err != nil {
ec <- err
return
}
cb(event, ec, args...)
}
}
func (client *DockerClient) StopAllMonitorEvents() {
atomic.StoreInt32(&client.monitorEvents, 0)
}
func (client *DockerClient) Version() (*Version, error) {
uri := fmt.Sprintf("/%s/version", APIVersion)
data, err := client.doRequest("GET", uri, nil, nil)
if err != nil {
return nil, err
}
version := &Version{}
err = json.Unmarshal(data, version)
if err != nil {
return nil, err
}
return version, nil
}
func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
v := url.Values{}
v.Set("fromImage", name)
uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode())
req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
if auth != nil {
req.Header.Add("X-Registry-Auth", auth.encode())
}
resp, err := client.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
var finalObj map[string]interface{}
for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
}
if err != io.EOF {
return err
}
if err, ok := finalObj["error"]; ok {
return fmt.Errorf("%v", err)
}
return nil
}
func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
argForce := 0
argVolumes := 0
if force == true {
argForce = 1
}
if volumes == true {
argVolumes = 1
}
args := fmt.Sprintf("force=%d&v=%d", argForce, argVolumes)
uri := fmt.Sprintf("/%s/containers/%s?%s", APIVersion, id, args)
_, err := client.doRequest("DELETE", uri, nil, nil)
return err
}
func (client *DockerClient) ListImages() ([]*Image, error) {
uri := fmt.Sprintf("/%s/images/json", APIVersion)
data, err := client.doRequest("GET", uri, nil, nil)
if err != nil {
return nil, err
}
var images []*Image
if err := json.Unmarshal(data, &images); err != nil {
return nil, err
}
return images, nil
}
func (client *DockerClient) RemoveImage(name string) error {
uri := fmt.Sprintf("/%s/images/%s", APIVersion, name)
_, err := client.doRequest("DELETE", uri, nil, nil)
return err
}
func (client *DockerClient) PauseContainer(id string) error {
uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id)
_, err := client.doRequest("POST", uri, nil, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) UnpauseContainer(id string) error {
uri := fmt.Sprintf("/%s/containers/%s/unpause", APIVersion, id)
_, err := client.doRequest("POST", uri, nil, nil)
if err != nil {
return err
}
return nil
}
func (client *DockerClient) Exec(config *ExecConfig) (string, error) {
data, err := json.Marshal(config)
if err != nil {
return "", err
}
uri := fmt.Sprintf("/containers/%s/exec", config.Container)
resp, err := client.doRequest("POST", uri, data, nil)
if err != nil {
return "", err
}
var createExecResp struct {
Id string
}
if err = json.Unmarshal(resp, &createExecResp); err != nil {
return "", err
}
uri = fmt.Sprintf("/exec/%s/start", createExecResp.Id)
resp, err = client.doRequest("POST", uri, data, nil)
if err != nil {
return "", err
}
return createExecResp.Id, nil
}