1019 lines
29 KiB
Go
1019 lines
29 KiB
Go
|
// Copyright 2015 go-dockerclient authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package docker
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"io/ioutil"
|
||
|
"net"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
func newTestClient(rt *FakeRoundTripper) Client {
|
||
|
endpoint := "http://localhost:4243"
|
||
|
u, _ := parseEndpoint("http://localhost:4243", false)
|
||
|
testAPIVersion, _ := NewAPIVersion("1.17")
|
||
|
client := Client{
|
||
|
HTTPClient: &http.Client{Transport: rt},
|
||
|
Dialer: &net.Dialer{},
|
||
|
endpoint: endpoint,
|
||
|
endpointURL: u,
|
||
|
SkipServerVersionCheck: true,
|
||
|
serverAPIVersion: testAPIVersion,
|
||
|
}
|
||
|
return client
|
||
|
}
|
||
|
|
||
|
type stdoutMock struct {
|
||
|
*bytes.Buffer
|
||
|
}
|
||
|
|
||
|
func (m stdoutMock) Close() error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type stdinMock struct {
|
||
|
*bytes.Buffer
|
||
|
}
|
||
|
|
||
|
func (m stdinMock) Close() error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func TestListImages(t *testing.T) {
|
||
|
body := `[
|
||
|
{
|
||
|
"Repository":"base",
|
||
|
"Tag":"ubuntu-12.10",
|
||
|
"Id":"b750fe79269d",
|
||
|
"Created":1364102658
|
||
|
},
|
||
|
{
|
||
|
"Repository":"base",
|
||
|
"Tag":"ubuntu-quantal",
|
||
|
"Id":"b750fe79269d",
|
||
|
"Created":1364102658
|
||
|
},
|
||
|
{
|
||
|
"RepoTag": [
|
||
|
"ubuntu:12.04",
|
||
|
"ubuntu:precise",
|
||
|
"ubuntu:latest"
|
||
|
],
|
||
|
"Id": "8dbd9e392a964c",
|
||
|
"Created": 1365714795,
|
||
|
"Size": 131506275,
|
||
|
"VirtualSize": 131506275
|
||
|
},
|
||
|
{
|
||
|
"RepoTag": [
|
||
|
"ubuntu:12.10",
|
||
|
"ubuntu:quantal"
|
||
|
],
|
||
|
"ParentId": "27cf784147099545",
|
||
|
"Id": "b750fe79269d2e",
|
||
|
"Created": 1364102658,
|
||
|
"Size": 24653,
|
||
|
"VirtualSize": 180116135
|
||
|
}
|
||
|
]`
|
||
|
var expected []APIImages
|
||
|
err := json.Unmarshal([]byte(body), &expected)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||
|
images, err := client.ListImages(ListImagesOptions{})
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(images, expected) {
|
||
|
t.Errorf("ListImages: Wrong return value. Want %#v. Got %#v.", expected, images)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestListImagesParameters(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "null", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
_, err := client.ListImages(ListImagesOptions{All: false})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "GET" {
|
||
|
t.Errorf("ListImages({All: false}: Wrong HTTP method. Want GET. Got %s.", req.Method)
|
||
|
}
|
||
|
if all := req.URL.Query().Get("all"); all != "0" && all != "" {
|
||
|
t.Errorf("ListImages({All: false}): Wrong parameter. Want all=0 or not present at all. Got all=%s", all)
|
||
|
}
|
||
|
fakeRT.Reset()
|
||
|
_, err = client.ListImages(ListImagesOptions{All: true})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req = fakeRT.requests[0]
|
||
|
if all := req.URL.Query().Get("all"); all != "1" {
|
||
|
t.Errorf("ListImages({All: true}): Wrong parameter. Want all=1. Got all=%s", all)
|
||
|
}
|
||
|
fakeRT.Reset()
|
||
|
_, err = client.ListImages(ListImagesOptions{Filters: map[string][]string{
|
||
|
"dangling": {"true"},
|
||
|
}})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req = fakeRT.requests[0]
|
||
|
body := req.URL.Query().Get("filters")
|
||
|
var filters map[string][]string
|
||
|
err = json.Unmarshal([]byte(body), &filters)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(filters["dangling"]) != 1 || filters["dangling"][0] != "true" {
|
||
|
t.Errorf("ListImages(dangling=[true]): Wrong filter map. Want dangling=[true], got dangling=%v", filters["dangling"])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImageHistory(t *testing.T) {
|
||
|
body := `[
|
||
|
{
|
||
|
"Id": "25daec02219d2d852f7526137213a9b199926b4b24e732eab5b8bc6c49bd470e",
|
||
|
"Tags": [
|
||
|
"debian:7.6",
|
||
|
"debian:latest",
|
||
|
"debian:7",
|
||
|
"debian:wheezy"
|
||
|
],
|
||
|
"Created": 1409856216,
|
||
|
"CreatedBy": "/bin/sh -c #(nop) CMD [/bin/bash]"
|
||
|
},
|
||
|
{
|
||
|
"Id": "41026a5347fb5be6ed16115bf22df8569697139f246186de9ae8d4f67c335dce",
|
||
|
"Created": 1409856213,
|
||
|
"CreatedBy": "/bin/sh -c #(nop) ADD file:1ee9e97209d00e3416a4543b23574cc7259684741a46bbcbc755909b8a053a38 in /",
|
||
|
"Size": 85178663
|
||
|
},
|
||
|
{
|
||
|
"Id": "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158",
|
||
|
"Tags": [
|
||
|
"scratch:latest"
|
||
|
],
|
||
|
"Created": 1371157430
|
||
|
}
|
||
|
]`
|
||
|
var expected []ImageHistory
|
||
|
err := json.Unmarshal([]byte(body), &expected)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||
|
history, err := client.ImageHistory("debian:latest")
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(history, expected) {
|
||
|
t.Errorf("ImageHistory: Wrong return value. Want %#v. Got %#v.", expected, history)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRemoveImage(t *testing.T) {
|
||
|
name := "test"
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||
|
client := newTestClient(fakeRT)
|
||
|
err := client.RemoveImage(name)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expectedMethod := "DELETE"
|
||
|
if req.Method != expectedMethod {
|
||
|
t.Errorf("RemoveImage(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
|
||
|
}
|
||
|
u, _ := url.Parse(client.getURL("/images/" + name))
|
||
|
if req.URL.Path != u.Path {
|
||
|
t.Errorf("RemoveImage(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRemoveImageNotFound(t *testing.T) {
|
||
|
client := newTestClient(&FakeRoundTripper{message: "no such image", status: http.StatusNotFound})
|
||
|
err := client.RemoveImage("test:")
|
||
|
if err != ErrNoSuchImage {
|
||
|
t.Errorf("RemoveImage: wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestRemoveImageExtended(t *testing.T) {
|
||
|
name := "test"
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||
|
client := newTestClient(fakeRT)
|
||
|
err := client.RemoveImageExtended(name, RemoveImageOptions{Force: true, NoPrune: true})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expectedMethod := "DELETE"
|
||
|
if req.Method != expectedMethod {
|
||
|
t.Errorf("RemoveImage(%q): Wrong HTTP method. Want %s. Got %s.", name, expectedMethod, req.Method)
|
||
|
}
|
||
|
u, _ := url.Parse(client.getURL("/images/" + name))
|
||
|
if req.URL.Path != u.Path {
|
||
|
t.Errorf("RemoveImage(%q): Wrong request path. Want %q. Got %q.", name, u.Path, req.URL.Path)
|
||
|
}
|
||
|
expectedQuery := "force=1&noprune=1"
|
||
|
if query := req.URL.Query().Encode(); query != expectedQuery {
|
||
|
t.Errorf("PushImage: Wrong query string. Want %q. Got %q.", expectedQuery, query)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInspectImage(t *testing.T) {
|
||
|
body := `{
|
||
|
"Id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||
|
"Parent":"27cf784147099545",
|
||
|
"Created":"2013-03-23T22:24:18.818426Z",
|
||
|
"Container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
|
||
|
"ContainerConfig":{"Memory":1},
|
||
|
"VirtualSize":12345
|
||
|
}`
|
||
|
|
||
|
created, err := time.Parse(time.RFC3339Nano, "2013-03-23T22:24:18.818426Z")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
expected := Image{
|
||
|
ID: "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
|
||
|
Parent: "27cf784147099545",
|
||
|
Created: created,
|
||
|
Container: "3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
|
||
|
ContainerConfig: Config{
|
||
|
Memory: 1,
|
||
|
},
|
||
|
VirtualSize: 12345,
|
||
|
}
|
||
|
fakeRT := &FakeRoundTripper{message: body, status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
image, err := client.InspectImage(expected.ID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(*image, expected) {
|
||
|
t.Errorf("InspectImage(%q): Wrong image returned. Want %#v. Got %#v.", expected.ID, expected, *image)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "GET" {
|
||
|
t.Errorf("InspectImage(%q): Wrong HTTP method. Want GET. Got %s.", expected.ID, req.Method)
|
||
|
}
|
||
|
u, _ := url.Parse(client.getURL("/images/" + expected.ID + "/json"))
|
||
|
if req.URL.Path != u.Path {
|
||
|
t.Errorf("InspectImage(%q): Wrong request URL. Want %q. Got %q.", expected.ID, u.Path, req.URL.Path)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInspectImageNotFound(t *testing.T) {
|
||
|
client := newTestClient(&FakeRoundTripper{message: "no such image", status: http.StatusNotFound})
|
||
|
name := "test"
|
||
|
image, err := client.InspectImage(name)
|
||
|
if image != nil {
|
||
|
t.Errorf("InspectImage(%q): expected <nil> image, got %#v.", name, image)
|
||
|
}
|
||
|
if err != ErrNoSuchImage {
|
||
|
t.Errorf("InspectImage(%q): wrong error. Want %#v. Got %#v.", name, ErrNoSuchImage, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPushImage(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
err := client.PushImage(PushImageOptions{Name: "test", OutputStream: &buf}, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expected := "Pushing 1/100"
|
||
|
if buf.String() != expected {
|
||
|
t.Errorf("PushImage: Wrong output. Want %q. Got %q.", expected, buf.String())
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "POST" {
|
||
|
t.Errorf("PushImage: Wrong HTTP method. Want POST. Got %s.", req.Method)
|
||
|
}
|
||
|
u, _ := url.Parse(client.getURL("/images/test/push"))
|
||
|
if req.URL.Path != u.Path {
|
||
|
t.Errorf("PushImage: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
|
||
|
}
|
||
|
if query := req.URL.Query().Encode(); query != "" {
|
||
|
t.Errorf("PushImage: Wrong query string. Want no parameters, got %q.", query)
|
||
|
}
|
||
|
|
||
|
auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth"))
|
||
|
if err != nil {
|
||
|
t.Errorf("PushImage: caught error decoding auth. %#v", err.Error())
|
||
|
}
|
||
|
if strings.TrimSpace(string(auth)) != "{}" {
|
||
|
t.Errorf("PushImage: wrong body. Want %q. Got %q.",
|
||
|
base64.URLEncoding.EncodeToString([]byte("{}")), req.Header.Get("X-Registry-Auth"))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPushImageWithRawJSON(t *testing.T) {
|
||
|
body := `
|
||
|
{"status":"Pushing..."}
|
||
|
{"status":"Pushing", "progress":"1/? (n/a)", "progressDetail":{"current":1}}}
|
||
|
{"status":"Image successfully pushed"}
|
||
|
`
|
||
|
fakeRT := &FakeRoundTripper{
|
||
|
message: body,
|
||
|
status: http.StatusOK,
|
||
|
header: map[string]string{
|
||
|
"Content-Type": "application/json",
|
||
|
},
|
||
|
}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
|
||
|
err := client.PushImage(PushImageOptions{
|
||
|
Name: "test",
|
||
|
OutputStream: &buf,
|
||
|
RawJSONStream: true,
|
||
|
}, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if buf.String() != body {
|
||
|
t.Errorf("PushImage: Wrong raw output. Want %q. Got %q.", body, buf.String())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPushImageWithAuthentication(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
inputAuth := AuthConfiguration{
|
||
|
Username: "gopher",
|
||
|
Password: "gopher123",
|
||
|
Email: "gopher@tsuru.io",
|
||
|
}
|
||
|
err := client.PushImage(PushImageOptions{Name: "test", OutputStream: &buf}, inputAuth)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
var gotAuth AuthConfiguration
|
||
|
|
||
|
auth, err := base64.URLEncoding.DecodeString(req.Header.Get("X-Registry-Auth"))
|
||
|
if err != nil {
|
||
|
t.Errorf("PushImage: caught error decoding auth. %#v", err.Error())
|
||
|
}
|
||
|
|
||
|
err = json.Unmarshal(auth, &gotAuth)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(gotAuth, inputAuth) {
|
||
|
t.Errorf("PushImage: wrong auth configuration. Want %#v. Got %#v.", inputAuth, gotAuth)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPushImageCustomRegistry(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pushing 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var authConfig AuthConfiguration
|
||
|
var buf bytes.Buffer
|
||
|
opts := PushImageOptions{
|
||
|
Name: "test", Registry: "docker.tsuru.io",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.PushImage(opts, authConfig)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expectedQuery := "registry=docker.tsuru.io"
|
||
|
if query := req.URL.Query().Encode(); query != expectedQuery {
|
||
|
t.Errorf("PushImage: Wrong query string. Want %q. Got %q.", expectedQuery, query)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPushImageNoName(t *testing.T) {
|
||
|
client := Client{}
|
||
|
err := client.PushImage(PushImageOptions{}, AuthConfiguration{})
|
||
|
if err != ErrNoSuchImage {
|
||
|
t.Errorf("PushImage: got wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImage(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
err := client.PullImage(PullImageOptions{Repository: "base", OutputStream: &buf},
|
||
|
AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
expected := "Pulling 1/100"
|
||
|
if buf.String() != expected {
|
||
|
t.Errorf("PullImage: Wrong output. Want %q. Got %q.", expected, buf.String())
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "POST" {
|
||
|
t.Errorf("PullImage: Wrong HTTP method. Want POST. Got %s.", req.Method)
|
||
|
}
|
||
|
u, _ := url.Parse(client.getURL("/images/create"))
|
||
|
if req.URL.Path != u.Path {
|
||
|
t.Errorf("PullImage: Wrong request path. Want %q. Got %q.", u.Path, req.URL.Path)
|
||
|
}
|
||
|
expectedQuery := "fromImage=base"
|
||
|
if query := req.URL.Query().Encode(); query != expectedQuery {
|
||
|
t.Errorf("PullImage: Wrong query strin. Want %q. Got %q.", expectedQuery, query)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImageWithRawJSON(t *testing.T) {
|
||
|
body := `
|
||
|
{"status":"Pulling..."}
|
||
|
{"status":"Pulling", "progress":"1 B/ 100 B", "progressDetail":{"current":1, "total":100}}
|
||
|
`
|
||
|
fakeRT := &FakeRoundTripper{
|
||
|
message: body,
|
||
|
status: http.StatusOK,
|
||
|
header: map[string]string{
|
||
|
"Content-Type": "application/json",
|
||
|
},
|
||
|
}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
err := client.PullImage(PullImageOptions{
|
||
|
Repository: "base",
|
||
|
OutputStream: &buf,
|
||
|
RawJSONStream: true,
|
||
|
}, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if buf.String() != body {
|
||
|
t.Errorf("PullImage: Wrong raw output. Want %q. Got %q", body, buf.String())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImageWithoutOutputStream(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := PullImageOptions{
|
||
|
Repository: "base",
|
||
|
Registry: "docker.tsuru.io",
|
||
|
}
|
||
|
err := client.PullImage(opts, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImageCustomRegistry(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := PullImageOptions{
|
||
|
Repository: "base",
|
||
|
Registry: "docker.tsuru.io",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.PullImage(opts, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImageTag(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "Pulling 1/100", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := PullImageOptions{
|
||
|
Repository: "base",
|
||
|
Registry: "docker.tsuru.io",
|
||
|
Tag: "latest",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.PullImage(opts, AuthConfiguration{})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromImage": {"base"}, "registry": {"docker.tsuru.io"}, "tag": {"latest"}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("PullImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestPullImageNoRepository(t *testing.T) {
|
||
|
var opts PullImageOptions
|
||
|
client := Client{}
|
||
|
err := client.PullImage(opts, AuthConfiguration{})
|
||
|
if err != ErrNoSuchImage {
|
||
|
t.Errorf("PullImage: got wrong error. Want %#v. Got %#v.", ErrNoSuchImage, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImportImageFromUrl(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := ImportImageOptions{
|
||
|
Source: "http://mycompany.com/file.tar",
|
||
|
Repository: "testimage",
|
||
|
Tag: "tag",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.ImportImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}, "tag": {opts.Tag}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImportImageFromInput(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
in := bytes.NewBufferString("tar content")
|
||
|
var buf bytes.Buffer
|
||
|
opts := ImportImageOptions{
|
||
|
Source: "-", Repository: "testimage",
|
||
|
InputStream: in, OutputStream: &buf,
|
||
|
Tag: "tag",
|
||
|
}
|
||
|
err := client.ImportImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}, "tag": {opts.Tag}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
body, err := ioutil.ReadAll(req.Body)
|
||
|
if err != nil {
|
||
|
t.Errorf("ImportImage: caugth error while reading body %#v", err.Error())
|
||
|
}
|
||
|
e := "tar content"
|
||
|
if string(body) != e {
|
||
|
t.Errorf("ImportImage: wrong body. Want %#v. Got %#v.", e, string(body))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImportImageDoesNotPassesInputIfSourceIsNotDash(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
in := bytes.NewBufferString("foo")
|
||
|
opts := ImportImageOptions{
|
||
|
Source: "http://test.com/container.tar", Repository: "testimage",
|
||
|
InputStream: in, OutputStream: &buf,
|
||
|
}
|
||
|
err := client.ImportImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromSrc": {opts.Source}, "repo": {opts.Repository}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
body, err := ioutil.ReadAll(req.Body)
|
||
|
if err != nil {
|
||
|
t.Errorf("ImportImage: caugth error while reading body %#v", err.Error())
|
||
|
}
|
||
|
if string(body) != "" {
|
||
|
t.Errorf("ImportImage: wrong body. Want nothing. Got %#v.", string(body))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImportImageShouldPassTarContentToBodyWhenSourceIsFilePath(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
tarPath := "testing/data/container.tar"
|
||
|
opts := ImportImageOptions{
|
||
|
Source: tarPath, Repository: "testimage",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.ImportImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
tar, err := os.Open(tarPath)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
tarContent, err := ioutil.ReadAll(tar)
|
||
|
body, err := ioutil.ReadAll(req.Body)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(tarContent, body) {
|
||
|
t.Errorf("ImportImage: wrong body. Want %#v content. Got %#v.", tarPath, body)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestImportImageShouldChangeSourceToDashWhenItsAFilePath(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
tarPath := "testing/data/container.tar"
|
||
|
opts := ImportImageOptions{
|
||
|
Source: tarPath, Repository: "testimage",
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.ImportImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"fromSrc": {"-"}, "repo": {opts.Repository}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("ImportImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageParameters(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := BuildImageOptions{
|
||
|
Name: "testImage",
|
||
|
NoCache: true,
|
||
|
SuppressOutput: true,
|
||
|
Pull: true,
|
||
|
RmTmpContainer: true,
|
||
|
ForceRmTmpContainer: true,
|
||
|
Memory: 1024,
|
||
|
Memswap: 2048,
|
||
|
CPUShares: 10,
|
||
|
CPUQuota: 7500,
|
||
|
CPUPeriod: 100000,
|
||
|
CPUSetCPUs: "0-3",
|
||
|
Ulimits: []ULimit{{Name: "nofile", Soft: 100, Hard: 200}},
|
||
|
InputStream: &buf,
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != nil && strings.Index(err.Error(), "build image fail") == -1 {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{
|
||
|
"t": {opts.Name},
|
||
|
"nocache": {"1"},
|
||
|
"q": {"1"},
|
||
|
"pull": {"1"},
|
||
|
"rm": {"1"},
|
||
|
"forcerm": {"1"},
|
||
|
"memory": {"1024"},
|
||
|
"memswap": {"2048"},
|
||
|
"cpushares": {"10"},
|
||
|
"cpuquota": {"7500"},
|
||
|
"cpuperiod": {"100000"},
|
||
|
"cpusetcpus": {"0-3"},
|
||
|
"ulimits": {"[{\"Name\":\"nofile\",\"Soft\":100,\"Hard\":200}]"},
|
||
|
}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageParametersForRemoteBuild(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := BuildImageOptions{
|
||
|
Name: "testImage",
|
||
|
Remote: "testing/data/container.tar",
|
||
|
SuppressOutput: true,
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"t": {opts.Name}, "remote": {opts.Remote}, "q": {"1"}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageMissingRepoAndNilInput(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := BuildImageOptions{
|
||
|
Name: "testImage",
|
||
|
SuppressOutput: true,
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != ErrMissingRepo {
|
||
|
t.Errorf("BuildImage: wrong error returned. Want %#v. Got %#v.", ErrMissingRepo, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageMissingOutputStream(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := BuildImageOptions{Name: "testImage"}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != ErrMissingOutputStream {
|
||
|
t.Errorf("BuildImage: wrong error returned. Want %#v. Got %#v.", ErrMissingOutputStream, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageWithRawJSON(t *testing.T) {
|
||
|
body := `
|
||
|
{"stream":"Step 0 : FROM ubuntu:latest\n"}
|
||
|
{"stream":" ---\u003e 4300eb9d3c8d\n"}
|
||
|
{"stream":"Step 1 : MAINTAINER docker <eng@docker.com>\n"}
|
||
|
{"stream":" ---\u003e Using cache\n"}
|
||
|
{"stream":" ---\u003e 3a3ed758c370\n"}
|
||
|
{"stream":"Step 2 : CMD /usr/bin/top\n"}
|
||
|
{"stream":" ---\u003e Running in 36b1479cc2e4\n"}
|
||
|
{"stream":" ---\u003e 4b6188aebe39\n"}
|
||
|
{"stream":"Removing intermediate container 36b1479cc2e4\n"}
|
||
|
{"stream":"Successfully built 4b6188aebe39\n"}
|
||
|
`
|
||
|
fakeRT := &FakeRoundTripper{
|
||
|
message: body,
|
||
|
status: http.StatusOK,
|
||
|
header: map[string]string{
|
||
|
"Content-Type": "application/json",
|
||
|
},
|
||
|
}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := BuildImageOptions{
|
||
|
Name: "testImage",
|
||
|
RmTmpContainer: true,
|
||
|
InputStream: &buf,
|
||
|
OutputStream: &buf,
|
||
|
RawJSONStream: true,
|
||
|
}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if buf.String() != body {
|
||
|
t.Errorf("BuildImage: Wrong raw output. Want %q. Got %q.", body, buf.String())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBuildImageRemoteWithoutName(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
var buf bytes.Buffer
|
||
|
opts := BuildImageOptions{
|
||
|
Remote: "testing/data/container.tar",
|
||
|
SuppressOutput: true,
|
||
|
OutputStream: &buf,
|
||
|
}
|
||
|
err := client.BuildImage(opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := map[string][]string{"t": {opts.Remote}, "remote": {opts.Remote}, "q": {"1"}}
|
||
|
got := map[string][]string(req.URL.Query())
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("BuildImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTagImageParameters(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := TagImageOptions{Repo: "testImage"}
|
||
|
err := client.TagImage("base", opts)
|
||
|
if err != nil && strings.Index(err.Error(), "tag image fail") == -1 {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
expected := "http://localhost:4243/images/base/tag?repo=testImage"
|
||
|
got := req.URL.String()
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("TagImage: wrong query string. Want %#v. Got %#v.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTagImageMissingRepo(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := TagImageOptions{Repo: "testImage"}
|
||
|
err := client.TagImage("", opts)
|
||
|
if err != ErrNoSuchImage {
|
||
|
t.Errorf("TestTag: wrong error returned. Want %#v. Got %#v.",
|
||
|
ErrNoSuchImage, err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIsUrl(t *testing.T) {
|
||
|
url := "http://foo.bar/"
|
||
|
result := isURL(url)
|
||
|
if !result {
|
||
|
t.Errorf("isURL: wrong match. Expected %#v to be a url. Got %#v.", url, result)
|
||
|
}
|
||
|
url = "/foo/bar.tar"
|
||
|
result = isURL(url)
|
||
|
if result {
|
||
|
t.Errorf("isURL: wrong match. Expected %#v to not be a url. Got %#v", url, result)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestLoadImage(t *testing.T) {
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
tar, err := os.Open("testing/data/container.tar")
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
} else {
|
||
|
defer tar.Close()
|
||
|
}
|
||
|
opts := LoadImageOptions{InputStream: tar}
|
||
|
err = client.LoadImage(opts)
|
||
|
if nil != err {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "POST" {
|
||
|
t.Errorf("LoadImage: wrong method. Expected %q. Got %q.", "POST", req.Method)
|
||
|
}
|
||
|
if req.URL.Path != "/images/load" {
|
||
|
t.Errorf("LoadImage: wrong URL. Expected %q. Got %q.", "/images/load", req.URL.Path)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestExportImage(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := ExportImageOptions{Name: "testimage", OutputStream: &buf}
|
||
|
err := client.ExportImage(opts)
|
||
|
if nil != err {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "GET" {
|
||
|
t.Errorf("ExportImage: wrong method. Expected %q. Got %q.", "GET", req.Method)
|
||
|
}
|
||
|
expectedPath := "/images/testimage/get"
|
||
|
if req.URL.Path != expectedPath {
|
||
|
t.Errorf("ExportIMage: wrong path. Expected %q. Got %q.", expectedPath, req.URL.Path)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestExportImages(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := ExportImagesOptions{Names: []string{"testimage1", "testimage2:latest"}, OutputStream: &buf}
|
||
|
err := client.ExportImages(opts)
|
||
|
if nil != err {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
req := fakeRT.requests[0]
|
||
|
if req.Method != "GET" {
|
||
|
t.Errorf("ExportImage: wrong method. Expected %q. Got %q.", "GET", req.Method)
|
||
|
}
|
||
|
expected := "http://localhost:4243/images/get?names=testimage1&names=testimage2%3Alatest"
|
||
|
got := req.URL.String()
|
||
|
if !reflect.DeepEqual(got, expected) {
|
||
|
t.Errorf("ExportIMage: wrong path. Expected %q. Got %q.", expected, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestExportImagesNoNames(t *testing.T) {
|
||
|
var buf bytes.Buffer
|
||
|
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||
|
client := newTestClient(fakeRT)
|
||
|
opts := ExportImagesOptions{Names: []string{}, OutputStream: &buf}
|
||
|
err := client.ExportImages(opts)
|
||
|
if err == nil {
|
||
|
t.Error("Expected an error")
|
||
|
}
|
||
|
if err != ErrMustSpecifyNames {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSearchImages(t *testing.T) {
|
||
|
body := `[
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":true,
|
||
|
"is_automated":true,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
},
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":true,
|
||
|
"is_automated":false,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":false,
|
||
|
"is_automated":true,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
}
|
||
|
]`
|
||
|
var expected []APIImageSearch
|
||
|
err := json.Unmarshal([]byte(body), &expected)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||
|
result, err := client.SearchImages("cassandra")
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(result, expected) {
|
||
|
t.Errorf("SearchImages: Wrong return value. Want %#v. Got %#v.", expected, result)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSearchImagesEx(t *testing.T) {
|
||
|
body := `[
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":true,
|
||
|
"is_automated":true,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
},
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":true,
|
||
|
"is_automated":false,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
}
|
||
|
,
|
||
|
{
|
||
|
"description":"A container with Cassandra 2.0.3",
|
||
|
"is_official":false,
|
||
|
"is_automated":true,
|
||
|
"name":"poklet/cassandra",
|
||
|
"star_count":17
|
||
|
}
|
||
|
]`
|
||
|
var expected []APIImageSearch
|
||
|
err := json.Unmarshal([]byte(body), &expected)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
client := newTestClient(&FakeRoundTripper{message: body, status: http.StatusOK})
|
||
|
auth := AuthConfiguration{}
|
||
|
result, err := client.SearchImagesEx("cassandra", auth)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if !reflect.DeepEqual(result, expected) {
|
||
|
t.Errorf("SearchImages: Wrong return value. Want %#v. Got %#v.", expected, result)
|
||
|
}
|
||
|
}
|