mirror of
https://github.com/containrrr/watchtower.git
synced 2025-12-16 07:00:13 +01:00
Set-up CircleCI builds
This commit is contained in:
parent
c02c4b9ec1
commit
31b6a30686
83 changed files with 18516 additions and 1 deletions
22
Godeps/_workspace/src/github.com/samalba/dockerclient/.gitignore
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/samalba/dockerclient/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
202
Godeps/_workspace/src/github.com/samalba/dockerclient/LICENSE
generated
vendored
Normal file
202
Godeps/_workspace/src/github.com/samalba/dockerclient/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2014 Sam Alba <sam.alba@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
83
Godeps/_workspace/src/github.com/samalba/dockerclient/README.md
generated
vendored
Normal file
83
Godeps/_workspace/src/github.com/samalba/dockerclient/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
Docker client library in Go
|
||||
===========================
|
||||
[](http://godoc.org/github.com/samalba/dockerclient)
|
||||
|
||||
Well maintained docker client library.
|
||||
|
||||
# How to use it?
|
||||
|
||||
Here is an example showing how to use it:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/samalba/dockerclient"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Callback used to listen to Docker's events
|
||||
func eventCallback(event *dockerclient.Event, ec chan error, args ...interface{}) {
|
||||
log.Printf("Received event: %#v\n", *event)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Init the client
|
||||
docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)
|
||||
|
||||
// Get only running containers
|
||||
containers, err := docker.ListContainers(false, false, "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, c := range containers {
|
||||
log.Println(c.Id, c.Names)
|
||||
}
|
||||
|
||||
// Inspect the first container returned
|
||||
if len(containers) > 0 {
|
||||
id := containers[0].Id
|
||||
info, _ := docker.InspectContainer(id)
|
||||
log.Println(info)
|
||||
}
|
||||
|
||||
// Create a container
|
||||
containerConfig := &dockerclient.ContainerConfig{
|
||||
Image: "ubuntu:14.04",
|
||||
Cmd: []string{"bash"},
|
||||
AttachStdin: true,
|
||||
Tty: true}
|
||||
containerId, err := docker.CreateContainer(containerConfig, "foobar")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Start the container
|
||||
hostConfig := &dockerclient.HostConfig{}
|
||||
err = docker.StartContainer(containerId, hostConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Stop the container (with 5 seconds timeout)
|
||||
docker.StopContainer(containerId, 5)
|
||||
|
||||
// Listen to events
|
||||
docker.StartMonitorEvents(eventCallback, nil)
|
||||
|
||||
// Hold the execution to look at the events coming
|
||||
time.Sleep(3600 * time.Second)
|
||||
}
|
||||
```
|
||||
|
||||
# Maintainers
|
||||
|
||||
List of people you can ping for feedback on Pull Requests or any questions.
|
||||
|
||||
- [Sam Alba](https://github.com/samalba)
|
||||
- [Michael Crosby](https://github.com/crosbymichael)
|
||||
- [Andrea Luzzardi](https://github.com/aluzzardi)
|
||||
- [Victor Vieux](https://github.com/vieux)
|
||||
- [Evan Hazlett](https://github.com/ehazlett)
|
||||
- [Donald Huang](https://github.com/donhcd)
|
||||
21
Godeps/_workspace/src/github.com/samalba/dockerclient/auth.go
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/samalba/dockerclient/auth.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// AuthConfig hold parameters for authenticating with the docker registry
|
||||
type AuthConfig struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// encode the auth configuration struct into base64 for the X-Registry-Auth header
|
||||
func (c *AuthConfig) encode() string {
|
||||
var buf bytes.Buffer
|
||||
json.NewEncoder(&buf).Encode(c)
|
||||
return base64.URLEncoding.EncodeToString(buf.Bytes())
|
||||
}
|
||||
15
Godeps/_workspace/src/github.com/samalba/dockerclient/auth_test.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/samalba/dockerclient/auth_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAuthEncode(t *testing.T) {
|
||||
a := AuthConfig{Username: "foo", Password: "password", Email: "bar@baz.com"}
|
||||
expected := "eyJ1c2VybmFtZSI6ImZvbyIsInBhc3N3b3JkIjoicGFzc3dvcmQiLCJlbWFpbCI6ImJhckBiYXouY29tIn0K"
|
||||
got := a.encode()
|
||||
|
||||
if expected != got {
|
||||
t.Errorf("testAuthEncode failed. Expected [%s] got [%s]", expected, got)
|
||||
}
|
||||
}
|
||||
617
Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go
generated
vendored
Normal file
617
Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient.go
generated
vendored
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
APIVersion = "v1.15"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("Not found")
|
||||
|
||||
defaultTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
type DockerClient struct {
|
||||
URL *url.URL
|
||||
HTTPClient *http.Client
|
||||
TLSConfig *tls.Config
|
||||
monitorStats int32
|
||||
eventStopChan chan (struct{})
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
StatusCode int
|
||||
Status string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Status, e.msg)
|
||||
}
|
||||
|
||||
func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
|
||||
return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout))
|
||||
}
|
||||
|
||||
func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) {
|
||||
u, err := url.Parse(daemonUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if u.Scheme == "" || u.Scheme == "tcp" {
|
||||
if tlsConfig == nil {
|
||||
u.Scheme = "http"
|
||||
} else {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
}
|
||||
httpClient := newHTTPClient(u, tlsConfig, timeout)
|
||||
return &DockerClient{u, httpClient, tlsConfig, 0, nil}, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
|
||||
b := bytes.NewBuffer(body)
|
||||
|
||||
reader, err := client.doStreamRequest(method, path, b, headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) doStreamRequest(method string, path string, in io.Reader, headers map[string]string) (io.ReadCloser, error) {
|
||||
if (method == "POST" || method == "PUT") && in == nil {
|
||||
in = bytes.NewReader(nil)
|
||||
}
|
||||
req, err := http.NewRequest(method, client.URL.String()+path, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
if headers != nil {
|
||||
for header, value := range headers {
|
||||
req.Header.Add(header, value)
|
||||
}
|
||||
}
|
||||
resp, err := client.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
|
||||
return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode == 404 {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
defer resp.Body.Close()
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) Info() (*Info, error) {
|
||||
uri := fmt.Sprintf("/%s/info", APIVersion)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &Info{}
|
||||
err = json.Unmarshal(data, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) ListContainers(all bool, size bool, filters string) ([]Container, error) {
|
||||
argAll := 0
|
||||
if all == true {
|
||||
argAll = 1
|
||||
}
|
||||
showSize := 0
|
||||
if size == true {
|
||||
showSize = 1
|
||||
}
|
||||
uri := fmt.Sprintf("/%s/containers/json?all=%d&size=%d", APIVersion, argAll, showSize)
|
||||
|
||||
if filters != "" {
|
||||
uri += "&filters=" + filters
|
||||
}
|
||||
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := []Container{}
|
||||
err = json.Unmarshal(data, &ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) InspectContainer(id string) (*ContainerInfo, error) {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/json", APIVersion, id)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := &ContainerInfo{}
|
||||
err = json.Unmarshal(data, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) CreateContainer(config *ContainerConfig, name string) (string, error) {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uri := fmt.Sprintf("/%s/containers/create", APIVersion)
|
||||
if name != "" {
|
||||
v := url.Values{}
|
||||
v.Set("name", name)
|
||||
uri = fmt.Sprintf("%s?%s", uri, v.Encode())
|
||||
}
|
||||
data, err = client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result := &RespContainersCreate{}
|
||||
err = json.Unmarshal(data, result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.Id, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error) {
|
||||
v := url.Values{}
|
||||
v.Add("follow", strconv.FormatBool(options.Follow))
|
||||
v.Add("stdout", strconv.FormatBool(options.Stdout))
|
||||
v.Add("stderr", strconv.FormatBool(options.Stderr))
|
||||
v.Add("timestamps", strconv.FormatBool(options.Timestamps))
|
||||
if options.Tail > 0 {
|
||||
v.Add("tail", strconv.FormatInt(options.Tail, 10))
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/%s/containers/%s/logs?%s", APIVersion, id, v.Encode())
|
||||
req, err := http.NewRequest("GET", client.URL.String()+uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
resp, err := client.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) ContainerChanges(id string) ([]*ContainerChanges, error) {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/changes", APIVersion, id)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes := []*ContainerChanges{}
|
||||
err = json.Unmarshal(data, &changes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) readJSONStream(stream io.ReadCloser, decode func(*json.Decoder) decodingResult, stopChan <-chan struct{}) <-chan decodingResult {
|
||||
resultChan := make(chan decodingResult)
|
||||
|
||||
go func() {
|
||||
decoder := json.NewDecoder(stream)
|
||||
stopped := make(chan struct{})
|
||||
go func() {
|
||||
<-stopChan
|
||||
stream.Close()
|
||||
stopped <- struct{}{}
|
||||
}()
|
||||
|
||||
defer close(resultChan)
|
||||
for {
|
||||
decodeResult := decode(decoder)
|
||||
select {
|
||||
case <-stopped:
|
||||
return
|
||||
default:
|
||||
resultChan <- decodeResult
|
||||
if decodeResult.err != nil {
|
||||
stream.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return resultChan
|
||||
}
|
||||
|
||||
func (client *DockerClient) StartContainer(id string, config *HostConfig) error {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri := fmt.Sprintf("/%s/containers/%s/start", APIVersion, id)
|
||||
_, err = client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) StopContainer(id string, timeout int) error {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/stop?t=%d", APIVersion, id, timeout)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) RestartContainer(id string, timeout int) error {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/restart?t=%d", APIVersion, id, timeout)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) KillContainer(id, signal string) error {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/kill?signal=%s", APIVersion, id, signal)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error) {
|
||||
v := url.Values{}
|
||||
if options != nil {
|
||||
if options.Since != 0 {
|
||||
v.Add("since", strconv.Itoa(options.Since))
|
||||
}
|
||||
if options.Until != 0 {
|
||||
v.Add("until", strconv.Itoa(options.Until))
|
||||
}
|
||||
if options.Filters != nil {
|
||||
filterMap := make(map[string][]string)
|
||||
if len(options.Filters.Event) > 0 {
|
||||
filterMap["event"] = []string{options.Filters.Event}
|
||||
}
|
||||
if len(options.Filters.Image) > 0 {
|
||||
filterMap["image"] = []string{options.Filters.Image}
|
||||
}
|
||||
if len(options.Filters.Container) > 0 {
|
||||
filterMap["container"] = []string{options.Filters.Container}
|
||||
}
|
||||
if len(filterMap) > 0 {
|
||||
filterJSONBytes, err := json.Marshal(filterMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Add("filters", string(filterJSONBytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
uri := fmt.Sprintf("%s/%s/events?%s", client.URL.String(), APIVersion, v.Encode())
|
||||
resp, err := client.HTTPClient.Get(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decode := func(decoder *json.Decoder) decodingResult {
|
||||
var event Event
|
||||
if err := decoder.Decode(&event); err != nil {
|
||||
return decodingResult{err: err}
|
||||
} else {
|
||||
return decodingResult{result: event}
|
||||
}
|
||||
}
|
||||
decodingResultChan := client.readJSONStream(resp.Body, decode, stopChan)
|
||||
eventOrErrorChan := make(chan EventOrError)
|
||||
go func() {
|
||||
for decodingResult := range decodingResultChan {
|
||||
event, _ := decodingResult.result.(Event)
|
||||
eventOrErrorChan <- EventOrError{
|
||||
Event: event,
|
||||
Error: decodingResult.err,
|
||||
}
|
||||
}
|
||||
close(eventOrErrorChan)
|
||||
}()
|
||||
return eventOrErrorChan, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) StartMonitorEvents(cb Callback, ec chan error, args ...interface{}) {
|
||||
client.eventStopChan = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
eventErrChan, err := client.MonitorEvents(nil, client.eventStopChan)
|
||||
if err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
|
||||
for e := range eventErrChan {
|
||||
if e.Error != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
cb(&e.Event, ec, args...)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (client *DockerClient) StopAllMonitorEvents() {
|
||||
close(client.eventStopChan)
|
||||
}
|
||||
|
||||
func (client *DockerClient) StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
||||
atomic.StoreInt32(&client.monitorStats, 1)
|
||||
go client.getStats(id, cb, ec, args...)
|
||||
}
|
||||
|
||||
func (client *DockerClient) getStats(id string, cb StatCallback, ec chan error, args ...interface{}) {
|
||||
uri := fmt.Sprintf("%s/%s/containers/%s/stats", client.URL.String(), APIVersion, id)
|
||||
resp, err := client.HTTPClient.Get(uri)
|
||||
if err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
for atomic.LoadInt32(&client.monitorStats) > 0 {
|
||||
var stats *Stats
|
||||
if err := dec.Decode(&stats); err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
cb(id, stats, ec, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (client *DockerClient) StopAllMonitorStats() {
|
||||
atomic.StoreInt32(&client.monitorStats, 0)
|
||||
}
|
||||
|
||||
func (client *DockerClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
|
||||
v := url.Values{}
|
||||
v.Set("repo", repo)
|
||||
v.Set("tag", tag)
|
||||
if force {
|
||||
v.Set("force", "1")
|
||||
}
|
||||
uri := fmt.Sprintf("/%s/images/%s/tag?%s", APIVersion, nameOrID, v.Encode())
|
||||
if _, err := client.doRequest("POST", uri, nil, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) Version() (*Version, error) {
|
||||
uri := fmt.Sprintf("/%s/version", APIVersion)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version := &Version{}
|
||||
err = json.Unmarshal(data, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) PullImage(name string, auth *AuthConfig) error {
|
||||
v := url.Values{}
|
||||
v.Set("fromImage", name)
|
||||
uri := fmt.Sprintf("/%s/images/create?%s", APIVersion, v.Encode())
|
||||
req, err := http.NewRequest("POST", client.URL.String()+uri, nil)
|
||||
if auth != nil {
|
||||
req.Header.Add("X-Registry-Auth", auth.encode())
|
||||
}
|
||||
resp, err := client.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode == 404 {
|
||||
return ErrNotFound
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s", string(data))
|
||||
}
|
||||
|
||||
var finalObj map[string]interface{}
|
||||
for decoder := json.NewDecoder(resp.Body); err == nil; err = decoder.Decode(&finalObj) {
|
||||
}
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
if err, ok := finalObj["error"]; ok {
|
||||
return fmt.Errorf("%v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) InspectImage(id string) (*ImageInfo, error) {
|
||||
uri := fmt.Sprintf("/%s/images/%s/json", APIVersion, id)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := &ImageInfo{}
|
||||
err = json.Unmarshal(data, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) LoadImage(reader io.Reader) error {
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("/%s/images/load", APIVersion)
|
||||
_, err = client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) RemoveContainer(id string, force, volumes bool) error {
|
||||
argForce := 0
|
||||
argVolumes := 0
|
||||
if force == true {
|
||||
argForce = 1
|
||||
}
|
||||
if volumes == true {
|
||||
argVolumes = 1
|
||||
}
|
||||
args := fmt.Sprintf("force=%d&v=%d", argForce, argVolumes)
|
||||
uri := fmt.Sprintf("/%s/containers/%s?%s", APIVersion, id, args)
|
||||
_, err := client.doRequest("DELETE", uri, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *DockerClient) ListImages() ([]*Image, error) {
|
||||
uri := fmt.Sprintf("/%s/images/json", APIVersion)
|
||||
data, err := client.doRequest("GET", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var images []*Image
|
||||
if err := json.Unmarshal(data, &images); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) RemoveImage(name string) ([]*ImageDelete, error) {
|
||||
uri := fmt.Sprintf("/%s/images/%s", APIVersion, name)
|
||||
data, err := client.doRequest("DELETE", uri, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var imageDelete []*ImageDelete
|
||||
if err := json.Unmarshal(data, &imageDelete); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imageDelete, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) PauseContainer(id string) error {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/pause", APIVersion, id)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (client *DockerClient) UnpauseContainer(id string) error {
|
||||
uri := fmt.Sprintf("/%s/containers/%s/unpause", APIVersion, id)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) Exec(config *ExecConfig) (string, error) {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uri := fmt.Sprintf("/containers/%s/exec", config.Container)
|
||||
resp, err := client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var createExecResp struct {
|
||||
Id string
|
||||
}
|
||||
if err = json.Unmarshal(resp, &createExecResp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
uri = fmt.Sprintf("/exec/%s/start", createExecResp.Id)
|
||||
resp, err = client.doRequest("POST", uri, data, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return createExecResp.Id, nil
|
||||
}
|
||||
|
||||
func (client *DockerClient) RenameContainer(oldName string, newName string) error {
|
||||
uri := fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName)
|
||||
_, err := client.doRequest("POST", uri, nil, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *DockerClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
|
||||
var fromSrc string
|
||||
v := &url.Values{}
|
||||
if source == "" {
|
||||
fromSrc = "-"
|
||||
} else {
|
||||
fromSrc = source
|
||||
}
|
||||
|
||||
v.Set("fromSrc", fromSrc)
|
||||
v.Set("repo", repository)
|
||||
if tag != "" {
|
||||
v.Set("tag", tag)
|
||||
}
|
||||
|
||||
var in io.Reader
|
||||
if fromSrc == "-" {
|
||||
in = tar
|
||||
}
|
||||
return client.doStreamRequest("POST", "/images/create?"+v.Encode(), in, nil)
|
||||
}
|
||||
219
Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go
generated
vendored
Normal file
219
Godeps/_workspace/src/github.com/samalba/dockerclient/dockerclient_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
)
|
||||
|
||||
func assertEqual(t *testing.T, a interface{}, b interface{}, message string) {
|
||||
if a == b {
|
||||
return
|
||||
}
|
||||
if len(message) == 0 {
|
||||
message = fmt.Sprintf("%v != %v", a, b)
|
||||
}
|
||||
t.Fatal(message)
|
||||
}
|
||||
|
||||
func testDockerClient(t *testing.T) *DockerClient {
|
||||
client, err := NewDockerClient(testHTTPServer.URL, nil)
|
||||
if err != nil {
|
||||
t.Fatal("Cannot init the docker client")
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
info, err := client.Info()
|
||||
if err != nil {
|
||||
t.Fatal("Cannot get server info")
|
||||
}
|
||||
assertEqual(t, info.Images, int64(1), "")
|
||||
assertEqual(t, info.Containers, int64(2), "")
|
||||
}
|
||||
|
||||
func TestKillContainer(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
if err := client.KillContainer("23132acf2ac", "5"); err != nil {
|
||||
t.Fatal("cannot kill container: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullImage(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
err := client.PullImage("busybox", nil)
|
||||
if err != nil {
|
||||
t.Fatal("unable to pull busybox")
|
||||
}
|
||||
|
||||
err = client.PullImage("haproxy", nil)
|
||||
if err != nil {
|
||||
t.Fatal("unable to pull haproxy")
|
||||
}
|
||||
|
||||
err = client.PullImage("wrongimg", nil)
|
||||
if err == nil {
|
||||
t.Fatal("should return error when it fails to pull wrongimg")
|
||||
}
|
||||
}
|
||||
|
||||
func TestListContainers(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
containers, err := client.ListContainers(true, false, "")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get containers: %s", err)
|
||||
}
|
||||
assertEqual(t, len(containers), 1, "")
|
||||
cnt := containers[0]
|
||||
assertEqual(t, cnt.SizeRw, int64(0), "")
|
||||
}
|
||||
|
||||
func TestContainerChanges(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
changes, err := client.ContainerChanges("foobar")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get container changes: %s", err)
|
||||
}
|
||||
assertEqual(t, len(changes), 3, "unexpected number of changes")
|
||||
c := changes[0]
|
||||
assertEqual(t, c.Path, "/dev", "unexpected")
|
||||
assertEqual(t, c.Kind, 0, "unexpected")
|
||||
}
|
||||
|
||||
func TestListContainersWithSize(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
containers, err := client.ListContainers(true, true, "")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get containers: %s", err)
|
||||
}
|
||||
assertEqual(t, len(containers), 1, "")
|
||||
cnt := containers[0]
|
||||
assertEqual(t, cnt.SizeRw, int64(123), "")
|
||||
}
|
||||
func TestListContainersWithFilters(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
containers, err := client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get containers: %s", err)
|
||||
}
|
||||
assertEqual(t, len(containers), 1, "")
|
||||
|
||||
containers, err = client.ListContainers(true, true, "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688cf']}")
|
||||
if err != nil {
|
||||
t.Fatal("cannot get containers: %s", err)
|
||||
}
|
||||
assertEqual(t, len(containers), 0, "")
|
||||
}
|
||||
|
||||
func TestContainerLogs(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
containerId := "foobar"
|
||||
logOptions := &LogOptions{
|
||||
Follow: true,
|
||||
Stdout: true,
|
||||
Stderr: true,
|
||||
Timestamps: true,
|
||||
Tail: 10,
|
||||
}
|
||||
logsReader, err := client.ContainerLogs(containerId, logOptions)
|
||||
if err != nil {
|
||||
t.Fatal("cannot read logs from server")
|
||||
}
|
||||
|
||||
stdoutBuffer := new(bytes.Buffer)
|
||||
stderrBuffer := new(bytes.Buffer)
|
||||
if _, err = stdcopy.StdCopy(stdoutBuffer, stderrBuffer, logsReader); err != nil {
|
||||
t.Fatal("cannot read logs from logs reader")
|
||||
}
|
||||
stdoutLogs := strings.TrimSpace(stdoutBuffer.String())
|
||||
stderrLogs := strings.TrimSpace(stderrBuffer.String())
|
||||
stdoutLogLines := strings.Split(stdoutLogs, "\n")
|
||||
stderrLogLines := strings.Split(stderrLogs, "\n")
|
||||
if len(stdoutLogLines) != 5 {
|
||||
t.Fatalf("wrong number of stdout logs: len=%d", len(stdoutLogLines))
|
||||
}
|
||||
if len(stderrLogLines) != 5 {
|
||||
t.Fatalf("wrong number of stderr logs: len=%d", len(stdoutLogLines))
|
||||
}
|
||||
for i, line := range stdoutLogLines {
|
||||
expectedSuffix := fmt.Sprintf("Z line %d", 41+2*i)
|
||||
if !strings.HasSuffix(line, expectedSuffix) {
|
||||
t.Fatalf("expected stdout log line \"%s\" to end with \"%s\"", line, expectedSuffix)
|
||||
}
|
||||
}
|
||||
for i, line := range stderrLogLines {
|
||||
expectedSuffix := fmt.Sprintf("Z line %d", 40+2*i)
|
||||
if !strings.HasSuffix(line, expectedSuffix) {
|
||||
t.Fatalf("expected stderr log line \"%s\" to end with \"%s\"", line, expectedSuffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonitorEvents(t *testing.T) {
|
||||
client := testDockerClient(t)
|
||||
decoder := json.NewDecoder(bytes.NewBufferString(eventsResp))
|
||||
var expectedEvents []Event
|
||||
for {
|
||||
var event Event
|
||||
if err := decoder.Decode(&event); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else {
|
||||
t.Fatalf("cannot parse expected resp: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
expectedEvents = append(expectedEvents, event)
|
||||
}
|
||||
}
|
||||
|
||||
// test passing stop chan
|
||||
stopChan := make(chan struct{})
|
||||
eventInfoChan, err := client.MonitorEvents(nil, stopChan)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot get events from server: %s", err.Error())
|
||||
}
|
||||
|
||||
eventInfo := <-eventInfoChan
|
||||
if eventInfo.Error != nil || eventInfo.Event != expectedEvents[0] {
|
||||
t.Fatalf("got:\n%#v\nexpected:\n%#v", eventInfo, expectedEvents[0])
|
||||
}
|
||||
close(stopChan)
|
||||
for i := 0; i < 3; i++ {
|
||||
_, ok := <-eventInfoChan
|
||||
if i == 2 && ok {
|
||||
t.Fatalf("read more than 2 events successfully after closing stopChan")
|
||||
}
|
||||
}
|
||||
|
||||
// test when you don't pass stop chan
|
||||
eventInfoChan, err = client.MonitorEvents(nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot get events from server: %s", err.Error())
|
||||
}
|
||||
|
||||
for i, expectedEvent := range expectedEvents {
|
||||
t.Logf("on iter %d\n", i)
|
||||
eventInfo := <-eventInfoChan
|
||||
if eventInfo.Error != nil || eventInfo.Event != expectedEvent {
|
||||
t.Fatalf("index %d, got:\n%#v\nexpected:\n%#v", i, eventInfo, expectedEvent)
|
||||
}
|
||||
t.Logf("done with iter %d\n", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDockerClientInterface(t *testing.T) {
|
||||
iface := reflect.TypeOf((*Client)(nil)).Elem()
|
||||
test := testDockerClient(t)
|
||||
|
||||
if !reflect.TypeOf(test).Implements(iface) {
|
||||
t.Fatalf("DockerClient does not implement the Client interface")
|
||||
}
|
||||
}
|
||||
235
Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go
generated
vendored
Normal file
235
Godeps/_workspace/src/github.com/samalba/dockerclient/engine_mock_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonlog"
|
||||
"github.com/docker/docker/pkg/stdcopy"
|
||||
"github.com/docker/docker/pkg/timeutils"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var (
|
||||
testHTTPServer *httptest.Server
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := mux.NewRouter()
|
||||
baseURL := "/" + APIVersion
|
||||
r.HandleFunc(baseURL+"/info", handlerGetInfo).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/json", handlerGetContainers).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/logs", handleContainerLogs).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/changes", handleContainerChanges).Methods("GET")
|
||||
r.HandleFunc(baseURL+"/containers/{id}/kill", handleContainerKill).Methods("POST")
|
||||
r.HandleFunc(baseURL+"/images/create", handleImagePull).Methods("POST")
|
||||
r.HandleFunc(baseURL+"/events", handleEvents).Methods("GET")
|
||||
testHTTPServer = httptest.NewServer(handlerAccessLog(r))
|
||||
}
|
||||
|
||||
func handlerAccessLog(handler http.Handler) http.Handler {
|
||||
logHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%s \"%s %s\"", r.RemoteAddr, r.Method, r.URL)
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(logHandler)
|
||||
}
|
||||
|
||||
func handleContainerKill(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "{%q:%q", "Id", "421373210afd132")
|
||||
}
|
||||
|
||||
func handleImagePull(w http.ResponseWriter, r *http.Request) {
|
||||
imageName := r.URL.Query()["fromImage"][0]
|
||||
responses := []map[string]interface{}{{
|
||||
"status": fmt.Sprintf("Pulling repository mydockerregistry/%s", imageName),
|
||||
}}
|
||||
switch imageName {
|
||||
case "busybox":
|
||||
responses = append(responses, map[string]interface{}{
|
||||
"status": "Status: Image is up to date for mydockerregistry/busybox",
|
||||
})
|
||||
case "haproxy":
|
||||
fmt.Fprintf(w, haproxyPullOutput)
|
||||
return
|
||||
default:
|
||||
errorMsg := fmt.Sprintf("Error: image %s not found", imageName)
|
||||
responses = append(responses, map[string]interface{}{
|
||||
"errorDetail": map[string]interface{}{
|
||||
"message": errorMsg,
|
||||
},
|
||||
"error": errorMsg,
|
||||
})
|
||||
}
|
||||
for _, response := range responses {
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
}
|
||||
|
||||
func handleContainerLogs(w http.ResponseWriter, r *http.Request) {
|
||||
var outStream, errStream io.Writer
|
||||
outStream = ioutils.NewWriteFlusher(w)
|
||||
|
||||
// not sure how to test follow
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
}
|
||||
stdout, stderr := getBoolValue(r.Form.Get("stdout")), getBoolValue(r.Form.Get("stderr"))
|
||||
if stderr {
|
||||
errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
|
||||
}
|
||||
if stdout {
|
||||
outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
|
||||
}
|
||||
var i int
|
||||
if tail, err := strconv.Atoi(r.Form.Get("tail")); err == nil && tail > 0 {
|
||||
i = 50 - tail
|
||||
if i < 0 {
|
||||
i = 0
|
||||
}
|
||||
}
|
||||
for ; i < 50; i++ {
|
||||
line := fmt.Sprintf("line %d", i)
|
||||
if getBoolValue(r.Form.Get("timestamps")) {
|
||||
l := &jsonlog.JSONLog{Log: line, Created: time.Now()}
|
||||
line = fmt.Sprintf("%s %s", l.Created.Format(timeutils.RFC3339NanoFixed), line)
|
||||
}
|
||||
if i%2 == 0 && stderr {
|
||||
fmt.Fprintln(errStream, line)
|
||||
} else if i%2 == 1 && stdout {
|
||||
fmt.Fprintln(outStream, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleContainerChanges(w http.ResponseWriter, r *http.Request) {
|
||||
writeHeaders(w, 200, "changes")
|
||||
body := `[
|
||||
{
|
||||
"Path": "/dev",
|
||||
"Kind": 0
|
||||
},
|
||||
{
|
||||
"Path": "/dev/kmsg",
|
||||
"Kind": 1
|
||||
},
|
||||
{
|
||||
"Path": "/test",
|
||||
"Kind": 1
|
||||
}
|
||||
]`
|
||||
w.Write([]byte(body))
|
||||
}
|
||||
|
||||
func getBoolValue(boolString string) bool {
|
||||
switch boolString {
|
||||
case "1":
|
||||
return true
|
||||
case "True":
|
||||
return true
|
||||
case "true":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func writeHeaders(w http.ResponseWriter, code int, jobName string) {
|
||||
h := w.Header()
|
||||
h.Add("Content-Type", "application/json")
|
||||
if jobName != "" {
|
||||
h.Add("Job-Name", jobName)
|
||||
}
|
||||
w.WriteHeader(code)
|
||||
}
|
||||
|
||||
func handlerGetInfo(w http.ResponseWriter, r *http.Request) {
|
||||
writeHeaders(w, 200, "info")
|
||||
body := `{
|
||||
"Containers": 2,
|
||||
"Debug": 1,
|
||||
"Driver": "aufs",
|
||||
"DriverStatus": [["Root Dir", "/mnt/sda1/var/lib/docker/aufs"],
|
||||
["Dirs", "0"]],
|
||||
"ExecutionDriver": "native-0.2",
|
||||
"IPv4Forwarding": 1,
|
||||
"Images": 1,
|
||||
"IndexServerAddress": "https://index.docker.io/v1/",
|
||||
"InitPath": "/usr/local/bin/docker",
|
||||
"InitSha1": "",
|
||||
"KernelVersion": "3.16.4-tinycore64",
|
||||
"MemoryLimit": 1,
|
||||
"NEventsListener": 0,
|
||||
"NFd": 10,
|
||||
"NGoroutines": 11,
|
||||
"OperatingSystem": "Boot2Docker 1.3.1 (TCL 5.4); master : a083df4 - Thu Jan 01 00:00:00 UTC 1970",
|
||||
"SwapLimit": 1}`
|
||||
w.Write([]byte(body))
|
||||
}
|
||||
|
||||
func handlerGetContainers(w http.ResponseWriter, r *http.Request) {
|
||||
writeHeaders(w, 200, "containers")
|
||||
body := `[
|
||||
{
|
||||
"Status": "Up 39 seconds",
|
||||
"Ports": [
|
||||
{
|
||||
"Type": "tcp",
|
||||
"PublicPort": 49163,
|
||||
"PrivatePort": 8080,
|
||||
"IP": "0.0.0.0"
|
||||
}
|
||||
],
|
||||
"Names": [
|
||||
"/trusting_heisenberg"
|
||||
],
|
||||
"Image": "foo:latest",
|
||||
"Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce",
|
||||
"Created": 1415720105,
|
||||
"Command": "/bin/go-run"
|
||||
}
|
||||
]`
|
||||
if v, ok := r.URL.Query()["size"]; ok {
|
||||
if v[0] == "1" {
|
||||
body = `[
|
||||
{
|
||||
"Status": "Up 39 seconds",
|
||||
"Ports": [
|
||||
{
|
||||
"Type": "tcp",
|
||||
"PublicPort": 49163,
|
||||
"PrivatePort": 8080,
|
||||
"IP": "0.0.0.0"
|
||||
}
|
||||
],
|
||||
"Names": [
|
||||
"/trusting_heisenberg"
|
||||
],
|
||||
"Image": "foo:latest",
|
||||
"Id": "332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce",
|
||||
"Created": 1415720105,
|
||||
"SizeRootFs": 12345,
|
||||
"SizeRW": 123,
|
||||
"Command": "/bin/go-run"
|
||||
}
|
||||
]`
|
||||
}
|
||||
}
|
||||
if v, ok := r.URL.Query()["filters"]; ok {
|
||||
if v[0] != "{'id':['332375cfbc23edb921a21026314c3497674ba8bdcb2c85e0e65ebf2017f688ce']}" {
|
||||
body = "[]"
|
||||
}
|
||||
}
|
||||
w.Write([]byte(body))
|
||||
}
|
||||
|
||||
func handleEvents(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(eventsResp))
|
||||
}
|
||||
13
Godeps/_workspace/src/github.com/samalba/dockerclient/example_responses.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/samalba/dockerclient/example_responses.go
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package dockerclient
|
||||
|
||||
var haproxyPullOutput = `{"status":"The image you are pulling has been verified","id":"haproxy:1"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
{"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"}
|
||||
`
|
||||
|
||||
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}`
|
||||
39
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/events.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/events.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/samalba/dockerclient"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func eventCallback(e *dockerclient.Event, ec chan error, args ...interface{}) {
|
||||
log.Println(e)
|
||||
}
|
||||
|
||||
var (
|
||||
client *dockerclient.DockerClient
|
||||
)
|
||||
|
||||
func waitForInterrupt() {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
for _ = range sigChan {
|
||||
client.StopAllMonitorEvents()
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
client = docker
|
||||
|
||||
client.StartMonitorEvents(eventCallback, nil)
|
||||
|
||||
waitForInterrupt()
|
||||
}
|
||||
43
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go
generated
vendored
Normal file
43
Godeps/_workspace/src/github.com/samalba/dockerclient/examples/stats/stats.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/samalba/dockerclient"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func statCallback(id string, stat *dockerclient.Stats, ec chan error, args ...interface{}) {
|
||||
log.Println(stat)
|
||||
}
|
||||
|
||||
func waitForInterrupt() {
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
|
||||
for _ = range sigChan {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
docker, err := dockerclient.NewDockerClient(os.Getenv("DOCKER_HOST"), nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
containerConfig := &dockerclient.ContainerConfig{Image: "busybox", Cmd: []string{"sh"}}
|
||||
containerId, err := docker.CreateContainer(containerConfig, "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Start the container
|
||||
err = docker.StartContainer(containerId, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
docker.StartMonitorStats(containerId, statCallback, nil)
|
||||
|
||||
waitForInterrupt()
|
||||
}
|
||||
44
Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go
generated
vendored
Normal file
44
Godeps/_workspace/src/github.com/samalba/dockerclient/interface.go
generated
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type Callback func(*Event, chan error, ...interface{})
|
||||
|
||||
type StatCallback func(string, *Stats, chan error, ...interface{})
|
||||
|
||||
type Client interface {
|
||||
Info() (*Info, error)
|
||||
ListContainers(all, size bool, filters string) ([]Container, error)
|
||||
InspectContainer(id string) (*ContainerInfo, error)
|
||||
InspectImage(id string) (*ImageInfo, error)
|
||||
CreateContainer(config *ContainerConfig, name string) (string, error)
|
||||
ContainerLogs(id string, options *LogOptions) (io.ReadCloser, error)
|
||||
ContainerChanges(id string) ([]*ContainerChanges, error)
|
||||
Exec(config *ExecConfig) (string, error)
|
||||
StartContainer(id string, config *HostConfig) error
|
||||
StopContainer(id string, timeout int) error
|
||||
RestartContainer(id string, timeout int) error
|
||||
KillContainer(id, signal string) error
|
||||
// MonitorEvents takes options and an optional stop channel, and returns
|
||||
// an EventOrError channel. If an error is ever sent, then no more
|
||||
// events will be sent. If a stop channel is provided, events will stop
|
||||
// being monitored after the stop channel is closed.
|
||||
MonitorEvents(options *MonitorEventsOptions, stopChan <-chan struct{}) (<-chan EventOrError, error)
|
||||
StartMonitorEvents(cb Callback, ec chan error, args ...interface{})
|
||||
StopAllMonitorEvents()
|
||||
StartMonitorStats(id string, cb StatCallback, ec chan error, args ...interface{})
|
||||
StopAllMonitorStats()
|
||||
TagImage(nameOrID string, repo string, tag string, force bool) error
|
||||
Version() (*Version, error)
|
||||
PullImage(name string, auth *AuthConfig) error
|
||||
LoadImage(reader io.Reader) error
|
||||
RemoveContainer(id string, force, volumes bool) error
|
||||
ListImages() ([]*Image, error)
|
||||
RemoveImage(name string) ([]*ImageDelete, error)
|
||||
PauseContainer(name string) error
|
||||
UnpauseContainer(name string) error
|
||||
RenameContainer(oldName string, newName string) error
|
||||
ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error)
|
||||
}
|
||||
152
Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go
generated
vendored
Normal file
152
Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock.go
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package mockclient
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/samalba/dockerclient"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func NewMockClient() *MockClient {
|
||||
return &MockClient{}
|
||||
}
|
||||
|
||||
func (client *MockClient) Info() (*dockerclient.Info, error) {
|
||||
args := client.Mock.Called()
|
||||
return args.Get(0).(*dockerclient.Info), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) ListContainers(all bool, size bool, filters string) ([]dockerclient.Container, error) {
|
||||
args := client.Mock.Called(all, size, filters)
|
||||
return args.Get(0).([]dockerclient.Container), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) InspectContainer(id string) (*dockerclient.ContainerInfo, error) {
|
||||
args := client.Mock.Called(id)
|
||||
return args.Get(0).(*dockerclient.ContainerInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) InspectImage(id string) (*dockerclient.ImageInfo, error) {
|
||||
args := client.Mock.Called(id)
|
||||
return args.Get(0).(*dockerclient.ImageInfo), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) CreateContainer(config *dockerclient.ContainerConfig, name string) (string, error) {
|
||||
args := client.Mock.Called(config, name)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) ContainerLogs(id string, options *dockerclient.LogOptions) (io.ReadCloser, error) {
|
||||
args := client.Mock.Called(id, options)
|
||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) ContainerChanges(id string) ([]*dockerclient.ContainerChanges, error) {
|
||||
args := client.Mock.Called(id)
|
||||
return args.Get(0).([]*dockerclient.ContainerChanges), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) StartContainer(id string, config *dockerclient.HostConfig) error {
|
||||
args := client.Mock.Called(id, config)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) StopContainer(id string, timeout int) error {
|
||||
args := client.Mock.Called(id, timeout)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) RestartContainer(id string, timeout int) error {
|
||||
args := client.Mock.Called(id, timeout)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) KillContainer(id, signal string) error {
|
||||
args := client.Mock.Called(id, signal)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) MonitorEvents(options *dockerclient.MonitorEventsOptions, stopChan <-chan struct{}) (<-chan dockerclient.EventOrError, error) {
|
||||
args := client.Mock.Called(options, stopChan)
|
||||
return args.Get(0).(<-chan dockerclient.EventOrError), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) StartMonitorEvents(cb dockerclient.Callback, ec chan error, args ...interface{}) {
|
||||
client.Mock.Called(cb, ec, args)
|
||||
}
|
||||
|
||||
func (client *MockClient) StopAllMonitorEvents() {
|
||||
client.Mock.Called()
|
||||
}
|
||||
|
||||
func (client *MockClient) TagImage(nameOrID string, repo string, tag string, force bool) error {
|
||||
args := client.Mock.Called(nameOrID, repo, tag, force)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) StartMonitorStats(id string, cb dockerclient.StatCallback, ec chan error, args ...interface{}) {
|
||||
client.Mock.Called(id, cb, ec, args)
|
||||
}
|
||||
|
||||
func (client *MockClient) StopAllMonitorStats() {
|
||||
client.Mock.Called()
|
||||
}
|
||||
|
||||
func (client *MockClient) Version() (*dockerclient.Version, error) {
|
||||
args := client.Mock.Called()
|
||||
return args.Get(0).(*dockerclient.Version), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) PullImage(name string, auth *dockerclient.AuthConfig) error {
|
||||
args := client.Mock.Called(name, auth)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) LoadImage(reader io.Reader) error {
|
||||
args := client.Mock.Called(reader)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) RemoveContainer(id string, force, volumes bool) error {
|
||||
args := client.Mock.Called(id, force, volumes)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) ListImages() ([]*dockerclient.Image, error) {
|
||||
args := client.Mock.Called()
|
||||
return args.Get(0).([]*dockerclient.Image), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) RemoveImage(name string) ([]*dockerclient.ImageDelete, error) {
|
||||
args := client.Mock.Called(name)
|
||||
return args.Get(0).([]*dockerclient.ImageDelete), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) PauseContainer(name string) error {
|
||||
args := client.Mock.Called(name)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) UnpauseContainer(name string) error {
|
||||
args := client.Mock.Called(name)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) Exec(config *dockerclient.ExecConfig) (string, error) {
|
||||
args := client.Mock.Called(config)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
func (client *MockClient) RenameContainer(oldName string, newName string) error {
|
||||
args := client.Mock.Called(oldName, newName)
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (client *MockClient) ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error) {
|
||||
args := client.Mock.Called(source, repository, tag, tar)
|
||||
return args.Get(0).(io.ReadCloser), args.Error(1)
|
||||
}
|
||||
32
Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock_test.go
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/samalba/dockerclient/mockclient/mock_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package mockclient
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/samalba/dockerclient"
|
||||
)
|
||||
|
||||
func TestMock(t *testing.T) {
|
||||
mock := NewMockClient()
|
||||
mock.On("Version").Return(&dockerclient.Version{Version: "foo"}, nil).Once()
|
||||
|
||||
v, err := mock.Version()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.Version != "foo" {
|
||||
t.Fatal(v)
|
||||
}
|
||||
|
||||
mock.Mock.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestMockInterface(t *testing.T) {
|
||||
iface := reflect.TypeOf((*dockerclient.Client)(nil)).Elem()
|
||||
mock := NewMockClient()
|
||||
|
||||
if !reflect.TypeOf(mock).Implements(iface) {
|
||||
t.Fatalf("Mock does not implement the Client interface")
|
||||
}
|
||||
}
|
||||
417
Godeps/_workspace/src/github.com/samalba/dockerclient/types.go
generated
vendored
Normal file
417
Godeps/_workspace/src/github.com/samalba/dockerclient/types.go
generated
vendored
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/units"
|
||||
)
|
||||
|
||||
type ContainerConfig struct {
|
||||
Hostname string
|
||||
Domainname string
|
||||
User string
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
ExposedPorts map[string]struct{}
|
||||
Tty bool
|
||||
OpenStdin bool
|
||||
StdinOnce bool
|
||||
Env []string
|
||||
Cmd []string
|
||||
Image string
|
||||
Volumes map[string]struct{}
|
||||
VolumeDriver string
|
||||
WorkingDir string
|
||||
Entrypoint []string
|
||||
NetworkDisabled bool
|
||||
MacAddress string
|
||||
OnBuild []string
|
||||
Labels map[string]string
|
||||
|
||||
// FIXME: The following fields have been removed since API v1.18
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CpuShares int64
|
||||
Cpuset string
|
||||
PortSpecs []string
|
||||
|
||||
// This is used only by the create command
|
||||
HostConfig HostConfig
|
||||
}
|
||||
|
||||
type HostConfig struct {
|
||||
Binds []string
|
||||
ContainerIDFile string
|
||||
LxcConf []map[string]string
|
||||
Memory int64
|
||||
MemorySwap int64
|
||||
CpuShares int64
|
||||
CpuPeriod int64
|
||||
CpusetCpus string
|
||||
CpusetMems string
|
||||
CpuQuota int64
|
||||
BlkioWeight int64
|
||||
OomKillDisable bool
|
||||
Privileged bool
|
||||
PortBindings map[string][]PortBinding
|
||||
Links []string
|
||||
PublishAllPorts bool
|
||||
Dns []string
|
||||
DnsSearch []string
|
||||
ExtraHosts []string
|
||||
VolumesFrom []string
|
||||
Devices []DeviceMapping
|
||||
NetworkMode string
|
||||
IpcMode string
|
||||
PidMode string
|
||||
UTSMode string
|
||||
CapAdd []string
|
||||
CapDrop []string
|
||||
RestartPolicy RestartPolicy
|
||||
SecurityOpt []string
|
||||
ReadonlyRootfs bool
|
||||
Ulimits []Ulimit
|
||||
LogConfig LogConfig
|
||||
CgroupParent string
|
||||
}
|
||||
|
||||
type DeviceMapping struct {
|
||||
PathOnHost string `json:"PathOnHost"`
|
||||
PathInContainer string `json:"PathInContainer"`
|
||||
CgroupPermissions string `json:"CgroupPermissions"`
|
||||
}
|
||||
|
||||
type ExecConfig struct {
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
Tty bool
|
||||
Cmd []string
|
||||
Container string
|
||||
Detach bool
|
||||
}
|
||||
|
||||
type LogOptions struct {
|
||||
Follow bool
|
||||
Stdout bool
|
||||
Stderr bool
|
||||
Timestamps bool
|
||||
Tail int64
|
||||
}
|
||||
|
||||
type MonitorEventsFilters struct {
|
||||
Event string `json:",omitempty"`
|
||||
Image string `json:",omitempty"`
|
||||
Container string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type MonitorEventsOptions struct {
|
||||
Since int
|
||||
Until int
|
||||
Filters *MonitorEventsFilters `json:",omitempty"`
|
||||
}
|
||||
|
||||
type RestartPolicy struct {
|
||||
Name string
|
||||
MaximumRetryCount int64
|
||||
}
|
||||
|
||||
type PortBinding struct {
|
||||
HostIp string
|
||||
HostPort string
|
||||
}
|
||||
|
||||
type State struct {
|
||||
Running bool
|
||||
Paused bool
|
||||
Restarting bool
|
||||
OOMKilled bool
|
||||
Dead bool
|
||||
Pid int
|
||||
ExitCode int
|
||||
Error string // contains last known error when starting the container
|
||||
StartedAt time.Time
|
||||
FinishedAt time.Time
|
||||
Ghost bool
|
||||
}
|
||||
|
||||
// String returns a human-readable description of the state
|
||||
// Stoken from docker/docker/daemon/state.go
|
||||
func (s *State) String() string {
|
||||
if s.Running {
|
||||
if s.Paused {
|
||||
return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
||||
}
|
||||
if s.Restarting {
|
||||
return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt)))
|
||||
}
|
||||
|
||||
if s.Dead {
|
||||
return "Dead"
|
||||
}
|
||||
|
||||
if s.FinishedAt.IsZero() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt)))
|
||||
}
|
||||
|
||||
// StateString returns a single string to describe state
|
||||
// Stoken from docker/docker/daemon/state.go
|
||||
func (s *State) StateString() string {
|
||||
if s.Running {
|
||||
if s.Paused {
|
||||
return "paused"
|
||||
}
|
||||
if s.Restarting {
|
||||
return "restarting"
|
||||
}
|
||||
return "running"
|
||||
}
|
||||
|
||||
if s.Dead {
|
||||
return "dead"
|
||||
}
|
||||
|
||||
return "exited"
|
||||
}
|
||||
|
||||
type ImageInfo struct {
|
||||
Architecture string
|
||||
Author string
|
||||
Comment string
|
||||
Config *ContainerConfig
|
||||
Container string
|
||||
ContainerConfig *ContainerConfig
|
||||
Created time.Time
|
||||
DockerVersion string
|
||||
Id string
|
||||
Os string
|
||||
Parent string
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
}
|
||||
|
||||
type ContainerInfo struct {
|
||||
Id string
|
||||
Created string
|
||||
Path string
|
||||
Name string
|
||||
Args []string
|
||||
ExecIDs []string
|
||||
Config *ContainerConfig
|
||||
State *State
|
||||
Image string
|
||||
NetworkSettings struct {
|
||||
IPAddress string `json:"IpAddress"`
|
||||
IPPrefixLen int `json:"IpPrefixLen"`
|
||||
Gateway string
|
||||
Bridge string
|
||||
Ports map[string][]PortBinding
|
||||
}
|
||||
SysInitPath string
|
||||
ResolvConfPath string
|
||||
Volumes map[string]string
|
||||
HostConfig *HostConfig
|
||||
}
|
||||
|
||||
type ContainerChanges struct {
|
||||
Path string
|
||||
Kind int
|
||||
}
|
||||
|
||||
type Port struct {
|
||||
IP string
|
||||
PrivatePort int
|
||||
PublicPort int
|
||||
Type string
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Id string
|
||||
Names []string
|
||||
Image string
|
||||
Command string
|
||||
Created int64
|
||||
Status string
|
||||
Ports []Port
|
||||
SizeRw int64
|
||||
SizeRootFs int64
|
||||
Labels map[string]string
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Id string
|
||||
Status string
|
||||
From string
|
||||
Time int64
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
ApiVersion string
|
||||
Arch string
|
||||
GitCommit string
|
||||
GoVersion string
|
||||
KernelVersion string
|
||||
Os string
|
||||
Version string
|
||||
}
|
||||
|
||||
type RespContainersCreate struct {
|
||||
Id string
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Created int64
|
||||
Id string
|
||||
ParentId string
|
||||
RepoTags []string
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
}
|
||||
|
||||
// Info is the struct returned by /info
|
||||
// The API is currently in flux, so Debug, MemoryLimit, SwapLimit, and
|
||||
// IPv4Forwarding are interfaces because in docker 1.6.1 they are 0 or 1 but in
|
||||
// master they are bools.
|
||||
type Info struct {
|
||||
ID string
|
||||
Containers int64
|
||||
Driver string
|
||||
DriverStatus [][]string
|
||||
ExecutionDriver string
|
||||
Images int64
|
||||
KernelVersion string
|
||||
OperatingSystem string
|
||||
NCPU int64
|
||||
MemTotal int64
|
||||
Name string
|
||||
Labels []string
|
||||
Debug interface{}
|
||||
NFd int64
|
||||
NGoroutines int64
|
||||
SystemTime string
|
||||
NEventsListener int64
|
||||
InitPath string
|
||||
InitSha1 string
|
||||
IndexServerAddress string
|
||||
MemoryLimit interface{}
|
||||
SwapLimit interface{}
|
||||
IPv4Forwarding interface{}
|
||||
BridgeNfIptables bool
|
||||
BridgeNfIp6tables bool
|
||||
DockerRootDir string
|
||||
HttpProxy string
|
||||
HttpsProxy string
|
||||
NoProxy string
|
||||
}
|
||||
|
||||
type ImageDelete struct {
|
||||
Deleted string
|
||||
Untagged string
|
||||
}
|
||||
|
||||
type EventOrError struct {
|
||||
Event
|
||||
Error error
|
||||
}
|
||||
|
||||
type decodingResult struct {
|
||||
result interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// The following are types for the API stats endpoint
|
||||
type ThrottlingData struct {
|
||||
// Number of periods with throttling active
|
||||
Periods uint64 `json:"periods"`
|
||||
// Number of periods when the container hit its throttling limit.
|
||||
ThrottledPeriods uint64 `json:"throttled_periods"`
|
||||
// Aggregate time the container was throttled for in nanoseconds.
|
||||
ThrottledTime uint64 `json:"throttled_time"`
|
||||
}
|
||||
|
||||
type CpuUsage struct {
|
||||
// Total CPU time consumed.
|
||||
// Units: nanoseconds.
|
||||
TotalUsage uint64 `json:"total_usage"`
|
||||
// Total CPU time consumed per core.
|
||||
// Units: nanoseconds.
|
||||
PercpuUsage []uint64 `json:"percpu_usage"`
|
||||
// Time spent by tasks of the cgroup in kernel mode.
|
||||
// Units: nanoseconds.
|
||||
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||
// Time spent by tasks of the cgroup in user mode.
|
||||
// Units: nanoseconds.
|
||||
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||
}
|
||||
|
||||
type CpuStats struct {
|
||||
CpuUsage CpuUsage `json:"cpu_usage"`
|
||||
SystemUsage uint64 `json:"system_cpu_usage"`
|
||||
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||
}
|
||||
|
||||
type NetworkStats struct {
|
||||
RxBytes uint64 `json:"rx_bytes"`
|
||||
RxPackets uint64 `json:"rx_packets"`
|
||||
RxErrors uint64 `json:"rx_errors"`
|
||||
RxDropped uint64 `json:"rx_dropped"`
|
||||
TxBytes uint64 `json:"tx_bytes"`
|
||||
TxPackets uint64 `json:"tx_packets"`
|
||||
TxErrors uint64 `json:"tx_errors"`
|
||||
TxDropped uint64 `json:"tx_dropped"`
|
||||
}
|
||||
|
||||
type MemoryStats struct {
|
||||
Usage uint64 `json:"usage"`
|
||||
MaxUsage uint64 `json:"max_usage"`
|
||||
Stats map[string]uint64 `json:"stats"`
|
||||
Failcnt uint64 `json:"failcnt"`
|
||||
Limit uint64 `json:"limit"`
|
||||
}
|
||||
|
||||
type BlkioStatEntry struct {
|
||||
Major uint64 `json:"major"`
|
||||
Minor uint64 `json:"minor"`
|
||||
Op string `json:"op"`
|
||||
Value uint64 `json:"value"`
|
||||
}
|
||||
|
||||
type BlkioStats struct {
|
||||
// number of bytes tranferred to and from the block device
|
||||
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive"`
|
||||
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive"`
|
||||
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive"`
|
||||
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive"`
|
||||
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive"`
|
||||
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive"`
|
||||
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive"`
|
||||
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive"`
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
Read time.Time `json:"read"`
|
||||
NetworkStats NetworkStats `json:"network,omitempty"`
|
||||
CpuStats CpuStats `json:"cpu_stats,omitempty"`
|
||||
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||
}
|
||||
|
||||
type Ulimit struct {
|
||||
Name string `json:"name"`
|
||||
Soft uint64 `json:"soft"`
|
||||
Hard uint64 `json:"hard"`
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Type string `json:"type"`
|
||||
Config map[string]string `json:"config"`
|
||||
}
|
||||
33
Godeps/_workspace/src/github.com/samalba/dockerclient/utils.go
generated
vendored
Normal file
33
Godeps/_workspace/src/github.com/samalba/dockerclient/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package dockerclient
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {
|
||||
httpTransport := &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
default:
|
||||
httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout(proto, addr, timeout)
|
||||
}
|
||||
case "unix":
|
||||
socketPath := u.Path
|
||||
unixDial := func(proto, addr string) (net.Conn, error) {
|
||||
return net.DialTimeout("unix", socketPath, timeout)
|
||||
}
|
||||
httpTransport.Dial = unixDial
|
||||
// Override the main URL object so the HTTP lib won't complain
|
||||
u.Scheme = "http"
|
||||
u.Host = "unix.sock"
|
||||
u.Path = ""
|
||||
}
|
||||
return &http.Client{Transport: httpTransport}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue