diff --git a/integration-cli/check_test.go b/integration-cli/check_test.go index 45cbf09451..3b6e341302 100644 --- a/integration-cli/check_test.go +++ b/integration-cli/check_test.go @@ -34,9 +34,6 @@ const ( var ( testEnv *environment.Execution - // FIXME(vdemeester) remove these and use environmentdaemonPid - protectedImages = map[string]struct{}{} - // the docker client binary to use dockerBinary = "docker" ) @@ -64,16 +61,6 @@ func TestMain(m *testing.M) { os.Exit(1) } - cmd := exec.Command(dockerBinary, "images", "-f", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}") - cmd.Env = appendBaseEnv(true) - out, err := cmd.CombinedOutput() - if err != nil { - panic(fmt.Errorf("err=%v\nout=%s\n", err, out)) - } - images := strings.Split(strings.TrimSpace(string(out)), "\n") - for _, img := range images { - protectedImages[img] = struct{}{} - } if testEnv.LocalDaemon() { fmt.Println("INFO: Testing against a local daemon") } else { @@ -84,6 +71,14 @@ func TestMain(m *testing.M) { } func Test(t *testing.T) { + cmd := exec.Command(dockerBinary, "images", "-f", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}") + cmd.Env = appendBaseEnv(true) + out, err := cmd.CombinedOutput() + if err != nil { + panic(fmt.Errorf("err=%v\nout=%s\n", err, out)) + } + images := strings.Split(strings.TrimSpace(string(out)), "\n") + testEnv.ProtectImage(t, images...) if testEnv.DaemonPlatform() == "linux" { ensureFrozenImagesLinux(t) } @@ -104,14 +99,7 @@ func (s *DockerSuite) OnTimeout(c *check.C) { } func (s *DockerSuite) TearDownTest(c *check.C) { - unpauseAllContainers(c) - deleteAllContainers(c) - deleteAllImages(c) - deleteAllVolumes(c) - deleteAllNetworks(c) - if testEnv.DaemonPlatform() == "linux" { - deleteAllPlugins(c) - } + testEnv.Clean(c, dockerBinary) } func init() { diff --git a/integration-cli/docker_utils_test.go b/integration-cli/docker_utils_test.go index 62ed1edeb5..3f424eacb6 100644 --- a/integration-cli/docker_utils_test.go +++ b/integration-cli/docker_utils_test.go @@ -20,23 +20,19 @@ import ( "time" "github.com/docker/docker/api/types" - volumetypes "github.com/docker/docker/api/types/volume" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/daemon" + "github.com/docker/docker/integration-cli/environment" "github.com/docker/docker/integration-cli/registry" "github.com/docker/docker/integration-cli/request" - "github.com/docker/docker/opts" "github.com/docker/docker/pkg/stringutils" icmd "github.com/docker/docker/pkg/testutil/cmd" "github.com/go-check/check" ) +// Deprecated func daemonHost() string { - daemonURLStr := "unix://" + opts.DefaultUnixSocket - if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { - daemonURLStr = daemonHostVar - } - return daemonURLStr + return environment.DaemonHost() } // FIXME(vdemeester) move this away are remove ignoreNoSuchContainer bool @@ -58,6 +54,7 @@ func getAllContainers(c *check.C) string { return result.Combined() } +// Deprecated func deleteAllContainers(c *check.C) { containers := getAllContainers(c) if containers != "" { @@ -66,136 +63,6 @@ func deleteAllContainers(c *check.C) { } } -func deleteAllNetworks(c *check.C) { - networks, err := getAllNetworks() - c.Assert(err, check.IsNil) - var errs []string - for _, n := range networks { - if n.Name == "bridge" || n.Name == "none" || n.Name == "host" { - continue - } - if testEnv.DaemonPlatform() == "windows" && strings.ToLower(n.Name) == "nat" { - // nat is a pre-defined network on Windows and cannot be removed - continue - } - status, b, err := request.SockRequest("DELETE", "/networks/"+n.Name, nil, daemonHost()) - if err != nil { - errs = append(errs, err.Error()) - continue - } - if status != http.StatusNoContent { - errs = append(errs, fmt.Sprintf("error deleting network %s: %s", n.Name, string(b))) - } - } - c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) -} - -func getAllNetworks() ([]types.NetworkResource, error) { - var networks []types.NetworkResource - _, b, err := request.SockRequest("GET", "/networks", nil, daemonHost()) - if err != nil { - return nil, err - } - if err := json.Unmarshal(b, &networks); err != nil { - return nil, err - } - return networks, nil -} - -func deleteAllPlugins(c *check.C) { - plugins, err := getAllPlugins() - c.Assert(err, checker.IsNil) - var errs []string - for _, p := range plugins { - pluginName := p.Name - status, b, err := request.SockRequest("DELETE", "/plugins/"+pluginName+"?force=1", nil, daemonHost()) - if err != nil { - errs = append(errs, err.Error()) - continue - } - if status != http.StatusOK { - errs = append(errs, fmt.Sprintf("error deleting plugin %s: %s", p.Name, string(b))) - } - } - c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) -} - -func getAllPlugins() (types.PluginsListResponse, error) { - var plugins types.PluginsListResponse - _, b, err := request.SockRequest("GET", "/plugins", nil, daemonHost()) - if err != nil { - return nil, err - } - if err := json.Unmarshal(b, &plugins); err != nil { - return nil, err - } - return plugins, nil -} - -func deleteAllVolumes(c *check.C) { - volumes, err := getAllVolumes() - c.Assert(err, checker.IsNil) - var errs []string - for _, v := range volumes { - status, b, err := request.SockRequest("DELETE", "/volumes/"+v.Name, nil, daemonHost()) - if err != nil { - errs = append(errs, err.Error()) - continue - } - if status != http.StatusNoContent { - errs = append(errs, fmt.Sprintf("error deleting volume %s: %s", v.Name, string(b))) - } - } - c.Assert(errs, checker.HasLen, 0, check.Commentf(strings.Join(errs, "\n"))) -} - -func getAllVolumes() ([]*types.Volume, error) { - var volumes volumetypes.VolumesListOKBody - _, b, err := request.SockRequest("GET", "/volumes", nil, daemonHost()) - if err != nil { - return nil, err - } - if err := json.Unmarshal(b, &volumes); err != nil { - return nil, err - } - return volumes.Volumes, nil -} - -func deleteAllImages(c *check.C) { - cmd := exec.Command(dockerBinary, "images", "--digests") - cmd.Env = appendBaseEnv(true) - out, err := cmd.CombinedOutput() - c.Assert(err, checker.IsNil) - lines := strings.Split(string(out), "\n")[1:] - imgMap := map[string]struct{}{} - for _, l := range lines { - if l == "" { - continue - } - fields := strings.Fields(l) - imgTag := fields[0] + ":" + fields[1] - if _, ok := protectedImages[imgTag]; !ok { - if fields[0] == "" || fields[1] == "" { - if fields[2] != "" { - imgMap[fields[0]+"@"+fields[2]] = struct{}{} - } else { - imgMap[fields[3]] = struct{}{} - } - // continue - } else { - imgMap[imgTag] = struct{}{} - } - } - } - if len(imgMap) != 0 { - imgs := make([]string, 0, len(imgMap)) - for k := range imgMap { - imgs = append(imgs, k) - } - dockerCmd(c, append([]string{"rmi", "-f"}, imgs...)...) - } -} - func getPausedContainers(c *check.C) []string { result := icmd.RunCommand(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") result.Assert(c, icmd.Success) @@ -206,6 +73,7 @@ func unpauseContainer(c *check.C, container string) { dockerCmd(c, "unpause", container) } +// Deprecated func unpauseAllContainers(c *check.C) { containers := getPausedContainers(c) for _, value := range containers { @@ -487,7 +355,7 @@ func newRemoteFileServer(c *check.C, ctx *FakeContext) *remoteFileServer { container = fmt.Sprintf("fileserver-cnt-%s", strings.ToLower(stringutils.GenerateRandomAlphaOnlyString(10))) ) - c.Assert(ensureHTTPServerImage(), checker.IsNil) + ensureHTTPServerImage(c) // Build the image fakeContextAddDockerfile(c, ctx, `FROM httpserver diff --git a/integration-cli/environment/clean.go b/integration-cli/environment/clean.go new file mode 100644 index 0000000000..6055c40a60 --- /dev/null +++ b/integration-cli/environment/clean.go @@ -0,0 +1,213 @@ +package environment + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + + "github.com/docker/docker/api/types" + volumetypes "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/integration-cli/request" + "github.com/docker/docker/opts" + icmd "github.com/docker/docker/pkg/testutil/cmd" +) + +type testingT interface { + logT + Fatalf(string, ...interface{}) +} + +type logT interface { + Logf(string, ...interface{}) +} + +// Clean the environment, preserving protected objects (images, containers, ...) +// and removing everything else. It's meant to run after any tests so that they don't +// depend on each others. +func (e *Execution) Clean(t testingT, dockerBinary string) { + unpauseAllContainers(t, dockerBinary) + deleteAllContainers(t, dockerBinary) + deleteAllImages(t, dockerBinary, e.protectedElements.images) + deleteAllVolumes(t, dockerBinary) + deleteAllNetworks(t, dockerBinary, e.DaemonPlatform()) + if e.DaemonPlatform() == "linux" { + deleteAllPlugins(t, dockerBinary) + } +} + +func unpauseAllContainers(t testingT, dockerBinary string) { + containers := getPausedContainers(t, dockerBinary) + if len(containers) > 0 { + icmd.RunCommand(dockerBinary, append([]string{"unpause"}, containers...)...).Assert(t, icmd.Success) + } +} + +func getPausedContainers(t testingT, dockerBinary string) []string { + result := icmd.RunCommand(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") + result.Assert(t, icmd.Success) + return strings.Fields(result.Combined()) +} + +func deleteAllContainers(t testingT, dockerBinary string) { + containers := getAllContainers(t, dockerBinary) + if len(containers) > 0 { + icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, containers...)...).Assert(t, icmd.Success) + } +} + +func getAllContainers(t testingT, dockerBinary string) []string { + result := icmd.RunCommand(dockerBinary, "ps", "-q", "-a") + result.Assert(t, icmd.Success) + return strings.Fields(result.Combined()) +} + +func deleteAllImages(t testingT, dockerBinary string, protectedImages map[string]struct{}) { + result := icmd.RunCommand(dockerBinary, "images", "--digests") + result.Assert(t, icmd.Success) + lines := strings.Split(string(result.Combined()), "\n")[1:] + imgMap := map[string]struct{}{} + for _, l := range lines { + if l == "" { + continue + } + fields := strings.Fields(l) + imgTag := fields[0] + ":" + fields[1] + if _, ok := protectedImages[imgTag]; !ok { + if fields[0] == "" || fields[1] == "" { + if fields[2] != "" { + imgMap[fields[0]+"@"+fields[2]] = struct{}{} + } else { + imgMap[fields[3]] = struct{}{} + } + // continue + } else { + imgMap[imgTag] = struct{}{} + } + } + } + if len(imgMap) != 0 { + imgs := make([]string, 0, len(imgMap)) + for k := range imgMap { + imgs = append(imgs, k) + } + icmd.RunCommand(dockerBinary, append([]string{"rmi", "-f"}, imgs...)...).Assert(t, icmd.Success) + } +} + +func deleteAllVolumes(t testingT, dockerBinary string) { + volumes, err := getAllVolumes() + if err != nil { + t.Fatalf("%v", err) + } + var errs []string + for _, v := range volumes { + status, b, err := request.SockRequest("DELETE", "/volumes/"+v.Name, nil, DaemonHost()) + if err != nil { + errs = append(errs, err.Error()) + continue + } + if status != http.StatusNoContent { + errs = append(errs, fmt.Sprintf("error deleting volume %s: %s", v.Name, string(b))) + } + } + if len(errs) > 0 { + t.Fatalf("%v", strings.Join(errs, "\n")) + } +} + +func getAllVolumes() ([]*types.Volume, error) { + var volumes volumetypes.VolumesListOKBody + _, b, err := request.SockRequest("GET", "/volumes", nil, DaemonHost()) + if err != nil { + return nil, err + } + if err := json.Unmarshal(b, &volumes); err != nil { + return nil, err + } + return volumes.Volumes, nil +} + +func deleteAllNetworks(t testingT, dockerBinary string, daemonPlatform string) { + networks, err := getAllNetworks() + if err != nil { + t.Fatalf("%v", err) + } + var errs []string + for _, n := range networks { + if n.Name == "bridge" || n.Name == "none" || n.Name == "host" { + continue + } + if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" { + // nat is a pre-defined network on Windows and cannot be removed + continue + } + status, b, err := request.SockRequest("DELETE", "/networks/"+n.Name, nil, DaemonHost()) + if err != nil { + errs = append(errs, err.Error()) + continue + } + if status != http.StatusNoContent { + errs = append(errs, fmt.Sprintf("error deleting network %s: %s", n.Name, string(b))) + } + } + if len(errs) > 0 { + t.Fatalf("%v", strings.Join(errs, "\n")) + } +} + +func getAllNetworks() ([]types.NetworkResource, error) { + var networks []types.NetworkResource + _, b, err := request.SockRequest("GET", "/networks", nil, DaemonHost()) + if err != nil { + return nil, err + } + if err := json.Unmarshal(b, &networks); err != nil { + return nil, err + } + return networks, nil +} + +func deleteAllPlugins(t testingT, dockerBinary string) { + plugins, err := getAllPlugins() + if err != nil { + t.Fatalf("%v", err) + } + var errs []string + for _, p := range plugins { + pluginName := p.Name + status, b, err := request.SockRequest("DELETE", "/plugins/"+pluginName+"?force=1", nil, DaemonHost()) + if err != nil { + errs = append(errs, err.Error()) + continue + } + if status != http.StatusOK { + errs = append(errs, fmt.Sprintf("error deleting plugin %s: %s", p.Name, string(b))) + } + } + if len(errs) > 0 { + t.Fatalf("%v", strings.Join(errs, "\n")) + } +} + +func getAllPlugins() (types.PluginsListResponse, error) { + var plugins types.PluginsListResponse + _, b, err := request.SockRequest("GET", "/plugins", nil, DaemonHost()) + if err != nil { + return nil, err + } + if err := json.Unmarshal(b, &plugins); err != nil { + return nil, err + } + return plugins, nil +} + +// DaemonHost return the daemon host string for this test execution +func DaemonHost() string { + daemonURLStr := "unix://" + opts.DefaultUnixSocket + if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" { + daemonURLStr = daemonHostVar + } + return daemonURLStr +} diff --git a/integration-cli/environment/environment.go b/integration-cli/environment/environment.go index 91f5591af1..dfb7a9d891 100644 --- a/integration-cli/environment/environment.go +++ b/integration-cli/environment/environment.go @@ -32,6 +32,8 @@ type Execution struct { // baseImage is the name of the base image for testing // Environment variable WINDOWS_BASE_IMAGE can override this baseImage string + + protectedElements protectedElements } // New creates a new Execution struct @@ -100,6 +102,9 @@ func New() (*Execution, error) { daemonPid: daemonPid, experimentalDaemon: info.ExperimentalBuild, baseImage: baseImage, + protectedElements: protectedElements{ + images: map[string]struct{}{}, + }, }, nil } func getDaemonDockerInfo() (types.Info, error) { diff --git a/integration-cli/environment/protect.go b/integration-cli/environment/protect.go new file mode 100644 index 0000000000..2b0dd6df2f --- /dev/null +++ b/integration-cli/environment/protect.go @@ -0,0 +1,12 @@ +package environment + +// ProtectImage adds the specified image(s) to be protected in case of clean +func (e *Execution) ProtectImage(t testingT, images ...string) { + for _, image := range images { + e.protectedElements.images[image] = struct{}{} + } +} + +type protectedElements struct { + images map[string]struct{} +} diff --git a/integration-cli/fixtures_linux_daemon_test.go b/integration-cli/fixtures_linux_daemon_test.go index ed3db7fe4b..4968514701 100644 --- a/integration-cli/fixtures_linux_daemon_test.go +++ b/integration-cli/fixtures_linux_daemon_test.go @@ -31,9 +31,7 @@ func ensureFrozenImagesLinux(t testingT) { t.Logf(dockerCmdWithError("images")) t.Fatalf("%+v", err) } - for _, img := range images { - protectedImages[img] = struct{}{} - } + defer testEnv.ProtectImage(t, images...) } var ensureSyscallTestOnce sync.Once @@ -46,7 +44,7 @@ func ensureSyscallTest(c *check.C) { if !doIt { return } - protectedImages["syscall-test:latest"] = struct{}{} + defer testEnv.ProtectImage(c, "syscall-test:latest") // if no match, must build in docker, which is significantly slower // (slower mostly because of the vfs graphdriver) @@ -104,7 +102,7 @@ func ensureSyscallTestBuild(c *check.C) { } func ensureNNPTest(c *check.C) { - protectedImages["nnp-test:latest"] = struct{}{} + defer testEnv.ProtectImage(c, "nnp-test:latest") if testEnv.DaemonPlatform() != runtime.GOOS { ensureNNPTestBuild(c) return diff --git a/integration-cli/fixtures_test.go b/integration-cli/fixtures_test.go index f5f7db3a61..9683e4a875 100644 --- a/integration-cli/fixtures_test.go +++ b/integration-cli/fixtures_test.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "io/ioutil" "os" "os/exec" @@ -11,21 +10,21 @@ import ( var ensureHTTPServerOnce sync.Once -func ensureHTTPServerImage() error { +func ensureHTTPServerImage(t testingT) { var doIt bool ensureHTTPServerOnce.Do(func() { doIt = true }) if !doIt { - return nil + return } - protectedImages["httpserver:latest"] = struct{}{} + defer testEnv.ProtectImage(t, "httpserver:latest") tmp, err := ioutil.TempDir("", "docker-http-server-test") if err != nil { - return fmt.Errorf("could not build http server: %v", err) + t.Fatalf("could not build http server: %v", err) } defer os.RemoveAll(tmp) @@ -40,7 +39,7 @@ func ensureHTTPServerImage() error { goCmd, lookErr := exec.LookPath("go") if lookErr != nil { - return fmt.Errorf("could not build http server: %v", lookErr) + t.Fatalf("could not build http server: %v", lookErr) } cmd := exec.Command(goCmd, "build", "-o", filepath.Join(tmp, "httpserver"), "github.com/docker/docker/contrib/httpserver") @@ -51,19 +50,18 @@ func ensureHTTPServerImage() error { }...) var out []byte if out, err = cmd.CombinedOutput(); err != nil { - return fmt.Errorf("could not build http server: %s", string(out)) + t.Fatalf("could not build http server: %s", string(out)) } cpCmd, lookErr := exec.LookPath("cp") if lookErr != nil { - return fmt.Errorf("could not build http server: %v", lookErr) + t.Fatalf("could not build http server: %v", lookErr) } if out, err = exec.Command(cpCmd, "../contrib/httpserver/Dockerfile", filepath.Join(tmp, "Dockerfile")).CombinedOutput(); err != nil { - return fmt.Errorf("could not build http server: %v", string(out)) + t.Fatalf("could not build http server: %v", string(out)) } if out, err = exec.Command(dockerBinary, "build", "-q", "-t", "httpserver", tmp).CombinedOutput(); err != nil { - return fmt.Errorf("could not build http server: %v", string(out)) + t.Fatalf("could not build http server: %v", string(out)) } - return nil }