Codebase list golang-github-samalba-dockerclient / 7d6cc1b
Initial release Dmitry Smirnov 8 years ago
27 changed file(s) with 106 addition(s) and 2588 deletion(s). Raw diff Collapse all Expand all
+0
-202
LICENSE less more
0 Apache License
1 Version 2.0, January 2004
2 http://www.apache.org/licenses/
3
4 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
5
6 1. Definitions.
7
8 "License" shall mean the terms and conditions for use, reproduction,
9 and distribution as defined by Sections 1 through 9 of this document.
10
11 "Licensor" shall mean the copyright owner or entity authorized by
12 the copyright owner that is granting the License.
13
14 "Legal Entity" shall mean the union of the acting entity and all
15 other entities that control, are controlled by, or are under common
16 control with that entity. For the purposes of this definition,
17 "control" means (i) the power, direct or indirect, to cause the
18 direction or management of such entity, whether by contract or
19 otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 outstanding shares, or (iii) beneficial ownership of such entity.
21
22 "You" (or "Your") shall mean an individual or Legal Entity
23 exercising permissions granted by this License.
24
25 "Source" form shall mean the preferred form for making modifications,
26 including but not limited to software source code, documentation
27 source, and configuration files.
28
29 "Object" form shall mean any form resulting from mechanical
30 transformation or translation of a Source form, including but
31 not limited to compiled object code, generated documentation,
32 and conversions to other media types.
33
34 "Work" shall mean the work of authorship, whether in Source or
35 Object form, made available under the License, as indicated by a
36 copyright notice that is included in or attached to the work
37 (an example is provided in the Appendix below).
38
39 "Derivative Works" shall mean any work, whether in Source or Object
40 form, that is based on (or derived from) the Work and for which the
41 editorial revisions, annotations, elaborations, or other modifications
42 represent, as a whole, an original work of authorship. For the purposes
43 of this License, Derivative Works shall not include works that remain
44 separable from, or merely link (or bind by name) to the interfaces of,
45 the Work and Derivative Works thereof.
46
47 "Contribution" shall mean any work of authorship, including
48 the original version of the Work and any modifications or additions
49 to that Work or Derivative Works thereof, that is intentionally
50 submitted to Licensor for inclusion in the Work by the copyright owner
51 or by an individual or Legal Entity authorized to submit on behalf of
52 the copyright owner. For the purposes of this definition, "submitted"
53 means any form of electronic, verbal, or written communication sent
54 to the Licensor or its representatives, including but not limited to
55 communication on electronic mailing lists, source code control systems,
56 and issue tracking systems that are managed by, or on behalf of, the
57 Licensor for the purpose of discussing and improving the Work, but
58 excluding communication that is conspicuously marked or otherwise
59 designated in writing by the copyright owner as "Not a Contribution."
60
61 "Contributor" shall mean Licensor and any individual or Legal Entity
62 on behalf of whom a Contribution has been received by Licensor and
63 subsequently incorporated within the Work.
64
65 2. Grant of Copyright License. Subject to the terms and conditions of
66 this License, each Contributor hereby grants to You a perpetual,
67 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
68 copyright license to reproduce, prepare Derivative Works of,
69 publicly display, publicly perform, sublicense, and distribute the
70 Work and such Derivative Works in Source or Object form.
71
72 3. Grant of Patent License. Subject to the terms and conditions of
73 this License, each Contributor hereby grants to You a perpetual,
74 worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75 (except as stated in this section) patent license to make, have made,
76 use, offer to sell, sell, import, and otherwise transfer the Work,
77 where such license applies only to those patent claims licensable
78 by such Contributor that are necessarily infringed by their
79 Contribution(s) alone or by combination of their Contribution(s)
80 with the Work to which such Contribution(s) was submitted. If You
81 institute patent litigation against any entity (including a
82 cross-claim or counterclaim in a lawsuit) alleging that the Work
83 or a Contribution incorporated within the Work constitutes direct
84 or contributory patent infringement, then any patent licenses
85 granted to You under this License for that Work shall terminate
86 as of the date such litigation is filed.
87
88 4. Redistribution. You may reproduce and distribute copies of the
89 Work or Derivative Works thereof in any medium, with or without
90 modifications, and in Source or Object form, provided that You
91 meet the following conditions:
92
93 (a) You must give any other recipients of the Work or
94 Derivative Works a copy of this License; and
95
96 (b) You must cause any modified files to carry prominent notices
97 stating that You changed the files; and
98
99 (c) You must retain, in the Source form of any Derivative Works
100 that You distribute, all copyright, patent, trademark, and
101 attribution notices from the Source form of the Work,
102 excluding those notices that do not pertain to any part of
103 the Derivative Works; and
104
105 (d) If the Work includes a "NOTICE" text file as part of its
106 distribution, then any Derivative Works that You distribute must
107 include a readable copy of the attribution notices contained
108 within such NOTICE file, excluding those notices that do not
109 pertain to any part of the Derivative Works, in at least one
110 of the following places: within a NOTICE text file distributed
111 as part of the Derivative Works; within the Source form or
112 documentation, if provided along with the Derivative Works; or,
113 within a display generated by the Derivative Works, if and
114 wherever such third-party notices normally appear. The contents
115 of the NOTICE file are for informational purposes only and
116 do not modify the License. You may add Your own attribution
117 notices within Derivative Works that You distribute, alongside
118 or as an addendum to the NOTICE text from the Work, provided
119 that such additional attribution notices cannot be construed
120 as modifying the License.
121
122 You may add Your own copyright statement to Your modifications and
123 may provide additional or different license terms and conditions
124 for use, reproduction, or distribution of Your modifications, or
125 for any such Derivative Works as a whole, provided Your use,
126 reproduction, and distribution of the Work otherwise complies with
127 the conditions stated in this License.
128
129 5. Submission of Contributions. Unless You explicitly state otherwise,
130 any Contribution intentionally submitted for inclusion in the Work
131 by You to the Licensor shall be under the terms and conditions of
132 this License, without any additional terms or conditions.
133 Notwithstanding the above, nothing herein shall supersede or modify
134 the terms of any separate license agreement you may have executed
135 with Licensor regarding such Contributions.
136
137 6. Trademarks. This License does not grant permission to use the trade
138 names, trademarks, service marks, or product names of the Licensor,
139 except as required for reasonable and customary use in describing the
140 origin of the Work and reproducing the content of the NOTICE file.
141
142 7. Disclaimer of Warranty. Unless required by applicable law or
143 agreed to in writing, Licensor provides the Work (and each
144 Contributor provides its Contributions) on an "AS IS" BASIS,
145 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
146 implied, including, without limitation, any warranties or conditions
147 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
148 PARTICULAR PURPOSE. You are solely responsible for determining the
149 appropriateness of using or redistributing the Work and assume any
150 risks associated with Your exercise of permissions under this License.
151
152 8. Limitation of Liability. In no event and under no legal theory,
153 whether in tort (including negligence), contract, or otherwise,
154 unless required by applicable law (such as deliberate and grossly
155 negligent acts) or agreed to in writing, shall any Contributor be
156 liable to You for damages, including any direct, indirect, special,
157 incidental, or consequential damages of any character arising as a
158 result of this License or out of the use or inability to use the
159 Work (including but not limited to damages for loss of goodwill,
160 work stoppage, computer failure or malfunction, or any and all
161 other commercial damages or losses), even if such Contributor
162 has been advised of the possibility of such damages.
163
164 9. Accepting Warranty or Additional Liability. While redistributing
165 the Work or Derivative Works thereof, You may choose to offer,
166 and charge a fee for, acceptance of support, warranty, indemnity,
167 or other liability obligations and/or rights consistent with this
168 License. However, in accepting such obligations, You may act only
169 on Your own behalf and on Your sole responsibility, not on behalf
170 of any other Contributor, and only if You agree to indemnify,
171 defend, and hold each Contributor harmless for any liability
172 incurred by, or claims asserted against, such Contributor by reason
173 of your accepting any such warranty or additional liability.
174
175 END OF TERMS AND CONDITIONS
176
177 APPENDIX: How to apply the Apache License to your work.
178
179 To apply the Apache License to your work, attach the following
180 boilerplate notice, with the fields enclosed by brackets "{}"
181 replaced with your own identifying information. (Don't include
182 the brackets!) The text should be enclosed in the appropriate
183 comment syntax for the file format. We also recommend that a
184 file or class name and description of purpose be included on the
185 same "printed page" as the copyright notice for easier
186 identification within third-party archives.
187
188 Copyright 2014 Sam Alba <sam.alba@gmail.com>
189
190 Licensed under the Apache License, Version 2.0 (the "License");
191 you may not use this file except in compliance with the License.
192 You may obtain a copy of the License at
193
194 http://www.apache.org/licenses/LICENSE-2.0
195
196 Unless required by applicable law or agreed to in writing, software
197 distributed under the License is distributed on an "AS IS" BASIS,
198 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
199 See the License for the specific language governing permissions and
200 limitations under the License.
201
+0
-98
README.md less more
0 Docker client library in Go
1 ===========================
2 [![GoDoc](http://godoc.org/github.com/samalba/dockerclient?status.png)](http://godoc.org/github.com/samalba/dockerclient)
3
4 Well maintained docker client library.
5
6 # How to use it?
7
8 Here is an example showing how to use it:
9
10 ```go
11 package main
12
13 import (
14 "github.com/samalba/dockerclient"
15 "log"
16 "time"
17 "os"
18 )
19
20 // Callback used to listen to Docker's events
21 func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) {
22 log.Printf("Received event: %#v\n", *event)
23 }
24
25 func main() {
26 // Init the client
27 docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
28
29 // Get only running containers
30 containers, err := docker.ListContainers(false, false, "")
31 if err != nil {
32 log.Fatal(err)
33 }
34 for _, c := range containers {
35 log.Println(c.Id, c.Names)
36 }
37
38 // Inspect the first container returned
39 if len(containers) > 0 {
40 id := containers[0].Id
41 info, _ := docker.InspectContainer(id)
42 log.Println(info)
43 }
44
45 // Build a docker image
46 // some.tar contains the build context (Dockerfile any any files it needs to add/copy)
47 dockerBuildContext, err := os.Open("some.tar")
48 defer dockerBuildContext.Close()
49 buildImageConfig := &dockerclient.BuildImage{
50 Context: dockerBuildContext,
51 RepoName: "your_image_name",
52 SuppressOutput: false,
53 }
54 reader, err := docker.BuildImage(buildImageConfig)
55 if err != nil {
56 log.Fatal(err)
57 }
58
59 // Create a container
60 containerConfig := &dockerclient.ContainerConfig{
61 Image: "ubuntu:14.04",
62 Cmd: []string{"bash"},
63 AttachStdin: true,
64 Tty: true}
65 containerId, err := docker.CreateContainer(containerConfig, "foobar")
66 if err != nil {
67 log.Fatal(err)
68 }
69
70 // Start the container
71 hostConfig := &dockerclient.HostConfig{}
72 err = docker.StartContainer(containerId, hostConfig)
73 if err != nil {
74 log.Fatal(err)
75 }
76
77 // Stop the container (with 5 seconds timeout)
78 docker.StopContainer(containerId, 5)
79
80 // Listen to events
81 docker.StartMonitorEvents(eventCallback, nil)
82
83 // Hold the execution to look at the events coming
84 time.Sleep(3600 * time.Second)
85 }
86 ```
87
88 # Maintainers
89
90 List of people you can ping for feedback on Pull Requests or any questions.
91
92 - [Sam Alba](https://github.com/samalba)
93 - [Michael Crosby](https://github.com/crosbymichael)
94 - [Andrea Luzzardi](https://github.com/aluzzardi)
95 - [Victor Vieux](https://github.com/vieux)
96 - [Evan Hazlett](https://github.com/ehazlett)
97 - [Donald Huang](https://github.com/donhcd)
+0
-38
auth.go less more
0 package dockerclient
1
2 import (
3 "bytes"
4 "encoding/base64"
5 "encoding/json"
6 )
7
8 // AuthConfig hold parameters for authenticating with the docker registry
9 type AuthConfig struct {
10 Username string `json:"username,omitempty"`
11 Password string `json:"password,omitempty"`
12 Email string `json:"email,omitempty"`
13 }
14
15 // encode the auth configuration struct into base64 for the X-Registry-Auth header
16 func (c *AuthConfig) encode() (string, error) {
17 var buf bytes.Buffer
18 if err := json.NewEncoder(&buf).Encode(c); err != nil {
19 return "", err
20 }
21 return base64.URLEncoding.EncodeToString(buf.Bytes()), nil
22 }
23
24 // ConfigFile holds parameters for authenticating during a BuildImage request
25 type ConfigFile struct {
26 Configs map[string]AuthConfig `json:"configs,omitempty"`
27 rootPath string
28 }
29
30 // encode the configuration struct into base64 for the X-Registry-Config header
31 func (c *ConfigFile) encode() (string, error) {
32 var buf bytes.Buffer
33 if err := json.NewEncoder(&buf).Encode(c); err != nil {
34 return "", err
35 }
36 return base64.URLEncoding.EncodeToString(buf.Bytes()), nil
37 }
+0
-15
auth_test.go less more
0 package dockerclient
1
2 import (
3 "testing"
4 )
5
6 func TestAuthEncode(t *testing.T) {
7 a := AuthConfig{Username: "foo", Password: "password", Email: "bar@baz.com"}
8 expected := "eyJ1c2VybmFtZSI6ImZvbyIsInBhc3N3b3JkIjoicGFzc3dvcmQiLCJlbWFpbCI6ImJhckBiYXouY29tIn0K"
9 got, _ := a.encode()
10
11 if expected != got {
12 t.Errorf("testAuthEncode failed. Expected [%s] got [%s]", expected, got)
13 }
14 }
0 golang-github-samalba-dockerclient (0.0~git20150905.0.77b723e-1) unstable; urgency=medium
1
2 * Initial release (Closes: TODO)
3
4 -- Dmitry Smirnov <onlyjob@debian.org> Tue, 08 Sep 2015 07:20:24 +1000
0 Source: golang-github-samalba-dockerclient
1 Section: devel
2 Priority: extra
3 Maintainer: pkg-go <pkg-go-maintainers@lists.alioth.debian.org>
4 Uploaders: Dmitry Smirnov <onlyjob@debian.org>
5 Build-Depends: debhelper (>= 9),
6 dh-golang,
7 golang-go,
8 golang-github-docker-docker-dev | golang-docker-dev,
9 golang-github-gorilla-mux-dev,
10 golang-github-stretchr-testify-dev | golang-testify-dev
11 Standards-Version: 3.9.6
12 Homepage: https://github.com/samalba/dockerclient
13 Vcs-Browser: https://anonscm.debian.org/cgit/pkg-go/packages/golang-github-samalba-dockerclient.git
14 Vcs-Git: git://anonscm.debian.org/pkg-go/packages/golang-github-samalba-dockerclient.git
15
16 Package: golang-github-samalba-dockerclient-dev
17 Architecture: all
18 Depends: ${shlibs:Depends},
19 ${misc:Depends},
20 golang-go,
21 golang-github-docker-docker-dev | golang-docker-dev,
22 golang-github-gorilla-mux-dev,
23 golang-github-stretchr-testify-dev | golang-testify-dev
24 Built-Using: ${misc:Built-Using}
25 Description: Docker client library in Go
26 Well maintained docker client library.
0 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
1 Upstream-Name: dockerclient
2 Source: https://github.com/samalba/dockerclient
3
4 Files: *
5 Copyright: 2014 Sam Alba <sam.alba@gmail.com>
6 License: Apache-2.0
7
8 Files: debian/*
9 Copyright: 2015 Dmitry Smirnov <onlyjob@debian.org>
10 License: GPL-3+
11
12 Files: debian/patches/*
13 Copyright: 2015 Dmitry Smirnov <onlyjob@debian.org>
14 License: GPL-3+ or Apache-2.0
15 Comment: patches can be licensed under the same terms as upstream.
16
17 License: Apache-2.0
18 Licensed under the Apache License, Version 2.0 (the "License");
19 you may not use this file except in compliance with the License.
20 You may obtain a copy of the License at
21 .
22 http://www.apache.org/licenses/LICENSE-2.0
23 .
24 Unless required by applicable law or agreed to in writing, software
25 distributed under the License is distributed on an "AS IS" BASIS,
26 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27 See the License for the specific language governing permissions and
28 limitations under the License.
29 .
30 On Debian systems, the complete text of the Apache version 2.0 license
31 can be found in "/usr/share/common-licenses/Apache-2.0".
32
33 License: GPL-3+
34 This program is free software: you can redistribute it and/or modify
35 it under the terms of the GNU General Public License as published by
36 the Free Software Foundation, either version 3 of the License, or
37 (at your option) any later version.
38 ․
39 This program is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 GNU General Public License for more details.
43 ․
44 The complete text of the GNU General Public License version 3
45 can be found in "/usr/share/common-licenses/GPL-3".
0 README*
0 examples/*
0 [git-dch]
1 id-length = 0
2
3 [import-orig]
4 pristine-tar = True
5 merge = False
(New empty file)
0 #!/usr/bin/make -f
1
2 # Uncomment this to turn on verbose mode.
3 #export DH_VERBOSE=1
4
5 export DH_GOPKG := github.com/samalba/dockerclient
6 export DH_GOLANG_EXCLUDES := examples
7
8 %:
9 dh $@ --buildsystem=golang --with=golang
10
11 override_dh_auto_test:
12 -dh_auto_test
0 3.0 (quilt)
0 # uscan(1) configuration file.
1 version=3
2
3 https://github.com/samalba/dockerclient/releases \
4 .*/archive/v?(\d[\d\.]+)\.tar\.gz
+0
-759
dockerclient.go less more
0 package dockerclient
1
2 import (
3 "bytes"
4 "crypto/tls"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "net/http"
11 "net/url"
12 "strconv"
13 "strings"
14 "sync/atomic"
15 "time"
16 )
17
18 const (
19 APIVersion = "v1.15"
20 )
21
22 var (
23 ErrNotFound = errors.New("Not found")
24
25 defaultTimeout = 30 * time.Second
26 )
27
28 type DockerClient struct {
29 URL *url.URL
30 HTTPClient *http.Client
31 TLSConfig *tls.Config
32 monitorStats int32
33 eventStopChan chan (struct{})
34 }
35
36 type Error struct {
37 StatusCode int
38 Status string
39 msg string
40 }
41
42 func (e Error) Error() string {
43 return fmt.Sprintf("%s: %s", e.Status, e.msg)
44 }
45
46 func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
47 return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout))
48 }
49
50 func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) {
51 u, err := url.Parse(daemonUrl)
52 if err != nil {
53 return nil, err
54 }
55 if u.Scheme == "" || u.Scheme == "tcp" {
56 if tlsConfig == nil {
57 u.Scheme = "http"
58 } else {
59 u.Scheme = "https"
60 }
61 }
62 httpClient := newHTTPClient(u, tlsConfig, timeout)
63 return &DockerClient{u, httpClient, tlsConfig, 0, nil}, nil
64 }
65
66 func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
67 b := bytes.NewBuffer(body)
68
69 reader, err := client.doStreamRequest(method, path, b, headers)
70 if err != nil {
71 return nil, err
72 }
73
74 defer reader.Close()
75 data, err := ioutil.ReadAll(reader)
76 if err != nil {
77 return nil, err
78 }
79 return data, nil
80 }
81
82 func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) {
83 if (method == "POST" || method == "PUT") && in == nil {
84 in = bytes.NewReader(nil)
85 }
86 req, err := http.NewRequest(method, client.URL.String()+path, in)
87 if err != nil {
88 return nil, err
89 }
90 req.Header.Add("Content-Type", "application/json")
91 if headers != nil {
92 for header, value := range headers {
93 req.Header.Add(header, value)
94 }
95 }
96 resp, err := client.HTTPClient.Do(req)
97 if err != nil {
98 if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
99 return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
100 }
101 return nil, err
102 }
103 if resp.StatusCode == 404 {
104 return nil, ErrNotFound
105 }
106 if resp.StatusCode >= 400 {
107 defer resp.Body.Close()
108 data, err := ioutil.ReadAll(resp.Body)
109 if err != nil {
110 return nil, err
111 }
112 return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
113 }
114
115 return resp.Body, nil
116 }
117
118 func (client *DockerClient) Info() (*Info, error) {
119 uri := fmt.Sprintf("/%s/info", APIVersion)
120 data, err := client.doRequest("GET", uri, nil, nil)
121 if err != nil {
122 return nil, err
123 }
124 ret := &Info{}
125 err = json.Unmarshal(data, &ret)
126 if err != nil {
127 return nil, err
128 }
129 return ret, nil
130 }
131
132 func (client *DockerClient) ListContainers(all bool, size bool, filters string) ([]Container, error) {
133 argAll := 0
134 if all == true {
135 argAll = 1
136 }
137 showSize := 0
138 if size == true {
139 showSize = 1
140 }
141 uri := fmt.Sprintf("/%s/containers/json?all=%d&size=%d", APIVersion, argAll, showSize)
142
143 if filters != "" {
144 uri += "&filters=" + filters
145 }
146
147 data, err := client.doRequest("GET", uri, nil, nil)
148 if err != nil {
149 return nil, err
150 }
151 ret := []Container{}
152 err = json.Unmarshal(data, &ret)
153 if err != nil {
154 return nil, err
155 }
156 return ret, nil
157 }
158
159 func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
160 uri := fmt.Sprintf("/%s/containers/%s/json", APIVersion, id)
161 data, err := client.doRequest("GET", uri, nil, nil)
162 if err != nil {
163 return nil, err
164 }
165 info := &ContainerInfo{}
166 err = json.Unmarshal(data, info)
167 if err != nil {
168 return nil, err
169 }
170 return info, nil
171 }
172
173 func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) {
174 data, err := json.Marshal(config)
175 if err != nil {
176 return "", err
177 }
178 uri := fmt.Sprintf("/%s/containers/create", APIVersion)
179 if name != "" {
180 v := url.Values{}
181 v.Set("name", name)
182 uri = fmt.Sprintf("%s?%s", uri, v.Encode())
183 }
184 data, err = client.doRequest("POST", uri, data, nil)
185 if err != nil {
186 return "", err
187 }
188 result := &RespContainersCreate{}
189 err = json.Unmarshal(data, result)
190 if err != nil {
191 return "", err
192 }
193 return result.Id, nil
194 }
195
196 func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) {
197 v := url.Values{}
198 v.Add("follow", strconv.FormatBool(options.Follow))
199 v.Add("stdout", strconv.FormatBool(options.Stdout))
200 v.Add("stderr", strconv.FormatBool(options.Stderr))
201 v.Add("timestamps", strconv.FormatBool(options.Timestamps))
202 if options.Tail > 0 {
203 v.Add("tail", strconv.FormatInt(options.Tail, 10))
204 }
205
206 uri := fmt.Sprintf("/%s/containers/%s/logs?%s", APIVersion, id, v.Encode())
207 req, err := http.NewRequest("GET", client.URL.String()+uri, nil)
208 if err != nil {
209 return nil, err
210 }
211 req.Header.Add("Content-Type", "application/json")
212 resp, err := client.HTTPClient.Do(req)
213 if err != nil {
214 return nil, err
215 }
216 return resp.Body, nil
217 }
218
219 func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) {
220 uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id)
221 data, err := client.doRequest("GET", uri, nil, nil)
222 if err != nil {
223 return nil, err
224 }
225 changes := []*ContainerChanges{}
226 err = json.Unmarshal(data, &changes)
227 if err != nil {
228 return nil, err
229 }
230 return changes, nil
231 }
232
233 func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult {
234 resultChan := make(chan decodingResult)
235
236 go func() {
237 decodeChan := make(chan decodingResult)
238
239 go func() {
240 decoder := json.NewDecoder(stream)
241 for {
242 decodeResult := decode(decoder)
243 decodeChan <- decodeResult
244 if decodeResult.err != nil {
245 close(decodeChan)
246 return
247 }
248 }
249 }()
250
251 defer close(resultChan)
252
253 for {
254 select {
255 case <-stopChan:
256 stream.Close()
257 for range decodeChan {
258 }
259 return
260 case decodeResult := <-decodeChan:
261 resultChan <- decodeResult
262 if decodeResult.err != nil {
263 stream.Close()
264 return
265 }
266 }
267 }
268
269 }()
270
271 return resultChan
272 }
273
274 func (client *DockerClient) ExecCreate(config *ExecConfig) (string, error) {
275 data, err := json.Marshal(config)
276 if err != nil {
277 return "", err
278 }
279 uri := fmt.Sprintf("/%s/containers/%s/exec", APIVersion, config.Container)
280 resp, err := client.doRequest("POST", uri, data, nil)
281 if err != nil {
282 return "", err
283 }
284 var createExecResp struct {
285 Id string
286 }
287 if err = json.Unmarshal(resp, &createExecResp); err != nil {
288 return "", err
289 }
290 return createExecResp.Id, nil
291 }
292
293 func (client *DockerClient) ExecStart(id string, config *ExecConfig) error {
294 data, err := json.Marshal(config)
295 if err != nil {
296 return err
297 }
298
299 uri := fmt.Sprintf("/%s/exec/%s/start", APIVersion, id)
300 if _, err := client.doRequest("POST", uri, data, nil); err != nil {
301 return err
302 }
303
304 return nil
305 }
306
307 func (client *DockerClient) ExecResize(id string, width, height int) error {
308 v := url.Values{}
309
310 w := strconv.Itoa(width)
311 h := strconv.Itoa(height)
312
313 v.Set("w", w)
314 v.Set("h", h)
315
316 uri := fmt.Sprintf("/%s/exec/%s/resize?%s", APIVersion, id, v.Encode())
317 if _, err := client.doRequest("POST", client.URL.String()+uri, nil, nil); err != nil {
318 return err
319 }
320
321 return nil
322 }
323
324 func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
325 data, err := json.Marshal(config)
326 if err != nil {
327 return err
328 }
329 uri := fmt.Sprintf("/%s/containers/%s/start", APIVersion, id)
330 _, err = client.doRequest("POST", uri, data, nil)
331 if err != nil {
332 return err
333 }
334 return nil
335 }
336
337 func (client *DockerClient) StopContainer(id string, timeout int) error {
338 uri := fmt.Sprintf("/%s/containers/%s/stop?t=%d", APIVersion, id, timeout)
339 _, err := client.doRequest("POST", uri, nil, nil)
340 if err != nil {
341 return err
342 }
343 return nil
344 }
345
346 func (client *DockerClient) RestartContainer(id string, timeout int) error {
347 uri := fmt.Sprintf("/%s/containers/%s/restart?t=%d", APIVersion, id, timeout)
348 _, err := client.doRequest("POST", uri, nil, nil)
349 if err != nil {
350 return err
351 }
352 return nil
353 }
354
355 func (client *DockerClient) KillContainer(id, signal string) error {
356 uri := fmt.Sprintf("/%s/containers/%s/kill?signal=%s", APIVersion, id, signal)
357 _, err := client.doRequest("POST", uri, nil, nil)
358 if err != nil {
359 return err
360 }
361 return nil
362 }
363
364 func (client *DockerClient) Wait(id string) <-chan WaitResult {
365 ch := make(chan WaitResult)
366 uri := fmt.Sprintf("/%s/containers/%s/wait", APIVersion, id)
367
368 go func() {
369 data, err := client.doRequest("POST", uri, nil, nil)
370 if err != nil {
371 ch <- WaitResult{ExitCode: -1, Error: err}
372 return
373 }
374
375 var result struct {
376 StatusCode int `json:"StatusCode"`
377 }
378 err = json.Unmarshal(data, &result)
379 ch <- WaitResult{ExitCode: result.StatusCode, Error: err}
380 }()
381 return ch
382 }
383
384 func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) {
385 v := url.Values{}
386 if options != nil {
387 if options.Since != 0 {
388 v.Add("since", strconv.Itoa(options.Since))
389 }
390 if options.Until != 0 {
391 v.Add("until", strconv.Itoa(options.Until))
392 }
393 if options.Filters != nil {
394 filterMap := make(map[string][]string)
395 if len(options.Filters.Event) > 0 {
396 filterMap["event"] = []string{options.Filters.Event}
397 }
398 if len(options.Filters.Image) > 0 {
399 filterMap["image"] = []string{options.Filters.Image}
400 }
401 if len(options.Filters.Container) > 0 {
402 filterMap["container"] = []string{options.Filters.Container}
403 }
404 if len(filterMap) > 0 {
405 filterJSONBytes, err := json.Marshal(filterMap)
406 if err != nil {
407 return nil, err
408 }
409 v.Add("filters", string(filterJSONBytes))
410 }
411 }
412 }
413 uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode())
414 resp, err := client.HTTPClient.Get(uri)
415 if err != nil {
416 return nil, err
417 }
418
419 decode := func(decoder *json.Decoder) decodingResult {
420 var event Event
421 if err := decoder.Decode(&event); err != nil {
422 return decodingResult{err: err}
423 } else {
424 return decodingResult{result: event}
425 }
426 }
427 decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan)
428 eventOrErrorChan := make(chan EventOrError)
429 go func() {
430 for decodingResult := range decodingResultChan {
431 event, _ := decodingResult.result.(Event)
432 eventOrErrorChan <- EventOrError{
433 Event: event,
434 Error: decodingResult.err,
435 }
436 }
437 close(eventOrErrorChan)
438 }()
439 return eventOrErrorChan, nil
440 }
441
442 func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) {
443 client.eventStopChan = make(chan struct{})
444
445 go func() {
446 eventErrChan, err := client.MonitorEvents(nil, client.eventStopChan)
447 if err != nil {
448 if ec != nil {
449 ec <- err
450 }
451 return
452 }
453
454 for e := range eventErrChan {
455 if e.Error != nil {
456 if ec != nil {
457 ec <- err
458 }
459 return
460 }
461 cb(&e.Event, ec, args...)
462 }
463 }()
464 }
465
466 func (client *DockerClient) StopAllMonitorEvents() {
467 close(client.eventStopChan)
468 }
469
470 func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
471 atomic.StoreInt32(&client.monitorStats, 1)
472 go client.getStats(id, cb, ec, args...)
473 }
474
475 func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
476 uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id)
477 resp, err := client.HTTPClient.Get(uri)
478 if err != nil {
479 ec <- err
480 return
481 }
482 defer resp.Body.Close()
483
484 dec := json.NewDecoder(resp.Body)
485 for atomic.LoadInt32(&client.monitorStats) > 0 {
486 var stats *Stats
487 if err := dec.Decode(&stats); err != nil {
488 ec <- err
489 return
490 }
491 cb(id, stats, ec, args...)
492 }
493 }
494
495 func (client *DockerClient) StopAllMonitorStats() {
496 atomic.StoreInt32(&client.monitorStats, 0)
497 }
498
499 func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
500 v := url.Values{}
501 v.Set("repo", repo)
502 v.Set("tag", tag)
503 if force {
504 v.Set("force", "1")
505 }
506 uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode())
507 if _, err := client.doRequest("POST", uri, nil, nil); err != nil {
508 return err
509 }
510 return nil
511 }
512
513 func (client *DockerClient) Version() (*Version, error) {
514 uri := fmt.Sprintf("/%s/version", APIVersion)
515 data, err := client.doRequest("GET", uri, nil, nil)
516 if err != nil {
517 return nil, err
518 }
519 version := &Version{}
520 err = json.Unmarshal(data, version)
521 if err != nil {
522 return nil, err
523 }
524 return version, nil
525 }
526
527 func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
528 v := url.Values{}
529 v.Set("fromImage", name)
530 uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode())
531 req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
532 if auth != nil {
533 encoded_auth, err := auth.encode()
534 if err != nil {
535 return err
536 }
537 req.Header.Add("X-Registry-Auth", encoded_auth)
538 }
539 resp, err := client.HTTPClient.Do(req)
540 if err != nil {
541 return err
542 }
543
544 defer resp.Body.Close()
545 if resp.StatusCode == 404 {
546 return ErrNotFound
547 }
548 if resp.StatusCode >= 400 {
549 data, err := ioutil.ReadAll(resp.Body)
550 if err != nil {
551 return err
552 }
553 return fmt.Errorf("%s", string(data))
554 }
555
556 var finalObj map[string]interface{}
557 for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
558 }
559 if err != io.EOF {
560 return err
561 }
562 if err, ok := finalObj["error"]; ok {
563 return fmt.Errorf("%v", err)
564 }
565 return nil
566 }
567
568 func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) {
569 uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id)
570 data, err := client.doRequest("GET", uri, nil, nil)
571 if err != nil {
572 return nil, err
573 }
574 info := &ImageInfo{}
575 err = json.Unmarshal(data, info)
576 if err != nil {
577 return nil, err
578 }
579 return info, nil
580 }
581
582 func (client *DockerClient) LoadImage(reader io.Reader) error {
583 data, err := ioutil.ReadAll(reader)
584 if err != nil {
585 return err
586 }
587
588 uri := fmt.Sprintf("/%s/images/load", APIVersion)
589 _, err = client.doRequest("POST", uri, data, nil)
590 if err != nil {
591 return err
592 }
593 return nil
594 }
595
596 func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
597 argForce := 0
598 argVolumes := 0
599 if force == true {
600 argForce = 1
601 }
602 if volumes == true {
603 argVolumes = 1
604 }
605 args := fmt.Sprintf("force=%d&v=%d", argForce, argVolumes)
606 uri := fmt.Sprintf("/%s/containers/%s?%s", APIVersion, id, args)
607 _, err := client.doRequest("DELETE", uri, nil, nil)
608 return err
609 }
610
611 func (client *DockerClient) ListImages(all bool) ([]*Image, error) {
612 argAll := 0
613 if all {
614 argAll = 1
615 }
616 uri := fmt.Sprintf("/%s/images/json?all=%d", APIVersion, argAll)
617 data, err := client.doRequest("GET", uri, nil, nil)
618 if err != nil {
619 return nil, err
620 }
621 var images []*Image
622 if err := json.Unmarshal(data, &images); err != nil {
623 return nil, err
624 }
625 return images, nil
626 }
627
628 func (client *DockerClient) RemoveImage(name string, force bool) ([]*ImageDelete, error) {
629 argForce := 0
630 if force {
631 argForce = 1
632 }
633
634 args := fmt.Sprintf("force=%d", argForce)
635 uri := fmt.Sprintf("/%s/images/%s?%s", APIVersion, name, args)
636 data, err := client.doRequest("DELETE", uri, nil, nil)
637 if err != nil {
638 return nil, err
639 }
640 var imageDelete []*ImageDelete
641 if err := json.Unmarshal(data, &imageDelete); err != nil {
642 return nil, err
643 }
644 return imageDelete, nil
645 }
646
647 func (client *DockerClient) PauseContainer(id string) error {
648 uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id)
649 _, err := client.doRequest("POST", uri, nil, nil)
650 if err != nil {
651 return err
652 }
653 return nil
654 }
655 func (client *DockerClient) UnpauseContainer(id string) error {
656 uri := fmt.Sprintf("/%s/containers/%s/unpause", APIVersion, id)
657 _, err := client.doRequest("POST", uri, nil, nil)
658 if err != nil {
659 return err
660 }
661 return nil
662 }
663
664 func (client *DockerClient) RenameContainer(oldName string, newName string) error {
665 uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName)
666 _, err := client.doRequest("POST", uri, nil, nil)
667 return err
668 }
669
670 func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
671 var fromSrc string
672 v := &url.Values{}
673 if source == "" {
674 fromSrc = "-"
675 } else {
676 fromSrc = source
677 }
678
679 v.Set("fromSrc", fromSrc)
680 v.Set("repo", repository)
681 if tag != "" {
682 v.Set("tag", tag)
683 }
684
685 var in io.Reader
686 if fromSrc == "-" {
687 in = tar
688 }
689 return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil)
690 }
691
692 func (client *DockerClient) BuildImage(image *BuildImage) (io.ReadCloser, error) {
693 v := url.Values{}
694
695 if image.DockerfileName != "" {
696 v.Set("dockerfile", image.DockerfileName)
697 }
698 if image.RepoName != "" {
699 v.Set("t", image.RepoName)
700 }
701 if image.RemoteURL != "" {
702 v.Set("remote", image.RemoteURL)
703 }
704 if image.NoCache {
705 v.Set("nocache", "1")
706 }
707 if image.Pull {
708 v.Set("pull", "1")
709 }
710 if image.Remove {
711 v.Set("rm", "1")
712 } else {
713 v.Set("rm", "0")
714 }
715 if image.ForceRemove {
716 v.Set("forcerm", "1")
717 }
718 if image.SuppressOutput {
719 v.Set("q", "1")
720 }
721
722 v.Set("memory", strconv.FormatInt(image.Memory, 10))
723 v.Set("memswap", strconv.FormatInt(image.MemorySwap, 10))
724 v.Set("cpushares", strconv.FormatInt(image.CpuShares, 10))
725 v.Set("cpuperiod", strconv.FormatInt(image.CpuPeriod, 10))
726 v.Set("cpuquota", strconv.FormatInt(image.CpuQuota, 10))
727 v.Set("cpusetcpus", image.CpuSetCpus)
728 v.Set("cpusetmems", image.CpuSetMems)
729 v.Set("cgroupparent", image.CgroupParent)
730
731 headers := make(map[string]string)
732 if image.Config != nil {
733 encoded_config, err := image.Config.encode()
734 if err != nil {
735 return nil, err
736 }
737 headers["X-Registry-Config"] = encoded_config
738 }
739 if image.Context != nil {
740 headers["Content-Type"] = "application/tar"
741 }
742
743 uri := fmt.Sprintf("/%s/build?%s", APIVersion, v.Encode())
744 return client.doStreamRequest("POST", uri, image.Context, headers)
745 }
746
747 func (client *DockerClient) ListVolumes() ([]*Volume, error) {
748 uri := fmt.Sprintf("/%s/volumes", APIVersion)
749 data, err := client.doRequest("GET", uri, nil, nil)
750 if err != nil {
751 return nil, err
752 }
753 var volumesList VolumesListResponse
754 if err := json.Unmarshal(data, &volumesList); err != nil {
755 return nil, err
756 }
757 return volumesList.Volumes, nil
758 }
+0
-240
dockerclient_test.go less more
0 package dockerclient
1
2 import (
3 "bytes"
4 "encoding/json"
5 "fmt"
6 "io"
7 "reflect"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/docker/docker/pkg/stdcopy"
13 )
14
15 func assertEqual(t *testing.T, a interface{}, b interface{}, message string) {
16 if a == b {
17 return
18 }
19 if len(message) == 0 {
20 message = fmt.Sprintf("%v != %v", a, b)
21 }
22 t.Fatal(message)
23 }
24
25 func testDockerClient(t *testing.T) *DockerClient {
26 client, err := NewDockerClient(testHTTPServer.URL, nil)
27 if err != nil {
28 t.Fatal("Cannot init the docker client")
29 }
30 return client
31 }
32
33 func TestInfo(t *testing.T) {
34 client := testDockerClient(t)
35 info, err := client.Info()
36 if err != nil {
37 t.Fatal("Cannot get server info")
38 }
39 assertEqual(t, info.Images, int64(1), "")
40 assertEqual(t, info.Containers, int64(2), "")
41 }
42
43 func TestKillContainer(t *testing.T) {
44 client := testDockerClient(t)
45 if err := client.KillContainer("23132acf2ac", "5"); err != nil {
46 t.Fatal("cannot kill container: %s", err)
47 }
48 }
49
50 func TestWait(t *testing.T) {
51 client := testDockerClient(t)
52
53 // This provokes an error on the server.
54 select {
55 case wr := <-client.Wait("1234"):
56 assertEqual(t, wr.ExitCode, int(-1), "")
57 case <-time.After(2 * time.Second):
58 t.Fatal("Timed out!")
59 }
60
61 // Valid case.
62 select {
63 case wr := <-client.Wait("valid-id"):
64 assertEqual(t, wr.ExitCode, int(0), "")
65 case <-time.After(2 * time.Second):
66 t.Fatal("Timed out!")
67 }
68 }
69
70 func TestPullImage(t *testing.T) {
71 client := testDockerClient(t)
72 err := client.PullImage("busybox", nil)
73 if err != nil {
74 t.Fatal("unable to pull busybox")
75 }
76
77 err = client.PullImage("haproxy", nil)
78 if err != nil {
79 t.Fatal("unable to pull haproxy")
80 }
81
82 err = client.PullImage("wrongimg", nil)
83 if err == nil {
84 t.Fatal("should return error when it fails to pull wrongimg")
85 }
86 }
87
88 func TestListContainers(t *testing.T) {
89 client := testDockerClient(t)
90 containers, err := client.ListContainers(true, false, "")
91 if err != nil {
92 t.Fatal("cannot get containers: %s", err)
93 }
94 assertEqual(t, len(containers), 1, "")
95 cnt := containers[0]
96 assertEqual(t, cnt.SizeRw, int64(0), "")
97 }
98
99 func TestContainerChanges(t *testing.T) {
100 client := testDockerClient(t)
101 changes, err := client.ContainerChanges("foobar")
102 if err != nil {
103 t.Fatal("cannot get container changes: %s", err)
104 }
105 assertEqual(t, len(changes), 3, "unexpected number of changes")
106 c := changes[0]
107 assertEqual(t, c.Path, "/dev", "unexpected")
108 assertEqual(t, c.Kind, 0, "unexpected")
109 }
110
111 func TestListContainersWithSize(t *testing.T) {
112 client := testDockerClient(t)
113 containers, err := client.ListContainers(true, true, "")
114 if err != nil {
115 t.Fatal("cannot get containers: %s", err)
116 }
117 assertEqual(t, len(containers), 1, "")
118 cnt := containers[0]
119 assertEqual(t, cnt.SizeRw, int64(123), "")
120 }
121 func TestListContainersWithFilters(t *testing.T) {
122 client := testDockerClient(t)
123 containers, err := client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}")
124 if err != nil {
125 t.Fatal("cannot get containers: %s", err)
126 }
127 assertEqual(t, len(containers), 1, "")
128
129 containers, err = client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688cf']}")
130 if err != nil {
131 t.Fatal("cannot get containers: %s", err)
132 }
133 assertEqual(t, len(containers), 0, "")
134 }
135
136 func TestContainerLogs(t *testing.T) {
137 client := testDockerClient(t)
138 containerId := "foobar"
139 logOptions := &LogOptions{
140 Follow: true,
141 Stdout: true,
142 Stderr: true,
143 Timestamps: true,
144 Tail: 10,
145 }
146 logsReader, err := client.ContainerLogs(containerId, logOptions)
147 if err != nil {
148 t.Fatal("cannot read logs from server")
149 }
150
151 stdoutBuffer := new(bytes.Buffer)
152 stderrBuffer := new(bytes.Buffer)
153 if _, err = stdcopy.StdCopy(stdoutBuffer, stderrBuffer, logsReader); err != nil {
154 t.Fatal("cannot read logs from logs reader")
155 }
156 stdoutLogs := strings.TrimSpace(stdoutBuffer.String())
157 stderrLogs := strings.TrimSpace(stderrBuffer.String())
158 stdoutLogLines := strings.Split(stdoutLogs, "\n")
159 stderrLogLines := strings.Split(stderrLogs, "\n")
160 if len(stdoutLogLines) != 5 {
161 t.Fatalf("wrong number of stdout logs: len=%d", len(stdoutLogLines))
162 }
163 if len(stderrLogLines) != 5 {
164 t.Fatalf("wrong number of stderr logs: len=%d", len(stdoutLogLines))
165 }
166 for i, line := range stdoutLogLines {
167 expectedSuffix := fmt.Sprintf("Z line %d", 41+2*i)
168 if !strings.HasSuffix(line, expectedSuffix) {
169 t.Fatalf("expected stdout log line \"%s\" to end with \"%s\"", line, expectedSuffix)
170 }
171 }
172 for i, line := range stderrLogLines {
173 expectedSuffix := fmt.Sprintf("Z line %d", 40+2*i)
174 if !strings.HasSuffix(line, expectedSuffix) {
175 t.Fatalf("expected stderr log line \"%s\" to end with \"%s\"", line, expectedSuffix)
176 }
177 }
178 }
179
180 func TestMonitorEvents(t *testing.T) {
181 client := testDockerClient(t)
182 decoder := json.NewDecoder(bytes.NewBufferString(eventsResp))
183 var expectedEvents []Event
184 for {
185 var event Event
186 if err := decoder.Decode(&event); err != nil {
187 if err == io.EOF {
188 break
189 } else {
190 t.Fatalf("cannot parse expected resp: %s", err.Error())
191 }
192 } else {
193 expectedEvents = append(expectedEvents, event)
194 }
195 }
196
197 // test passing stop chan
198 stopChan := make(chan struct{})
199 eventInfoChan, err := client.MonitorEvents(nil, stopChan)
200 if err != nil {
201 t.Fatalf("cannot get events from server: %s", err.Error())
202 }
203
204 eventInfo := <-eventInfoChan
205 if eventInfo.Error != nil || eventInfo.Event != expectedEvents[0] {
206 t.Fatalf("got:\n%#v\nexpected:\n%#v", eventInfo, expectedEvents[0])
207 }
208 close(stopChan)
209 for i := 0; i < 3; i++ {
210 _, ok := <-eventInfoChan
211 if i == 2 && ok {
212 t.Fatalf("read more than 2 events successfully after closing stopChan")
213 }
214 }
215
216 // test when you don't pass stop chan
217 eventInfoChan, err = client.MonitorEvents(nil, nil)
218 if err != nil {
219 t.Fatalf("cannot get events from server: %s", err.Error())
220 }
221
222 for i, expectedEvent := range expectedEvents {
223 t.Logf("on iter %d\n", i)
224 eventInfo := <-eventInfoChan
225 if eventInfo.Error != nil || eventInfo.Event != expectedEvent {
226 t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", i, eventInfo, expectedEvent)
227 }
228 t.Logf("done with iter %d\n", i)
229 }
230 }
231
232 func TestDockerClientInterface(t *testing.T) {
233 iface := reflect.TypeOf((*Client)(nil)).Elem()
234 test := testDockerClient(t)
235
236 if !reflect.TypeOf(test).Implements(iface) {
237 t.Fatalf("DockerClient does not implement the Client interface")
238 }
239 }
+0
-245
engine_mock_test.go less more
0 package dockerclient
1
2 import (
3 "encoding/json"
4 "fmt"
5 "io"
6 "log"
7 "net/http"
8 "net/http/httptest"
9 "strconv"
10 "time"
11
12 "github.com/docker/docker/pkg/ioutils"
13 "github.com/docker/docker/pkg/jsonlog"
14 "github.com/docker/docker/pkg/stdcopy"
15 "github.com/docker/docker/pkg/timeutils"
16 "github.com/gorilla/mux"
17 )
18
19 var (
20 testHTTPServer *httptest.Server
21 )
22
23 func init() {
24 r := mux.NewRouter()
25 baseURL := "/" + APIVersion
26 r.HandleFunc(baseURL+"/info", handlerGetInfo).Methods("GET")
27 r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET")
28 r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET")
29 r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET")
30 r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST")
31 r.HandleFunc(baseURL+"/containers/{id}/wait", handleWait).Methods("POST")
32 r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST")
33 r.HandleFunc(baseURL+"/events", handleEvents).Methods("GET")
34 testHTTPServer = httptest.NewServer(handlerAccessLog(r))
35 }
36
37 func handlerAccessLog(handler http.Handler) http.Handler {
38 logHandler := func(w http.ResponseWriter, r *http.Request) {
39 log.Printf("%s \"%s %s\"", r.RemoteAddr, r.Method, r.URL)
40 handler.ServeHTTP(w, r)
41 }
42 return http.HandlerFunc(logHandler)
43 }
44
45 func handleContainerKill(w http.ResponseWriter, r *http.Request) {
46 fmt.Fprintf(w, "{%q:%q", "Id", "421373210afd132")
47 }
48
49 func handleWait(w http.ResponseWriter, r *http.Request) {
50 vars := mux.Vars(r)
51 if vars["id"] == "valid-id" {
52 fmt.Fprintf(w, `{"StatusCode":0}`)
53 } else {
54 http.Error(w, "failed", 500)
55 }
56 }
57
58 func handleImagePull(w http.ResponseWriter, r *http.Request) {
59 imageName := r.URL.Query()["fromImage"][0]
60 responses := []map[string]interface{}{{
61 "status": fmt.Sprintf("Pulling repository mydockerregistry/%s", imageName),
62 }}
63 switch imageName {
64 case "busybox":
65 responses = append(responses, map[string]interface{}{
66 "status": "Status: Image is up to date for mydockerregistry/busybox",
67 })
68 case "haproxy":
69 fmt.Fprintf(w, haproxyPullOutput)
70 return
71 default:
72 errorMsg := fmt.Sprintf("Error: image %s not found", imageName)
73 responses = append(responses, map[string]interface{}{
74 "errorDetail": map[string]interface{}{
75 "message": errorMsg,
76 },
77 "error": errorMsg,
78 })
79 }
80 for _, response := range responses {
81 json.NewEncoder(w).Encode(response)
82 }
83 }
84
85 func handleContainerLogs(w http.ResponseWriter, r *http.Request) {
86 var outStream, errStream io.Writer
87 outStream = ioutils.NewWriteFlusher(w)
88
89 // not sure how to test follow
90 if err := r.ParseForm(); err != nil {
91 http.Error(w, err.Error(), 500)
92 }
93 stdout, stderr := getBoolValue(r.Form.Get("stdout")), getBoolValue(r.Form.Get("stderr"))
94 if stderr {
95 errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
96 }
97 if stdout {
98 outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
99 }
100 var i int
101 if tail, err := strconv.Atoi(r.Form.Get("tail")); err == nil && tail > 0 {
102 i = 50 - tail
103 if i < 0 {
104 i = 0
105 }
106 }
107 for ; i < 50; i++ {
108 line := fmt.Sprintf("line %d", i)
109 if getBoolValue(r.Form.Get("timestamps")) {
110 l := &jsonlog.JSONLog{Log: line, Created: time.Now().UTC()}
111 line = fmt.Sprintf("%s %s", l.Created.Format(timeutils.RFC3339NanoFixed), line)
112 }
113 if i%2 == 0 && stderr {
114 fmt.Fprintln(errStream, line)
115 } else if i%2 == 1 && stdout {
116 fmt.Fprintln(outStream, line)
117 }
118 }
119 }
120
121 func handleContainerChanges(w http.ResponseWriter, r *http.Request) {
122 writeHeaders(w, 200, "changes")
123 body := `[
124 {
125 "Path": "/dev",
126 "Kind": 0
127 },
128 {
129 "Path": "/dev/kmsg",
130 "Kind": 1
131 },
132 {
133 "Path": "/test",
134 "Kind": 1
135 }
136 ]`
137 w.Write([]byte(body))
138 }
139
140 func getBoolValue(boolString string) bool {
141 switch boolString {
142 case "1":
143 return true
144 case "True":
145 return true
146 case "true":
147 return true
148 default:
149 return false
150 }
151 }
152
153 func writeHeaders(w http.ResponseWriter, code int, jobName string) {
154 h := w.Header()
155 h.Add("Content-Type", "application/json")
156 if jobName != "" {
157 h.Add("Job-Name", jobName)
158 }
159 w.WriteHeader(code)
160 }
161
162 func handlerGetInfo(w http.ResponseWriter, r *http.Request) {
163 writeHeaders(w, 200, "info")
164 body := `{
165 "Containers": 2,
166 "Debug": 1,
167 "Driver": "aufs",
168 "DriverStatus": [["Root Dir", "/mnt/sda1/var/lib/docker/aufs"],
169 ["Dirs", "0"]],
170 "ExecutionDriver": "native-0.2",
171 "IPv4Forwarding": 1,
172 "Images": 1,
173 "IndexServerAddress": "https://index.docker.io/v1/",
174 "InitPath": "/usr/local/bin/docker",
175 "InitSha1": "",
176 "KernelVersion": "3.16.4-tinycore64",
177 "MemoryLimit": 1,
178 "NEventsListener": 0,
179 "NFd": 10,
180 "NGoroutines": 11,
181 "OperatingSystem": "Boot2Docker 1.3.1 (TCL 5.4); master : a083df4 - Thu Jan 01 00:00:00 UTC 1970",
182 "SwapLimit": 1}`
183 w.Write([]byte(body))
184 }
185
186 func handlerGetContainers(w http.ResponseWriter, r *http.Request) {
187 writeHeaders(w, 200, "containers")
188 body := `[
189 {
190 "Status": "Up 39 seconds",
191 "Ports": [
192 {
193 "Type": "tcp",
194 "PublicPort": 49163,
195 "PrivatePort": 8080,
196 "IP": "0.0.0.0"
197 }
198 ],
199 "Names": [
200 "/trusting_heisenberg"
201 ],
202 "Image": "foo:latest",
203 "Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce",
204 "Created": 1415720105,
205 "Command": "/bin/go-run"
206 }
207 ]`
208 if v, ok := r.URL.Query()["size"]; ok {
209 if v[0] == "1" {
210 body = `[
211 {
212 "Status": "Up 39 seconds",
213 "Ports": [
214 {
215 "Type": "tcp",
216 "PublicPort": 49163,
217 "PrivatePort": 8080,
218 "IP": "0.0.0.0"
219 }
220 ],
221 "Names": [
222 "/trusting_heisenberg"
223 ],
224 "Image": "foo:latest",
225 "Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce",
226 "Created": 1415720105,
227 "SizeRootFs": 12345,
228 "SizeRW": 123,
229 "Command": "/bin/go-run"
230 }
231 ]`
232 }
233 }
234 if v, ok := r.URL.Query()["filters"]; ok {
235 if v[0] != "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}" {
236 body = "[]"
237 }
238 }
239 w.Write([]byte(body))
240 }
241
242 func handleEvents(w http.ResponseWriter, r *http.Request) {
243 w.Write([]byte(eventsResp))
244 }
+0
-13
example_responses.go less more
0 package dockerclient
1
2 var haproxyPullOutput = `{"status":"The image you are pulling has been verified","id":"haproxy:1"}
3 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.4"}
4 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"63a1b9929e14"}{"status":"Already exists","progressDetail":{},"id":"af43bf7d176e"}{"status":"Already exists","progressDetail":{},"id":"851aac2d69aa"}{"status":"Already exists","progressDetail":{},"id":"345053a92c95"}{"status":"Already exists","progressDetail":{},"id":"b41231d429c9"}{"status":"The image you are pulling has been verified","id":"haproxy:1.4.25"}
5 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"63a1b9929e14"}{"status":"Already exists","progressDetail":{},"id":"af43bf7d176e"}{"status":"Already exists","progressDetail":{},"id":"851aac2d69aa"}{"status":"Already exists","progressDetail":{},"id":"345053a92c95"}{"status":"Already exists","progressDetail":{},"id":"b41231d429c9"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5"}
6 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5.10"}
7 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"The image you are pulling has been verified","id":"haproxy:1.5.9"}
8 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"3d894e6f7e63"}{"status":"Already exists","progressDetail":{},"id":"4d949c40bc77"}{"status":"Already exists","progressDetail":{},"id":"55e031889365"}{"status":"Already exists","progressDetail":{},"id":"c7aa675e1876"}{"status":"The image you are pulling has been verified","id":"haproxy:latest"}
9 {"status":"Already exists","progressDetail":{},"id":"511136ea3c5a"}{"status":"Already exists","progressDetail":{},"id":"1aeada447715"}{"status":"Already exists","progressDetail":{},"id":"479215127fa7"}{"status":"Already exists","progressDetail":{},"id":"66301eb54a7d"}{"status":"Already exists","progressDetail":{},"id":"e3990b07573f"}{"status":"Already exists","progressDetail":{},"id":"ecb4b23ca7ce"}{"status":"Already exists","progressDetail":{},"id":"f453e940c177"}{"status":"Already exists","progressDetail":{},"id":"fc5ea1bc05ab"}{"status":"Already exists","progressDetail":{},"id":"380557f8f7b3"}{"status":"Status: Image is up to date for haproxy"}
10 `
11
12 var eventsResp = `{"status":"pull","id":"nginx:latest","time":1428620433}{"status":"create","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"start","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620433}{"status":"die","id":"9b818c3b8291708fdcecd7c4086b75c222cb503be10a93d9c11040886032a48b","from":"nginx:latest","time":1428620442}{"status":"create","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"start","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"die","id":"352d0b412aae5a5d2b14ae9d88be59dc276602d9edb9dcc33e138e475b3e4720","from":"52.11.96.81/foobar/ubuntu:latest","time":1428620444}{"status":"pull","id":"debian:latest","time":1428620453}{"status":"create","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"start","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"die","id":"668887b5729946546b3072655dc6da08f0e3210111b68b704eb842adfce53f6c","from":"debian:latest","time":1428620453}{"status":"create","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"start","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620458}{"status":"pause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620462}{"status":"unpause","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620466}{"status":"die","id":"eb4a19ec21ab29bbbffbf3ee2e2df9d99cb749780e1eff06a591cee5ba505180","from":"nginx:latest","time":1428620469}`
+0
-39
examples/events.go less more
0 package main
1
2 import (
3 "github.com/samalba/dockerclient"
4 "log"
5 "os"
6 "os/signal"
7 "syscall"
8 )
9
10 func eventCallback(e *dockerclient.Event, ec chan error, args ...interface{}) {
11 log.Println(e)
12 }
13
14 var (
15 client *dockerclient.DockerClient
16 )
17
18 func waitForInterrupt() {
19 sigChan := make(chan os.Signal, 1)
20 signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
21 for _ = range sigChan {
22 client.StopAllMonitorEvents()
23 os.Exit(0)
24 }
25 }
26
27 func main() {
28 docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil)
29 if err != nil {
30 log.Fatal(err)
31 }
32
33 client = docker
34
35 client.StartMonitorEvents(eventCallback, nil)
36
37 waitForInterrupt()
38 }
+0
-43
examples/stats/stats.go less more
0 package main
1
2 import (
3 "github.com/samalba/dockerclient"
4 "log"
5 "os"
6 "os/signal"
7 "syscall"
8 )
9
10 func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) {
11 log.Println(stat)
12 }
13
14 func waitForInterrupt() {
15 sigChan := make(chan os.Signal, 1)
16 signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
17 for _ = range sigChan {
18 os.Exit(0)
19 }
20 }
21
22 func main() {
23 docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil)
24 if err != nil {
25 log.Fatal(err)
26 }
27
28 containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}}
29 containerId, err := docker.CreateContainer(containerConfig, "")
30 if err != nil {
31 log.Fatal(err)
32 }
33
34 // Start the container
35 err = docker.StartContainer(containerId, nil)
36 if err != nil {
37 log.Fatal(err)
38 }
39 docker.StartMonitorStats(containerId, statCallback, nil)
40
41 waitForInterrupt()
42 }
+0
-49
interface.go less more
0 package dockerclient
1
2 import (
3 "io"
4 )
5
6 type Callback func(*Event, chan error, ...interface{})
7
8 type StatCallback func(string, *Stats, chan error, ...interface{})
9
10 type Client interface {
11 Info() (*Info, error)
12 ListContainers(all, size bool, filters string) ([]Container, error)
13 InspectContainer(id string) (*ContainerInfo, error)
14 InspectImage(id string) (*ImageInfo, error)
15 CreateContainer(config *ContainerConfig, name string) (string, error)
16 ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error)
17 ContainerChanges(id string) ([]*ContainerChanges, error)
18 ExecCreate(config *ExecConfig) (string, error)
19 ExecStart(id string, config *ExecConfig) error
20 ExecResize(id string, width, height int) error
21 StartContainer(id string, config *HostConfig) error
22 StopContainer(id string, timeout int) error
23 RestartContainer(id string, timeout int) error
24 KillContainer(id, signal string) error
25 Wait(id string) <-chan WaitResult
26 // MonitorEvents takes options and an optional stop channel, and returns
27 // an EventOrError channel. If an error is ever sent, then no more
28 // events will be sent. If a stop channel is provided, events will stop
29 // being monitored after the stop channel is closed.
30 MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error)
31 StartMonitorEvents(cb Callback, ec chan error, args ...interface{})
32 StopAllMonitorEvents()
33 StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{})
34 StopAllMonitorStats()
35 TagImage(nameOrID string, repo string, tag string, force bool) error
36 Version() (*Version, error)
37 PullImage(name string, auth *AuthConfig) error
38 LoadImage(reader io.Reader) error
39 RemoveContainer(id string, force, volumes bool) error
40 ListImages(all bool) ([]*Image, error)
41 RemoveImage(name string, force bool) ([]*ImageDelete, error)
42 PauseContainer(name string) error
43 UnpauseContainer(name string) error
44 RenameContainer(oldName string, newName string) error
45 ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error)
46 BuildImage(image *BuildImage) (io.ReadCloser, error)
47 ListVolumes() ([]*Volume, error)
48 }
+0
-177
mockclient/mock.go less more
0 package mockclient
1
2 import (
3 "io"
4
5 "github.com/samalba/dockerclient"
6 "github.com/stretchr/testify/mock"
7 )
8
9 type MockClient struct {
10 mock.Mock
11 }
12
13 func NewMockClient() *MockClient {
14 return &MockClient{}
15 }
16
17 func (client *MockClient) Info() (*dockerclient.Info, error) {
18 args := client.Mock.Called()
19 return args.Get(0).(*dockerclient.Info), args.Error(1)
20 }
21
22 func (client *MockClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) {
23 args := client.Mock.Called(all, size, filters)
24 return args.Get(0).([]dockerclient.Container), args.Error(1)
25 }
26
27 func (client *MockClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) {
28 args := client.Mock.Called(id)
29 return args.Get(0).(*dockerclient.ContainerInfo), args.Error(1)
30 }
31
32 func (client *MockClient) InspectImage(id string) (*dockerclient.ImageInfo, error) {
33 args := client.Mock.Called(id)
34 return args.Get(0).(*dockerclient.ImageInfo), args.Error(1)
35 }
36
37 func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) {
38 args := client.Mock.Called(config, name)
39 return args.String(0), args.Error(1)
40 }
41
42 func (client *MockClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) {
43 args := client.Mock.Called(id, options)
44 return args.Get(0).(io.ReadCloser), args.Error(1)
45 }
46
47 func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) {
48 args := client.Mock.Called(id)
49 return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1)
50 }
51
52 func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error {
53 args := client.Mock.Called(id, config)
54 return args.Error(0)
55 }
56
57 func (client *MockClient) StopContainer(id string, timeout int) error {
58 args := client.Mock.Called(id, timeout)
59 return args.Error(0)
60 }
61
62 func (client *MockClient) RestartContainer(id string, timeout int) error {
63 args := client.Mock.Called(id, timeout)
64 return args.Error(0)
65 }
66
67 func (client *MockClient) KillContainer(id, signal string) error {
68 args := client.Mock.Called(id, signal)
69 return args.Error(0)
70 }
71
72 func (client *MockClient) Wait(id string) <-chan dockerclient.WaitResult {
73 args := client.Mock.Called(id)
74 return args.Get(0).(<-chan dockerclient.WaitResult)
75 }
76
77 func (client *MockClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) {
78 args := client.Mock.Called(options, stopChan)
79 return args.Get(0).(<-chan dockerclient.EventOrError), args.Error(1)
80 }
81
82 func (client *MockClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) {
83 client.Mock.Called(cb, ec, args)
84 }
85
86 func (client *MockClient) StopAllMonitorEvents() {
87 client.Mock.Called()
88 }
89
90 func (client *MockClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
91 args := client.Mock.Called(nameOrID, repo, tag, force)
92 return args.Error(0)
93 }
94
95 func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) {
96 client.Mock.Called(id, cb, ec, args)
97 }
98
99 func (client *MockClient) StopAllMonitorStats() {
100 client.Mock.Called()
101 }
102
103 func (client *MockClient) Version() (*dockerclient.Version, error) {
104 args := client.Mock.Called()
105 return args.Get(0).(*dockerclient.Version), args.Error(1)
106 }
107
108 func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig) error {
109 args := client.Mock.Called(name, auth)
110 return args.Error(0)
111 }
112
113 func (client *MockClient) LoadImage(reader io.Reader) error {
114 args := client.Mock.Called(reader)
115 return args.Error(0)
116 }
117
118 func (client *MockClient) RemoveContainer(id string, force, volumes bool) error {
119 args := client.Mock.Called(id, force, volumes)
120 return args.Error(0)
121 }
122
123 func (client *MockClient) ListImages(all bool) ([]*dockerclient.Image, error) {
124 args := client.Mock.Called(all)
125 return args.Get(0).([]*dockerclient.Image), args.Error(1)
126 }
127
128 func (client *MockClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) {
129 args := client.Mock.Called(name, force)
130 return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1)
131 }
132
133 func (client *MockClient) PauseContainer(name string) error {
134 args := client.Mock.Called(name)
135 return args.Error(0)
136 }
137
138 func (client *MockClient) UnpauseContainer(name string) error {
139 args := client.Mock.Called(name)
140 return args.Error(0)
141 }
142
143 func (client *MockClient) ExecCreate(config *dockerclient.ExecConfig) (string, error) {
144 args := client.Mock.Called(config)
145 return args.String(0), args.Error(1)
146 }
147
148 func (client *MockClient) ExecStart(id string, config *dockerclient.ExecConfig) error {
149 args := client.Mock.Called(id, config)
150 return args.Error(0)
151 }
152
153 func (client *MockClient) ExecResize(id string, width, height int) error {
154 args := client.Mock.Called(id, width, height)
155 return args.Error(0)
156 }
157
158 func (client *MockClient) RenameContainer(oldName string, newName string) error {
159 args := client.Mock.Called(oldName, newName)
160 return args.Error(0)
161 }
162
163 func (client *MockClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
164 args := client.Mock.Called(source, repository, tag, tar)
165 return args.Get(0).(io.ReadCloser), args.Error(1)
166 }
167
168 func (client *MockClient) BuildImage(image *dockerclient.BuildImage) (io.ReadCloser, error) {
169 args := client.Mock.Called(image)
170 return args.Get(0).(io.ReadCloser), args.Error(1)
171 }
172
173 func (client *MockClient) ListVolumes() ([]*dockerclient.Volume, error) {
174 args := client.Mock.Called()
175 return args.Get(0).([]*dockerclient.Volume), args.Error(1)
176 }
+0
-32
mockclient/mock_test.go less more
0 package mockclient
1
2 import (
3 "reflect"
4 "testing"
5
6 "github.com/samalba/dockerclient"
7 )
8
9 func TestMock(t *testing.T) {
10 mock := NewMockClient()
11 mock.On("Version").Return(&dockerclient.Version{Version: "foo"}, nil).Once()
12
13 v, err := mock.Version()
14 if err != nil {
15 t.Fatal(err)
16 }
17 if v.Version != "foo" {
18 t.Fatal(v)
19 }
20
21 mock.Mock.AssertExpectations(t)
22 }
23
24 func TestMockInterface(t *testing.T) {
25 iface := reflect.TypeOf((*dockerclient.Client)(nil)).Elem()
26 mock := NewMockClient()
27
28 if !reflect.TypeOf(mock).Implements(iface) {
29 t.Fatalf("Mock does not implement the Client interface")
30 }
31 }
+0
-151
nopclient/nop.go less more
0 package nopclient
1
2 import (
3 "errors"
4 "io"
5
6 "github.com/samalba/dockerclient"
7 )
8
9 var (
10 ErrNoEngine = errors.New("Engine no longer exists")
11 )
12
13 type NopClient struct {
14 }
15
16 func NewNopClient() *NopClient {
17 return &NopClient{}
18 }
19
20 func (client *NopClient) Info() (*dockerclient.Info, error) {
21 return nil, ErrNoEngine
22 }
23
24 func (client *NopClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) {
25 return nil, ErrNoEngine
26 }
27
28 func (client *NopClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) {
29 return nil, ErrNoEngine
30 }
31
32 func (client *NopClient) InspectImage(id string) (*dockerclient.ImageInfo, error) {
33 return nil, ErrNoEngine
34 }
35
36 func (client *NopClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) {
37 return "", ErrNoEngine
38 }
39
40 func (client *NopClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) {
41 return nil, ErrNoEngine
42 }
43
44 func (client *NopClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) {
45 return nil, ErrNoEngine
46 }
47
48 func (client *NopClient) StartContainer(id string, config *dockerclient.HostConfig) error {
49 return ErrNoEngine
50 }
51
52 func (client *NopClient) StopContainer(id string, timeout int) error {
53 return ErrNoEngine
54 }
55
56 func (client *NopClient) RestartContainer(id string, timeout int) error {
57 return ErrNoEngine
58 }
59
60 func (client *NopClient) KillContainer(id, signal string) error {
61 return ErrNoEngine
62 }
63
64 func (client *NopClient) Wait(id string) <-chan dockerclient.WaitResult {
65 return nil
66 }
67
68 func (client *NopClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) {
69 return nil, ErrNoEngine
70 }
71
72 func (client *NopClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) {
73 return
74 }
75
76 func (client *NopClient) StopAllMonitorEvents() {
77 return
78 }
79
80 func (client *NopClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
81 return ErrNoEngine
82 }
83
84 func (client *NopClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) {
85 return
86 }
87
88 func (client *NopClient) StopAllMonitorStats() {
89 return
90 }
91
92 func (client *NopClient) Version() (*dockerclient.Version, error) {
93 return nil, ErrNoEngine
94 }
95
96 func (client *NopClient) PullImage(name string, auth *dockerclient.AuthConfig) error {
97 return ErrNoEngine
98 }
99
100 func (client *NopClient) LoadImage(reader io.Reader) error {
101 return ErrNoEngine
102 }
103
104 func (client *NopClient) RemoveContainer(id string, force, volumes bool) error {
105 return ErrNoEngine
106 }
107
108 func (client *NopClient) ListImages(all bool) ([]*dockerclient.Image, error) {
109 return nil, ErrNoEngine
110 }
111
112 func (client *NopClient) RemoveImage(name string, force bool) ([]*dockerclient.ImageDelete, error) {
113 return nil, ErrNoEngine
114 }
115
116 func (client *NopClient) PauseContainer(name string) error {
117 return ErrNoEngine
118 }
119
120 func (client *NopClient) UnpauseContainer(name string) error {
121 return ErrNoEngine
122 }
123
124 func (client *NopClient) ExecCreate(config *dockerclient.ExecConfig) (string, error) {
125 return "", ErrNoEngine
126 }
127
128 func (client *NopClient) ExecStart(id string, config *dockerclient.ExecConfig) error {
129 return ErrNoEngine
130 }
131
132 func (client *NopClient) ExecResize(id string, width, height int) error {
133 return ErrNoEngine
134 }
135
136 func (client *NopClient) RenameContainer(oldName string, newName string) error {
137 return ErrNoEngine
138 }
139
140 func (client *NopClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
141 return nil, ErrNoEngine
142 }
143
144 func (client *NopClient) BuildImage(image *dockerclient.BuildImage) (io.ReadCloser, error) {
145 return nil, ErrNoEngine
146 }
147
148 func (client *NopClient) ListVolumes() ([]*dockerclient.Volume, error) {
149 return nil, ErrNoEngine
150 }
+0
-454
types.go less more
0 package dockerclient
1
2 import (
3 "fmt"
4 "io"
5 "time"
6
7 "github.com/docker/docker/pkg/units"
8 )
9
10 type ContainerConfig struct {
11 Hostname string
12 Domainname string
13 User string
14 AttachStdin bool
15 AttachStdout bool
16 AttachStderr bool
17 ExposedPorts map[string]struct{}
18 Tty bool
19 OpenStdin bool
20 StdinOnce bool
21 Env []string
22 Cmd []string
23 Image string
24 Volumes map[string]struct{}
25 VolumeDriver string
26 WorkingDir string
27 Entrypoint []string
28 NetworkDisabled bool
29 MacAddress string
30 OnBuild []string
31 Labels map[string]string
32
33 // FIXME: The following fields have been removed since API v1.18
34 Memory int64
35 MemorySwap int64
36 CpuShares int64
37 Cpuset string
38 PortSpecs []string
39
40 // This is used only by the create command
41 HostConfig HostConfig
42 }
43
44 type HostConfig struct {
45 Binds []string
46 ContainerIDFile string
47 LxcConf []map[string]string
48 Memory int64
49 MemorySwap int64
50 CpuShares int64
51 CpuPeriod int64
52 CpusetCpus string
53 CpusetMems string
54 CpuQuota int64
55 BlkioWeight int64
56 OomKillDisable bool
57 Privileged bool
58 PortBindings map[string][]PortBinding
59 Links []string
60 PublishAllPorts bool
61 Dns []string
62 DnsSearch []string
63 ExtraHosts []string
64 VolumesFrom []string
65 Devices []DeviceMapping
66 NetworkMode string
67 IpcMode string
68 PidMode string
69 UTSMode string
70 CapAdd []string
71 CapDrop []string
72 RestartPolicy RestartPolicy
73 SecurityOpt []string
74 ReadonlyRootfs bool
75 Ulimits []Ulimit
76 LogConfig LogConfig
77 CgroupParent string
78 }
79
80 type DeviceMapping struct {
81 PathOnHost string `json:"PathOnHost"`
82 PathInContainer string `json:"PathInContainer"`
83 CgroupPermissions string `json:"CgroupPermissions"`
84 }
85
86 type ExecConfig struct {
87 AttachStdin bool
88 AttachStdout bool
89 AttachStderr bool
90 Tty bool
91 Cmd []string
92 Container string
93 Detach bool
94 }
95
96 type LogOptions struct {
97 Follow bool
98 Stdout bool
99 Stderr bool
100 Timestamps bool
101 Tail int64
102 }
103
104 type MonitorEventsFilters struct {
105 Event string `json:",omitempty"`
106 Image string `json:",omitempty"`
107 Container string `json:",omitempty"`
108 }
109
110 type MonitorEventsOptions struct {
111 Since int
112 Until int
113 Filters *MonitorEventsFilters `json:",omitempty"`
114 }
115
116 type RestartPolicy struct {
117 Name string
118 MaximumRetryCount int64
119 }
120
121 type PortBinding struct {
122 HostIp string
123 HostPort string
124 }
125
126 type State struct {
127 Running bool
128 Paused bool
129 Restarting bool
130 OOMKilled bool
131 Dead bool
132 Pid int
133 ExitCode int
134 Error string // contains last known error when starting the container
135 StartedAt time.Time
136 FinishedAt time.Time
137 Ghost bool
138 }
139
140 // String returns a human-readable description of the state
141 // Stoken from docker/docker/daemon/state.go
142 func (s *State) String() string {
143 if s.Running {
144 if s.Paused {
145 return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
146 }
147 if s.Restarting {
148 return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
149 }
150
151 return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
152 }
153
154 if s.Dead {
155 return "Dead"
156 }
157
158 if s.FinishedAt.IsZero() {
159 return ""
160 }
161
162 return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
163 }
164
165 // StateString returns a single string to describe state
166 // Stoken from docker/docker/daemon/state.go
167 func (s *State) StateString() string {
168 if s.Running {
169 if s.Paused {
170 return "paused"
171 }
172 if s.Restarting {
173 return "restarting"
174 }
175 return "running"
176 }
177
178 if s.Dead {
179 return "dead"
180 }
181
182 return "exited"
183 }
184
185 type ImageInfo struct {
186 Architecture string
187 Author string
188 Comment string
189 Config *ContainerConfig
190 Container string
191 ContainerConfig *ContainerConfig
192 Created time.Time
193 DockerVersion string
194 Id string
195 Os string
196 Parent string
197 Size int64
198 VirtualSize int64
199 }
200
201 type ContainerInfo struct {
202 Id string
203 Created string
204 Path string
205 Name string
206 Args []string
207 ExecIDs []string
208 Config *ContainerConfig
209 State *State
210 Image string
211 NetworkSettings struct {
212 IPAddress string `json:"IpAddress"`
213 IPPrefixLen int `json:"IpPrefixLen"`
214 Gateway string
215 Bridge string
216 Ports map[string][]PortBinding
217 }
218 SysInitPath string
219 ResolvConfPath string
220 Volumes map[string]string
221 HostConfig *HostConfig
222 }
223
224 type ContainerChanges struct {
225 Path string
226 Kind int
227 }
228
229 type Port struct {
230 IP string
231 PrivatePort int
232 PublicPort int
233 Type string
234 }
235
236 type Container struct {
237 Id string
238 Names []string
239 Image string
240 Command string
241 Created int64
242 Status string
243 Ports []Port
244 SizeRw int64
245 SizeRootFs int64
246 Labels map[string]string
247 }
248
249 type Event struct {
250 Id string
251 Status string
252 From string
253 Time int64
254 }
255
256 type Version struct {
257 ApiVersion string
258 Arch string
259 GitCommit string
260 GoVersion string
261 KernelVersion string
262 Os string
263 Version string
264 }
265
266 type RespContainersCreate struct {
267 Id string
268 Warnings []string
269 }
270
271 type Image struct {
272 Created int64
273 Id string
274 ParentId string
275 RepoTags []string
276 Size int64
277 VirtualSize int64
278 }
279
280 // Info is the struct returned by /info
281 // The API is currently in flux, so Debug, MemoryLimit, SwapLimit, and
282 // IPv4Forwarding are interfaces because in docker 1.6.1 they are 0 or 1 but in
283 // master they are bools.
284 type Info struct {
285 ID string
286 Containers int64
287 Driver string
288 DriverStatus [][]string
289 ExecutionDriver string
290 Images int64
291 KernelVersion string
292 OperatingSystem string
293 NCPU int64
294 MemTotal int64
295 Name string
296 Labels []string
297 Debug interface{}
298 NFd int64
299 NGoroutines int64
300 SystemTime string
301 NEventsListener int64
302 InitPath string
303 InitSha1 string
304 IndexServerAddress string
305 MemoryLimit interface{}
306 SwapLimit interface{}
307 IPv4Forwarding interface{}
308 BridgeNfIptables bool
309 BridgeNfIp6tables bool
310 DockerRootDir string
311 HttpProxy string
312 HttpsProxy string
313 NoProxy string
314 }
315
316 type ImageDelete struct {
317 Deleted string
318 Untagged string
319 }
320
321 type EventOrError struct {
322 Event
323 Error error
324 }
325
326 type WaitResult struct {
327 ExitCode int
328 Error error
329 }
330
331 type decodingResult struct {
332 result interface{}
333 err error
334 }
335
336 // The following are types for the API stats endpoint
337 type ThrottlingData struct {
338 // Number of periods with throttling active
339 Periods uint64 `json:"periods"`
340 // Number of periods when the container hit its throttling limit.
341 ThrottledPeriods uint64 `json:"throttled_periods"`
342 // Aggregate time the container was throttled for in nanoseconds.
343 ThrottledTime uint64 `json:"throttled_time"`
344 }
345
346 type CpuUsage struct {
347 // Total CPU time consumed.
348 // Units: nanoseconds.
349 TotalUsage uint64 `json:"total_usage"`
350 // Total CPU time consumed per core.
351 // Units: nanoseconds.
352 PercpuUsage []uint64 `json:"percpu_usage"`
353 // Time spent by tasks of the cgroup in kernel mode.
354 // Units: nanoseconds.
355 UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
356 // Time spent by tasks of the cgroup in user mode.
357 // Units: nanoseconds.
358 UsageInUsermode uint64 `json:"usage_in_usermode"`
359 }
360
361 type CpuStats struct {
362 CpuUsage CpuUsage `json:"cpu_usage"`
363 SystemUsage uint64 `json:"system_cpu_usage"`
364 ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
365 }
366
367 type NetworkStats struct {
368 RxBytes uint64 `json:"rx_bytes"`
369 RxPackets uint64 `json:"rx_packets"`
370 RxErrors uint64 `json:"rx_errors"`
371 RxDropped uint64 `json:"rx_dropped"`
372 TxBytes uint64 `json:"tx_bytes"`
373 TxPackets uint64 `json:"tx_packets"`
374 TxErrors uint64 `json:"tx_errors"`
375 TxDropped uint64 `json:"tx_dropped"`
376 }
377
378 type MemoryStats struct {
379 Usage uint64 `json:"usage"`
380 MaxUsage uint64 `json:"max_usage"`
381 Stats map[string]uint64 `json:"stats"`
382 Failcnt uint64 `json:"failcnt"`
383 Limit uint64 `json:"limit"`
384 }
385
386 type BlkioStatEntry struct {
387 Major uint64 `json:"major"`
388 Minor uint64 `json:"minor"`
389 Op string `json:"op"`
390 Value uint64 `json:"value"`
391 }
392
393 type BlkioStats struct {
394 // number of bytes tranferred to and from the block device
395 IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
396 IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
397 IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
398 IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
399 IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
400 IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
401 IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
402 SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
403 }
404
405 type Stats struct {
406 Read time.Time `json:"read"`
407 NetworkStats NetworkStats `json:"network,omitempty"`
408 CpuStats CpuStats `json:"cpu_stats,omitempty"`
409 MemoryStats MemoryStats `json:"memory_stats,omitempty"`
410 BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
411 }
412
413 type Ulimit struct {
414 Name string `json:"name"`
415 Soft uint64 `json:"soft"`
416 Hard uint64 `json:"hard"`
417 }
418
419 type LogConfig struct {
420 Type string `json:"type"`
421 Config map[string]string `json:"config"`
422 }
423
424 type BuildImage struct {
425 Config *ConfigFile
426 DockerfileName string
427 Context io.Reader
428 RemoteURL string
429 RepoName string
430 SuppressOutput bool
431 NoCache bool
432 Remove bool
433 ForceRemove bool
434 Pull bool
435 Memory int64
436 MemorySwap int64
437 CpuShares int64
438 CpuPeriod int64
439 CpuQuota int64
440 CpuSetCpus string
441 CpuSetMems string
442 CgroupParent string
443 }
444
445 type Volume struct {
446 Name string // Name is the name of the volume
447 Driver string // Driver is the Driver name used to create the volume
448 Mountpoint string // Mountpoint is the location on disk of the volume
449 }
450
451 type VolumesListResponse struct {
452 Volumes []*Volume // Volumes is the list of volumes being returned
453 }
+0
-33
utils.go less more
0 package dockerclient
1
2 import (
3 "crypto/tls"
4 "net"
5 "net/http"
6 "net/url"
7 "time"
8 )
9
10 func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {
11 httpTransport := &http.Transport{
12 TLSClientConfig: tlsConfig,
13 }
14
15 switch u.Scheme {
16 default:
17 httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
18 return net.DialTimeout(proto, addr, timeout)
19 }
20 case "unix":
21 socketPath := u.Path
22 unixDial := func(proto, addr string) (net.Conn, error) {
23 return net.DialTimeout("unix", socketPath, timeout)
24 }
25 httpTransport.Dial = unixDial
26 // Override the main URL object so the HTTP lib won't complain
27 u.Scheme = "http"
28 u.Host = "unix.sock"
29 u.Path = ""
30 }
31 return &http.Client{Transport: httpTransport}
32 }