split all non-cli portions into a new internal/test/environment package
Set a test environment on packages instead of creating new ones.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
| ... | ... |
@@ -2,10 +2,12 @@ package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
+ "io/ioutil" |
|
| 5 | 6 |
"net/http/httptest" |
| 6 | 7 |
"os" |
| 7 | 8 |
"path" |
| 8 | 9 |
"path/filepath" |
| 10 |
+ "strconv" |
|
| 9 | 11 |
"sync" |
| 10 | 12 |
"syscall" |
| 11 | 13 |
"testing" |
| ... | ... |
@@ -20,6 +22,7 @@ import ( |
| 20 | 20 |
"github.com/docker/docker/integration-cli/environment" |
| 21 | 21 |
"github.com/docker/docker/integration-cli/fixtures/plugin" |
| 22 | 22 |
"github.com/docker/docker/integration-cli/registry" |
| 23 |
+ ienv "github.com/docker/docker/internal/test/environment" |
|
| 23 | 24 |
"github.com/docker/docker/pkg/reexec" |
| 24 | 25 |
"github.com/go-check/check" |
| 25 | 26 |
"golang.org/x/net/context" |
| ... | ... |
@@ -57,20 +60,14 @@ func init() {
|
| 57 | 57 |
|
| 58 | 58 |
func TestMain(m *testing.M) {
|
| 59 | 59 |
dockerBinary = testEnv.DockerBinary() |
| 60 |
- |
|
| 61 |
- if testEnv.LocalDaemon() {
|
|
| 62 |
- fmt.Println("INFO: Testing against a local daemon")
|
|
| 63 |
- } else {
|
|
| 64 |
- fmt.Println("INFO: Testing against a remote daemon")
|
|
| 65 |
- } |
|
| 66 |
- exitCode := m.Run() |
|
| 67 |
- os.Exit(exitCode) |
|
| 60 |
+ testEnv.Print() |
|
| 61 |
+ os.Exit(m.Run()) |
|
| 68 | 62 |
} |
| 69 | 63 |
|
| 70 | 64 |
func Test(t *testing.T) {
|
| 71 |
- cli.EnsureTestEnvIsLoaded(t) |
|
| 72 |
- fakestorage.EnsureTestEnvIsLoaded(t) |
|
| 73 |
- environment.ProtectImages(t, testEnv) |
|
| 65 |
+ cli.SetTestEnvironment(testEnv) |
|
| 66 |
+ fakestorage.SetTestEnvironment(&testEnv.Execution) |
|
| 67 |
+ ienv.ProtectImages(t, &testEnv.Execution) |
|
| 74 | 68 |
check.TestingT(t) |
| 75 | 69 |
} |
| 76 | 70 |
|
| ... | ... |
@@ -82,13 +79,25 @@ type DockerSuite struct {
|
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 | 84 |
func (s *DockerSuite) OnTimeout(c *check.C) {
|
| 85 |
- if testEnv.DaemonPID() > 0 && testEnv.LocalDaemon() {
|
|
| 86 |
- daemon.SignalDaemonDump(testEnv.DaemonPID()) |
|
| 85 |
+ path := filepath.Join(os.Getenv("DEST"), "docker.pid")
|
|
| 86 |
+ b, err := ioutil.ReadFile(path) |
|
| 87 |
+ if err != nil {
|
|
| 88 |
+ c.Fatalf("Failed to get daemon PID from %s\n", path)
|
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ rawPid, err := strconv.ParseInt(string(b), 10, 32) |
|
| 92 |
+ if err != nil {
|
|
| 93 |
+ c.Fatalf("Failed to parse pid from %s: %s\n", path, err)
|
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ daemonPid := int(rawPid) |
|
| 97 |
+ if daemonPid > 0 && testEnv.IsLocalDaemon() {
|
|
| 98 |
+ daemon.SignalDaemonDump(daemonPid) |
|
| 87 | 99 |
} |
| 88 | 100 |
} |
| 89 | 101 |
|
| 90 | 102 |
func (s *DockerSuite) TearDownTest(c *check.C) {
|
| 91 |
- testEnv.Clean(c, dockerBinary) |
|
| 103 |
+ testEnv.Clean(c) |
|
| 92 | 104 |
} |
| 93 | 105 |
|
| 94 | 106 |
func init() {
|
| ... | ... |
@@ -11,9 +11,11 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
"github.com/docker/docker/integration-cli/cli/build/fakecontext" |
| 13 | 13 |
"github.com/docker/docker/integration-cli/cli/build/fakestorage" |
| 14 |
+ "github.com/stretchr/testify/require" |
|
| 14 | 15 |
) |
| 15 | 16 |
|
| 16 | 17 |
type testingT interface {
|
| 18 |
+ require.TestingT |
|
| 17 | 19 |
logT |
| 18 | 20 |
Fatal(args ...interface{})
|
| 19 | 21 |
Fatalf(string, ...interface{})
|
| ... | ... |
@@ -8,39 +8,20 @@ import ( |
| 8 | 8 |
"net/url" |
| 9 | 9 |
"os" |
| 10 | 10 |
"strings" |
| 11 |
- "sync" |
|
| 12 | 11 |
|
| 13 | 12 |
"github.com/docker/docker/integration-cli/cli" |
| 14 | 13 |
"github.com/docker/docker/integration-cli/cli/build" |
| 15 | 14 |
"github.com/docker/docker/integration-cli/cli/build/fakecontext" |
| 16 |
- "github.com/docker/docker/integration-cli/environment" |
|
| 17 | 15 |
"github.com/docker/docker/integration-cli/request" |
| 16 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 18 | 17 |
"github.com/docker/docker/pkg/stringutils" |
| 18 |
+ "github.com/stretchr/testify/require" |
|
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 |
-var ( |
|
| 22 |
- testEnv *environment.Execution |
|
| 23 |
- onlyOnce sync.Once |
|
| 24 |
-) |
|
| 25 |
- |
|
| 26 |
-// EnsureTestEnvIsLoaded make sure the test environment is loaded for this package |
|
| 27 |
-func EnsureTestEnvIsLoaded(t testingT) {
|
|
| 28 |
- var doIt bool |
|
| 29 |
- var err error |
|
| 30 |
- onlyOnce.Do(func() {
|
|
| 31 |
- doIt = true |
|
| 32 |
- }) |
|
| 33 |
- |
|
| 34 |
- if !doIt {
|
|
| 35 |
- return |
|
| 36 |
- } |
|
| 37 |
- testEnv, err = environment.New() |
|
| 38 |
- if err != nil {
|
|
| 39 |
- t.Fatalf("error loading testenv : %v", err)
|
|
| 40 |
- } |
|
| 41 |
-} |
|
| 21 |
+var testEnv *environment.Execution |
|
| 42 | 22 |
|
| 43 | 23 |
type testingT interface {
|
| 24 |
+ require.TestingT |
|
| 44 | 25 |
logT |
| 45 | 26 |
Fatal(args ...interface{})
|
| 46 | 27 |
Fatalf(string, ...interface{})
|
| ... | ... |
@@ -58,11 +39,20 @@ type Fake interface {
|
| 58 | 58 |
CtxDir() string |
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
+// SetTestEnvironment sets a static test environment |
|
| 62 |
+// TODO: decouple this package from environment |
|
| 63 |
+func SetTestEnvironment(env *environment.Execution) {
|
|
| 64 |
+ testEnv = env |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 61 | 67 |
// New returns a static file server that will be use as build context. |
| 62 | 68 |
func New(t testingT, dir string, modifiers ...func(*fakecontext.Fake) error) Fake {
|
| 69 |
+ if testEnv == nil {
|
|
| 70 |
+ t.Fatal("fakstorage package requires SetTestEnvironment() to be called before use.")
|
|
| 71 |
+ } |
|
| 63 | 72 |
ctx := fakecontext.New(t, dir, modifiers...) |
| 64 |
- if testEnv.LocalDaemon() {
|
|
| 65 |
- return newLocalFakeStorage(t, ctx) |
|
| 73 |
+ if testEnv.IsLocalDaemon() {
|
|
| 74 |
+ return newLocalFakeStorage(ctx) |
|
| 66 | 75 |
} |
| 67 | 76 |
return newRemoteFileServer(t, ctx) |
| 68 | 77 |
} |
| ... | ... |
@@ -86,7 +76,7 @@ func (s *localFileStorage) Close() error {
|
| 86 | 86 |
return s.Fake.Close() |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 |
-func newLocalFakeStorage(t testingT, ctx *fakecontext.Fake) *localFileStorage {
|
|
| 89 |
+func newLocalFakeStorage(ctx *fakecontext.Fake) *localFileStorage {
|
|
| 90 | 90 |
handler := http.FileServer(http.Dir(ctx.Dir)) |
| 91 | 91 |
server := httptest.NewServer(handler) |
| 92 | 92 |
return &localFileStorage{
|
| ... | ... |
@@ -4,7 +4,6 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io" |
| 6 | 6 |
"strings" |
| 7 |
- "sync" |
|
| 8 | 7 |
"time" |
| 9 | 8 |
|
| 10 | 9 |
"github.com/docker/docker/integration-cli/daemon" |
| ... | ... |
@@ -13,26 +12,12 @@ import ( |
| 13 | 13 |
"github.com/pkg/errors" |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 |
-var ( |
|
| 17 |
- testEnv *environment.Execution |
|
| 18 |
- onlyOnce sync.Once |
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-// EnsureTestEnvIsLoaded make sure the test environment is loaded for this package |
|
| 22 |
-func EnsureTestEnvIsLoaded(t testingT) {
|
|
| 23 |
- var doIt bool |
|
| 24 |
- var err error |
|
| 25 |
- onlyOnce.Do(func() {
|
|
| 26 |
- doIt = true |
|
| 27 |
- }) |
|
| 16 |
+var testEnv *environment.Execution |
|
| 28 | 17 |
|
| 29 |
- if !doIt {
|
|
| 30 |
- return |
|
| 31 |
- } |
|
| 32 |
- testEnv, err = environment.New() |
|
| 33 |
- if err != nil {
|
|
| 34 |
- t.Fatalf("error loading testenv : %v", err)
|
|
| 35 |
- } |
|
| 18 |
+// SetTestEnvironment sets a static test environment |
|
| 19 |
+// TODO: decouple this package from environment |
|
| 20 |
+func SetTestEnvironment(env *environment.Execution) {
|
|
| 21 |
+ testEnv = env |
|
| 36 | 22 |
} |
| 37 | 23 |
|
| 38 | 24 |
// CmdOperator defines functions that can modify a command |
| ... | ... |
@@ -130,7 +115,7 @@ func Docker(cmd icmd.Cmd, cmdOperators ...CmdOperator) *icmd.Result {
|
| 130 | 130 |
// validateArgs is a checker to ensure tests are not running commands which are |
| 131 | 131 |
// not supported on platforms. Specifically on Windows this is 'busybox top'. |
| 132 | 132 |
func validateArgs(args ...string) error {
|
| 133 |
- if testEnv.DaemonPlatform() != "windows" {
|
|
| 133 |
+ if testEnv.DaemonInfo.OSType != "windows" {
|
|
| 134 | 134 |
return nil |
| 135 | 135 |
} |
| 136 | 136 |
foundBusybox := -1 |
| ... | ... |
@@ -2215,7 +2215,7 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
|
| 2215 | 2215 |
|
| 2216 | 2216 |
out, err = inspectMountSourceField("dark_helmet", prefix+slash+`foo`)
|
| 2217 | 2217 |
c.Assert(err, check.IsNil) |
| 2218 |
- if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.VolumesConfigPath())) {
|
|
| 2218 |
+ if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
|
|
| 2219 | 2219 |
c.Fatalf("Volume was not defined for %s/foo\n%q", prefix, out)
|
| 2220 | 2220 |
} |
| 2221 | 2221 |
|
| ... | ... |
@@ -2226,7 +2226,7 @@ func (s *DockerSuite) TestRunVolumesCleanPaths(c *check.C) {
|
| 2226 | 2226 |
|
| 2227 | 2227 |
out, err = inspectMountSourceField("dark_helmet", prefix+slash+"bar")
|
| 2228 | 2228 |
c.Assert(err, check.IsNil) |
| 2229 |
- if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.VolumesConfigPath())) {
|
|
| 2229 |
+ if !strings.Contains(strings.ToLower(out), strings.ToLower(testEnv.PlatformDefaults.VolumesConfigPath)) {
|
|
| 2230 | 2230 |
c.Fatalf("Volume was not defined for %s/bar\n%q", prefix, out)
|
| 2231 | 2231 |
} |
| 2232 | 2232 |
} |
| ... | ... |
@@ -234,7 +234,7 @@ func readFile(src string, c *check.C) (content string) {
|
| 234 | 234 |
} |
| 235 | 235 |
|
| 236 | 236 |
func containerStorageFile(containerID, basename string) string {
|
| 237 |
- return filepath.Join(testEnv.ContainerStoragePath(), containerID, basename) |
|
| 237 |
+ return filepath.Join(testEnv.PlatformDefaults.ContainerStoragePath, containerID, basename) |
|
| 238 | 238 |
} |
| 239 | 239 |
|
| 240 | 240 |
// docker commands that use this function must be run with the '-d' switch. |
| ... | ... |
@@ -266,7 +266,7 @@ func readContainerFileWithExec(c *check.C, containerID, filename string) []byte |
| 266 | 266 |
|
| 267 | 267 |
// daemonTime provides the current time on the daemon host |
| 268 | 268 |
func daemonTime(c *check.C) time.Time {
|
| 269 |
- if testEnv.LocalDaemon() {
|
|
| 269 |
+ if testEnv.IsLocalDaemon() {
|
|
| 270 | 270 |
return time.Now() |
| 271 | 271 |
} |
| 272 | 272 |
cli, err := client.NewEnvClient() |
| 273 | 273 |
deleted file mode 100644 |
| ... | ... |
@@ -1,198 +0,0 @@ |
| 1 |
-package environment |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "regexp" |
|
| 5 |
- "strings" |
|
| 6 |
- |
|
| 7 |
- "github.com/docker/docker/api/types" |
|
| 8 |
- "github.com/docker/docker/api/types/filters" |
|
| 9 |
- "github.com/docker/docker/client" |
|
| 10 |
- "github.com/gotestyourself/gotestyourself/icmd" |
|
| 11 |
- "golang.org/x/net/context" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-type testingT interface {
|
|
| 15 |
- logT |
|
| 16 |
- Fatalf(string, ...interface{})
|
|
| 17 |
-} |
|
| 18 |
- |
|
| 19 |
-type logT interface {
|
|
| 20 |
- Logf(string, ...interface{})
|
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-// Clean the environment, preserving protected objects (images, containers, ...) |
|
| 24 |
-// and removing everything else. It's meant to run after any tests so that they don't |
|
| 25 |
-// depend on each others. |
|
| 26 |
-func (e *Execution) Clean(t testingT, dockerBinary string) {
|
|
| 27 |
- cli, err := client.NewEnvClient() |
|
| 28 |
- if err != nil {
|
|
| 29 |
- t.Fatalf("%v", err)
|
|
| 30 |
- } |
|
| 31 |
- defer cli.Close() |
|
| 32 |
- |
|
| 33 |
- if (e.DaemonPlatform() != "windows") || (e.DaemonPlatform() == "windows" && e.Isolation() == "hyperv") {
|
|
| 34 |
- unpauseAllContainers(t, dockerBinary) |
|
| 35 |
- } |
|
| 36 |
- deleteAllContainers(t, dockerBinary) |
|
| 37 |
- deleteAllImages(t, dockerBinary, e.protectedElements.images) |
|
| 38 |
- deleteAllVolumes(t, cli) |
|
| 39 |
- deleteAllNetworks(t, cli, e.DaemonPlatform()) |
|
| 40 |
- if e.DaemonPlatform() == "linux" {
|
|
| 41 |
- deleteAllPlugins(t, cli, dockerBinary) |
|
| 42 |
- } |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-func unpauseAllContainers(t testingT, dockerBinary string) {
|
|
| 46 |
- containers := getPausedContainers(t, dockerBinary) |
|
| 47 |
- if len(containers) > 0 {
|
|
| 48 |
- icmd.RunCommand(dockerBinary, append([]string{"unpause"}, containers...)...).Assert(t, icmd.Success)
|
|
| 49 |
- } |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-func getPausedContainers(t testingT, dockerBinary string) []string {
|
|
| 53 |
- result := icmd.RunCommand(dockerBinary, "ps", "-f", "status=paused", "-q", "-a") |
|
| 54 |
- result.Assert(t, icmd.Success) |
|
| 55 |
- return strings.Fields(result.Combined()) |
|
| 56 |
-} |
|
| 57 |
- |
|
| 58 |
-var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`) |
|
| 59 |
- |
|
| 60 |
-func deleteAllContainers(t testingT, dockerBinary string) {
|
|
| 61 |
- containers := getAllContainers(t, dockerBinary) |
|
| 62 |
- if len(containers) > 0 {
|
|
| 63 |
- result := icmd.RunCommand(dockerBinary, append([]string{"rm", "-fv"}, containers...)...)
|
|
| 64 |
- if result.Error != nil {
|
|
| 65 |
- // If the error is "No such container: ..." this means the container doesn't exists anymore, |
|
| 66 |
- // or if it is "... removal of container ... is already in progress" it will be removed eventually. |
|
| 67 |
- // We can safely ignore those. |
|
| 68 |
- if strings.Contains(result.Stderr(), "No such container") || alreadyExists.MatchString(result.Stderr()) {
|
|
| 69 |
- return |
|
| 70 |
- } |
|
| 71 |
- t.Fatalf("error removing containers %v : %v (%s)", containers, result.Error, result.Combined())
|
|
| 72 |
- } |
|
| 73 |
- } |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-func getAllContainers(t testingT, dockerBinary string) []string {
|
|
| 77 |
- result := icmd.RunCommand(dockerBinary, "ps", "-q", "-a") |
|
| 78 |
- result.Assert(t, icmd.Success) |
|
| 79 |
- return strings.Fields(result.Combined()) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func deleteAllImages(t testingT, dockerBinary string, protectedImages map[string]struct{}) {
|
|
| 83 |
- result := icmd.RunCommand(dockerBinary, "images", "--digests") |
|
| 84 |
- result.Assert(t, icmd.Success) |
|
| 85 |
- lines := strings.Split(string(result.Combined()), "\n")[1:] |
|
| 86 |
- imgMap := map[string]struct{}{}
|
|
| 87 |
- for _, l := range lines {
|
|
| 88 |
- if l == "" {
|
|
| 89 |
- continue |
|
| 90 |
- } |
|
| 91 |
- fields := strings.Fields(l) |
|
| 92 |
- imgTag := fields[0] + ":" + fields[1] |
|
| 93 |
- if _, ok := protectedImages[imgTag]; !ok {
|
|
| 94 |
- if fields[0] == "<none>" || fields[1] == "<none>" {
|
|
| 95 |
- if fields[2] != "<none>" {
|
|
| 96 |
- imgMap[fields[0]+"@"+fields[2]] = struct{}{}
|
|
| 97 |
- } else {
|
|
| 98 |
- imgMap[fields[3]] = struct{}{}
|
|
| 99 |
- } |
|
| 100 |
- // continue |
|
| 101 |
- } else {
|
|
| 102 |
- imgMap[imgTag] = struct{}{}
|
|
| 103 |
- } |
|
| 104 |
- } |
|
| 105 |
- } |
|
| 106 |
- if len(imgMap) != 0 {
|
|
| 107 |
- imgs := make([]string, 0, len(imgMap)) |
|
| 108 |
- for k := range imgMap {
|
|
| 109 |
- imgs = append(imgs, k) |
|
| 110 |
- } |
|
| 111 |
- icmd.RunCommand(dockerBinary, append([]string{"rmi", "-f"}, imgs...)...).Assert(t, icmd.Success)
|
|
| 112 |
- } |
|
| 113 |
-} |
|
| 114 |
- |
|
| 115 |
-func deleteAllVolumes(t testingT, c client.APIClient) {
|
|
| 116 |
- var errs []string |
|
| 117 |
- volumes, err := getAllVolumes(c) |
|
| 118 |
- if err != nil {
|
|
| 119 |
- t.Fatalf("%v", err)
|
|
| 120 |
- } |
|
| 121 |
- for _, v := range volumes {
|
|
| 122 |
- err := c.VolumeRemove(context.Background(), v.Name, true) |
|
| 123 |
- if err != nil {
|
|
| 124 |
- errs = append(errs, err.Error()) |
|
| 125 |
- continue |
|
| 126 |
- } |
|
| 127 |
- } |
|
| 128 |
- if len(errs) > 0 {
|
|
| 129 |
- t.Fatalf("%v", strings.Join(errs, "\n"))
|
|
| 130 |
- } |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func getAllVolumes(c client.APIClient) ([]*types.Volume, error) {
|
|
| 134 |
- volumes, err := c.VolumeList(context.Background(), filters.Args{})
|
|
| 135 |
- if err != nil {
|
|
| 136 |
- return nil, err |
|
| 137 |
- } |
|
| 138 |
- return volumes.Volumes, nil |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 |
-func deleteAllNetworks(t testingT, c client.APIClient, daemonPlatform string) {
|
|
| 142 |
- networks, err := getAllNetworks(c) |
|
| 143 |
- if err != nil {
|
|
| 144 |
- t.Fatalf("%v", err)
|
|
| 145 |
- } |
|
| 146 |
- var errs []string |
|
| 147 |
- for _, n := range networks {
|
|
| 148 |
- if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
|
|
| 149 |
- continue |
|
| 150 |
- } |
|
| 151 |
- if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
|
|
| 152 |
- // nat is a pre-defined network on Windows and cannot be removed |
|
| 153 |
- continue |
|
| 154 |
- } |
|
| 155 |
- err := c.NetworkRemove(context.Background(), n.ID) |
|
| 156 |
- if err != nil {
|
|
| 157 |
- errs = append(errs, err.Error()) |
|
| 158 |
- continue |
|
| 159 |
- } |
|
| 160 |
- } |
|
| 161 |
- if len(errs) > 0 {
|
|
| 162 |
- t.Fatalf("%v", strings.Join(errs, "\n"))
|
|
| 163 |
- } |
|
| 164 |
-} |
|
| 165 |
- |
|
| 166 |
-func getAllNetworks(c client.APIClient) ([]types.NetworkResource, error) {
|
|
| 167 |
- networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
|
|
| 168 |
- if err != nil {
|
|
| 169 |
- return nil, err |
|
| 170 |
- } |
|
| 171 |
- return networks, nil |
|
| 172 |
-} |
|
| 173 |
- |
|
| 174 |
-func deleteAllPlugins(t testingT, c client.APIClient, dockerBinary string) {
|
|
| 175 |
- plugins, err := getAllPlugins(c) |
|
| 176 |
- if err != nil {
|
|
| 177 |
- t.Fatalf("%v", err)
|
|
| 178 |
- } |
|
| 179 |
- var errs []string |
|
| 180 |
- for _, p := range plugins {
|
|
| 181 |
- err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
|
|
| 182 |
- if err != nil {
|
|
| 183 |
- errs = append(errs, err.Error()) |
|
| 184 |
- continue |
|
| 185 |
- } |
|
| 186 |
- } |
|
| 187 |
- if len(errs) > 0 {
|
|
| 188 |
- t.Fatalf("%v", strings.Join(errs, "\n"))
|
|
| 189 |
- } |
|
| 190 |
-} |
|
| 191 |
- |
|
| 192 |
-func getAllPlugins(c client.APIClient) (types.PluginsListResponse, error) {
|
|
| 193 |
- plugins, err := c.PluginList(context.Background(), filters.Args{})
|
|
| 194 |
- if err != nil {
|
|
| 195 |
- return nil, err |
|
| 196 |
- } |
|
| 197 |
- return plugins, nil |
|
| 198 |
-} |
| ... | ... |
@@ -1,19 +1,11 @@ |
| 1 | 1 |
package environment |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "fmt" |
|
| 5 |
- "io/ioutil" |
|
| 6 | 4 |
"os" |
| 5 |
+ |
|
| 7 | 6 |
"os/exec" |
| 8 |
- "path/filepath" |
|
| 9 |
- "strconv" |
|
| 10 |
- "strings" |
|
| 11 | 7 |
|
| 12 |
- "github.com/docker/docker/api/types" |
|
| 13 |
- "github.com/docker/docker/api/types/container" |
|
| 14 |
- "github.com/docker/docker/client" |
|
| 15 |
- "github.com/docker/docker/opts" |
|
| 16 |
- "golang.org/x/net/context" |
|
| 8 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 17 | 9 |
) |
| 18 | 10 |
|
| 19 | 11 |
var ( |
| ... | ... |
@@ -23,89 +15,28 @@ var ( |
| 23 | 23 |
|
| 24 | 24 |
func init() {
|
| 25 | 25 |
if DefaultClientBinary == "" {
|
| 26 |
- // TODO: to be removed once we no longer depend on the docker cli for integration tests |
|
| 27 |
- //panic("TEST_CLIENT_BINARY must be set")
|
|
| 28 | 26 |
DefaultClientBinary = "docker" |
| 29 | 27 |
} |
| 30 | 28 |
} |
| 31 | 29 |
|
| 32 |
-// Execution holds informations about the test execution environment. |
|
| 30 |
+// Execution contains information about the current test execution and daemon |
|
| 31 |
+// under test |
|
| 33 | 32 |
type Execution struct {
|
| 34 |
- daemonPlatform string |
|
| 35 |
- localDaemon bool |
|
| 36 |
- experimentalDaemon bool |
|
| 37 |
- daemonStorageDriver string |
|
| 38 |
- isolation container.Isolation |
|
| 39 |
- daemonPid int |
|
| 40 |
- daemonKernelVersion string |
|
| 41 |
- // For a local daemon on Linux, these values will be used for testing |
|
| 42 |
- // user namespace support as the standard graph path(s) will be |
|
| 43 |
- // appended with the root remapped uid.gid prefix |
|
| 44 |
- dockerBasePath string |
|
| 45 |
- volumesConfigPath string |
|
| 46 |
- containerStoragePath string |
|
| 47 |
- // baseImage is the name of the base image for testing |
|
| 48 |
- // Environment variable WINDOWS_BASE_IMAGE can override this |
|
| 49 |
- baseImage string |
|
| 33 |
+ environment.Execution |
|
| 50 | 34 |
dockerBinary string |
| 35 |
+} |
|
| 51 | 36 |
|
| 52 |
- protectedElements protectedElements |
|
| 37 |
+// DockerBinary returns the docker binary for this testing environment |
|
| 38 |
+func (e *Execution) DockerBinary() string {
|
|
| 39 |
+ return e.dockerBinary |
|
| 53 | 40 |
} |
| 54 | 41 |
|
| 55 |
-// New creates a new Execution struct |
|
| 42 |
+// New returns details about the testing environment |
|
| 56 | 43 |
func New() (*Execution, error) {
|
| 57 |
- localDaemon := true |
|
| 58 |
- // Deterministically working out the environment in which CI is running |
|
| 59 |
- // to evaluate whether the daemon is local or remote is not possible through |
|
| 60 |
- // a build tag. |
|
| 61 |
- // |
|
| 62 |
- // For example Windows to Linux CI under Jenkins tests the 64-bit |
|
| 63 |
- // Windows binary build with the daemon build tag, but calls a remote |
|
| 64 |
- // Linux daemon. |
|
| 65 |
- // |
|
| 66 |
- // We can't just say if Windows then assume the daemon is local as at |
|
| 67 |
- // some point, we will be testing the Windows CLI against a Windows daemon. |
|
| 68 |
- // |
|
| 69 |
- // Similarly, it will be perfectly valid to also run CLI tests from |
|
| 70 |
- // a Linux CLI (built with the daemon tag) against a Windows daemon. |
|
| 71 |
- if len(os.Getenv("DOCKER_REMOTE_DAEMON")) > 0 {
|
|
| 72 |
- localDaemon = false |
|
| 73 |
- } |
|
| 74 |
- info, err := getDaemonDockerInfo() |
|
| 44 |
+ env, err := environment.New() |
|
| 75 | 45 |
if err != nil {
|
| 76 | 46 |
return nil, err |
| 77 | 47 |
} |
| 78 |
- daemonPlatform := info.OSType |
|
| 79 |
- if daemonPlatform != "linux" && daemonPlatform != "windows" {
|
|
| 80 |
- return nil, fmt.Errorf("Cannot run tests against platform: %s", daemonPlatform)
|
|
| 81 |
- } |
|
| 82 |
- baseImage := "scratch" |
|
| 83 |
- volumesConfigPath := filepath.Join(info.DockerRootDir, "volumes") |
|
| 84 |
- containerStoragePath := filepath.Join(info.DockerRootDir, "containers") |
|
| 85 |
- // Make sure in context of daemon, not the local platform. Note we can't |
|
| 86 |
- // use filepath.FromSlash or ToSlash here as they are a no-op on Unix. |
|
| 87 |
- if daemonPlatform == "windows" {
|
|
| 88 |
- volumesConfigPath = strings.Replace(volumesConfigPath, `/`, `\`, -1) |
|
| 89 |
- containerStoragePath = strings.Replace(containerStoragePath, `/`, `\`, -1) |
|
| 90 |
- |
|
| 91 |
- baseImage = "microsoft/windowsservercore" |
|
| 92 |
- if len(os.Getenv("WINDOWS_BASE_IMAGE")) > 0 {
|
|
| 93 |
- baseImage = os.Getenv("WINDOWS_BASE_IMAGE")
|
|
| 94 |
- fmt.Println("INFO: Windows Base image is ", baseImage)
|
|
| 95 |
- } |
|
| 96 |
- } else {
|
|
| 97 |
- volumesConfigPath = strings.Replace(volumesConfigPath, `\`, `/`, -1) |
|
| 98 |
- containerStoragePath = strings.Replace(containerStoragePath, `\`, `/`, -1) |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- var daemonPid int |
|
| 102 |
- dest := os.Getenv("DEST")
|
|
| 103 |
- b, err := ioutil.ReadFile(filepath.Join(dest, "docker.pid")) |
|
| 104 |
- if err == nil {
|
|
| 105 |
- if p, err := strconv.ParseInt(string(b), 10, 32); err == nil {
|
|
| 106 |
- daemonPid = int(p) |
|
| 107 |
- } |
|
| 108 |
- } |
|
| 109 | 48 |
|
| 110 | 49 |
dockerBinary, err := exec.LookPath(DefaultClientBinary) |
| 111 | 50 |
if err != nil {
|
| ... | ... |
@@ -113,117 +44,36 @@ func New() (*Execution, error) {
|
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 | 115 |
return &Execution{
|
| 116 |
- localDaemon: localDaemon, |
|
| 117 |
- daemonPlatform: daemonPlatform, |
|
| 118 |
- daemonStorageDriver: info.Driver, |
|
| 119 |
- daemonKernelVersion: info.KernelVersion, |
|
| 120 |
- dockerBasePath: info.DockerRootDir, |
|
| 121 |
- volumesConfigPath: volumesConfigPath, |
|
| 122 |
- containerStoragePath: containerStoragePath, |
|
| 123 |
- isolation: info.Isolation, |
|
| 124 |
- daemonPid: daemonPid, |
|
| 125 |
- experimentalDaemon: info.ExperimentalBuild, |
|
| 126 |
- baseImage: baseImage, |
|
| 127 |
- dockerBinary: dockerBinary, |
|
| 128 |
- protectedElements: protectedElements{
|
|
| 129 |
- images: map[string]struct{}{},
|
|
| 130 |
- }, |
|
| 116 |
+ Execution: *env, |
|
| 117 |
+ dockerBinary: dockerBinary, |
|
| 131 | 118 |
}, nil |
| 132 | 119 |
} |
| 133 |
-func getDaemonDockerInfo() (types.Info, error) {
|
|
| 134 |
- // FIXME(vdemeester) should be safe to use as is |
|
| 135 |
- client, err := client.NewEnvClient() |
|
| 136 |
- if err != nil {
|
|
| 137 |
- return types.Info{}, err
|
|
| 138 |
- } |
|
| 139 |
- return client.Info(context.Background()) |
|
| 120 |
+ |
|
| 121 |
+// DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker) |
|
| 122 |
+// TODO: remove |
|
| 123 |
+// Deprecated: use Execution.DaemonInfo.DockerRootDir |
|
| 124 |
+func (e *Execution) DockerBasePath() string {
|
|
| 125 |
+ return e.DaemonInfo.DockerRootDir |
|
| 140 | 126 |
} |
| 141 | 127 |
|
| 142 |
-// LocalDaemon is true if the daemon under test is on the same |
|
| 143 |
-// host as the CLI. |
|
| 144 |
-func (e *Execution) LocalDaemon() bool {
|
|
| 145 |
- return e.localDaemon |
|
| 128 |
+// ExperimentalDaemon tell whether the main daemon has |
|
| 129 |
+// experimental features enabled or not |
|
| 130 |
+// Deprecated: use DaemonInfo.ExperimentalBuild |
|
| 131 |
+func (e *Execution) ExperimentalDaemon() bool {
|
|
| 132 |
+ return e.DaemonInfo.ExperimentalBuild |
|
| 146 | 133 |
} |
| 147 | 134 |
|
| 148 | 135 |
// DaemonPlatform is held globally so that tests can make intelligent |
| 149 | 136 |
// decisions on how to configure themselves according to the platform |
| 150 | 137 |
// of the daemon. This is initialized in docker_utils by sending |
| 151 | 138 |
// a version call to the daemon and examining the response header. |
| 139 |
+// Deprecated: use Execution.DaemonInfo.OSType |
|
| 152 | 140 |
func (e *Execution) DaemonPlatform() string {
|
| 153 |
- return e.daemonPlatform |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-// DockerBasePath is the base path of the docker folder (by default it is -/var/run/docker) |
|
| 157 |
-func (e *Execution) DockerBasePath() string {
|
|
| 158 |
- return e.dockerBasePath |
|
| 159 |
-} |
|
| 160 |
- |
|
| 161 |
-// VolumesConfigPath is the path of the volume configuration for the testing daemon |
|
| 162 |
-func (e *Execution) VolumesConfigPath() string {
|
|
| 163 |
- return e.volumesConfigPath |
|
| 164 |
-} |
|
| 165 |
- |
|
| 166 |
-// ContainerStoragePath is the path where the container are stored for the testing daemon |
|
| 167 |
-func (e *Execution) ContainerStoragePath() string {
|
|
| 168 |
- return e.containerStoragePath |
|
| 169 |
-} |
|
| 170 |
- |
|
| 171 |
-// DaemonStorageDriver is held globally so that tests can know the storage |
|
| 172 |
-// driver of the daemon. This is initialized in docker_utils by sending |
|
| 173 |
-// a version call to the daemon and examining the response header. |
|
| 174 |
-func (e *Execution) DaemonStorageDriver() string {
|
|
| 175 |
- return e.daemonStorageDriver |
|
| 176 |
-} |
|
| 177 |
- |
|
| 178 |
-// Isolation is the isolation mode of the daemon under test |
|
| 179 |
-func (e *Execution) Isolation() container.Isolation {
|
|
| 180 |
- return e.isolation |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-// DaemonPID is the pid of the main test daemon |
|
| 184 |
-func (e *Execution) DaemonPID() int {
|
|
| 185 |
- return e.daemonPid |
|
| 186 |
-} |
|
| 187 |
- |
|
| 188 |
-// ExperimentalDaemon tell whether the main daemon has |
|
| 189 |
-// experimental features enabled or not |
|
| 190 |
-func (e *Execution) ExperimentalDaemon() bool {
|
|
| 191 |
- return e.experimentalDaemon |
|
| 141 |
+ return e.DaemonInfo.OSType |
|
| 192 | 142 |
} |
| 193 | 143 |
|
| 194 | 144 |
// MinimalBaseImage is the image used for minimal builds (it depends on the platform) |
| 145 |
+// Deprecated: use Execution.PlatformDefaults.BaseImage |
|
| 195 | 146 |
func (e *Execution) MinimalBaseImage() string {
|
| 196 |
- return e.baseImage |
|
| 197 |
-} |
|
| 198 |
- |
|
| 199 |
-// DaemonKernelVersion is the kernel version of the daemon as a string, as returned |
|
| 200 |
-// by an INFO call to the daemon. |
|
| 201 |
-func (e *Execution) DaemonKernelVersion() string {
|
|
| 202 |
- return e.daemonKernelVersion |
|
| 203 |
-} |
|
| 204 |
- |
|
| 205 |
-// DaemonKernelVersionNumeric is the kernel version of the daemon as an integer. |
|
| 206 |
-// Mostly useful on Windows where DaemonKernelVersion holds the full string such |
|
| 207 |
-// as `10.0 14393 (14393.447.amd64fre.rs1_release_inmarket.161102-0100)`, but |
|
| 208 |
-// integration tests really only need the `14393` piece to make decisions. |
|
| 209 |
-func (e *Execution) DaemonKernelVersionNumeric() int {
|
|
| 210 |
- if e.daemonPlatform != "windows" {
|
|
| 211 |
- return -1 |
|
| 212 |
- } |
|
| 213 |
- v, _ := strconv.Atoi(strings.Split(e.daemonKernelVersion, " ")[1]) |
|
| 214 |
- return v |
|
| 215 |
-} |
|
| 216 |
- |
|
| 217 |
-// DockerBinary returns the docker binary for this testing environment |
|
| 218 |
-func (e *Execution) DockerBinary() string {
|
|
| 219 |
- return e.dockerBinary |
|
| 220 |
-} |
|
| 221 |
- |
|
| 222 |
-// DaemonHost return the daemon host string for this test execution |
|
| 223 |
-func DaemonHost() string {
|
|
| 224 |
- daemonURLStr := "unix://" + opts.DefaultUnixSocket |
|
| 225 |
- if daemonHostVar := os.Getenv("DOCKER_HOST"); daemonHostVar != "" {
|
|
| 226 |
- daemonURLStr = daemonHostVar |
|
| 227 |
- } |
|
| 228 |
- return daemonURLStr |
|
| 147 |
+ return e.PlatformDefaults.BaseImage |
|
| 229 | 148 |
} |
| 230 | 149 |
deleted file mode 100644 |
| ... | ... |
@@ -1,48 +0,0 @@ |
| 1 |
-package environment |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "strings" |
|
| 5 |
- |
|
| 6 |
- "github.com/docker/docker/integration-cli/fixtures/load" |
|
| 7 |
- "github.com/gotestyourself/gotestyourself/icmd" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-type protectedElements struct {
|
|
| 11 |
- images map[string]struct{}
|
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// ProtectImage adds the specified image(s) to be protected in case of clean |
|
| 15 |
-func (e *Execution) ProtectImage(t testingT, images ...string) {
|
|
| 16 |
- for _, image := range images {
|
|
| 17 |
- e.protectedElements.images[image] = struct{}{}
|
|
| 18 |
- } |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-// ProtectImages protects existing images and on linux frozen images from being |
|
| 22 |
-// cleaned up at the end of test runs |
|
| 23 |
-func ProtectImages(t testingT, testEnv *Execution) {
|
|
| 24 |
- images := getExistingImages(t, testEnv) |
|
| 25 |
- |
|
| 26 |
- if testEnv.DaemonPlatform() == "linux" {
|
|
| 27 |
- images = append(images, ensureFrozenImagesLinux(t, testEnv)...) |
|
| 28 |
- } |
|
| 29 |
- testEnv.ProtectImage(t, images...) |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func getExistingImages(t testingT, testEnv *Execution) []string {
|
|
| 33 |
- // TODO: use API instead of cli |
|
| 34 |
- result := icmd.RunCommand(testEnv.dockerBinary, "images", "-f", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}")
|
|
| 35 |
- result.Assert(t, icmd.Success) |
|
| 36 |
- return strings.Split(strings.TrimSpace(result.Stdout()), "\n") |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 |
-func ensureFrozenImagesLinux(t testingT, testEnv *Execution) []string {
|
|
| 40 |
- images := []string{"busybox:latest", "hello-world:frozen", "debian:jessie"}
|
|
| 41 |
- err := load.FrozenImagesLinux(testEnv.DockerBinary(), images...) |
|
| 42 |
- if err != nil {
|
|
| 43 |
- result := icmd.RunCommand(testEnv.DockerBinary(), "image", "ls") |
|
| 44 |
- t.Logf(result.String()) |
|
| 45 |
- t.Fatalf("%+v", err)
|
|
| 46 |
- } |
|
| 47 |
- return images |
|
| 48 |
-} |
| ... | ... |
@@ -79,7 +79,7 @@ func ensureSyscallTest(c *check.C) {
|
| 79 | 79 |
} |
| 80 | 80 |
|
| 81 | 81 |
func ensureSyscallTestBuild(c *check.C) {
|
| 82 |
- err := load.FrozenImagesLinux(dockerBinary, "buildpack-deps:jessie") |
|
| 82 |
+ err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie") |
|
| 83 | 83 |
c.Assert(err, checker.IsNil) |
| 84 | 84 |
|
| 85 | 85 |
var buildArgs []string |
| ... | ... |
@@ -126,7 +126,7 @@ func ensureNNPTest(c *check.C) {
|
| 126 | 126 |
} |
| 127 | 127 |
|
| 128 | 128 |
func ensureNNPTestBuild(c *check.C) {
|
| 129 |
- err := load.FrozenImagesLinux(dockerBinary, "buildpack-deps:jessie") |
|
| 129 |
+ err := load.FrozenImagesLinux(testEnv.APIClient(), "buildpack-deps:jessie") |
|
| 130 | 130 |
c.Assert(err, checker.IsNil) |
| 131 | 131 |
|
| 132 | 132 |
var buildArgs []string |
| ... | ... |
@@ -8,7 +8,8 @@ import ( |
| 8 | 8 |
"strings" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
-type skipT interface {
|
|
| 11 |
+// SkipT is the interface required to skip tests |
|
| 12 |
+type SkipT interface {
|
|
| 12 | 13 |
Skip(reason string) |
| 13 | 14 |
} |
| 14 | 15 |
|
| ... | ... |
@@ -17,7 +18,7 @@ type Test func() bool |
| 17 | 17 |
|
| 18 | 18 |
// Is checks if the environment satisfies the requirements |
| 19 | 19 |
// for the test to run or skips the tests. |
| 20 |
-func Is(s skipT, requirements ...Test) {
|
|
| 20 |
+func Is(s SkipT, requirements ...Test) {
|
|
| 21 | 21 |
for _, r := range requirements {
|
| 22 | 22 |
isValid := r() |
| 23 | 23 |
if !isValid {
|
| ... | ... |
@@ -6,57 +6,47 @@ import ( |
| 6 | 6 |
"net/http" |
| 7 | 7 |
"os" |
| 8 | 8 |
"os/exec" |
| 9 |
+ "strconv" |
|
| 9 | 10 |
"strings" |
| 10 | 11 |
"time" |
| 11 | 12 |
|
| 12 | 13 |
"github.com/docker/docker/integration-cli/requirement" |
| 13 |
- "github.com/go-check/check" |
|
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 |
-func PlatformIs(platform string) bool {
|
|
| 17 |
- return testEnv.DaemonPlatform() == platform |
|
| 18 |
-} |
|
| 19 |
- |
|
| 20 |
-func ArchitectureIs(arch string) bool {
|
|
| 21 |
- return os.Getenv("DOCKER_ENGINE_GOARCH") == arch
|
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 | 16 |
func ArchitectureIsNot(arch string) bool {
|
| 25 | 17 |
return os.Getenv("DOCKER_ENGINE_GOARCH") != arch
|
| 26 | 18 |
} |
| 27 | 19 |
|
| 28 |
-func StorageDriverIs(storageDriver string) bool {
|
|
| 29 |
- return strings.HasPrefix(testEnv.DaemonStorageDriver(), storageDriver) |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func StorageDriverIsNot(storageDriver string) bool {
|
|
| 33 |
- return !strings.HasPrefix(testEnv.DaemonStorageDriver(), storageDriver) |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 | 20 |
func DaemonIsWindows() bool {
|
| 37 |
- return PlatformIs("windows")
|
|
| 21 |
+ return testEnv.DaemonInfo.OSType == "windows" |
|
| 38 | 22 |
} |
| 39 | 23 |
|
| 40 | 24 |
func DaemonIsWindowsAtLeastBuild(buildNumber int) func() bool {
|
| 41 | 25 |
return func() bool {
|
| 42 |
- return DaemonIsWindows() && testEnv.DaemonKernelVersionNumeric() >= buildNumber |
|
| 26 |
+ if testEnv.DaemonInfo.OSType != "windows" {
|
|
| 27 |
+ return false |
|
| 28 |
+ } |
|
| 29 |
+ version := testEnv.DaemonInfo.KernelVersion |
|
| 30 |
+ numVersion, _ := strconv.Atoi(strings.Split(version, " ")[1]) |
|
| 31 |
+ return numVersion >= buildNumber |
|
| 43 | 32 |
} |
| 44 | 33 |
} |
| 45 | 34 |
|
| 46 | 35 |
func DaemonIsLinux() bool {
|
| 47 |
- return PlatformIs("linux")
|
|
| 36 |
+ return testEnv.DaemonInfo.OSType == "linux" |
|
| 48 | 37 |
} |
| 49 | 38 |
|
| 39 |
+// Deprecated: use skip.IfCondition(t, !testEnv.DaemonInfo.ExperimentalBuild) |
|
| 50 | 40 |
func ExperimentalDaemon() bool {
|
| 51 |
- return testEnv.ExperimentalDaemon() |
|
| 41 |
+ return testEnv.DaemonInfo.ExperimentalBuild |
|
| 52 | 42 |
} |
| 53 | 43 |
|
| 54 | 44 |
func NotExperimentalDaemon() bool {
|
| 55 |
- return !testEnv.ExperimentalDaemon() |
|
| 45 |
+ return !testEnv.DaemonInfo.ExperimentalBuild |
|
| 56 | 46 |
} |
| 57 | 47 |
|
| 58 | 48 |
func IsAmd64() bool {
|
| 59 |
- return ArchitectureIs("amd64")
|
|
| 49 |
+ return os.Getenv("DOCKER_ENGINE_GOARCH") == "amd64"
|
|
| 60 | 50 |
} |
| 61 | 51 |
|
| 62 | 52 |
func NotArm() bool {
|
| ... | ... |
@@ -76,7 +66,7 @@ func NotS390X() bool {
|
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 | 78 |
func SameHostDaemon() bool {
|
| 79 |
- return testEnv.LocalDaemon() |
|
| 79 |
+ return testEnv.IsLocalDaemon() |
|
| 80 | 80 |
} |
| 81 | 81 |
|
| 82 | 82 |
func UnixCli() bool {
|
| ... | ... |
@@ -127,12 +117,8 @@ func NotaryServerHosting() bool {
|
| 127 | 127 |
return err == nil |
| 128 | 128 |
} |
| 129 | 129 |
|
| 130 |
-func NotOverlay() bool {
|
|
| 131 |
- return StorageDriverIsNot("overlay")
|
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 | 130 |
func Devicemapper() bool {
|
| 135 |
- return StorageDriverIs("devicemapper")
|
|
| 131 |
+ return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper") |
|
| 136 | 132 |
} |
| 137 | 133 |
|
| 138 | 134 |
func IPv6() bool {
|
| ... | ... |
@@ -177,21 +163,21 @@ func UserNamespaceInKernel() bool {
|
| 177 | 177 |
} |
| 178 | 178 |
|
| 179 | 179 |
func IsPausable() bool {
|
| 180 |
- if testEnv.DaemonPlatform() == "windows" {
|
|
| 181 |
- return testEnv.Isolation() == "hyperv" |
|
| 180 |
+ if testEnv.DaemonInfo.OSType == "windows" {
|
|
| 181 |
+ return testEnv.DaemonInfo.Isolation == "hyperv" |
|
| 182 | 182 |
} |
| 183 | 183 |
return true |
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
func NotPausable() bool {
|
| 187 |
- if testEnv.DaemonPlatform() == "windows" {
|
|
| 188 |
- return testEnv.Isolation() == "process" |
|
| 187 |
+ if testEnv.DaemonInfo.OSType == "windows" {
|
|
| 188 |
+ return testEnv.DaemonInfo.Isolation == "process" |
|
| 189 | 189 |
} |
| 190 | 190 |
return false |
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 | 193 |
func IsolationIs(expectedIsolation string) bool {
|
| 194 |
- return testEnv.DaemonPlatform() == "windows" && string(testEnv.Isolation()) == expectedIsolation |
|
| 194 |
+ return testEnv.DaemonInfo.OSType == "windows" && string(testEnv.DaemonInfo.Isolation) == expectedIsolation |
|
| 195 | 195 |
} |
| 196 | 196 |
|
| 197 | 197 |
func IsolationIsHyperv() bool {
|
| ... | ... |
@@ -204,6 +190,6 @@ func IsolationIsProcess() bool {
|
| 204 | 204 |
|
| 205 | 205 |
// testRequires checks if the environment satisfies the requirements |
| 206 | 206 |
// for the test to run or skips the tests. |
| 207 |
-func testRequires(c *check.C, requirements ...requirement.Test) {
|
|
| 207 |
+func testRequires(c requirement.SkipT, requirements ...requirement.Test) {
|
|
| 208 | 208 |
requirement.Is(c, requirements...) |
| 209 | 209 |
} |
| ... | ... |
@@ -101,7 +101,7 @@ func overlay2Supported() bool {
|
| 101 | 101 |
return false |
| 102 | 102 |
} |
| 103 | 103 |
|
| 104 |
- daemonV, err := kernel.ParseRelease(testEnv.DaemonKernelVersion()) |
|
| 104 |
+ daemonV, err := kernel.ParseRelease(testEnv.DaemonInfo.KernelVersion) |
|
| 105 | 105 |
if err != nil {
|
| 106 | 106 |
return false |
| 107 | 107 |
} |
| ... | ... |
@@ -5,12 +5,10 @@ import ( |
| 5 | 5 |
"os" |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 |
- "github.com/docker/docker/integration-cli/environment" |
|
| 8 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 |
-var ( |
|
| 12 |
- testEnv *environment.Execution |
|
| 13 |
-) |
|
| 11 |
+var testEnv *environment.Execution |
|
| 14 | 12 |
|
| 15 | 13 |
func TestMain(m *testing.M) {
|
| 16 | 14 |
var err error |
| ... | ... |
@@ -20,18 +18,11 @@ func TestMain(m *testing.M) {
|
| 20 | 20 |
os.Exit(1) |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- // TODO: replace this with `testEnv.Print()` to print the full env |
|
| 24 |
- if testEnv.LocalDaemon() {
|
|
| 25 |
- fmt.Println("INFO: Testing against a local daemon")
|
|
| 26 |
- } else {
|
|
| 27 |
- fmt.Println("INFO: Testing against a remote daemon")
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- res := m.Run() |
|
| 31 |
- os.Exit(res) |
|
| 23 |
+ testEnv.Print() |
|
| 24 |
+ os.Exit(m.Run()) |
|
| 32 | 25 |
} |
| 33 | 26 |
|
| 34 | 27 |
func setupTest(t *testing.T) func() {
|
| 35 | 28 |
environment.ProtectImages(t, testEnv) |
| 36 |
- return func() { testEnv.Clean(t, testEnv.DockerBinary()) }
|
|
| 29 |
+ return func() { testEnv.Clean(t) }
|
|
| 37 | 30 |
} |
| ... | ... |
@@ -110,7 +110,7 @@ const defaultSwarmPort = 2477 |
| 110 | 110 |
func newSwarm(t *testing.T) *daemon.Swarm {
|
| 111 | 111 |
d := &daemon.Swarm{
|
| 112 | 112 |
Daemon: daemon.New(t, "", dockerdBinary, daemon.Config{
|
| 113 |
- Experimental: testEnv.ExperimentalDaemon(), |
|
| 113 |
+ Experimental: testEnv.DaemonInfo.ExperimentalBuild, |
|
| 114 | 114 |
}), |
| 115 | 115 |
// TODO: better method of finding an unused port |
| 116 | 116 |
Port: defaultSwarmPort, |
| ... | ... |
@@ -5,7 +5,7 @@ import ( |
| 5 | 5 |
"os" |
| 6 | 6 |
"testing" |
| 7 | 7 |
|
| 8 |
- "github.com/docker/docker/integration-cli/environment" |
|
| 8 |
+ "github.com/docker/docker/internal/test/environment" |
|
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 | 11 |
var testEnv *environment.Execution |
| ... | ... |
@@ -20,18 +20,11 @@ func TestMain(m *testing.M) {
|
| 20 | 20 |
os.Exit(1) |
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 |
- // TODO: replace this with `testEnv.Print()` to print the full env |
|
| 24 |
- if testEnv.LocalDaemon() {
|
|
| 25 |
- fmt.Println("INFO: Testing against a local daemon")
|
|
| 26 |
- } else {
|
|
| 27 |
- fmt.Println("INFO: Testing against a remote daemon")
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- res := m.Run() |
|
| 31 |
- os.Exit(res) |
|
| 23 |
+ testEnv.Print() |
|
| 24 |
+ os.Exit(m.Run()) |
|
| 32 | 25 |
} |
| 33 | 26 |
|
| 34 | 27 |
func setupTest(t *testing.T) func() {
|
| 35 | 28 |
environment.ProtectImages(t, testEnv) |
| 36 |
- return func() { testEnv.Clean(t, testEnv.DockerBinary()) }
|
|
| 29 |
+ return func() { testEnv.Clean(t) }
|
|
| 37 | 30 |
} |
| 38 | 31 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,164 @@ |
| 0 |
+package environment |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "regexp" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/api/types" |
|
| 7 |
+ "github.com/docker/docker/api/types/filters" |
|
| 8 |
+ "github.com/docker/docker/client" |
|
| 9 |
+ "github.com/stretchr/testify/assert" |
|
| 10 |
+ "github.com/stretchr/testify/require" |
|
| 11 |
+ "golang.org/x/net/context" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+type testingT interface {
|
|
| 15 |
+ require.TestingT |
|
| 16 |
+ logT |
|
| 17 |
+ Fatalf(string, ...interface{})
|
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+type logT interface {
|
|
| 21 |
+ Logf(string, ...interface{})
|
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// Clean the environment, preserving protected objects (images, containers, ...) |
|
| 25 |
+// and removing everything else. It's meant to run after any tests so that they don't |
|
| 26 |
+// depend on each others. |
|
| 27 |
+func (e *Execution) Clean(t testingT) {
|
|
| 28 |
+ client := e.APIClient() |
|
| 29 |
+ |
|
| 30 |
+ platform := e.DaemonInfo.OSType |
|
| 31 |
+ if (platform != "windows") || (platform == "windows" && e.DaemonInfo.Isolation == "hyperv") {
|
|
| 32 |
+ unpauseAllContainers(t, client) |
|
| 33 |
+ } |
|
| 34 |
+ deleteAllContainers(t, client) |
|
| 35 |
+ deleteAllImages(t, client, e.protectedElements.images) |
|
| 36 |
+ deleteAllVolumes(t, client) |
|
| 37 |
+ deleteAllNetworks(t, client, platform) |
|
| 38 |
+ if platform == "linux" {
|
|
| 39 |
+ deleteAllPlugins(t, client) |
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func unpauseAllContainers(t testingT, client client.ContainerAPIClient) {
|
|
| 44 |
+ ctx := context.Background() |
|
| 45 |
+ containers := getPausedContainers(ctx, t, client) |
|
| 46 |
+ if len(containers) > 0 {
|
|
| 47 |
+ for _, container := range containers {
|
|
| 48 |
+ err := client.ContainerUnpause(ctx, container.ID) |
|
| 49 |
+ assert.NoError(t, err, "failed to unpause container %s", container.ID) |
|
| 50 |
+ } |
|
| 51 |
+ } |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func getPausedContainers(ctx context.Context, t testingT, client client.ContainerAPIClient) []types.Container {
|
|
| 55 |
+ filter := filters.NewArgs() |
|
| 56 |
+ filter.Add("status", "paused")
|
|
| 57 |
+ containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
|
| 58 |
+ Filters: filter, |
|
| 59 |
+ Quiet: true, |
|
| 60 |
+ All: true, |
|
| 61 |
+ }) |
|
| 62 |
+ assert.NoError(t, err, "failed to list containers") |
|
| 63 |
+ return containers |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+var alreadyExists = regexp.MustCompile(`Error response from daemon: removal of container (\w+) is already in progress`) |
|
| 67 |
+ |
|
| 68 |
+func deleteAllContainers(t testingT, apiclient client.ContainerAPIClient) {
|
|
| 69 |
+ ctx := context.Background() |
|
| 70 |
+ containers := getAllContainers(ctx, t, apiclient) |
|
| 71 |
+ if len(containers) == 0 {
|
|
| 72 |
+ return |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ for _, container := range containers {
|
|
| 76 |
+ err := apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
|
|
| 77 |
+ Force: true, |
|
| 78 |
+ RemoveVolumes: true, |
|
| 79 |
+ }) |
|
| 80 |
+ if err == nil || client.IsErrNotFound(err) || alreadyExists.MatchString(err.Error()) {
|
|
| 81 |
+ continue |
|
| 82 |
+ } |
|
| 83 |
+ assert.NoError(t, err, "failed to remove %s", container.ID) |
|
| 84 |
+ } |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func getAllContainers(ctx context.Context, t testingT, client client.ContainerAPIClient) []types.Container {
|
|
| 88 |
+ containers, err := client.ContainerList(ctx, types.ContainerListOptions{
|
|
| 89 |
+ Quiet: true, |
|
| 90 |
+ All: true, |
|
| 91 |
+ }) |
|
| 92 |
+ assert.NoError(t, err, "failed to list containers") |
|
| 93 |
+ return containers |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func deleteAllImages(t testingT, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
|
|
| 97 |
+ images, err := apiclient.ImageList(context.Background(), types.ImageListOptions{})
|
|
| 98 |
+ assert.NoError(t, err, "failed to list images") |
|
| 99 |
+ |
|
| 100 |
+ ctx := context.Background() |
|
| 101 |
+ for _, image := range images {
|
|
| 102 |
+ tags := tagsFromImageSummary(image) |
|
| 103 |
+ if len(tags) == 0 {
|
|
| 104 |
+ t.Logf("Removing image %s", image.ID)
|
|
| 105 |
+ removeImage(ctx, t, apiclient, image.ID) |
|
| 106 |
+ continue |
|
| 107 |
+ } |
|
| 108 |
+ for _, tag := range tags {
|
|
| 109 |
+ if _, ok := protectedImages[tag]; !ok {
|
|
| 110 |
+ t.Logf("Removing image %s", tag)
|
|
| 111 |
+ removeImage(ctx, t, apiclient, tag) |
|
| 112 |
+ continue |
|
| 113 |
+ } |
|
| 114 |
+ } |
|
| 115 |
+ } |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+func removeImage(ctx context.Context, t testingT, apiclient client.ImageAPIClient, ref string) {
|
|
| 119 |
+ _, err := apiclient.ImageRemove(ctx, ref, types.ImageRemoveOptions{
|
|
| 120 |
+ Force: true, |
|
| 121 |
+ }) |
|
| 122 |
+ if client.IsErrNotFound(err) {
|
|
| 123 |
+ return |
|
| 124 |
+ } |
|
| 125 |
+ assert.NoError(t, err, "failed to remove image %s", ref) |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+func deleteAllVolumes(t testingT, c client.VolumeAPIClient) {
|
|
| 129 |
+ volumes, err := c.VolumeList(context.Background(), filters.Args{})
|
|
| 130 |
+ assert.NoError(t, err, "failed to list volumes") |
|
| 131 |
+ |
|
| 132 |
+ for _, v := range volumes.Volumes {
|
|
| 133 |
+ err := c.VolumeRemove(context.Background(), v.Name, true) |
|
| 134 |
+ assert.NoError(t, err, "failed to remove volume %s", v.Name) |
|
| 135 |
+ } |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+func deleteAllNetworks(t testingT, c client.NetworkAPIClient, daemonPlatform string) {
|
|
| 139 |
+ networks, err := c.NetworkList(context.Background(), types.NetworkListOptions{})
|
|
| 140 |
+ assert.NoError(t, err, "failed to list networks") |
|
| 141 |
+ |
|
| 142 |
+ for _, n := range networks {
|
|
| 143 |
+ if n.Name == "bridge" || n.Name == "none" || n.Name == "host" {
|
|
| 144 |
+ continue |
|
| 145 |
+ } |
|
| 146 |
+ if daemonPlatform == "windows" && strings.ToLower(n.Name) == "nat" {
|
|
| 147 |
+ // nat is a pre-defined network on Windows and cannot be removed |
|
| 148 |
+ continue |
|
| 149 |
+ } |
|
| 150 |
+ err := c.NetworkRemove(context.Background(), n.ID) |
|
| 151 |
+ assert.NoError(t, err, "failed to remove network %s", n.ID) |
|
| 152 |
+ } |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+func deleteAllPlugins(t testingT, c client.PluginAPIClient) {
|
|
| 156 |
+ plugins, err := c.PluginList(context.Background(), filters.Args{})
|
|
| 157 |
+ assert.NoError(t, err, "failed to list plugins") |
|
| 158 |
+ |
|
| 159 |
+ for _, p := range plugins {
|
|
| 160 |
+ err := c.PluginRemove(context.Background(), p.Name, types.PluginRemoveOptions{Force: true})
|
|
| 161 |
+ assert.NoError(t, err, "failed to remove plugin %s", p.ID) |
|
| 162 |
+ } |
|
| 163 |
+} |
| 0 | 164 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,117 @@ |
| 0 |
+package environment |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+ "path/filepath" |
|
| 6 |
+ "strings" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/api/types" |
|
| 9 |
+ "github.com/docker/docker/client" |
|
| 10 |
+ "github.com/pkg/errors" |
|
| 11 |
+ "golang.org/x/net/context" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// Execution contains information about the current test execution and daemon |
|
| 15 |
+// under test |
|
| 16 |
+type Execution struct {
|
|
| 17 |
+ client client.APIClient |
|
| 18 |
+ DaemonInfo types.Info |
|
| 19 |
+ PlatformDefaults PlatformDefaults |
|
| 20 |
+ protectedElements protectedElements |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// PlatformDefaults are defaults values for the platform of the daemon under test |
|
| 24 |
+type PlatformDefaults struct {
|
|
| 25 |
+ BaseImage string |
|
| 26 |
+ VolumesConfigPath string |
|
| 27 |
+ ContainerStoragePath string |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// New creates a new Execution struct |
|
| 31 |
+func New() (*Execution, error) {
|
|
| 32 |
+ client, err := client.NewEnvClient() |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ return nil, errors.Wrapf(err, "failed to create client") |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ info, err := client.Info(context.Background()) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ return nil, errors.Wrapf(err, "failed to get info from daemon") |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ return &Execution{
|
|
| 43 |
+ client: client, |
|
| 44 |
+ DaemonInfo: info, |
|
| 45 |
+ PlatformDefaults: getPlatformDefaults(info), |
|
| 46 |
+ protectedElements: newProtectedElements(), |
|
| 47 |
+ }, nil |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+func getPlatformDefaults(info types.Info) PlatformDefaults {
|
|
| 51 |
+ volumesPath := filepath.Join(info.DockerRootDir, "volumes") |
|
| 52 |
+ containersPath := filepath.Join(info.DockerRootDir, "containers") |
|
| 53 |
+ |
|
| 54 |
+ switch info.OSType {
|
|
| 55 |
+ case "linux": |
|
| 56 |
+ return PlatformDefaults{
|
|
| 57 |
+ BaseImage: "scratch", |
|
| 58 |
+ VolumesConfigPath: toSlash(volumesPath), |
|
| 59 |
+ ContainerStoragePath: toSlash(containersPath), |
|
| 60 |
+ } |
|
| 61 |
+ case "windows": |
|
| 62 |
+ baseImage := "microsoft/windowsservercore" |
|
| 63 |
+ if override := os.Getenv("WINDOWS_BASE_IMAGE"); override != "" {
|
|
| 64 |
+ baseImage = override |
|
| 65 |
+ fmt.Println("INFO: Windows Base image is ", baseImage)
|
|
| 66 |
+ } |
|
| 67 |
+ return PlatformDefaults{
|
|
| 68 |
+ BaseImage: baseImage, |
|
| 69 |
+ VolumesConfigPath: filepath.FromSlash(volumesPath), |
|
| 70 |
+ ContainerStoragePath: filepath.FromSlash(containersPath), |
|
| 71 |
+ } |
|
| 72 |
+ default: |
|
| 73 |
+ panic(fmt.Sprintf("unknown info.OSType for daemon: %s", info.OSType))
|
|
| 74 |
+ } |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+// Make sure in context of daemon, not the local platform. Note we can't |
|
| 78 |
+// use filepath.FromSlash or ToSlash here as they are a no-op on Unix. |
|
| 79 |
+func toSlash(path string) string {
|
|
| 80 |
+ return strings.Replace(path, `\`, `/`, -1) |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// IsLocalDaemon is true if the daemon under test is on the same |
|
| 84 |
+// host as the CLI. |
|
| 85 |
+// |
|
| 86 |
+// Deterministically working out the environment in which CI is running |
|
| 87 |
+// to evaluate whether the daemon is local or remote is not possible through |
|
| 88 |
+// a build tag. |
|
| 89 |
+// |
|
| 90 |
+// For example Windows to Linux CI under Jenkins tests the 64-bit |
|
| 91 |
+// Windows binary build with the daemon build tag, but calls a remote |
|
| 92 |
+// Linux daemon. |
|
| 93 |
+// |
|
| 94 |
+// We can't just say if Windows then assume the daemon is local as at |
|
| 95 |
+// some point, we will be testing the Windows CLI against a Windows daemon. |
|
| 96 |
+// |
|
| 97 |
+// Similarly, it will be perfectly valid to also run CLI tests from |
|
| 98 |
+// a Linux CLI (built with the daemon tag) against a Windows daemon. |
|
| 99 |
+func (e *Execution) IsLocalDaemon() bool {
|
|
| 100 |
+ return os.Getenv("DOCKER_REMOTE_DAEMON") == ""
|
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// Print the execution details to stdout |
|
| 104 |
+// TODO: print everything |
|
| 105 |
+func (e *Execution) Print() {
|
|
| 106 |
+ if e.IsLocalDaemon() {
|
|
| 107 |
+ fmt.Println("INFO: Testing against a local daemon")
|
|
| 108 |
+ } else {
|
|
| 109 |
+ fmt.Println("INFO: Testing against a remote daemon")
|
|
| 110 |
+ } |
|
| 111 |
+} |
|
| 112 |
+ |
|
| 113 |
+// APIClient returns an APIClient connected to the daemon under test |
|
| 114 |
+func (e *Execution) APIClient() client.APIClient {
|
|
| 115 |
+ return e.client |
|
| 116 |
+} |
| 0 | 117 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,78 @@ |
| 0 |
+package environment |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/api/types" |
|
| 6 |
+ "github.com/docker/docker/api/types/filters" |
|
| 7 |
+ "github.com/docker/docker/integration-cli/fixtures/load" |
|
| 8 |
+ "github.com/stretchr/testify/require" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+type protectedElements struct {
|
|
| 12 |
+ images map[string]struct{}
|
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+// ProtectImage adds the specified image(s) to be protected in case of clean |
|
| 16 |
+func (e *Execution) ProtectImage(t testingT, images ...string) {
|
|
| 17 |
+ for _, image := range images {
|
|
| 18 |
+ e.protectedElements.images[image] = struct{}{}
|
|
| 19 |
+ } |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func newProtectedElements() protectedElements {
|
|
| 23 |
+ return protectedElements{
|
|
| 24 |
+ images: map[string]struct{}{},
|
|
| 25 |
+ } |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// ProtectImages protects existing images and on linux frozen images from being |
|
| 29 |
+// cleaned up at the end of test runs |
|
| 30 |
+func ProtectImages(t testingT, testEnv *Execution) {
|
|
| 31 |
+ images := getExistingImages(t, testEnv) |
|
| 32 |
+ |
|
| 33 |
+ if testEnv.DaemonInfo.OSType == "linux" {
|
|
| 34 |
+ images = append(images, ensureFrozenImagesLinux(t, testEnv)...) |
|
| 35 |
+ } |
|
| 36 |
+ testEnv.ProtectImage(t, images...) |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func getExistingImages(t testingT, testEnv *Execution) []string {
|
|
| 40 |
+ client := testEnv.APIClient() |
|
| 41 |
+ filter := filters.NewArgs() |
|
| 42 |
+ filter.Add("dangling", "false")
|
|
| 43 |
+ imageList, err := client.ImageList(context.Background(), types.ImageListOptions{
|
|
| 44 |
+ Filters: filter, |
|
| 45 |
+ }) |
|
| 46 |
+ require.NoError(t, err, "failed to list images") |
|
| 47 |
+ |
|
| 48 |
+ images := []string{}
|
|
| 49 |
+ for _, image := range imageList {
|
|
| 50 |
+ images = append(images, tagsFromImageSummary(image)...) |
|
| 51 |
+ } |
|
| 52 |
+ return images |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func tagsFromImageSummary(image types.ImageSummary) []string {
|
|
| 56 |
+ result := []string{}
|
|
| 57 |
+ for _, tag := range image.RepoTags {
|
|
| 58 |
+ if tag != "<none>:<none>" {
|
|
| 59 |
+ result = append(result, tag) |
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ for _, digest := range image.RepoDigests {
|
|
| 63 |
+ if digest != "<none>@<none>" {
|
|
| 64 |
+ result = append(result, digest) |
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+ return result |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+func ensureFrozenImagesLinux(t testingT, testEnv *Execution) []string {
|
|
| 71 |
+ images := []string{"busybox:latest", "hello-world:frozen", "debian:jessie"}
|
|
| 72 |
+ err := load.FrozenImagesLinux(testEnv.APIClient(), images...) |
|
| 73 |
+ if err != nil {
|
|
| 74 |
+ t.Fatalf("Failed to load frozen images: %s", err)
|
|
| 75 |
+ } |
|
| 76 |
+ return images |
|
| 77 |
+} |