mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-30 10:11:23 +02:00
715 lines
17 KiB
Go
715 lines
17 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
|
|
monitorStats int32
|
|
eventStopChan chan (struct{})
|
|
}
|
|
|
|
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}, nil
|
|
}
|
|
|
|
func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
|
|
b := bytes.NewBuffer(body)
|
|
|
|
reader, err := client.doStreamRequest(method, path, b, headers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer reader.Close()
|
|
data, err := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) {
|
|
if (method == "POST" || method == "PUT") && in == nil {
|
|
in = bytes.NewReader(nil)
|
|
}
|
|
req, err := http.NewRequest(method, client.URL.String()+path, in)
|
|
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
|
|
}
|
|
if resp.StatusCode == 404 {
|
|
return nil, ErrNotFound
|
|
}
|
|
if resp.StatusCode >= 400 {
|
|
defer resp.Body.Close()
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
|
|
}
|
|
|
|
return resp.Body, 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) ContainerChanges(id string) ([]*ContainerChanges, error) {
|
|
uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id)
|
|
data, err := client.doRequest("GET", uri, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changes := []*ContainerChanges{}
|
|
err = json.Unmarshal(data, &changes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return changes, nil
|
|
}
|
|
|
|
func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult {
|
|
resultChan := make(chan decodingResult)
|
|
|
|
go func() {
|
|
decodeChan := make(chan decodingResult)
|
|
|
|
go func() {
|
|
decoder := json.NewDecoder(stream)
|
|
for {
|
|
decodeResult := decode(decoder)
|
|
decodeChan <- decodeResult
|
|
if decodeResult.err != nil {
|
|
close(decodeChan)
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
defer close(resultChan)
|
|
|
|
for {
|
|
select {
|
|
case <-stopChan:
|
|
stream.Close()
|
|
for _ = range decodeChan {
|
|
}
|
|
return
|
|
case decodeResult := <-decodeChan:
|
|
resultChan <- decodeResult
|
|
if decodeResult.err != nil {
|
|
stream.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
}()
|
|
|
|
return resultChan
|
|
}
|
|
|
|
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) Wait(id string) <-chan WaitResult {
|
|
ch := make(chan WaitResult)
|
|
uri := fmt.Sprintf("/%s/containers/%s/wait", APIVersion, id)
|
|
|
|
go func() {
|
|
data, err := client.doRequest("POST", uri, nil, nil)
|
|
if err != nil {
|
|
ch <- WaitResult{ExitCode: -1, Error: err}
|
|
return
|
|
}
|
|
|
|
var result struct {
|
|
StatusCode int `json:"StatusCode"`
|
|
}
|
|
err = json.Unmarshal(data, &result)
|
|
ch <- WaitResult{ExitCode: result.StatusCode, Error: err}
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) {
|
|
v := url.Values{}
|
|
if options != nil {
|
|
if options.Since != 0 {
|
|
v.Add("since", strconv.Itoa(options.Since))
|
|
}
|
|
if options.Until != 0 {
|
|
v.Add("until", strconv.Itoa(options.Until))
|
|
}
|
|
if options.Filters != nil {
|
|
filterMap := make(map[string][]string)
|
|
if len(options.Filters.Event) > 0 {
|
|
filterMap["event"] = []string{options.Filters.Event}
|
|
}
|
|
if len(options.Filters.Image) > 0 {
|
|
filterMap["image"] = []string{options.Filters.Image}
|
|
}
|
|
if len(options.Filters.Container) > 0 {
|
|
filterMap["container"] = []string{options.Filters.Container}
|
|
}
|
|
if len(filterMap) > 0 {
|
|
filterJSONBytes, err := json.Marshal(filterMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v.Add("filters", string(filterJSONBytes))
|
|
}
|
|
}
|
|
}
|
|
uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode())
|
|
resp, err := client.HTTPClient.Get(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
decode := func(decoder *json.Decoder) decodingResult {
|
|
var event Event
|
|
if err := decoder.Decode(&event); err != nil {
|
|
return decodingResult{err: err}
|
|
} else {
|
|
return decodingResult{result: event}
|
|
}
|
|
}
|
|
decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan)
|
|
eventOrErrorChan := make(chan EventOrError)
|
|
go func() {
|
|
for decodingResult := range decodingResultChan {
|
|
event, _ := decodingResult.result.(Event)
|
|
eventOrErrorChan <- EventOrError{
|
|
Event: event,
|
|
Error: decodingResult.err,
|
|
}
|
|
}
|
|
close(eventOrErrorChan)
|
|
}()
|
|
return eventOrErrorChan, nil
|
|
}
|
|
|
|
func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) {
|
|
client.eventStopChan = make(chan struct{})
|
|
|
|
go func() {
|
|
eventErrChan, err := client.MonitorEvents(nil, client.eventStopChan)
|
|
if err != nil {
|
|
if ec != nil {
|
|
ec <- err
|
|
}
|
|
return
|
|
}
|
|
|
|
for e := range eventErrChan {
|
|
if e.Error != nil {
|
|
if ec != nil {
|
|
ec <- err
|
|
}
|
|
return
|
|
}
|
|
cb(&e.Event, ec, args...)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (client *DockerClient) StopAllMonitorEvents() {
|
|
close(client.eventStopChan)
|
|
}
|
|
|
|
func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
|
atomic.StoreInt32(&client.monitorStats, 1)
|
|
go client.getStats(id, cb, ec, args...)
|
|
}
|
|
|
|
func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
|
uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id)
|
|
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.monitorStats) > 0 {
|
|
var stats *Stats
|
|
if err := dec.Decode(&stats); err != nil {
|
|
ec <- err
|
|
return
|
|
}
|
|
cb(id, stats, ec, args...)
|
|
}
|
|
}
|
|
|
|
func (client *DockerClient) StopAllMonitorStats() {
|
|
atomic.StoreInt32(&client.monitorStats, 0)
|
|
}
|
|
|
|
func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
|
|
v := url.Values{}
|
|
v.Set("repo", repo)
|
|
v.Set("tag", tag)
|
|
if force {
|
|
v.Set("force", "1")
|
|
}
|
|
uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode())
|
|
if _, err := client.doRequest("POST", uri, nil, nil); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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 {
|
|
encoded_auth, err := auth.encode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Add("X-Registry-Auth", encoded_auth)
|
|
}
|
|
resp, err := client.HTTPClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode == 404 {
|
|
return ErrNotFound
|
|
}
|
|
if resp.StatusCode >= 400 {
|
|
data, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return fmt.Errorf("%s", string(data))
|
|
}
|
|
|
|
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) InspectImage(id string) (*ImageInfo, error) {
|
|
uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id)
|
|
data, err := client.doRequest("GET", uri, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
info := &ImageInfo{}
|
|
err = json.Unmarshal(data, info)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (client *DockerClient) LoadImage(reader io.Reader) error {
|
|
data, err := ioutil.ReadAll(reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uri := fmt.Sprintf("/%s/images/load", APIVersion)
|
|
_, err = client.doRequest("POST", uri, data, nil)
|
|
if err != nil {
|
|
return 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(all bool) ([]*Image, error) {
|
|
argAll := 0
|
|
if all {
|
|
argAll = 1
|
|
}
|
|
uri := fmt.Sprintf("/%s/images/json?all=%d", APIVersion, argAll)
|
|
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) ([]*ImageDelete, error) {
|
|
uri := fmt.Sprintf("/%s/images/%s", APIVersion, name)
|
|
data, err := client.doRequest("DELETE", uri, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var imageDelete []*ImageDelete
|
|
if err := json.Unmarshal(data, &imageDelete); err != nil {
|
|
return nil, err
|
|
}
|
|
return imageDelete, nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (client *DockerClient) RenameContainer(oldName string, newName string) error {
|
|
uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName)
|
|
_, err := client.doRequest("POST", uri, nil, nil)
|
|
return err
|
|
}
|
|
|
|
func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
|
|
var fromSrc string
|
|
v := &url.Values{}
|
|
if source == "" {
|
|
fromSrc = "-"
|
|
} else {
|
|
fromSrc = source
|
|
}
|
|
|
|
v.Set("fromSrc", fromSrc)
|
|
v.Set("repo", repository)
|
|
if tag != "" {
|
|
v.Set("tag", tag)
|
|
}
|
|
|
|
var in io.Reader
|
|
if fromSrc == "-" {
|
|
in = tar
|
|
}
|
|
return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil)
|
|
}
|
|
|
|
func (client *DockerClient) BuildImage(image *BuildImage) (io.ReadCloser, error) {
|
|
v := url.Values{}
|
|
|
|
if image.DockerfileName != "" {
|
|
v.Set("dockerfile", image.DockerfileName)
|
|
}
|
|
if image.RepoName != "" {
|
|
v.Set("t", image.RepoName)
|
|
}
|
|
if image.RemoteURL != "" {
|
|
v.Set("remote", image.RemoteURL)
|
|
}
|
|
if image.NoCache {
|
|
v.Set("nocache", "1")
|
|
}
|
|
if image.Pull {
|
|
v.Set("pull", "1")
|
|
}
|
|
if image.Remove {
|
|
v.Set("rm", "1")
|
|
} else {
|
|
v.Set("rm", "0")
|
|
}
|
|
if image.ForceRemove {
|
|
v.Set("forcerm", "1")
|
|
}
|
|
if image.SuppressOutput {
|
|
v.Set("q", "1")
|
|
}
|
|
|
|
v.Set("memory", strconv.FormatInt(image.Memory, 10))
|
|
v.Set("memswap", strconv.FormatInt(image.MemorySwap, 10))
|
|
v.Set("cpushares", strconv.FormatInt(image.CpuShares, 10))
|
|
v.Set("cpuperiod", strconv.FormatInt(image.CpuPeriod, 10))
|
|
v.Set("cpuquota", strconv.FormatInt(image.CpuQuota, 10))
|
|
v.Set("cpusetcpus", image.CpuSetCpus)
|
|
v.Set("cpusetmems", image.CpuSetMems)
|
|
v.Set("cgroupparent", image.CgroupParent)
|
|
|
|
headers := make(map[string]string)
|
|
if image.Config != nil {
|
|
encoded_config, err := image.Config.encode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
headers["X-Registry-Config"] = encoded_config
|
|
}
|
|
if image.Context != nil {
|
|
headers["Content-Type"] = "application/tar"
|
|
}
|
|
|
|
uri := fmt.Sprintf("/%s/build?%s", APIVersion, v.Encode())
|
|
return client.doStreamRequest("POST", uri, image.Context, headers)
|
|
}
|