1package dockerclient
2
3import (
4	"bytes"
5	"crypto/tls"
6	"encoding/json"
7	"errors"
8	"fmt"
9	"io"
10	"io/ioutil"
11	"net/http"
12	"net/url"
13	"strconv"
14	"strings"
15	"sync/atomic"
16	"time"
17)
18
19const (
20	APIVersion = "v1.15"
21)
22
23var (
24	ErrImageNotFound     = errors.New("Image not found")
25	ErrNotFound          = errors.New("Not found")
26	ErrConnectionRefused = errors.New("Cannot connect to the docker engine endpoint")
27
28	defaultTimeout = 30 * time.Second
29)
30
31type DockerClient struct {
32	URL           *url.URL
33	HTTPClient    *http.Client
34	TLSConfig     *tls.Config
35	monitorStats  int32
36	eventStopChan chan (struct{})
37}
38
39type Error struct {
40	StatusCode int
41	Status     string
42	msg        string
43}
44
45func (e Error) Error() string {
46	return fmt.Sprintf("%s: %s", e.Status, e.msg)
47}
48
49func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
50	return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout))
51}
52
53func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) {
54	u, err := url.Parse(daemonUrl)
55	if err != nil {
56		return nil, err
57	}
58	if u.Scheme == "" || u.Scheme == "tcp" {
59		if tlsConfig == nil {
60			u.Scheme = "http"
61		} else {
62			u.Scheme = "https"
63		}
64	}
65	httpClient := newHTTPClient(u, tlsConfig, timeout)
66	return &DockerClient{u, httpClient, tlsConfig, 0, nil}, nil
67}
68
69func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
70	b := bytes.NewBuffer(body)
71
72	reader, err := client.doStreamRequest(method, path, b, headers)
73	if err != nil {
74		return nil, err
75	}
76
77	defer reader.Close()
78	data, err := ioutil.ReadAll(reader)
79	if err != nil {
80		return nil, err
81	}
82	return data, nil
83}
84
85func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) {
86	if (method == "POST" || method == "PUT") && in == nil {
87		in = bytes.NewReader(nil)
88	}
89	req, err := http.NewRequest(method, client.URL.String()+path, in)
90	if err != nil {
91		return nil, err
92	}
93	req.Header.Add("Content-Type", "application/json")
94	if headers != nil {
95		for header, value := range headers {
96			req.Header.Add(header, value)
97		}
98	}
99	resp, err := client.HTTPClient.Do(req)
100	if err != nil {
101		if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
102			return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
103		}
104		if strings.Contains(err.Error(), "connection refused") {
105			return nil, ErrConnectionRefused
106		}
107		return nil, err
108	}
109	if resp.StatusCode == 404 {
110		defer resp.Body.Close()
111		data, err := ioutil.ReadAll(resp.Body)
112		if err != nil {
113			return nil, ErrNotFound
114		}
115		if len(data) > 0 {
116			// check if is image not found error
117			if strings.Index(string(data), "No such image") != -1 {
118				return nil, ErrImageNotFound
119			}
120			return nil, errors.New(string(data))
121		}
122		return nil, ErrNotFound
123	}
124	if resp.StatusCode >= 400 {
125		defer resp.Body.Close()
126		data, err := ioutil.ReadAll(resp.Body)
127		if err != nil {
128			return nil, err
129		}
130		return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
131	}
132
133	return resp.Body, nil
134}
135
136func (client *DockerClient) Info() (*Info, error) {
137	uri := fmt.Sprintf("/%s/info", APIVersion)
138	data, err := client.doRequest("GET", uri, nil, nil)
139	if err != nil {
140		return nil, err
141	}
142	ret := &Info{}
143	err = json.Unmarshal(data, &ret)
144	if err != nil {
145		return nil, err
146	}
147	return ret, nil
148}
149
150func (client *DockerClient) ListContainers(all bool, size bool, filters string) ([]Container, error) {
151	argAll := 0
152	if all == true {
153		argAll = 1
154	}
155	showSize := 0
156	if size == true {
157		showSize = 1
158	}
159	uri := fmt.Sprintf("/%s/containers/json?all=%d&size=%d", APIVersion, argAll, showSize)
160
161	if filters != "" {
162		uri += "&filters=" + filters
163	}
164
165	data, err := client.doRequest("GET", uri, nil, nil)
166	if err != nil {
167		return nil, err
168	}
169	ret := []Container{}
170	err = json.Unmarshal(data, &ret)
171	if err != nil {
172		return nil, err
173	}
174	return ret, nil
175}
176
177func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
178	uri := fmt.Sprintf("/%s/containers/%s/json", APIVersion, id)
179	data, err := client.doRequest("GET", uri, nil, nil)
180	if err != nil {
181		return nil, err
182	}
183	info := &ContainerInfo{}
184	err = json.Unmarshal(data, info)
185	if err != nil {
186		return nil, err
187	}
188	return info, nil
189}
190
191func (client *DockerClient) CreateContainer(config *ContainerConfig, name string, auth *AuthConfig) (string, error) {
192	data, err := json.Marshal(config)
193	if err != nil {
194		return "", err
195	}
196	uri := fmt.Sprintf("/%s/containers/create", APIVersion)
197	if name != "" {
198		v := url.Values{}
199		v.Set("name", name)
200		uri = fmt.Sprintf("%s?%s", uri, v.Encode())
201	}
202	headers := map[string]string{}
203	if auth != nil {
204		encoded_auth, err := auth.encode()
205		if err != nil {
206			return "", err
207		}
208		headers["X-Registry-Auth"] = encoded_auth
209	}
210	data, err = client.doRequest("POST", uri, data, headers)
211	if err != nil {
212		return "", err
213	}
214	result := &RespContainersCreate{}
215	err = json.Unmarshal(data, result)
216	if err != nil {
217		return "", fmt.Errorf(string(data))
218	}
219	return result.Id, nil
220}
221
222func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) {
223	v := url.Values{}
224	v.Add("follow", strconv.FormatBool(options.Follow))
225	v.Add("stdout", strconv.FormatBool(options.Stdout))
226	v.Add("stderr", strconv.FormatBool(options.Stderr))
227	v.Add("timestamps", strconv.FormatBool(options.Timestamps))
228	if options.Tail > 0 {
229		v.Add("tail", strconv.FormatInt(options.Tail, 10))
230	}
231
232	uri := fmt.Sprintf("/%s/containers/%s/logs?%s", APIVersion, id, v.Encode())
233	req, err := http.NewRequest("GET", client.URL.String()+uri, nil)
234	if err != nil {
235		return nil, err
236	}
237	req.Header.Add("Content-Type", "application/json")
238	resp, err := client.HTTPClient.Do(req)
239	if err != nil {
240		return nil, err
241	}
242	return resp.Body, nil
243}
244
245func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) {
246	uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id)
247	data, err := client.doRequest("GET", uri, nil, nil)
248	if err != nil {
249		return nil, err
250	}
251	changes := []*ContainerChanges{}
252	err = json.Unmarshal(data, &changes)
253	if err != nil {
254		return nil, err
255	}
256	return changes, nil
257}
258
259func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult {
260	resultChan := make(chan decodingResult)
261
262	go func() {
263		decodeChan := make(chan decodingResult)
264
265		go func() {
266			decoder := json.NewDecoder(stream)
267			for {
268				decodeResult := decode(decoder)
269				decodeChan <- decodeResult
270				if decodeResult.err != nil {
271					close(decodeChan)
272					return
273				}
274			}
275		}()
276
277		defer close(resultChan)
278
279		for {
280			select {
281			case <-stopChan:
282				stream.Close()
283				for range decodeChan {
284				}
285				return
286			case decodeResult := <-decodeChan:
287				resultChan <- decodeResult
288				if decodeResult.err != nil {
289					stream.Close()
290					return
291				}
292			}
293		}
294
295	}()
296
297	return resultChan
298}
299
300func (client *DockerClient) ExecCreate(config *ExecConfig) (string, error) {
301	data, err := json.Marshal(config)
302	if err != nil {
303		return "", err
304	}
305	uri := fmt.Sprintf("/%s/containers/%s/exec", APIVersion, config.Container)
306	resp, err := client.doRequest("POST", uri, data, nil)
307	if err != nil {
308		return "", err
309	}
310	var createExecResp struct {
311		Id string
312	}
313	if err = json.Unmarshal(resp, &createExecResp); err != nil {
314		return "", err
315	}
316	return createExecResp.Id, nil
317}
318
319func (client *DockerClient) ExecStart(id string, config *ExecConfig) error {
320	data, err := json.Marshal(config)
321	if err != nil {
322		return err
323	}
324
325	uri := fmt.Sprintf("/%s/exec/%s/start", APIVersion, id)
326	if _, err := client.doRequest("POST", uri, data, nil); err != nil {
327		return err
328	}
329
330	return nil
331}
332
333func (client *DockerClient) ExecResize(id string, width, height int) error {
334	v := url.Values{}
335
336	w := strconv.Itoa(width)
337	h := strconv.Itoa(height)
338
339	v.Set("w", w)
340	v.Set("h", h)
341
342	uri := fmt.Sprintf("/%s/exec/%s/resize?%s", APIVersion, id, v.Encode())
343	if _, err := client.doRequest("POST", client.URL.String()+uri, nil, nil); err != nil {
344		return err
345	}
346
347	return nil
348}
349
350func (client *DockerClient) AttachContainer(id string, options *AttachOptions) (io.ReadCloser, error) {
351	v := url.Values{}
352	if options != nil {
353		if options.Logs {
354			v.Set("logs", "1")
355		}
356		if options.Stream {
357			v.Set("stream", "1")
358		}
359		if options.Stdin {
360			v.Set("stdin", "1")
361		}
362		if options.Stdout {
363			v.Set("stdout", "1")
364		}
365		if options.Stderr {
366			v.Set("stderr", "1")
367		}
368	}
369	uri := fmt.Sprintf("/%s/containers/%s/attach?%s", APIVersion, id, v.Encode())
370	return client.doStreamRequest("POST", uri, nil, nil)
371}
372
373func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
374	data, err := json.Marshal(config)
375	if err != nil {
376		return err
377	}
378	uri := fmt.Sprintf("/%s/containers/%s/start", APIVersion, id)
379	_, err = client.doRequest("POST", uri, data, nil)
380	if err != nil {
381		return err
382	}
383	return nil
384}
385
386func (client *DockerClient) StopContainer(id string, timeout int) error {
387	uri := fmt.Sprintf("/%s/containers/%s/stop?t=%d", APIVersion, id, timeout)
388	_, err := client.doRequest("POST", uri, nil, nil)
389	if err != nil {
390		return err
391	}
392	return nil
393}
394
395func (client *DockerClient) RestartContainer(id string, timeout int) error {
396	uri := fmt.Sprintf("/%s/containers/%s/restart?t=%d", APIVersion, id, timeout)
397	_, err := client.doRequest("POST", uri, nil, nil)
398	if err != nil {
399		return err
400	}
401	return nil
402}
403
404func (client *DockerClient) KillContainer(id, signal string) error {
405	uri := fmt.Sprintf("/%s/containers/%s/kill?signal=%s", APIVersion, id, signal)
406	_, err := client.doRequest("POST", uri, nil, nil)
407	if err != nil {
408		return err
409	}
410	return nil
411}
412
413func (client *DockerClient) Wait(id string) <-chan WaitResult {
414	ch := make(chan WaitResult)
415	uri := fmt.Sprintf("/%s/containers/%s/wait", APIVersion, id)
416
417	go func() {
418		data, err := client.doRequest("POST", uri, nil, nil)
419		if err != nil {
420			ch <- WaitResult{ExitCode: -1, Error: err}
421			return
422		}
423
424		var result struct {
425			StatusCode int `json:"StatusCode"`
426		}
427		err = json.Unmarshal(data, &result)
428		ch <- WaitResult{ExitCode: result.StatusCode, Error: err}
429	}()
430	return ch
431}
432
433func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) {
434	v := url.Values{}
435	if options != nil {
436		if options.Since != 0 {
437			v.Add("since", strconv.Itoa(options.Since))
438		}
439		if options.Until != 0 {
440			v.Add("until", strconv.Itoa(options.Until))
441		}
442		if options.Filters != nil {
443			filterMap := make(map[string][]string)
444			if len(options.Filters.Event) > 0 {
445				filterMap["event"] = []string{options.Filters.Event}
446			}
447			if len(options.Filters.Image) > 0 {
448				filterMap["image"] = []string{options.Filters.Image}
449			}
450			if len(options.Filters.Container) > 0 {
451				filterMap["container"] = []string{options.Filters.Container}
452			}
453			if len(filterMap) > 0 {
454				filterJSONBytes, err := json.Marshal(filterMap)
455				if err != nil {
456					return nil, err
457				}
458				v.Add("filters", string(filterJSONBytes))
459			}
460		}
461	}
462	uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode())
463	resp, err := client.HTTPClient.Get(uri)
464	if err != nil {
465		return nil, err
466	}
467
468	decode := func(decoder *json.Decoder) decodingResult {
469		var event Event
470		if err := decoder.Decode(&event); err != nil {
471			return decodingResult{err: err}
472		} else {
473			return decodingResult{result: event}
474		}
475	}
476	decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan)
477	eventOrErrorChan := make(chan EventOrError)
478	go func() {
479		for decodingResult := range decodingResultChan {
480			event, _ := decodingResult.result.(Event)
481			eventOrErrorChan <- EventOrError{
482				Event: event,
483				Error: decodingResult.err,
484			}
485		}
486		close(eventOrErrorChan)
487	}()
488	return eventOrErrorChan, nil
489}
490
491func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) {
492	client.eventStopChan = make(chan struct{})
493
494	go func() {
495		eventErrChan, err := client.MonitorEvents(nil, client.eventStopChan)
496		if err != nil {
497			if ec != nil {
498				ec <- err
499			}
500			return
501		}
502
503		for e := range eventErrChan {
504			if e.Error != nil {
505				if ec != nil {
506					ec <- err
507				}
508				return
509			}
510			cb(&e.Event, ec, args...)
511		}
512	}()
513}
514
515func (client *DockerClient) StopAllMonitorEvents() {
516	if client.eventStopChan == nil {
517		return
518	}
519	close(client.eventStopChan)
520}
521
522func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
523	atomic.StoreInt32(&client.monitorStats, 1)
524	go client.getStats(id, cb, ec, args...)
525}
526
527func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
528	uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id)
529	resp, err := client.HTTPClient.Get(uri)
530	if err != nil {
531		ec <- err
532		return
533	}
534	defer resp.Body.Close()
535
536	dec := json.NewDecoder(resp.Body)
537	for atomic.LoadInt32(&client.monitorStats) > 0 {
538		var stats *Stats
539		if err := dec.Decode(&stats); err != nil {
540			ec <- err
541			return
542		}
543		cb(id, stats, ec, args...)
544	}
545}
546
547func (client *DockerClient) StopAllMonitorStats() {
548	atomic.StoreInt32(&client.monitorStats, 0)
549}
550
551func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
552	v := url.Values{}
553	v.Set("repo", repo)
554	v.Set("tag", tag)
555	if force {
556		v.Set("force", "1")
557	}
558	uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode())
559	if _, err := client.doRequest("POST", uri, nil, nil); err != nil {
560		return err
561	}
562	return nil
563}
564
565func (client *DockerClient) Version() (*Version, error) {
566	uri := fmt.Sprintf("/%s/version", APIVersion)
567	data, err := client.doRequest("GET", uri, nil, nil)
568	if err != nil {
569		return nil, err
570	}
571	version := &Version{}
572	err = json.Unmarshal(data, version)
573	if err != nil {
574		return nil, err
575	}
576	return version, nil
577}
578
579func (client *DockerClient) PushImage(name string, tag string, auth *AuthConfig) error {
580	v := url.Values{}
581	if tag != "" {
582		v.Set("tag", tag)
583	}
584	uri := fmt.Sprintf("/%s/images/%s/push?%s", APIVersion, url.QueryEscape(name), v.Encode())
585	req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
586	if auth != nil {
587		if encodedAuth, err := auth.encode(); err != nil {
588			return err
589		} else {
590			req.Header.Add("X-Registry-Auth", encodedAuth)
591		}
592	}
593	resp, err := client.HTTPClient.Do(req)
594	if err != nil {
595		return err
596	}
597	defer resp.Body.Close()
598	var finalObj map[string]interface{}
599	for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
600	}
601	if err != io.EOF {
602		return err
603	}
604	if err, ok := finalObj["error"]; ok {
605		return fmt.Errorf("%v", err)
606	}
607	return nil
608}
609
610func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
611	v := url.Values{}
612	v.Set("fromImage", name)
613	uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode())
614	req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
615	if auth != nil {
616		encoded_auth, err := auth.encode()
617		if err != nil {
618			return err
619		}
620		req.Header.Add("X-Registry-Auth", encoded_auth)
621	}
622	resp, err := client.HTTPClient.Do(req)
623	if err != nil {
624		return err
625	}
626
627	defer resp.Body.Close()
628	if resp.StatusCode == 404 {
629		return ErrNotFound
630	}
631	if resp.StatusCode >= 400 {
632		data, err := ioutil.ReadAll(resp.Body)
633		if err != nil {
634			return err
635		}
636		return fmt.Errorf("%s", string(data))
637	}
638
639	var finalObj map[string]interface{}
640	for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
641	}
642	if err != io.EOF {
643		return err
644	}
645	if err, ok := finalObj["error"]; ok {
646		return fmt.Errorf("%v", err)
647	}
648	return nil
649}
650
651func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) {
652	uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id)
653	data, err := client.doRequest("GET", uri, nil, nil)
654	if err != nil {
655		return nil, err
656	}
657	info := &ImageInfo{}
658	err = json.Unmarshal(data, info)
659	if err != nil {
660		return nil, err
661	}
662	return info, nil
663}
664
665func (client *DockerClient) LoadImage(reader io.Reader) error {
666	uri := fmt.Sprintf("/%s/images/load", APIVersion)
667	_, err := client.doStreamRequest("POST", uri, reader, nil)
668	return err
669}
670
671func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
672	argForce := 0
673	argVolumes := 0
674	if force == true {
675		argForce = 1
676	}
677	if volumes == true {
678		argVolumes = 1
679	}
680	args := fmt.Sprintf("force=%d&v=%d", argForce, argVolumes)
681	uri := fmt.Sprintf("/%s/containers/%s?%s", APIVersion, id, args)
682	_, err := client.doRequest("DELETE", uri, nil, nil)
683	return err
684}
685
686func (client *DockerClient) ListImages(all bool) ([]*Image, error) {
687	argAll := 0
688	if all {
689		argAll = 1
690	}
691	uri := fmt.Sprintf("/%s/images/json?all=%d", APIVersion, argAll)
692	data, err := client.doRequest("GET", uri, nil, nil)
693	if err != nil {
694		return nil, err
695	}
696	var images []*Image
697	if err := json.Unmarshal(data, &images); err != nil {
698		return nil, err
699	}
700	return images, nil
701}
702
703func (client *DockerClient) RemoveImage(name string, force bool) ([]*ImageDelete, error) {
704	argForce := 0
705	if force {
706		argForce = 1
707	}
708
709	args := fmt.Sprintf("force=%d", argForce)
710	uri := fmt.Sprintf("/%s/images/%s?%s", APIVersion, name, args)
711	data, err := client.doRequest("DELETE", uri, nil, nil)
712	if err != nil {
713		return nil, err
714	}
715	var imageDelete []*ImageDelete
716	if err := json.Unmarshal(data, &imageDelete); err != nil {
717		return nil, err
718	}
719	return imageDelete, nil
720}
721
722func (client *DockerClient) PauseContainer(id string) error {
723	uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id)
724	_, err := client.doRequest("POST", uri, nil, nil)
725	if err != nil {
726		return err
727	}
728	return nil
729}
730func (client *DockerClient) UnpauseContainer(id string) error {
731	uri := fmt.Sprintf("/%s/containers/%s/unpause", APIVersion, id)
732	_, err := client.doRequest("POST", uri, nil, nil)
733	if err != nil {
734		return err
735	}
736	return nil
737}
738
739func (client *DockerClient) RenameContainer(oldName string, newName string) error {
740	uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName)
741	_, err := client.doRequest("POST", uri, nil, nil)
742	return err
743}
744
745func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
746	var fromSrc string
747	v := &url.Values{}
748	if source == "" {
749		fromSrc = "-"
750	} else {
751		fromSrc = source
752	}
753
754	v.Set("fromSrc", fromSrc)
755	v.Set("repo", repository)
756	if tag != "" {
757		v.Set("tag", tag)
758	}
759
760	var in io.Reader
761	if fromSrc == "-" {
762		in = tar
763	}
764	return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil)
765}
766
767func (client *DockerClient) BuildImage(image *BuildImage) (io.ReadCloser, error) {
768	v := url.Values{}
769
770	if image.DockerfileName != "" {
771		v.Set("dockerfile", image.DockerfileName)
772	}
773	if image.RepoName != "" {
774		v.Set("t", image.RepoName)
775	}
776	if image.RemoteURL != "" {
777		v.Set("remote", image.RemoteURL)
778	}
779	if image.NoCache {
780		v.Set("nocache", "1")
781	}
782	if image.Pull {
783		v.Set("pull", "1")
784	}
785	if image.Remove {
786		v.Set("rm", "1")
787	} else {
788		v.Set("rm", "0")
789	}
790	if image.ForceRemove {
791		v.Set("forcerm", "1")
792	}
793	if image.SuppressOutput {
794		v.Set("q", "1")
795	}
796
797	v.Set("memory", strconv.FormatInt(image.Memory, 10))
798	v.Set("memswap", strconv.FormatInt(image.MemorySwap, 10))
799	v.Set("cpushares", strconv.FormatInt(image.CpuShares, 10))
800	v.Set("cpuperiod", strconv.FormatInt(image.CpuPeriod, 10))
801	v.Set("cpuquota", strconv.FormatInt(image.CpuQuota, 10))
802	v.Set("cpusetcpus", image.CpuSetCpus)
803	v.Set("cpusetmems", image.CpuSetMems)
804	v.Set("cgroupparent", image.CgroupParent)
805	if image.BuildArgs != nil {
806		buildArgsJSON, err := json.Marshal(image.BuildArgs)
807		if err != nil {
808			return nil, err
809		}
810		v.Set("buildargs", string(buildArgsJSON))
811	}
812
813	headers := make(map[string]string)
814	if image.Config != nil {
815		encoded_config, err := image.Config.encode()
816		if err != nil {
817			return nil, err
818		}
819		headers["X-Registry-Config"] = encoded_config
820	}
821	if image.Context != nil {
822		headers["Content-Type"] = "application/tar"
823	}
824
825	uri := fmt.Sprintf("/%s/build?%s", APIVersion, v.Encode())
826	return client.doStreamRequest("POST", uri, image.Context, headers)
827}
828
829func (client *DockerClient) ListVolumes() ([]*Volume, error) {
830	uri := fmt.Sprintf("/%s/volumes", APIVersion)
831	data, err := client.doRequest("GET", uri, nil, nil)
832	if err != nil {
833		return nil, err
834	}
835	var volumesList VolumesListResponse
836	if err := json.Unmarshal(data, &volumesList); err != nil {
837		return nil, err
838	}
839	return volumesList.Volumes, nil
840}
841
842func (client *DockerClient) RemoveVolume(name string) error {
843	uri := fmt.Sprintf("/%s/volumes/%s", APIVersion, name)
844	_, err := client.doRequest("DELETE", uri, nil, nil)
845	return err
846}
847
848func (client *DockerClient) CreateVolume(request *VolumeCreateRequest) (*Volume, error) {
849	data, err := json.Marshal(request)
850	if err != nil {
851		return nil, err
852	}
853	uri := fmt.Sprintf("/%s/volumes/create", APIVersion)
854	data, err = client.doRequest("POST", uri, data, nil)
855	if err != nil {
856		return nil, err
857	}
858	volume := &Volume{}
859	err = json.Unmarshal(data, volume)
860	return volume, err
861}
862
863func (client *DockerClient) ListNetworks(filters string) ([]*NetworkResource, error) {
864	uri := fmt.Sprintf("/%s/networks", APIVersion)
865
866	if filters != "" {
867		uri += "&filters=" + filters
868	}
869
870	data, err := client.doRequest("GET", uri, nil, nil)
871	if err != nil {
872		return nil, err
873	}
874	ret := []*NetworkResource{}
875	err = json.Unmarshal(data, &ret)
876	if err != nil {
877		return nil, err
878	}
879	return ret, nil
880}
881
882func (client *DockerClient) InspectNetwork(id string) (*NetworkResource, error) {
883	uri := fmt.Sprintf("/%s/networks/%s", APIVersion, id)
884
885	data, err := client.doRequest("GET", uri, nil, nil)
886	if err != nil {
887		return nil, err
888	}
889	ret := &NetworkResource{}
890	err = json.Unmarshal(data, ret)
891	if err != nil {
892		return nil, err
893	}
894
895	return ret, nil
896}
897
898func (client *DockerClient) CreateNetwork(config *NetworkCreate) (*NetworkCreateResponse, error) {
899	data, err := json.Marshal(config)
900	if err != nil {
901		return nil, err
902	}
903	uri := fmt.Sprintf("/%s/networks/create", APIVersion)
904	data, err = client.doRequest("POST", uri, data, nil)
905	if err != nil {
906		return nil, err
907	}
908	ret := &NetworkCreateResponse{}
909	err = json.Unmarshal(data, ret)
910	return ret, nil
911}
912
913func (client *DockerClient) ConnectNetwork(id, container string) error {
914	data, err := json.Marshal(NetworkConnect{Container: container})
915	if err != nil {
916		return err
917	}
918	uri := fmt.Sprintf("/%s/networks/%s/connect", APIVersion, id)
919	_, err = client.doRequest("POST", uri, data, nil)
920	return err
921}
922
923func (client *DockerClient) DisconnectNetwork(id, container string) error {
924	data, err := json.Marshal(NetworkDisconnect{Container: container})
925	if err != nil {
926		return err
927	}
928	uri := fmt.Sprintf("/%s/networks/%s/disconnect", APIVersion, id)
929	_, err = client.doRequest("POST", uri, data, nil)
930	return err
931}
932
933func (client *DockerClient) RemoveNetwork(id string) error {
934	uri := fmt.Sprintf("/%s/networks/%s", APIVersion, id)
935	_, err := client.doRequest("DELETE", uri, nil, nil)
936	return err
937}
938