mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
Merge pull request #1235 from weaveworks/970-reinstating-restarting-ruckus
Update docker client, to get better state strings in the UI
This commit is contained in:
@@ -31,6 +31,7 @@ const (
|
||||
ContainerHostname = "docker_container_hostname"
|
||||
ContainerIPsWithScopes = "docker_container_ips_with_scopes"
|
||||
ContainerState = "docker_container_state"
|
||||
ContainerStateHuman = "docker_container_state_human"
|
||||
ContainerUptime = "docker_container_uptime"
|
||||
ContainerRestartCount = "docker_container_restart_count"
|
||||
ContainerNetworkMode = "docker_container_network_mode"
|
||||
@@ -55,9 +56,12 @@ const (
|
||||
CPUUsageInKernelmode = "docker_cpu_usage_in_kernelmode"
|
||||
CPUSystemCPUUsage = "docker_cpu_system_cpu_usage"
|
||||
|
||||
StateRunning = "running"
|
||||
StateStopped = "stopped"
|
||||
StatePaused = "paused"
|
||||
StateCreated = "created"
|
||||
StateDead = "dead"
|
||||
StateExited = "exited"
|
||||
StatePaused = "paused"
|
||||
StateRestarting = "restarting"
|
||||
StateRunning = "running"
|
||||
|
||||
NetworkModeHost = "host"
|
||||
|
||||
@@ -90,6 +94,7 @@ type Container interface {
|
||||
Hostname() string
|
||||
GetNode(string, []net.IP) report.Node
|
||||
State() string
|
||||
StateString() string
|
||||
HasTTY() bool
|
||||
Container() *docker.Container
|
||||
StartGatheringStats() error
|
||||
@@ -144,12 +149,11 @@ func (c *container) HasTTY() bool {
|
||||
}
|
||||
|
||||
func (c *container) State() string {
|
||||
if c.container.State.Paused {
|
||||
return StatePaused
|
||||
} else if c.container.State.Running {
|
||||
return StateRunning
|
||||
}
|
||||
return StateStopped
|
||||
return c.container.State.String()
|
||||
}
|
||||
|
||||
func (c *container) StateString() string {
|
||||
return c.container.State.StateString()
|
||||
}
|
||||
|
||||
func (c *container) Container() *docker.Container {
|
||||
@@ -333,16 +337,15 @@ func (c *container) GetNode(hostID string, localAddrs []net.IP) report.Node {
|
||||
ipsWithScopes = append(ipsWithScopes, report.MakeScopedAddressNodeID(hostID, ip))
|
||||
}
|
||||
|
||||
state := c.State()
|
||||
|
||||
result := report.MakeNodeWith(map[string]string{
|
||||
ContainerID: c.ID(),
|
||||
ContainerName: strings.TrimPrefix(c.container.Name, "/"),
|
||||
ContainerCreated: c.container.Created.Format(time.RFC822),
|
||||
ContainerCommand: c.container.Path + " " + strings.Join(c.container.Args, " "),
|
||||
ImageID: c.Image(),
|
||||
ContainerHostname: c.Hostname(),
|
||||
ContainerState: state,
|
||||
ContainerID: c.ID(),
|
||||
ContainerName: strings.TrimPrefix(c.container.Name, "/"),
|
||||
ContainerCreated: c.container.Created.Format(time.RFC822),
|
||||
ContainerCommand: c.container.Path + " " + strings.Join(c.container.Args, " "),
|
||||
ImageID: c.Image(),
|
||||
ContainerHostname: c.Hostname(),
|
||||
ContainerState: c.StateString(),
|
||||
ContainerStateHuman: c.State(),
|
||||
}).WithSets(report.EmptySets.
|
||||
Add(ContainerPorts, c.ports(localAddrs)).
|
||||
Add(ContainerIPs, report.MakeStringSet(ips...)).
|
||||
@@ -390,3 +393,9 @@ func ExtractContainerIPsWithScopes(nmd report.Node) []string {
|
||||
v, _ := nmd.Sets.Lookup(ContainerIPsWithScopes)
|
||||
return []string(v)
|
||||
}
|
||||
|
||||
// ContainerIsStopped checks if the docker container is in one of our "stopped" states
|
||||
func ContainerIsStopped(c Container) bool {
|
||||
state := c.StateString()
|
||||
return (state != StateRunning && state != StateRestarting && state != StatePaused)
|
||||
}
|
||||
|
||||
@@ -74,15 +74,16 @@ func TestContainer(t *testing.T) {
|
||||
// Now see if we go them
|
||||
uptime := (now.Sub(startTime) / time.Second) * time.Second
|
||||
want := report.MakeNode().WithLatests(map[string]string{
|
||||
"docker_container_command": " ",
|
||||
"docker_container_created": "01 Jan 01 00:00 UTC",
|
||||
"docker_container_id": "ping",
|
||||
"docker_container_name": "pong",
|
||||
"docker_image_id": "baz",
|
||||
"docker_label_foo1": "bar1",
|
||||
"docker_label_foo2": "bar2",
|
||||
"docker_container_state": "running",
|
||||
"docker_container_uptime": uptime.String(),
|
||||
"docker_container_command": " ",
|
||||
"docker_container_created": "01 Jan 01 00:00 UTC",
|
||||
"docker_container_id": "ping",
|
||||
"docker_container_name": "pong",
|
||||
"docker_image_id": "baz",
|
||||
"docker_label_foo1": "bar1",
|
||||
"docker_label_foo2": "bar2",
|
||||
"docker_container_state": "running",
|
||||
"docker_container_state_human": "Up 6 years",
|
||||
"docker_container_uptime": uptime.String(),
|
||||
}).WithSets(report.EmptySets.
|
||||
Add("docker_container_ports", report.MakeStringSet("1.2.3.4:80->80/tcp", "81/tcp")).
|
||||
Add("docker_container_ips", report.MakeStringSet("1.2.3.4")).
|
||||
|
||||
@@ -39,6 +39,10 @@ func (c *mockContainer) Hostname() string {
|
||||
}
|
||||
|
||||
func (c *mockContainer) State() string {
|
||||
return "Up 3 minutes"
|
||||
}
|
||||
|
||||
func (c *mockContainer) StateString() string {
|
||||
return docker.StateRunning
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ func (t *Tagger) tag(tree process.Tree, topology *report.Topology) {
|
||||
}
|
||||
})
|
||||
|
||||
if c == nil || c.State() == StateStopped || c.PID() == 1 {
|
||||
if c == nil || ContainerIsStopped(c) || c.PID() == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ var labels = map[string]string{
|
||||
docker.ContainerIPs: "IPs",
|
||||
docker.ContainerPorts: "Ports",
|
||||
docker.ContainerRestartCount: "Restart #",
|
||||
docker.ContainerState: "State",
|
||||
docker.ContainerStateHuman: "State",
|
||||
docker.ContainerUptime: "Uptime",
|
||||
docker.ImageID: "Image ID",
|
||||
docker.MemoryUsage: "Memory",
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
}
|
||||
containerNodeMetadata = []MetadataRowTemplate{
|
||||
Latest{ID: docker.ContainerID, Truncate: 12, Prime: true},
|
||||
Latest{ID: docker.ContainerState, Prime: true},
|
||||
Latest{ID: docker.ContainerStateHuman, Prime: true},
|
||||
Latest{ID: docker.ContainerCommand, Prime: true},
|
||||
Latest{ID: docker.ImageID, Truncate: 12},
|
||||
Latest{ID: docker.ContainerUptime},
|
||||
|
||||
@@ -22,13 +22,13 @@ func TestNodeMetadata(t *testing.T) {
|
||||
node: report.MakeNodeWith(map[string]string{
|
||||
docker.ContainerID: fixture.ClientContainerID,
|
||||
docker.LabelPrefix + "label1": "label1value",
|
||||
docker.ContainerState: docker.StateRunning,
|
||||
docker.ContainerStateHuman: docker.StateRunning,
|
||||
}).WithTopology(report.Container).WithSets(report.EmptySets.
|
||||
Add(docker.ContainerIPs, report.MakeStringSet("10.10.10.0/24", "10.10.10.1/24")),
|
||||
),
|
||||
want: []detailed.MetadataRow{
|
||||
{ID: docker.ContainerID, Value: fixture.ClientContainerID, Prime: true},
|
||||
{ID: docker.ContainerState, Value: "running", Prime: true},
|
||||
{ID: docker.ContainerStateHuman, Value: "running", Prime: true},
|
||||
{ID: docker.ContainerIPs, Value: "10.10.10.0/24, 10.10.10.1/24"},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -178,7 +178,7 @@ func TestMakeDetailedContainerNode(t *testing.T) {
|
||||
Pseudo: false,
|
||||
Metadata: []detailed.MetadataRow{
|
||||
{ID: "docker_container_id", Value: fixture.ServerContainerID, Prime: true},
|
||||
{ID: "docker_container_state", Value: "running", Prime: true},
|
||||
{ID: "docker_container_state_human", Value: "running", Prime: true},
|
||||
{ID: "docker_image_id", Value: fixture.ServerContainerImageID},
|
||||
},
|
||||
DockerLabels: []detailed.MetadataRow{
|
||||
|
||||
@@ -161,18 +161,18 @@ func FilterNoop(in Renderer) Renderer {
|
||||
|
||||
// FilterStopped filters out stopped containers.
|
||||
func FilterStopped(r Renderer) Renderer {
|
||||
return MakeFilter(IsStopped, r)
|
||||
return MakeFilter(IsRunning, r)
|
||||
}
|
||||
|
||||
// IsStopped checks if the node is a stopped docker container
|
||||
func IsStopped(n report.Node) bool {
|
||||
containerState, ok := n.Latest.Lookup(docker.ContainerState)
|
||||
return !ok || containerState != docker.StateStopped
|
||||
// IsRunning checks if the node is a running docker container
|
||||
func IsRunning(n report.Node) bool {
|
||||
state, ok := n.Latest.Lookup(docker.ContainerState)
|
||||
return !ok || (state == docker.StateRunning || state == docker.StateRestarting || state == docker.StatePaused)
|
||||
}
|
||||
|
||||
// FilterRunning filters out running containers.
|
||||
func FilterRunning(r Renderer) Renderer {
|
||||
return MakeFilter(Complement(IsStopped), r)
|
||||
return MakeFilter(Complement(IsRunning), r)
|
||||
}
|
||||
|
||||
// FilterNonProcspied removes endpoints which were not found in procspy.
|
||||
|
||||
@@ -258,6 +258,7 @@ var (
|
||||
kubernetes.PodID: ClientPodID,
|
||||
kubernetes.Namespace: KubernetesNamespace,
|
||||
docker.ContainerState: docker.StateRunning,
|
||||
docker.ContainerStateHuman: docker.StateRunning,
|
||||
}).WithID(ClientContainerNodeID).WithTopology(report.Container).WithParents(report.EmptySets.
|
||||
Add("host", report.MakeStringSet(ClientHostNodeID)).
|
||||
Add("container_image", report.MakeStringSet(ClientContainerImageNodeID)).
|
||||
@@ -270,6 +271,7 @@ var (
|
||||
docker.ContainerID: ServerContainerID,
|
||||
docker.ContainerName: "task-name-5-server-aceb93e2f2b797caba01",
|
||||
docker.ContainerState: docker.StateRunning,
|
||||
docker.ContainerStateHuman: docker.StateRunning,
|
||||
docker.ImageID: ServerContainerImageID,
|
||||
report.HostNodeID: ServerHostNodeID,
|
||||
docker.LabelPrefix + detailed.AmazonECSContainerNameLabel: "server",
|
||||
|
||||
7
vendor/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
7
vendor/github.com/fsouza/go-dockerclient/AUTHORS
generated
vendored
@@ -14,6 +14,7 @@ Ben Marini <ben@remind101.com>
|
||||
Ben McCann <benmccann.com>
|
||||
Ben Parees <bparees@redhat.com>
|
||||
Benno van den Berg <bennovandenberg@gmail.com>
|
||||
Bradley Cicenas <bradley.cicenas@gmail.com>
|
||||
Brendan Fosberry <brendan@codeship.com>
|
||||
Brian Lalor <blalor@bravo5.org>
|
||||
Brian P. Hamachek <brian@brianhama.com>
|
||||
@@ -48,6 +49,8 @@ Fabio Rehm <fgrehm@gmail.com>
|
||||
Fatih Arslan <ftharsln@gmail.com>
|
||||
Flavia Missi <flaviamissi@gmail.com>
|
||||
Francisco Souza <f@souza.cc>
|
||||
Frank Groeneveld <frank@frankgroeneveld.nl>
|
||||
George Moura <gwmoura@gmail.com>
|
||||
Grégoire Delattre <gregoire.delattre@gmail.com>
|
||||
Guillermo Álvarez Fernández <guillermo@cientifico.net>
|
||||
Harry Zhang <harryzhang@zju.edu.cn>
|
||||
@@ -84,7 +87,9 @@ Michael Schmatz <michaelschmatz@gmail.com>
|
||||
Michal Fojtik <mfojtik@redhat.com>
|
||||
Mike Dillon <mike.dillon@synctree.com>
|
||||
Mrunal Patel <mrunalp@gmail.com>
|
||||
Nate Jones <nate@endot.org>
|
||||
Nguyen Sy Thanh Son <sonnst@sigma-solutions.eu>
|
||||
Nicholas Van Wiggeren <nvanwiggeren@digitalocean.com>
|
||||
Nick Ethier <ncethier@gmail.com>
|
||||
Omeid Matten <public@omeid.me>
|
||||
Orivej Desh <orivej@gmx.fr>
|
||||
@@ -98,9 +103,11 @@ Philippe Lafoucrière <philippe.lafoucriere@tech-angels.com>
|
||||
Rafe Colton <rafael.colton@gmail.com>
|
||||
Rob Miller <rob@kalistra.com>
|
||||
Robert Williamson <williamson.robert@gmail.com>
|
||||
Roman Khlystik <roman.khlystik@gmail.com>
|
||||
Salvador Gironès <salvadorgirones@gmail.com>
|
||||
Sam Rijs <srijs@airpost.net>
|
||||
Sami Wagiaalla <swagiaal@redhat.com>
|
||||
Samuel Archambault <sarchambault@lapresse.ca>
|
||||
Samuel Karp <skarp@amazon.com>
|
||||
Silas Sewell <silas@sewell.org>
|
||||
Simon Eskildsen <sirup@sirupsen.com>
|
||||
|
||||
2
vendor/github.com/fsouza/go-dockerclient/LICENSE
generated
vendored
2
vendor/github.com/fsouza/go-dockerclient/LICENSE
generated
vendored
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2015, go-dockerclient authors
|
||||
Copyright (c) 2016, go-dockerclient authors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
28
vendor/github.com/fsouza/go-dockerclient/Makefile
generated
vendored
28
vendor/github.com/fsouza/go-dockerclient/Makefile
generated
vendored
@@ -11,8 +11,7 @@
|
||||
cov \
|
||||
clean
|
||||
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^external/')
|
||||
PKGS = ./. ./testing
|
||||
PKGS = . ./testing
|
||||
|
||||
all: test
|
||||
|
||||
@@ -22,32 +21,31 @@ vendor:
|
||||
|
||||
lint:
|
||||
@ go get -v github.com/golang/lint/golint
|
||||
$(foreach file,$(SRCS),golint $(file) || exit;)
|
||||
@for file in $$(git ls-files '*.go' | grep -v 'external/'); do \
|
||||
export output="$$(golint $${file} | grep -v 'type name will be used as docker.DockerInfo')"; \
|
||||
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
|
||||
done; \
|
||||
exit $${status:-0}
|
||||
|
||||
vet:
|
||||
@-go get -v golang.org/x/tools/cmd/vet
|
||||
$(foreach pkg,$(PKGS),go vet $(pkg);)
|
||||
|
||||
fmt:
|
||||
gofmt -w $(SRCS)
|
||||
gofmt -s -w $(PKGS)
|
||||
|
||||
fmtcheck:
|
||||
$(foreach file,$(SRCS),gofmt -d $(file);)
|
||||
|
||||
prepare_docker:
|
||||
sudo stop docker
|
||||
sudo rm -rf /var/lib/docker
|
||||
sudo rm -f `which docker`
|
||||
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install docker-engine=$(DOCKER_VERSION)-0~$(shell lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
||||
@ export output=$$(gofmt -s -d $(PKGS)); \
|
||||
[ -n "$${output}" ] && echo "$${output}" && export status=1; \
|
||||
exit $${status:-0}
|
||||
|
||||
pretest: lint vet fmtcheck
|
||||
|
||||
test: pretest
|
||||
gotest:
|
||||
$(foreach pkg,$(PKGS),go test $(pkg) || exit;)
|
||||
|
||||
test: pretest gotest
|
||||
|
||||
integration:
|
||||
go test -tags docker_integration -run TestIntegration -v
|
||||
|
||||
|
||||
2
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
2
vendor/github.com/fsouza/go-dockerclient/README.markdown
generated
vendored
@@ -4,7 +4,7 @@
|
||||
[](https://godoc.org/github.com/fsouza/go-dockerclient)
|
||||
|
||||
This package presents a client for the Docker remote API. It also provides
|
||||
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/api/swarm-api/).
|
||||
support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/).
|
||||
|
||||
This package also provides support for docker's network API, which is a simple
|
||||
passthrough to the libnetwork remote API. Note that docker's network API is
|
||||
|
||||
8
vendor/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
8
vendor/github.com/fsouza/go-dockerclient/auth.go
generated
vendored
@@ -82,10 +82,12 @@ func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) {
|
||||
buf.ReadFrom(r)
|
||||
byteData := buf.Bytes()
|
||||
|
||||
var confsWrapper map[string]map[string]dockerConfig
|
||||
confsWrapper := struct {
|
||||
Auths map[string]dockerConfig `json:"auths"`
|
||||
}{}
|
||||
if err := json.Unmarshal(byteData, &confsWrapper); err == nil {
|
||||
if confs, ok := confsWrapper["auths"]; ok {
|
||||
return confs, nil
|
||||
if len(confsWrapper.Auths) > 0 {
|
||||
return confsWrapper.Auths, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
vendor/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
28
vendor/github.com/fsouza/go-dockerclient/auth_test.go
generated
vendored
@@ -49,6 +49,34 @@ func TestAuthBadConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthAndOtherFields(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
|
||||
read := strings.NewReader(fmt.Sprintf(`{
|
||||
"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}},
|
||||
"detachKeys": "ctrl-e,e",
|
||||
"HttpHeaders": { "MyHeader": "MyValue" }}`, auth))
|
||||
|
||||
ac, err := NewAuthConfigurations(read)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c, ok := ac.Configs["docker.io"]
|
||||
if !ok {
|
||||
t.Error("NewAuthConfigurations: Expected Configs to contain docker.io")
|
||||
}
|
||||
if got, want := c.Email, "user@example.com"; got != want {
|
||||
t.Errorf(`AuthConfigurations.Configs["docker.io"].Email: wrong result. Want %q. Got %q`, want, got)
|
||||
}
|
||||
if got, want := c.Username, "user"; got != want {
|
||||
t.Errorf(`AuthConfigurations.Configs["docker.io"].Username: wrong result. Want %q. Got %q`, want, got)
|
||||
}
|
||||
if got, want := c.Password, "pass"; got != want {
|
||||
t.Errorf(`AuthConfigurations.Configs["docker.io"].Password: wrong result. Want %q. Got %q`, want, got)
|
||||
}
|
||||
if got, want := c.ServerAddress, "docker.io"; got != want {
|
||||
t.Errorf(`AuthConfigurations.Configs["docker.io"].ServerAddress: wrong result. Want %q. Got %q`, want, got)
|
||||
}
|
||||
}
|
||||
func TestAuthConfig(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("user:pass"))
|
||||
read := strings.NewReader(fmt.Sprintf(`{"auths":{"docker.io":{"auth":"%s","email":"user@example.com"}}}`, auth))
|
||||
|
||||
2
vendor/github.com/fsouza/go-dockerclient/client.go
generated
vendored
2
vendor/github.com/fsouza/go-dockerclient/client.go
generated
vendored
@@ -555,6 +555,8 @@ type hijackOptions struct {
|
||||
data interface{}
|
||||
}
|
||||
|
||||
// CloseWaiter is an interface with methods for closing the underlying resource
|
||||
// and then waiting for it to finish processing.
|
||||
type CloseWaiter interface {
|
||||
io.Closer
|
||||
Wait() error
|
||||
|
||||
212
vendor/github.com/fsouza/go-dockerclient/container.go
generated
vendored
212
vendor/github.com/fsouza/go-dockerclient/container.go
generated
vendored
@@ -14,6 +14,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fsouza/go-dockerclient/external/github.com/docker/go-units"
|
||||
)
|
||||
|
||||
// ErrContainerAlreadyExists is the error returned by CreateContainer when the
|
||||
@@ -52,7 +54,14 @@ type APIContainers struct {
|
||||
SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty"`
|
||||
SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty"`
|
||||
Names []string `json:"Names,omitempty" yaml:"Names,omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels, omitempty"`
|
||||
Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty"`
|
||||
Networks NetworkList `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkList encapsulates a map of networks, as returned by the Docker API in
|
||||
// ListContainers.
|
||||
type NetworkList struct {
|
||||
Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty"`
|
||||
}
|
||||
|
||||
// ListContainers returns a slice of containers matching the given criteria.
|
||||
@@ -92,26 +101,73 @@ func (p Port) Proto() string {
|
||||
|
||||
// State represents the state of a container.
|
||||
type State struct {
|
||||
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
|
||||
Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
|
||||
Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
|
||||
OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"`
|
||||
Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
|
||||
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
|
||||
Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
|
||||
StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"`
|
||||
FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"`
|
||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
||||
Running bool `json:"Running,omitempty" yaml:"Running,omitempty"`
|
||||
Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty"`
|
||||
Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty"`
|
||||
OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty"`
|
||||
RemovalInProgress bool `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty"`
|
||||
Dead bool `json:"Dead,omitempty" yaml:"Dead,omitempty"`
|
||||
Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty"`
|
||||
ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty"`
|
||||
Error string `json:"Error,omitempty" yaml:"Error,omitempty"`
|
||||
StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty"`
|
||||
FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty"`
|
||||
}
|
||||
|
||||
// String returns the string representation of a state.
|
||||
// String returns a human-readable description of the state
|
||||
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.RemovalInProgress {
|
||||
return "Removal In Progress"
|
||||
}
|
||||
|
||||
if s.Dead {
|
||||
return "Dead"
|
||||
}
|
||||
|
||||
if s.StartedAt.IsZero() {
|
||||
return "Created"
|
||||
}
|
||||
|
||||
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
|
||||
func (s *State) StateString() string {
|
||||
if s.Running {
|
||||
if s.Paused {
|
||||
return "paused"
|
||||
}
|
||||
return fmt.Sprintf("Up %s", time.Now().UTC().Sub(s.StartedAt))
|
||||
if s.Restarting {
|
||||
return "restarting"
|
||||
}
|
||||
return "running"
|
||||
}
|
||||
return fmt.Sprintf("Exit %d", s.ExitCode)
|
||||
|
||||
if s.Dead {
|
||||
return "dead"
|
||||
}
|
||||
|
||||
if s.StartedAt.IsZero() {
|
||||
return "created"
|
||||
}
|
||||
|
||||
return "exited"
|
||||
}
|
||||
|
||||
// PortBinding represents the host/container port mapping as returned in the
|
||||
@@ -135,6 +191,7 @@ type ContainerNetwork struct {
|
||||
IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty"`
|
||||
Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty"`
|
||||
EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty"`
|
||||
NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkSettings contains network-related information about a container
|
||||
@@ -308,6 +365,34 @@ type Container struct {
|
||||
AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateContainerOptions specify parameters to the UpdateContainer function.
|
||||
//
|
||||
// See https://goo.gl/Y6fXUy for more details.
|
||||
type UpdateContainerOptions struct {
|
||||
BlkioWeight int `json:"BlkioWeight"`
|
||||
CPUShares int `json:"CpuShares"`
|
||||
CPUPeriod int `json:"CpuPeriod"`
|
||||
CPUQuota int `json:"CpuQuota"`
|
||||
CpusetCpus string `json:"CpusetCpus"`
|
||||
CpusetMems string `json:"CpusetMems"`
|
||||
Memory int `json:"Memory"`
|
||||
MemorySwap int `json:"MemorySwap"`
|
||||
MemoryReservation int `json:"MemoryReservation"`
|
||||
KernelMemory int `json:"KernelMemory"`
|
||||
}
|
||||
|
||||
// UpdateContainer updates the container at ID with the options
|
||||
//
|
||||
// See https://goo.gl/Y6fXUy for more details.
|
||||
func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error {
|
||||
resp, err := c.do("POST", fmt.Sprintf("/containers/"+id+"/update"), doOptions{data: opts, forceJSON: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// RenameContainerOptions specify parameters to the RenameContainer function.
|
||||
//
|
||||
// See https://goo.gl/laSOIy for more details.
|
||||
@@ -469,48 +554,71 @@ type Device struct {
|
||||
CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty"`
|
||||
}
|
||||
|
||||
// BlockWeight represents a relative device weight for an individual device inside
|
||||
// of a container
|
||||
//
|
||||
// See https://goo.gl/FSdP0H for more details.
|
||||
type BlockWeight struct {
|
||||
Path string `json:"Path,omitempty"`
|
||||
Weight string `json:"Weight,omitempty"`
|
||||
}
|
||||
|
||||
// BlockLimit represents a read/write limit in IOPS or Bandwidth for a device
|
||||
// inside of a container
|
||||
//
|
||||
// See https://goo.gl/FSdP0H for more details.
|
||||
type BlockLimit struct {
|
||||
Path string `json:"Path,omitempty"`
|
||||
Rate string `json:"Rate,omitempty"`
|
||||
}
|
||||
|
||||
// HostConfig contains the container options related to starting a container on
|
||||
// a given host
|
||||
type HostConfig struct {
|
||||
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
||||
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
||||
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
||||
GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"`
|
||||
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
||||
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
||||
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
||||
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
|
||||
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
|
||||
DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"`
|
||||
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
||||
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
||||
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
|
||||
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
|
||||
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
||||
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
||||
CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
|
||||
CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
|
||||
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
|
||||
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
|
||||
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
|
||||
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
|
||||
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
|
||||
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`
|
||||
Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty"`
|
||||
CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty"`
|
||||
CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty"`
|
||||
GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty"`
|
||||
ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty"`
|
||||
LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty"`
|
||||
Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty"`
|
||||
PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty"`
|
||||
Links []string `json:"Links,omitempty" yaml:"Links,omitempty"`
|
||||
PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty"`
|
||||
DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty"` // For Docker API v1.10 and above only
|
||||
DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty"`
|
||||
DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty"`
|
||||
ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty"`
|
||||
VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty"`
|
||||
NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty"`
|
||||
IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty"`
|
||||
PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty"`
|
||||
UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty"`
|
||||
RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty"`
|
||||
Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty"`
|
||||
LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty"`
|
||||
ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty"`
|
||||
SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty"`
|
||||
CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty"`
|
||||
Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty"`
|
||||
MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty"`
|
||||
MemorySwappiness int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty"`
|
||||
OOMKillDisable bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable"`
|
||||
CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty"`
|
||||
CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty"`
|
||||
CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty"`
|
||||
CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty"`
|
||||
CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty"`
|
||||
CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty"`
|
||||
BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight"`
|
||||
BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice"`
|
||||
BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps"`
|
||||
BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps"`
|
||||
BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps"`
|
||||
BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps"`
|
||||
Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty"`
|
||||
VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty"`
|
||||
OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty"`
|
||||
}
|
||||
|
||||
// StartContainer starts a container, returning an error in case of failure.
|
||||
|
||||
84
vendor/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
84
vendor/github.com/fsouza/go-dockerclient/container_test.go
generated
vendored
@@ -16,7 +16,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,13 +31,37 @@ func TestStateString(t *testing.T) {
|
||||
input State
|
||||
expected string
|
||||
}{
|
||||
{State{Running: true, Paused: true}, "^paused$"},
|
||||
{State{Running: true, StartedAt: started}, "^Up 3h.*$"},
|
||||
{State{Running: false, ExitCode: 7}, "^Exit 7$"},
|
||||
{State{Running: true, Paused: true, StartedAt: started}, "Up 3 hours (Paused)"},
|
||||
{State{Running: true, Restarting: true, ExitCode: 7, FinishedAt: started}, "Restarting (7) 3 hours ago"},
|
||||
{State{Running: true, StartedAt: started}, "Up 3 hours"},
|
||||
{State{RemovalInProgress: true}, "Removal In Progress"},
|
||||
{State{Dead: true}, "Dead"},
|
||||
{State{}, "Created"},
|
||||
{State{StartedAt: started}, ""},
|
||||
{State{ExitCode: 7, StartedAt: started, FinishedAt: started}, "Exited (7) 3 hours ago"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
re := regexp.MustCompile(tt.expected)
|
||||
if got := tt.input.String(); !re.MatchString(got) {
|
||||
if got := tt.input.String(); got != tt.expected {
|
||||
t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStateString(t *testing.T) {
|
||||
started := time.Now().Add(-3 * time.Hour)
|
||||
var tests = []struct {
|
||||
input State
|
||||
expected string
|
||||
}{
|
||||
{State{Running: true, Paused: true}, "paused"},
|
||||
{State{Running: true, Restarting: true}, "restarting"},
|
||||
{State{Running: true}, "running"},
|
||||
{State{Dead: true}, "dead"},
|
||||
{State{}, "created"},
|
||||
{State{StartedAt: started}, "exited"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := tt.input.StateString(); got != tt.expected {
|
||||
t.Errorf("State.String(): wrong result. Want %q. Got %q.", tt.expected, got)
|
||||
}
|
||||
}
|
||||
@@ -438,6 +461,7 @@ func TestInspectContainerNetwork(t *testing.T) {
|
||||
"MacAddress": "",
|
||||
"Networks": {
|
||||
"swl-net": {
|
||||
"NetworkID": "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812",
|
||||
"EndpointID": "683e3092275782a53c3b0968cc7e3a10f23264022ded9cb20490902f96fc5981",
|
||||
"Gateway": "",
|
||||
"IPAddress": "10.0.0.3",
|
||||
@@ -454,7 +478,8 @@ func TestInspectContainerNetwork(t *testing.T) {
|
||||
fakeRT := &FakeRoundTripper{message: jsonContainer, status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
id := "81e1bbe20b55"
|
||||
exp := "10.0.0.3"
|
||||
expIP := "10.0.0.3"
|
||||
expNetworkID := "7ea29fc1412292a2d7bba362f9253545fecdfa8ce9a6e37dd10ba8bee7129812"
|
||||
|
||||
container, err := client.InspectContainer(id)
|
||||
if err != nil {
|
||||
@@ -471,8 +496,19 @@ func TestInspectContainerNetwork(t *testing.T) {
|
||||
t.Logf("%s %v", net, ip)
|
||||
}
|
||||
}
|
||||
if ip != exp {
|
||||
t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, exp, ip)
|
||||
if ip != expIP {
|
||||
t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, expIP, ip)
|
||||
}
|
||||
|
||||
var networkID string
|
||||
for _, net := range networks.MapKeys() {
|
||||
if net.Interface().(string) == container.HostConfig.NetworkMode {
|
||||
networkID = networks.MapIndex(net).FieldByName("NetworkID").Interface().(string)
|
||||
t.Logf("%s %v", net, networkID)
|
||||
}
|
||||
}
|
||||
if networkID != expNetworkID {
|
||||
t.Errorf("InspectContainerNetworks(%q): Expected %#v. Got %#v.", id, expNetworkID, networkID)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("InspectContainerNetworks(%q): No method Networks for NetworkSettings", id)
|
||||
@@ -728,6 +764,36 @@ func TestCreateContainerWithHostConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateContainer(t *testing.T) {
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
id := "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2"
|
||||
update := UpdateContainerOptions{Memory: 12345, CpusetMems: "0,1"}
|
||||
err := client.UpdateContainer(id, update)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
if req.Method != "POST" {
|
||||
t.Errorf("UpdateContainer: wrong HTTP method. Want %q. Got %q.", "POST", req.Method)
|
||||
}
|
||||
expectedURL, _ := url.Parse(client.getURL("/containers/" + id + "/update"))
|
||||
if gotPath := req.URL.Path; gotPath != expectedURL.Path {
|
||||
t.Errorf("UpdateContainer: Wrong path in request. Want %q. Got %q.", expectedURL.Path, gotPath)
|
||||
}
|
||||
expectedContentType := "application/json"
|
||||
if contentType := req.Header.Get("Content-Type"); contentType != expectedContentType {
|
||||
t.Errorf("UpdateContainer: Wrong content-type in request. Want %q. Got %q.", expectedContentType, contentType)
|
||||
}
|
||||
var out UpdateContainerOptions
|
||||
if err := json.NewDecoder(req.Body).Decode(&out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, update) {
|
||||
t.Errorf("UpdateContainer: wrong body, got: %#v, want %#v", out, update)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartContainer(t *testing.T) {
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
|
||||
76
vendor/github.com/fsouza/go-dockerclient/event.go
generated
vendored
76
vendor/github.com/fsouza/go-dockerclient/event.go
generated
vendored
@@ -18,12 +18,38 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// APIEvents represents an event returned by the API.
|
||||
// APIEvents represents events coming from the Docker API
|
||||
// The fields in the Docker API changed in API version 1.22, and
|
||||
// events for more than images and containers are now fired off.
|
||||
// To maintain forward and backward compatibility, go-dockerclient
|
||||
// replicates the event in both the new and old format as faithfully as possible.
|
||||
//
|
||||
// For events that only exist in 1.22 in later, `Status` is filled in as
|
||||
// `"Type:Action"` instead of just `Action` to allow for older clients to
|
||||
// differentiate and not break if they rely on the pre-1.22 Status types.
|
||||
//
|
||||
// The transformEvent method can be consulted for more information about how
|
||||
// events are translated from new/old API formats
|
||||
type APIEvents struct {
|
||||
Status string `json:"Status,omitempty" yaml:"Status,omitempty"`
|
||||
ID string `json:"ID,omitempty" yaml:"ID,omitempty"`
|
||||
From string `json:"From,omitempty" yaml:"From,omitempty"`
|
||||
Time int64 `json:"Time,omitempty" yaml:"Time,omitempty"`
|
||||
// New API Fields in 1.22
|
||||
Action string `json:"action,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Actor APIActor `json:"actor,omitempty"`
|
||||
|
||||
// Old API fields for < 1.22
|
||||
Status string `json:"status,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
|
||||
// Fields in both
|
||||
Time int64 `json:"time,omitempty"`
|
||||
TimeNano int64 `json:"timeNano,omitempty"`
|
||||
}
|
||||
|
||||
// APIActor represents an actor that accomplishes something for an event
|
||||
type APIActor struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Attributes map[string]string `json:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
type eventMonitoringState struct {
|
||||
@@ -52,6 +78,7 @@ var (
|
||||
|
||||
// EOFEvent is sent when the event listener receives an EOF error.
|
||||
EOFEvent = &APIEvents{
|
||||
Type: "EOF",
|
||||
Status: "EOF",
|
||||
}
|
||||
)
|
||||
@@ -297,8 +324,47 @@ func (c *Client) eventHijack(startTime int64, eventChan chan *APIEvents, errChan
|
||||
if !c.eventMonitor.isEnabled() {
|
||||
return
|
||||
}
|
||||
transformEvent(&event)
|
||||
eventChan <- &event
|
||||
}
|
||||
}(res, conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
// transformEvent takes an event and determines what version it is from
|
||||
// then populates both versions of the event
|
||||
func transformEvent(event *APIEvents) {
|
||||
// if event version is <= 1.21 there will be no Action and no Type
|
||||
if event.Action == "" && event.Type == "" {
|
||||
event.Action = event.Status
|
||||
event.Actor.ID = event.ID
|
||||
event.Actor.Attributes = map[string]string{}
|
||||
switch event.Status {
|
||||
case "delete", "import", "pull", "push", "tag", "untag":
|
||||
event.Type = "image"
|
||||
default:
|
||||
event.Type = "container"
|
||||
if event.From != "" {
|
||||
event.Actor.Attributes["image"] = event.From
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if event.Status == "" {
|
||||
if event.Type == "image" || event.Type == "container" {
|
||||
event.Status = event.Action
|
||||
} else {
|
||||
// Because just the Status has been overloaded with different Types
|
||||
// if an event is not for an image or a container, we prepend the type
|
||||
// to avoid problems for people relying on actions being only for
|
||||
// images and containers
|
||||
event.Status = event.Type + ":" + event.Action
|
||||
}
|
||||
}
|
||||
if event.ID == "" {
|
||||
event.ID = event.Actor.ID
|
||||
}
|
||||
if event.From == "" {
|
||||
event.From = event.Actor.Attributes["image"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
200
vendor/github.com/fsouza/go-dockerclient/event_test.go
generated
vendored
200
vendor/github.com/fsouza/go-dockerclient/event_test.go
generated
vendored
@@ -8,10 +8,10 @@ import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -51,12 +51,168 @@ func TestTLSEventListeners(t *testing.T) {
|
||||
}
|
||||
|
||||
func testEventListeners(testName string, t *testing.T, buildServer func(http.Handler) *httptest.Server, buildClient func(string) (*Client, error)) {
|
||||
response := `{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
||||
response := `{"action":"pull","type":"image","actor":{"id":"busybox:latest","attributes":{}},"time":1442421700,"timeNano":1442421700598988358}
|
||||
{"action":"create","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716853979870}
|
||||
{"action":"attach","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716894759198}
|
||||
{"action":"start","type":"container","actor":{"id":"5745704abe9caa5","attributes":{"image":"busybox"}},"time":1442421716,"timeNano":1442421716983607193}
|
||||
{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
||||
{"status":"start","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
|
||||
{"status":"stop","id":"dfdf82bd3881","from":"base:latest","time":1374067966}
|
||||
{"status":"destroy","id":"dfdf82bd3881","from":"base:latest","time":1374067970}
|
||||
`
|
||||
{"Action":"create","Actor":{"Attributes":{"HAProxyMode":"http","HealthCheck":"HttpGet","HealthCheckArgs":"http://127.0.0.1:39051/status/check","ServicePort_8080":"17801","image":"datanerd.us/siteeng/sample-app-go:latest","name":"sample-app-client-go-69818c1223ddb5"},"ID":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16"},"Type":"container","from":"datanerd.us/siteeng/sample-app-go:latest","id":"a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16","status":"create","time":1459133932,"timeNano":1459133932961735842}`
|
||||
|
||||
wantResponse := []*APIEvents{
|
||||
{
|
||||
Action: "pull",
|
||||
Type: "image",
|
||||
Actor: APIActor{
|
||||
ID: "busybox:latest",
|
||||
Attributes: map[string]string{},
|
||||
},
|
||||
|
||||
Status: "pull",
|
||||
ID: "busybox:latest",
|
||||
|
||||
Time: 1442421700,
|
||||
TimeNano: 1442421700598988358,
|
||||
},
|
||||
{
|
||||
Action: "create",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "5745704abe9caa5",
|
||||
Attributes: map[string]string{
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "create",
|
||||
ID: "5745704abe9caa5",
|
||||
From: "busybox",
|
||||
|
||||
Time: 1442421716,
|
||||
TimeNano: 1442421716853979870,
|
||||
},
|
||||
{
|
||||
Action: "attach",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "5745704abe9caa5",
|
||||
Attributes: map[string]string{
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "attach",
|
||||
ID: "5745704abe9caa5",
|
||||
From: "busybox",
|
||||
|
||||
Time: 1442421716,
|
||||
TimeNano: 1442421716894759198,
|
||||
},
|
||||
{
|
||||
Action: "start",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "5745704abe9caa5",
|
||||
Attributes: map[string]string{
|
||||
"image": "busybox",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "start",
|
||||
ID: "5745704abe9caa5",
|
||||
From: "busybox",
|
||||
|
||||
Time: 1442421716,
|
||||
TimeNano: 1442421716983607193,
|
||||
},
|
||||
|
||||
{
|
||||
Action: "create",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "dfdf82bd3881",
|
||||
Attributes: map[string]string{
|
||||
"image": "base:latest",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "create",
|
||||
ID: "dfdf82bd3881",
|
||||
From: "base:latest",
|
||||
|
||||
Time: 1374067924,
|
||||
},
|
||||
{
|
||||
Action: "start",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "dfdf82bd3881",
|
||||
Attributes: map[string]string{
|
||||
"image": "base:latest",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "start",
|
||||
ID: "dfdf82bd3881",
|
||||
From: "base:latest",
|
||||
|
||||
Time: 1374067924,
|
||||
},
|
||||
{
|
||||
Action: "stop",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "dfdf82bd3881",
|
||||
Attributes: map[string]string{
|
||||
"image": "base:latest",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "stop",
|
||||
ID: "dfdf82bd3881",
|
||||
From: "base:latest",
|
||||
|
||||
Time: 1374067966,
|
||||
},
|
||||
{
|
||||
Action: "destroy",
|
||||
Type: "container",
|
||||
Actor: APIActor{
|
||||
ID: "dfdf82bd3881",
|
||||
Attributes: map[string]string{
|
||||
"image": "base:latest",
|
||||
},
|
||||
},
|
||||
|
||||
Status: "destroy",
|
||||
ID: "dfdf82bd3881",
|
||||
From: "base:latest",
|
||||
|
||||
Time: 1374067970,
|
||||
},
|
||||
{
|
||||
Action: "create",
|
||||
Type: "container",
|
||||
Status: "create",
|
||||
From: "datanerd.us/siteeng/sample-app-go:latest",
|
||||
ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16",
|
||||
Time: 1459133932,
|
||||
TimeNano: 1459133932961735842,
|
||||
Actor: APIActor{
|
||||
ID: "a925eaf4084d5c3bcf337b2abb05f566ebb94276dff34f6effb00d8ecd380e16",
|
||||
Attributes: map[string]string{
|
||||
"HAProxyMode": "http",
|
||||
"HealthCheck": "HttpGet",
|
||||
"HealthCheckArgs": "http://127.0.0.1:39051/status/check",
|
||||
"ServicePort_8080": "17801",
|
||||
"image": "datanerd.us/siteeng/sample-app-go:latest",
|
||||
"name": "sample-app-client-go-69818c1223ddb5",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
server := buildServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rsc := bufio.NewScanner(strings.NewReader(response))
|
||||
for rsc.Scan() {
|
||||
@@ -87,46 +243,16 @@ func testEventListeners(testName string, t *testing.T, buildServer func(http.Han
|
||||
}
|
||||
|
||||
timeout := time.After(1 * time.Second)
|
||||
var count int
|
||||
|
||||
for {
|
||||
for i := 0; i < 9; i++ {
|
||||
select {
|
||||
case msg := <-listener:
|
||||
t.Logf("Received: %v", *msg)
|
||||
count++
|
||||
err = checkEvent(count, msg)
|
||||
if err != nil {
|
||||
t.Fatalf("Check event failed: %s", err)
|
||||
}
|
||||
if count == 4 {
|
||||
return
|
||||
t.Logf("%d: Received: %v", i, msg)
|
||||
if !reflect.DeepEqual(msg, wantResponse[i]) {
|
||||
t.Fatalf("%d: wanted: %#v\n got: %#v", i, wantResponse[i], msg)
|
||||
}
|
||||
case <-timeout:
|
||||
t.Fatalf("%s timed out waiting on events", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkEvent(index int, event *APIEvents) error {
|
||||
if event.ID != "dfdf82bd3881" {
|
||||
return fmt.Errorf("event ID did not match. Expected dfdf82bd3881 got %s", event.ID)
|
||||
}
|
||||
if event.From != "base:latest" {
|
||||
return fmt.Errorf("event from did not match. Expected base:latest got %s", event.From)
|
||||
}
|
||||
var status string
|
||||
switch index {
|
||||
case 1:
|
||||
status = "create"
|
||||
case 2:
|
||||
status = "start"
|
||||
case 3:
|
||||
status = "stop"
|
||||
case 4:
|
||||
status = "destroy"
|
||||
}
|
||||
if event.Status != status {
|
||||
return fmt.Errorf("event status did not match. Expected %s got %s", status, event.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
978
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_unix_test.go
generated
vendored
Normal file
978
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/archive/copy_unix_test.go
generated
vendored
Normal file
@@ -0,0 +1,978 @@
|
||||
// +build !windows
|
||||
|
||||
// TODO Windows: Some of these tests may be salvagable and portable to Windows.
|
||||
|
||||
package archive
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func removeAllPaths(paths ...string) {
|
||||
for _, path := range paths {
|
||||
os.RemoveAll(path)
|
||||
}
|
||||
}
|
||||
|
||||
func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) {
|
||||
var err error
|
||||
|
||||
if tmpDirA, err = ioutil.TempDir("", "archive-copy-test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if tmpDirB, err = ioutil.TempDir("", "archive-copy-test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func isNotDir(err error) bool {
|
||||
return strings.Contains(err.Error(), "not a directory")
|
||||
}
|
||||
|
||||
func joinTrailingSep(pathElements ...string) string {
|
||||
joined := filepath.Join(pathElements...)
|
||||
|
||||
return fmt.Sprintf("%s%c", joined, filepath.Separator)
|
||||
}
|
||||
|
||||
func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) {
|
||||
t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB)
|
||||
|
||||
fileA, err := os.Open(filenameA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fileA.Close()
|
||||
|
||||
fileB, err := os.Open(filenameB)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fileB.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
|
||||
if _, err = io.Copy(hasher, fileA); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hashA := hasher.Sum(nil)
|
||||
hasher.Reset()
|
||||
|
||||
if _, err = io.Copy(hasher, fileB); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hashB := hasher.Sum(nil)
|
||||
|
||||
if !bytes.Equal(hashA, hashB) {
|
||||
err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) {
|
||||
t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir)
|
||||
|
||||
var changes []Change
|
||||
|
||||
if changes, err = ChangesDirs(newDir, oldDir); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(changes) != 0 {
|
||||
err = fmt.Errorf("expected no changes between directories, but got: %v", changes)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func logDirContents(t *testing.T, dirPath string) {
|
||||
logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
t.Errorf("stat error for path %q: %s", path, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
path = joinTrailingSep(path)
|
||||
}
|
||||
|
||||
t.Logf("\t%s", path)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
t.Logf("logging directory contents: %q", dirPath)
|
||||
|
||||
if err := filepath.Walk(dirPath, logWalkedPaths); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) {
|
||||
t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath)
|
||||
|
||||
return CopyResource(srcPath, dstPath, false)
|
||||
}
|
||||
|
||||
func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) {
|
||||
t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath)
|
||||
|
||||
return CopyResource(srcPath, dstPath, true)
|
||||
}
|
||||
|
||||
// Basic assumptions about SRC and DST:
|
||||
// 1. SRC must exist.
|
||||
// 2. If SRC ends with a trailing separator, it must be a directory.
|
||||
// 3. DST parent directory must exist.
|
||||
// 4. If DST exists as a file, it must not end with a trailing separator.
|
||||
|
||||
// First get these easy error cases out of the way.
|
||||
|
||||
// Test for error when SRC does not exist.
|
||||
func TestCopyErrSrcNotExists(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for error when SRC ends in a trailing
|
||||
// path separator but it exists as a file.
|
||||
func TestCopyErrSrcNotDir(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) {
|
||||
t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for error when SRC is a valid file or directory,
|
||||
// but the DST parent directory does not exist.
|
||||
func TestCopyErrDstParentNotExists(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
|
||||
|
||||
// Try with a file source.
|
||||
content, err := TarResource(srcInfo)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
defer content.Close()
|
||||
|
||||
// Copy to a file whose parent does not exist.
|
||||
if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil {
|
||||
t.Fatal("expected IsNotExist error, but got nil instead")
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
// Try with a directory source.
|
||||
srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
|
||||
|
||||
content, err = TarResource(srcInfo)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
defer content.Close()
|
||||
|
||||
// Copy to a directory whose parent does not exist.
|
||||
if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil {
|
||||
t.Fatal("expected IsNotExist error, but got nil instead")
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
t.Fatalf("expected IsNotExist error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for error when DST ends in a trailing
|
||||
// path separator but exists as a file.
|
||||
func TestCopyErrDstNotDir(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
// Try with a file source.
|
||||
srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false}
|
||||
|
||||
content, err := TarResource(srcInfo)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
defer content.Close()
|
||||
|
||||
if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
|
||||
t.Fatal("expected IsNotDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if !isNotDir(err) {
|
||||
t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
// Try with a directory source.
|
||||
srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true}
|
||||
|
||||
content, err = TarResource(srcInfo)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
defer content.Close()
|
||||
|
||||
if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil {
|
||||
t.Fatal("expected IsNotDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if !isNotDir(err) {
|
||||
t.Fatalf("expected IsNotDir error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Possibilities are reduced to the remaining 10 cases:
|
||||
//
|
||||
// case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action
|
||||
// ===================================================================================================
|
||||
// A | no | - | no | - | no | create file
|
||||
// B | no | - | no | - | yes | error
|
||||
// C | no | - | yes | no | - | overwrite file
|
||||
// D | no | - | yes | yes | - | create file in dst dir
|
||||
// E | yes | no | no | - | - | create dir, copy contents
|
||||
// F | yes | no | yes | no | - | error
|
||||
// G | yes | no | yes | yes | - | copy dir and contents
|
||||
// H | yes | yes | no | - | - | create dir, copy contents
|
||||
// I | yes | yes | yes | no | - | error
|
||||
// J | yes | yes | yes | yes | - | copy dir contents
|
||||
//
|
||||
|
||||
// A. SRC specifies a file and DST (no trailing path separator) doesn't
|
||||
// exist. This should create a file with the name DST and copy the
|
||||
// contents of the source file into it.
|
||||
func TestCopyCaseA(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcPath := filepath.Join(tmpDirA, "file1")
|
||||
dstPath := filepath.Join(tmpDirB, "itWorks.txt")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcPath, dstPath); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Remove(dstPath)
|
||||
|
||||
symlinkPath := filepath.Join(tmpDirA, "symlink3")
|
||||
symlinkPath1 := filepath.Join(tmpDirA, "symlink4")
|
||||
linkTarget := filepath.Join(tmpDirA, "file1")
|
||||
|
||||
if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Remove(dstPath)
|
||||
if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// B. SRC specifies a file and DST (with trailing path separator) doesn't
|
||||
// exist. This should cause an error because the copy operation cannot
|
||||
// create a directory when copying a single file.
|
||||
func TestCopyCaseB(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcPath := filepath.Join(tmpDirA, "file1")
|
||||
dstDir := joinTrailingSep(tmpDirB, "testDir")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcPath, dstDir); err == nil {
|
||||
t.Fatal("expected ErrDirNotExists error, but got nil instead")
|
||||
}
|
||||
|
||||
if err != ErrDirNotExists {
|
||||
t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
symlinkPath := filepath.Join(tmpDirA, "symlink3")
|
||||
|
||||
if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil {
|
||||
t.Fatal("expected ErrDirNotExists error, but got nil instead")
|
||||
}
|
||||
if err != ErrDirNotExists {
|
||||
t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// C. SRC specifies a file and DST exists as a file. This should overwrite
|
||||
// the file at DST with the contents of the source file.
|
||||
func TestCopyCaseC(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcPath := filepath.Join(tmpDirA, "file1")
|
||||
dstPath := filepath.Join(tmpDirB, "file2")
|
||||
|
||||
var err error
|
||||
|
||||
// Ensure they start out different.
|
||||
if err = fileContentsEqual(t, srcPath, dstPath); err == nil {
|
||||
t.Fatal("expected different file contents")
|
||||
}
|
||||
|
||||
if err = testCopyHelper(t, srcPath, dstPath); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// C. Symbol link following version:
|
||||
// SRC specifies a file and DST exists as a file. This should overwrite
|
||||
// the file at DST with the contents of the source file.
|
||||
func TestCopyCaseCFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
symlinkPathBad := filepath.Join(tmpDirA, "symlink1")
|
||||
symlinkPath := filepath.Join(tmpDirA, "symlink3")
|
||||
linkTarget := filepath.Join(tmpDirA, "file1")
|
||||
dstPath := filepath.Join(tmpDirB, "file2")
|
||||
|
||||
var err error
|
||||
|
||||
// first to test broken link
|
||||
if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
// test symbol link -> symbol link -> target
|
||||
// Ensure they start out different.
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err == nil {
|
||||
t.Fatal("expected different file contents")
|
||||
}
|
||||
|
||||
if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// D. SRC specifies a file and DST exists as a directory. This should place
|
||||
// a copy of the source file inside it using the basename from SRC. Ensure
|
||||
// this works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseD(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcPath := filepath.Join(tmpDirA, "file1")
|
||||
dstDir := filepath.Join(tmpDirB, "dir1")
|
||||
dstPath := filepath.Join(dstDir, "file1")
|
||||
|
||||
var err error
|
||||
|
||||
// Ensure that dstPath doesn't exist.
|
||||
if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("did not expect dstPath %q to exist", dstPath)
|
||||
}
|
||||
|
||||
if err = testCopyHelper(t, srcPath, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir1")
|
||||
|
||||
if err = testCopyHelper(t, srcPath, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, srcPath, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// D. Symbol link following version:
|
||||
// SRC specifies a file and DST exists as a directory. This should place
|
||||
// a copy of the source file inside it using the basename from SRC. Ensure
|
||||
// this works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseDFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcPath := filepath.Join(tmpDirA, "symlink4")
|
||||
linkTarget := filepath.Join(tmpDirA, "file1")
|
||||
dstDir := filepath.Join(tmpDirB, "dir1")
|
||||
dstPath := filepath.Join(dstDir, "symlink4")
|
||||
|
||||
var err error
|
||||
|
||||
// Ensure that dstPath doesn't exist.
|
||||
if _, err = os.Stat(dstPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("did not expect dstPath %q to exist", dstPath)
|
||||
}
|
||||
|
||||
if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir1")
|
||||
|
||||
if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = fileContentsEqual(t, linkTarget, dstPath); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// E. SRC specifies a directory and DST does not exist. This should create a
|
||||
// directory at DST and copy the contents of the SRC directory into the DST
|
||||
// directory. Ensure this works whether DST has a trailing path separator or
|
||||
// not.
|
||||
func TestCopyCaseE(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcDir := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "testDir")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "testDir")
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// E. Symbol link following version:
|
||||
// SRC specifies a directory and DST does not exist. This should create a
|
||||
// directory at DST and copy the contents of the SRC directory into the DST
|
||||
// directory. Ensure this works whether DST has a trailing path separator or
|
||||
// not.
|
||||
func TestCopyCaseEFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcDir := filepath.Join(tmpDirA, "dirSymlink")
|
||||
linkTarget := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "testDir")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "testDir")
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// F. SRC specifies a directory and DST exists as a file. This should cause an
|
||||
// error as it is not possible to overwrite a file with a directory.
|
||||
func TestCopyCaseF(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := filepath.Join(tmpDirA, "dir1")
|
||||
symSrcDir := filepath.Join(tmpDirA, "dirSymlink")
|
||||
dstFile := filepath.Join(tmpDirB, "file1")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstFile); err == nil {
|
||||
t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if err != ErrCannotCopyDir {
|
||||
t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
// now test with symbol link
|
||||
if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
|
||||
t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if err != ErrCannotCopyDir {
|
||||
t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// G. SRC specifies a directory and DST exists as a directory. This should copy
|
||||
// the SRC directory and all its contents to the DST directory. Ensure this
|
||||
// works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseG(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "dir2")
|
||||
resultDir := filepath.Join(dstDir, "dir1")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir2")
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, resultDir, srcDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// G. Symbol link version:
|
||||
// SRC specifies a directory and DST exists as a directory. This should copy
|
||||
// the SRC directory and all its contents to the DST directory. Ensure this
|
||||
// works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseGFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := filepath.Join(tmpDirA, "dirSymlink")
|
||||
linkTarget := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "dir2")
|
||||
resultDir := filepath.Join(dstDir, "dirSymlink")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, resultDir, linkTarget); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir2")
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, resultDir, linkTarget); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// H. SRC specifies a directory's contents only and DST does not exist. This
|
||||
// should create a directory at DST and copy the contents of the SRC
|
||||
// directory (but not the directory itself) into the DST directory. Ensure
|
||||
// this works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseH(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
|
||||
dstDir := filepath.Join(tmpDirB, "testDir")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "testDir")
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// H. Symbol link following version:
|
||||
// SRC specifies a directory's contents only and DST does not exist. This
|
||||
// should create a directory at DST and copy the contents of the SRC
|
||||
// directory (but not the directory itself) into the DST directory. Ensure
|
||||
// this works whether DST has a trailing path separator or not.
|
||||
func TestCopyCaseHFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
|
||||
srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
|
||||
linkTarget := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "testDir")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "testDir")
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Log("dir contents not equal")
|
||||
logDirContents(t, tmpDirA)
|
||||
logDirContents(t, tmpDirB)
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// I. SRC specifies a directory's contents only and DST exists as a file. This
|
||||
// should cause an error as it is not possible to overwrite a file with a
|
||||
// directory.
|
||||
func TestCopyCaseI(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
|
||||
symSrcDir := filepath.Join(tmpDirB, "dirSymlink")
|
||||
dstFile := filepath.Join(tmpDirB, "file1")
|
||||
|
||||
var err error
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstFile); err == nil {
|
||||
t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if err != ErrCannotCopyDir {
|
||||
t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
|
||||
}
|
||||
|
||||
// now try with symbol link of dir
|
||||
if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil {
|
||||
t.Fatal("expected ErrCannotCopyDir error, but got nil instead")
|
||||
}
|
||||
|
||||
if err != ErrCannotCopyDir {
|
||||
t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// J. SRC specifies a directory's contents only and DST exists as a directory.
|
||||
// This should copy the contents of the SRC directory (but not the directory
|
||||
// itself) into the DST directory. Ensure this works whether DST has a
|
||||
// trailing path separator or not.
|
||||
func TestCopyCaseJ(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := joinTrailingSep(tmpDirA, "dir1") + "."
|
||||
dstDir := filepath.Join(tmpDirB, "dir5")
|
||||
|
||||
var err error
|
||||
|
||||
// first to create an empty dir
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir5")
|
||||
|
||||
if err = testCopyHelper(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, srcDir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// J. Symbol link following version:
|
||||
// SRC specifies a directory's contents only and DST exists as a directory.
|
||||
// This should copy the contents of the SRC directory (but not the directory
|
||||
// itself) into the DST directory. Ensure this works whether DST has a
|
||||
// trailing path separator or not.
|
||||
func TestCopyCaseJFSym(t *testing.T) {
|
||||
tmpDirA, tmpDirB := getTestTempDirs(t)
|
||||
defer removeAllPaths(tmpDirA, tmpDirB)
|
||||
|
||||
// Load A and B with some sample files and directories.
|
||||
createSampleDir(t, tmpDirA)
|
||||
createSampleDir(t, tmpDirB)
|
||||
|
||||
srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "."
|
||||
linkTarget := filepath.Join(tmpDirA, "dir1")
|
||||
dstDir := filepath.Join(tmpDirB, "dir5")
|
||||
|
||||
var err error
|
||||
|
||||
// first to create an empty dir
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Now try again but using a trailing path separator for dstDir.
|
||||
|
||||
if err = os.RemoveAll(dstDir); err != nil {
|
||||
t.Fatalf("unable to remove dstDir: %s", err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil {
|
||||
t.Fatalf("unable to make dstDir: %s", err)
|
||||
}
|
||||
|
||||
dstDir = joinTrailingSep(tmpDirB, "dir5")
|
||||
|
||||
if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil {
|
||||
t.Fatalf("unexpected error %T: %s", err, err)
|
||||
}
|
||||
|
||||
if err = dirContentsEqual(t, dstDir, linkTarget); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
14
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix.go
generated
vendored
Normal file
14
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_unix.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build !windows
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//setCTime will set the create time on a file. On Unix, the create
|
||||
//time is updated as a side effect of setting the modified time, so
|
||||
//no action is required.
|
||||
func setCTime(path string, ctime time.Time) error {
|
||||
return nil
|
||||
}
|
||||
27
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows.go
generated
vendored
Normal file
27
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/chtimes_windows.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// +build windows
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
//setCTime will set the create time on a file. On Windows, this requires
|
||||
//calling SetFileTime and explicitly including the create time.
|
||||
func setCTime(path string, ctime time.Time) error {
|
||||
ctimespec := syscall.NsecToTimespec(ctime.UnixNano())
|
||||
pathp, e := syscall.UTF16PtrFromString(path)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
h, e := syscall.CreateFile(pathp,
|
||||
syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_WRITE, nil,
|
||||
syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer syscall.Close(h)
|
||||
c := syscall.NsecToFiletime(syscall.TimespecToNsec(ctimespec))
|
||||
return syscall.SetFileTime(h, &c, nil, nil)
|
||||
}
|
||||
15
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_openbsd.go
generated
vendored
Normal file
15
vendor/github.com/fsouza/go-dockerclient/external/github.com/docker/docker/pkg/system/stat_openbsd.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// fromStatT creates a system.StatT type from a syscall.Stat_t type
|
||||
func fromStatT(s *syscall.Stat_t) (*StatT, error) {
|
||||
return &StatT{size: s.Size,
|
||||
mode: uint32(s.Mode),
|
||||
uid: s.Uid,
|
||||
gid: s.Gid,
|
||||
rdev: uint64(s.Rdev),
|
||||
mtim: s.Mtim}, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !linux,!windows,!freebsd,!solaris
|
||||
// +build !linux,!windows,!freebsd,!solaris,!openbsd
|
||||
|
||||
package system
|
||||
|
||||
|
||||
12
vendor/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context.go
generated
vendored
12
vendor/github.com/fsouza/go-dockerclient/external/golang.org/x/net/context/context.go
generated
vendored
@@ -210,13 +210,13 @@ type CancelFunc func()
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
c := newCancelCtx(parent)
|
||||
propagateCancel(parent, &c)
|
||||
return &c, func() { c.cancel(true, Canceled) }
|
||||
propagateCancel(parent, c)
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
|
||||
// newCancelCtx returns an initialized cancelCtx.
|
||||
func newCancelCtx(parent Context) cancelCtx {
|
||||
return cancelCtx{
|
||||
func newCancelCtx(parent Context) *cancelCtx {
|
||||
return &cancelCtx{
|
||||
Context: parent,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
@@ -259,7 +259,7 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
||||
case *cancelCtx:
|
||||
return c, true
|
||||
case *timerCtx:
|
||||
return &c.cancelCtx, true
|
||||
return c.cancelCtx, true
|
||||
case *valueCtx:
|
||||
parent = c.Context
|
||||
default:
|
||||
@@ -377,7 +377,7 @@ func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
// implement Done and Err. It implements cancel by stopping its timer then
|
||||
// delegating to cancelCtx.cancel.
|
||||
type timerCtx struct {
|
||||
cancelCtx
|
||||
*cancelCtx
|
||||
timer *time.Timer // Under cancelCtx.mu.
|
||||
|
||||
deadline time.Time
|
||||
|
||||
24
vendor/github.com/fsouza/go-dockerclient/image.go
generated
vendored
24
vendor/github.com/fsouza/go-dockerclient/image.go
generated
vendored
@@ -32,6 +32,7 @@ type APIImages struct {
|
||||
// Image is the type representing a docker image and its various properties
|
||||
type Image struct {
|
||||
ID string `json:"Id" yaml:"Id"`
|
||||
RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty"`
|
||||
Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty"`
|
||||
Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty"`
|
||||
Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty"`
|
||||
@@ -421,6 +422,17 @@ type BuildImageOptions struct {
|
||||
AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header
|
||||
ContextDir string `qs:"-"`
|
||||
Ulimits []ULimit `qs:"-"`
|
||||
BuildArgs []BuildArg `qs:"-"`
|
||||
}
|
||||
|
||||
// BuildArg represents arguments that can be passed to the image when building
|
||||
// it from a Dockerfile.
|
||||
//
|
||||
// For more details about the Docker building process, see
|
||||
// http://goo.gl/tlPXPu.
|
||||
type BuildArg struct {
|
||||
Name string `json:"Name,omitempty" yaml:"Name,omitempty"`
|
||||
Value string `json:"Value,omitempty" yaml:"Value,omitempty"`
|
||||
}
|
||||
|
||||
// BuildImage builds an image from a tarball's url or a Dockerfile in the input
|
||||
@@ -463,6 +475,18 @@ func (c *Client) BuildImage(opts BuildImageOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.BuildArgs) > 0 {
|
||||
v := make(map[string]string)
|
||||
for _, arg := range opts.BuildArgs {
|
||||
v[arg.Name] = arg.Value
|
||||
}
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
item := url.Values(map[string][]string{})
|
||||
item.Add("buildargs", string(b))
|
||||
qs = fmt.Sprintf("%s&%s", qs, item.Encode())
|
||||
}
|
||||
}
|
||||
|
||||
return c.stream("POST", fmt.Sprintf("/build?%s", qs), streamOptions{
|
||||
setRawTerminal: true,
|
||||
rawJSONStream: opts.RawJSONStream,
|
||||
|
||||
4
vendor/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
4
vendor/github.com/fsouza/go-dockerclient/image_test.go
generated
vendored
@@ -685,6 +685,7 @@ func TestBuildImageParameters(t *testing.T) {
|
||||
CPUPeriod: 100000,
|
||||
CPUSetCPUs: "0-3",
|
||||
Ulimits: []ULimit{{Name: "nofile", Soft: 100, Hard: 200}},
|
||||
BuildArgs: []BuildArg{{Name: "SOME_VAR", Value: "some_value"}},
|
||||
InputStream: &buf,
|
||||
OutputStream: &buf,
|
||||
}
|
||||
@@ -706,7 +707,8 @@ func TestBuildImageParameters(t *testing.T) {
|
||||
"cpuquota": {"7500"},
|
||||
"cpuperiod": {"100000"},
|
||||
"cpusetcpus": {"0-3"},
|
||||
"ulimits": {"[{\"Name\":\"nofile\",\"Soft\":100,\"Hard\":200}]"},
|
||||
"ulimits": {`[{"Name":"nofile","Soft":100,"Hard":200}]`},
|
||||
"buildargs": {`{"SOME_VAR":"some_value"}`},
|
||||
}
|
||||
got := map[string][]string(req.URL.Query())
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
|
||||
2
vendor/github.com/fsouza/go-dockerclient/integration_test.go
generated
vendored
2
vendor/github.com/fsouza/go-dockerclient/integration_test.go
generated
vendored
@@ -46,7 +46,7 @@ func TestIntegrationPullCreateStartLogs(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
if status != 0 {
|
||||
t.Error("WaitContainer(%q): wrong status. Want 0. Got %d", container.ID, status)
|
||||
t.Errorf("WaitContainer(%q): wrong status. Want 0. Got %d", container.ID, status)
|
||||
}
|
||||
var stdout, stderr bytes.Buffer
|
||||
logsOpts := LogsOptions{
|
||||
|
||||
75
vendor/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
75
vendor/github.com/fsouza/go-dockerclient/misc.go
generated
vendored
@@ -4,7 +4,10 @@
|
||||
|
||||
package docker
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version returns version information about the docker server.
|
||||
//
|
||||
@@ -22,17 +25,81 @@ func (c *Client) Version() (*Env, error) {
|
||||
return &env, nil
|
||||
}
|
||||
|
||||
// DockerInfo contains information about the Docker server
|
||||
//
|
||||
// See https://goo.gl/bHUoz9 for more details.
|
||||
type DockerInfo struct {
|
||||
ID string
|
||||
Containers int
|
||||
ContainersRunning int
|
||||
ContainersPaused int
|
||||
ContainersStopped int
|
||||
Images int
|
||||
Driver string
|
||||
DriverStatus [][2]string
|
||||
SystemStatus [][2]string
|
||||
Plugins PluginsInfo
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
KernelMemory bool
|
||||
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||
CPUShares bool
|
||||
CPUSet bool
|
||||
IPv4Forwarding bool
|
||||
BridgeNfIptables bool
|
||||
BridgeNfIP6tables bool `json:"BridgeNfIp6tables"`
|
||||
Debug bool
|
||||
NFd int
|
||||
OomKillDisable bool
|
||||
NGoroutines int
|
||||
SystemTime string
|
||||
ExecutionDriver string
|
||||
LoggingDriver string
|
||||
CgroupDriver string
|
||||
NEventsListener int
|
||||
KernelVersion string
|
||||
OperatingSystem string
|
||||
OSType string
|
||||
Architecture string
|
||||
IndexServerAddress string
|
||||
NCPU int
|
||||
MemTotal int64
|
||||
DockerRootDir string
|
||||
HTTPProxy string `json:"HttpProxy"`
|
||||
HTTPSProxy string `json:"HttpsProxy"`
|
||||
NoProxy string
|
||||
Name string
|
||||
Labels []string
|
||||
ExperimentalBuild bool
|
||||
ServerVersion string
|
||||
ClusterStore string
|
||||
ClusterAdvertise string
|
||||
}
|
||||
|
||||
// PluginsInfo is a struct with the plugins registered with the docker daemon
|
||||
//
|
||||
// for more information, see: https://goo.gl/bHUoz9
|
||||
type PluginsInfo struct {
|
||||
// List of Volume plugins registered
|
||||
Volume []string
|
||||
// List of Network plugins registered
|
||||
Network []string
|
||||
// List of Authorization plugins registered
|
||||
Authorization []string
|
||||
}
|
||||
|
||||
// Info returns system-wide information about the Docker server.
|
||||
//
|
||||
// See https://goo.gl/ElTHi2 for more details.
|
||||
func (c *Client) Info() (*Env, error) {
|
||||
func (c *Client) Info() (*DockerInfo, error) {
|
||||
resp, err := c.do("GET", "/info", doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var info Env
|
||||
if err := info.Decode(resp.Body); err != nil {
|
||||
var info DockerInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &info, nil
|
||||
|
||||
32
vendor/github.com/fsouza/go-dockerclient/misc_test.go
generated
vendored
32
vendor/github.com/fsouza/go-dockerclient/misc_test.go
generated
vendored
@@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -71,32 +70,29 @@ func TestInfo(t *testing.T) {
|
||||
body := `{
|
||||
"Containers":11,
|
||||
"Images":16,
|
||||
"Debug":0,
|
||||
"Debug":false,
|
||||
"NFd":11,
|
||||
"NGoroutines":21,
|
||||
"MemoryLimit":1,
|
||||
"SwapLimit":0
|
||||
"MemoryLimit":true,
|
||||
"SwapLimit":false
|
||||
}`
|
||||
fakeRT := FakeRoundTripper{message: body, status: http.StatusOK}
|
||||
client := newTestClient(&fakeRT)
|
||||
expected := Env{}
|
||||
expected.SetInt("Containers", 11)
|
||||
expected.SetInt("Images", 16)
|
||||
expected.SetBool("Debug", false)
|
||||
expected.SetInt("NFd", 11)
|
||||
expected.SetInt("NGoroutines", 21)
|
||||
expected.SetBool("MemoryLimit", true)
|
||||
expected.SetBool("SwapLimit", false)
|
||||
expected := &DockerInfo{
|
||||
Containers: 11,
|
||||
Images: 16,
|
||||
Debug: false,
|
||||
NFd: 11,
|
||||
NGoroutines: 21,
|
||||
MemoryLimit: true,
|
||||
SwapLimit: false,
|
||||
}
|
||||
info, err := client.Info()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
infoSlice := []string(*info)
|
||||
expectedSlice := []string(expected)
|
||||
sort.Strings(infoSlice)
|
||||
sort.Strings(expectedSlice)
|
||||
if !reflect.DeepEqual(expectedSlice, infoSlice) {
|
||||
t.Errorf("Info(): Wrong result.\nWant %#v.\nGot %#v.", expected, *info)
|
||||
if !reflect.DeepEqual(expected, info) {
|
||||
t.Errorf("Info(): Wrong result.\nWant %#v.\nGot %#v.", expected, info)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
if req.Method != "GET" {
|
||||
|
||||
65
vendor/github.com/fsouza/go-dockerclient/network.go
generated
vendored
65
vendor/github.com/fsouza/go-dockerclient/network.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -26,6 +27,7 @@ type Network struct {
|
||||
IPAM IPAMOptions
|
||||
Containers map[string]Endpoint
|
||||
Options map[string]string
|
||||
Internal bool
|
||||
}
|
||||
|
||||
// Endpoint contains network resources allocated and used for a container in a network
|
||||
@@ -55,6 +57,31 @@ func (c *Client) ListNetworks() ([]Network, error) {
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// NetworkFilterOpts is an aggregation of key=value that Docker
|
||||
// uses to filter networks
|
||||
type NetworkFilterOpts map[string]map[string]bool
|
||||
|
||||
// FilteredListNetworks returns all networks with the filters applied
|
||||
//
|
||||
// See goo.gl/zd2mx4 for more details.
|
||||
func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error) {
|
||||
params := bytes.NewBuffer(nil)
|
||||
if err := json.NewEncoder(params).Encode(&opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
path := "/networks?filters=" + params.String()
|
||||
resp, err := c.do("GET", path, doOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
var networks []Network
|
||||
if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// NetworkInfo returns information about a network by its ID.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
@@ -158,14 +185,40 @@ func (c *Client) RemoveNetwork(id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NetworkConnectionOptions specify parameters to the ConnectNetwork and DisconnectNetwork function.
|
||||
// NetworkConnectionOptions specify parameters to the ConnectNetwork and
|
||||
// DisconnectNetwork function.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type NetworkConnectionOptions struct {
|
||||
Container string
|
||||
|
||||
// EndpointConfig is only applicable to the ConnectNetwork call
|
||||
EndpointConfig *EndpointConfig `json:"EndpointConfig,omitempty"`
|
||||
|
||||
// Force is only applicable to the DisconnectNetwork call
|
||||
Force bool
|
||||
}
|
||||
|
||||
// ConnectNetwork adds a container to a network or returns an error in case of failure.
|
||||
// EndpointConfig stores network endpoint details
|
||||
//
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type EndpointConfig struct {
|
||||
IPAMConfig *EndpointIPAMConfig
|
||||
Links []string
|
||||
Aliases []string
|
||||
}
|
||||
|
||||
// EndpointIPAMConfig represents IPAM configurations for an
|
||||
// endpoint
|
||||
//
|
||||
// See https://goo.gl/RV7BJU for more details.
|
||||
type EndpointIPAMConfig struct {
|
||||
IPv4Address string `json:",omitempty"`
|
||||
IPv6Address string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ConnectNetwork adds a container to a network or returns an error in case of
|
||||
// failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error {
|
||||
@@ -180,7 +233,8 @@ func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisconnectNetwork removes a container from a network or returns an error in case of failure.
|
||||
// DisconnectNetwork removes a container from a network or returns an error in
|
||||
// case of failure.
|
||||
//
|
||||
// See https://goo.gl/6GugX3 for more details.
|
||||
func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error {
|
||||
@@ -204,7 +258,8 @@ func (err *NoSuchNetwork) Error() string {
|
||||
return fmt.Sprintf("No such network: %s", err.ID)
|
||||
}
|
||||
|
||||
// NoSuchNetwork is the error returned when a given network or container does not exist.
|
||||
// NoSuchNetworkOrContainer is the error returned when a given network or
|
||||
// container does not exist.
|
||||
type NoSuchNetworkOrContainer struct {
|
||||
NetworkID string
|
||||
ContainerID string
|
||||
|
||||
79
vendor/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
79
vendor/github.com/fsouza/go-dockerclient/network_test.go
generated
vendored
@@ -5,6 +5,7 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -42,6 +43,39 @@ func TestListNetworks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteredListNetworks(t *testing.T) {
|
||||
jsonNetworks := `[
|
||||
{
|
||||
"ID": "9fb1e39c",
|
||||
"Name": "foo",
|
||||
"Type": "bridge",
|
||||
"Endpoints":[{"ID": "c080be979dda", "Name": "lllll2222", "Network": "9fb1e39c"}]
|
||||
}
|
||||
]`
|
||||
var expected []Network
|
||||
err := json.Unmarshal([]byte(jsonNetworks), &expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantQuery := "filters={\"name\":{\"blah\":true}}\n"
|
||||
fakeRT := &FakeRoundTripper{message: jsonNetworks, status: http.StatusOK}
|
||||
client := newTestClient(fakeRT)
|
||||
opts := NetworkFilterOpts{
|
||||
"name": map[string]bool{"blah": true},
|
||||
}
|
||||
containers, err := client.FilteredListNetworks(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(containers, expected) {
|
||||
t.Errorf("ListNetworks: Expected %#v. Got %#v.", expected, containers)
|
||||
}
|
||||
query := fakeRT.requests[0].URL.RawQuery
|
||||
if query != wantQuery {
|
||||
t.Errorf("FilteredListNetworks: Expected query: %q, got: %q", wantQuery, query)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkInfo(t *testing.T) {
|
||||
jsonNetwork := `{
|
||||
"ID": "8dfafdbc3a40",
|
||||
@@ -118,7 +152,7 @@ func TestNetworkConnect(t *testing.T) {
|
||||
id := "8dfafdbc3a40"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
client := newTestClient(fakeRT)
|
||||
opts := NetworkConnectionOptions{"foobar"}
|
||||
opts := NetworkConnectionOptions{Container: "foobar"}
|
||||
err := client.ConnectNetwork(id, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -134,9 +168,46 @@ func TestNetworkConnect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkConnectWithEndpoint(t *testing.T) {
|
||||
wantJSON := `{"Container":"foobar","EndpointConfig":{"IPAMConfig":{"IPv4Address":"8.8.8.8"},"Links":null,"Aliases":null},"Force":false}`
|
||||
var wantObj NetworkConnectionOptions
|
||||
json.NewDecoder(bytes.NewBuffer([]byte(wantJSON))).Decode(&wantObj)
|
||||
id := "8dfafdbc3a40"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
client := newTestClient(fakeRT)
|
||||
opts := NetworkConnectionOptions{
|
||||
Container: "foobar",
|
||||
EndpointConfig: &EndpointConfig{
|
||||
IPAMConfig: &EndpointIPAMConfig{
|
||||
IPv4Address: "8.8.8.8",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := client.ConnectNetwork(id, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := fakeRT.requests[0]
|
||||
expectedMethod := "POST"
|
||||
if req.Method != expectedMethod {
|
||||
t.Errorf("ConnectNetwork(%q): Wrong HTTP method. Want %s. Got %s.", id, expectedMethod, req.Method)
|
||||
}
|
||||
u, _ := url.Parse(client.getURL("/networks/" + id + "/connect"))
|
||||
if req.URL.Path != u.Path {
|
||||
t.Errorf("ConnectNetwork(%q): Wrong request path. Want %q. Got %q.", id, u.Path, req.URL.Path)
|
||||
}
|
||||
var in NetworkConnectionOptions
|
||||
if err := json.NewDecoder(req.Body).Decode(&in); err != nil {
|
||||
t.Errorf("ConnectNetwork: error parsing JSON data sent: %q", err)
|
||||
}
|
||||
if !reflect.DeepEqual(in, wantObj) {
|
||||
t.Errorf("ConnectNetwork: wanted %#v send, got: %#v", wantObj, in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkConnectNotFound(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
|
||||
opts := NetworkConnectionOptions{"foobar"}
|
||||
opts := NetworkConnectionOptions{Container: "foobar"}
|
||||
err := client.ConnectNetwork("8dfafdbc3a40", opts)
|
||||
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
|
||||
t.Errorf("ConnectNetwork: wrong error type: %s.", serr)
|
||||
@@ -147,7 +218,7 @@ func TestNetworkDisconnect(t *testing.T) {
|
||||
id := "8dfafdbc3a40"
|
||||
fakeRT := &FakeRoundTripper{message: "", status: http.StatusNoContent}
|
||||
client := newTestClient(fakeRT)
|
||||
opts := NetworkConnectionOptions{"foobar"}
|
||||
opts := NetworkConnectionOptions{Container: "foobar"}
|
||||
err := client.DisconnectNetwork(id, opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -165,7 +236,7 @@ func TestNetworkDisconnect(t *testing.T) {
|
||||
|
||||
func TestNetworkDisconnectNotFound(t *testing.T) {
|
||||
client := newTestClient(&FakeRoundTripper{message: "no such network container", status: http.StatusNotFound})
|
||||
opts := NetworkConnectionOptions{"foobar"}
|
||||
opts := NetworkConnectionOptions{Container: "foobar"}
|
||||
err := client.DisconnectNetwork("8dfafdbc3a40", opts)
|
||||
if serr, ok := err.(*NoSuchNetworkOrContainer); !ok {
|
||||
t.Errorf("DisconnectNetwork: wrong error type: %s.", serr)
|
||||
|
||||
90
vendor/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
90
vendor/github.com/fsouza/go-dockerclient/testing/server.go
generated
vendored
@@ -144,6 +144,7 @@ func (s *DockerServer) buildMuxer() {
|
||||
s.mux.Path("/volumes/create").Methods("POST").HandlerFunc(s.handlerWrapper(s.createVolume))
|
||||
s.mux.Path("/volumes/{name:.*}").Methods("GET").HandlerFunc(s.handlerWrapper(s.inspectVolume))
|
||||
s.mux.Path("/volumes/{name:.*}").Methods("DELETE").HandlerFunc(s.handlerWrapper(s.removeVolume))
|
||||
s.mux.Path("/info").Methods("GET").HandlerFunc(s.handlerWrapper(s.infoDocker))
|
||||
}
|
||||
|
||||
// SetHook changes the hook function used by the server.
|
||||
@@ -743,10 +744,9 @@ func (s *DockerServer) commitContainer(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
var config *docker.Config
|
||||
config := new(docker.Config)
|
||||
runConfig := r.URL.Query().Get("run")
|
||||
if runConfig != "" {
|
||||
config = new(docker.Config)
|
||||
err = json.Unmarshal([]byte(runConfig), config)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -828,7 +828,8 @@ func (s *DockerServer) pullImage(w http.ResponseWriter, r *http.Request) {
|
||||
fromImageName := r.URL.Query().Get("fromImage")
|
||||
tag := r.URL.Query().Get("tag")
|
||||
image := docker.Image{
|
||||
ID: s.generateID(),
|
||||
ID: s.generateID(),
|
||||
Config: &docker.Config{},
|
||||
}
|
||||
s.iMut.Lock()
|
||||
s.images = append(s.images, image)
|
||||
@@ -1244,3 +1245,86 @@ func (s *DockerServer) removeVolume(w http.ResponseWriter, r *http.Request) {
|
||||
s.volStore[vol.volume.Name] = nil
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (s *DockerServer) infoDocker(w http.ResponseWriter, r *http.Request) {
|
||||
s.cMut.RLock()
|
||||
defer s.cMut.RUnlock()
|
||||
s.iMut.RLock()
|
||||
defer s.iMut.RUnlock()
|
||||
var running, stopped, paused int
|
||||
for _, c := range s.containers {
|
||||
if c.State.Running {
|
||||
running++
|
||||
} else {
|
||||
stopped++
|
||||
}
|
||||
if c.State.Paused {
|
||||
paused++
|
||||
}
|
||||
}
|
||||
envs := map[string]interface{}{
|
||||
"ID": "AAAA:XXXX:0000:BBBB:AAAA:XXXX:0000:BBBB:AAAA:XXXX:0000:BBBB",
|
||||
"Containers": len(s.containers),
|
||||
"ContainersRunning": running,
|
||||
"ContainersPaused": paused,
|
||||
"ContainersStopped": stopped,
|
||||
"Images": len(s.images),
|
||||
"Driver": "aufs",
|
||||
"DriverStatus": [][]string{},
|
||||
"SystemStatus": nil,
|
||||
"Plugins": map[string]interface{}{
|
||||
"Volume": []string{
|
||||
"local",
|
||||
},
|
||||
"Network": []string{
|
||||
"bridge",
|
||||
"null",
|
||||
"host",
|
||||
},
|
||||
"Authorization": nil,
|
||||
},
|
||||
"MemoryLimit": true,
|
||||
"SwapLimit": false,
|
||||
"CpuCfsPeriod": true,
|
||||
"CpuCfsQuota": true,
|
||||
"CPUShares": true,
|
||||
"CPUSet": true,
|
||||
"IPv4Forwarding": true,
|
||||
"BridgeNfIptables": true,
|
||||
"BridgeNfIp6tables": true,
|
||||
"Debug": false,
|
||||
"NFd": 79,
|
||||
"OomKillDisable": true,
|
||||
"NGoroutines": 101,
|
||||
"SystemTime": "2016-02-25T18:13:10.25870078Z",
|
||||
"ExecutionDriver": "native-0.2",
|
||||
"LoggingDriver": "json-file",
|
||||
"NEventsListener": 0,
|
||||
"KernelVersion": "3.13.0-77-generic",
|
||||
"OperatingSystem": "Ubuntu 14.04.3 LTS",
|
||||
"OSType": "linux",
|
||||
"Architecture": "x86_64",
|
||||
"IndexServerAddress": "https://index.docker.io/v1/",
|
||||
"RegistryConfig": map[string]interface{}{
|
||||
"InsecureRegistryCIDRs": []string{},
|
||||
"IndexConfigs": map[string]interface{}{},
|
||||
"Mirrors": nil,
|
||||
},
|
||||
"InitSha1": "e2042dbb0fcf49bb9da199186d9a5063cda92a01",
|
||||
"InitPath": "/usr/lib/docker/dockerinit",
|
||||
"NCPU": 1,
|
||||
"MemTotal": 2099204096,
|
||||
"DockerRootDir": "/var/lib/docker",
|
||||
"HttpProxy": "",
|
||||
"HttpsProxy": "",
|
||||
"NoProxy": "",
|
||||
"Name": "vagrant-ubuntu-trusty-64",
|
||||
"Labels": nil,
|
||||
"ExperimentalBuild": false,
|
||||
"ServerVersion": "1.10.1",
|
||||
"ClusterStore": "",
|
||||
"ClusterAdvertise": "",
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(envs)
|
||||
}
|
||||
|
||||
29
vendor/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
29
vendor/github.com/fsouza/go-dockerclient/testing/server_test.go
generated
vendored
@@ -381,6 +381,9 @@ func TestCommitContainer(t *testing.T) {
|
||||
if got := recorder.Body.String(); got != expected {
|
||||
t.Errorf("CommitContainer: wrong response body. Want %q. Got %q.", expected, got)
|
||||
}
|
||||
if server.images[0].Config == nil {
|
||||
t.Error("CommitContainer: image Config should not be nil.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommitContainerComplete(t *testing.T) {
|
||||
@@ -1059,6 +1062,9 @@ func TestPullImage(t *testing.T) {
|
||||
if _, ok := server.imgIDs["base"]; !ok {
|
||||
t.Error("PullImage: Repository should not be empty.")
|
||||
}
|
||||
if server.images[0].Config == nil {
|
||||
t.Error("PullImage: Image Config should not be nil.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPullImageWithTag(t *testing.T) {
|
||||
@@ -2101,3 +2107,26 @@ func TestUploadToContainerMissingContainer(t *testing.T) {
|
||||
t.Errorf("UploadToContainer: wrong status. Want %d. Got %d.", http.StatusNotFound, recorder.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfoDocker(t *testing.T) {
|
||||
server, _ := NewServer("127.0.0.1:0", nil, nil)
|
||||
addContainers(server, 1)
|
||||
server.buildMuxer()
|
||||
recorder := httptest.NewRecorder()
|
||||
request, _ := http.NewRequest("GET", "/info", nil)
|
||||
server.ServeHTTP(recorder, request)
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("InfoDocker: wrong status. Want %d. Got %d.", http.StatusOK, recorder.Code)
|
||||
}
|
||||
var infoData map[string]interface{}
|
||||
err := json.Unmarshal(recorder.Body.Bytes(), &infoData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if infoData["Containers"].(float64) != 1.0 {
|
||||
t.Fatalf("InfoDocker: wrong containers count. Want %f. Got %f.", 1.0, infoData["Containers"])
|
||||
}
|
||||
if infoData["DockerRootDir"].(string) != "/var/lib/docker" {
|
||||
t.Fatalf("InfoDocker: wrong docker root. Want /var/lib/docker. Got %s.", infoData["DockerRootDir"])
|
||||
}
|
||||
}
|
||||
|
||||
17
vendor/github.com/fsouza/go-dockerclient/travis-scripts/install.bash
generated
vendored
Normal file
17
vendor/github.com/fsouza/go-dockerclient/travis-scripts/install.bash
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
|
||||
sudo stop docker || true
|
||||
sudo rm -rf /var/lib/docker
|
||||
sudo rm -f `which docker`
|
||||
|
||||
set -e
|
||||
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install docker-engine=${DOCKER_VERSION}-0~$(lsb_release -cs) -y --force-yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold"
|
||||
fi
|
||||
15
vendor/github.com/fsouza/go-dockerclient/travis-scripts/run-tests.bash
generated
vendored
Normal file
15
vendor/github.com/fsouza/go-dockerclient/travis-scripts/run-tests.bash
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Copyright 2016 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.
|
||||
|
||||
if ! [[ $TRAVIS_GO_VERSION =~ ^1\.[34] ]]; then
|
||||
make lint
|
||||
fi
|
||||
|
||||
make vet fmtcheck gotest
|
||||
|
||||
if [[ $TRAVIS_OS_NAME == "linux" ]]; then
|
||||
DOCKER_HOST=tcp://127.0.0.1:2375 make integration
|
||||
fi
|
||||
2
vendor/manifest
vendored
2
vendor/manifest
vendored
@@ -546,7 +546,7 @@
|
||||
{
|
||||
"importpath": "github.com/fsouza/go-dockerclient",
|
||||
"repository": "https://github.com/fsouza/go-dockerclient",
|
||||
"revision": "0099401a7342ad77e71ca9f9a57c5e72fb80f6b2",
|
||||
"revision": "1b46a3bbaf08d19385915795bae730a318f3dd69",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user