Browse code

pkg/system: fix compile on darwin (macOS)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2020/11/11 01:43:44
Showing 9 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,89 @@
0
+package system // import "github.com/docker/docker/pkg/system"
1
+
2
+import (
3
+	"os"
4
+	"syscall"
5
+	"testing"
6
+	"time"
7
+)
8
+
9
+// TestChtimesLinux tests Chtimes access time on a tempfile on Linux
10
+func TestChtimesLinux(t *testing.T) {
11
+	file, dir := prepareTempFile(t)
12
+	defer os.RemoveAll(dir)
13
+
14
+	beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second)
15
+	unixEpochTime := time.Unix(0, 0)
16
+	afterUnixEpochTime := time.Unix(100, 0)
17
+	unixMaxTime := maxTime
18
+
19
+	// Test both aTime and mTime set to Unix Epoch
20
+	Chtimes(file, unixEpochTime, unixEpochTime)
21
+
22
+	f, err := os.Stat(file)
23
+	if err != nil {
24
+		t.Fatal(err)
25
+	}
26
+
27
+	stat := f.Sys().(*syscall.Stat_t)
28
+	aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
29
+	if aTime != unixEpochTime {
30
+		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
31
+	}
32
+
33
+	// Test aTime before Unix Epoch and mTime set to Unix Epoch
34
+	Chtimes(file, beforeUnixEpochTime, unixEpochTime)
35
+
36
+	f, err = os.Stat(file)
37
+	if err != nil {
38
+		t.Fatal(err)
39
+	}
40
+
41
+	stat = f.Sys().(*syscall.Stat_t)
42
+	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
43
+	if aTime != unixEpochTime {
44
+		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
45
+	}
46
+
47
+	// Test aTime set to Unix Epoch and mTime before Unix Epoch
48
+	Chtimes(file, unixEpochTime, beforeUnixEpochTime)
49
+
50
+	f, err = os.Stat(file)
51
+	if err != nil {
52
+		t.Fatal(err)
53
+	}
54
+
55
+	stat = f.Sys().(*syscall.Stat_t)
56
+	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
57
+	if aTime != unixEpochTime {
58
+		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
59
+	}
60
+
61
+	// Test both aTime and mTime set to after Unix Epoch (valid time)
62
+	Chtimes(file, afterUnixEpochTime, afterUnixEpochTime)
63
+
64
+	f, err = os.Stat(file)
65
+	if err != nil {
66
+		t.Fatal(err)
67
+	}
68
+
69
+	stat = f.Sys().(*syscall.Stat_t)
70
+	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
71
+	if aTime != afterUnixEpochTime {
72
+		t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime)
73
+	}
74
+
75
+	// Test both aTime and mTime set to Unix max time
76
+	Chtimes(file, unixMaxTime, unixMaxTime)
77
+
78
+	f, err = os.Stat(file)
79
+	if err != nil {
80
+		t.Fatal(err)
81
+	}
82
+
83
+	stat = f.Sys().(*syscall.Stat_t)
84
+	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
85
+	if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) {
86
+		t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second))
87
+	}
88
+}
0 89
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+// +build !windows
1
+
2
+package system // import "github.com/docker/docker/pkg/system"
3
+
4
+import (
5
+	"time"
6
+)
7
+
8
+// setCTime will set the create time on a file. On Unix, the create
9
+// time is updated as a side effect of setting the modified time, so
10
+// no action is required.
11
+func setCTime(path string, ctime time.Time) error {
12
+	return nil
13
+}
0 14
deleted file mode 100644
... ...
@@ -1,14 +0,0 @@
1
-// +build !windows
2
-
3
-package system // import "github.com/docker/docker/pkg/system"
4
-
5
-import (
6
-	"time"
7
-)
8
-
9
-// setCTime will set the create time on a file. On Unix, the create
10
-// time is updated as a side effect of setting the modified time, so
11
-// no action is required.
12
-func setCTime(path string, ctime time.Time) error {
13
-	return nil
14
-}
15 1
deleted file mode 100644
... ...
@@ -1,91 +0,0 @@
1
-// +build !windows
2
-
3
-package system // import "github.com/docker/docker/pkg/system"
4
-
5
-import (
6
-	"os"
7
-	"syscall"
8
-	"testing"
9
-	"time"
10
-)
11
-
12
-// TestChtimesLinux tests Chtimes access time on a tempfile on Linux
13
-func TestChtimesLinux(t *testing.T) {
14
-	file, dir := prepareTempFile(t)
15
-	defer os.RemoveAll(dir)
16
-
17
-	beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second)
18
-	unixEpochTime := time.Unix(0, 0)
19
-	afterUnixEpochTime := time.Unix(100, 0)
20
-	unixMaxTime := maxTime
21
-
22
-	// Test both aTime and mTime set to Unix Epoch
23
-	Chtimes(file, unixEpochTime, unixEpochTime)
24
-
25
-	f, err := os.Stat(file)
26
-	if err != nil {
27
-		t.Fatal(err)
28
-	}
29
-
30
-	stat := f.Sys().(*syscall.Stat_t)
31
-	aTime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
32
-	if aTime != unixEpochTime {
33
-		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
34
-	}
35
-
36
-	// Test aTime before Unix Epoch and mTime set to Unix Epoch
37
-	Chtimes(file, beforeUnixEpochTime, unixEpochTime)
38
-
39
-	f, err = os.Stat(file)
40
-	if err != nil {
41
-		t.Fatal(err)
42
-	}
43
-
44
-	stat = f.Sys().(*syscall.Stat_t)
45
-	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
46
-	if aTime != unixEpochTime {
47
-		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
48
-	}
49
-
50
-	// Test aTime set to Unix Epoch and mTime before Unix Epoch
51
-	Chtimes(file, unixEpochTime, beforeUnixEpochTime)
52
-
53
-	f, err = os.Stat(file)
54
-	if err != nil {
55
-		t.Fatal(err)
56
-	}
57
-
58
-	stat = f.Sys().(*syscall.Stat_t)
59
-	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
60
-	if aTime != unixEpochTime {
61
-		t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime)
62
-	}
63
-
64
-	// Test both aTime and mTime set to after Unix Epoch (valid time)
65
-	Chtimes(file, afterUnixEpochTime, afterUnixEpochTime)
66
-
67
-	f, err = os.Stat(file)
68
-	if err != nil {
69
-		t.Fatal(err)
70
-	}
71
-
72
-	stat = f.Sys().(*syscall.Stat_t)
73
-	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
74
-	if aTime != afterUnixEpochTime {
75
-		t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime)
76
-	}
77
-
78
-	// Test both aTime and mTime set to Unix max time
79
-	Chtimes(file, unixMaxTime, unixMaxTime)
80
-
81
-	f, err = os.Stat(file)
82
-	if err != nil {
83
-		t.Fatal(err)
84
-	}
85
-
86
-	stat = f.Sys().(*syscall.Stat_t)
87
-	aTime = time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) // nolint: unconvert
88
-	if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) {
89
-		t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second))
90
-	}
91
-}
92 1
new file mode 100644
... ...
@@ -0,0 +1,78 @@
0
+// +build !darwin,!windows
1
+
2
+package system // import "github.com/docker/docker/pkg/system"
3
+
4
+import (
5
+	"os"
6
+	"syscall"
7
+	"time"
8
+
9
+	"github.com/moby/sys/mount"
10
+	"github.com/pkg/errors"
11
+)
12
+
13
+// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
14
+// often be remedied.
15
+// Only use `EnsureRemoveAll` if you really want to make every effort to remove
16
+// a directory.
17
+//
18
+// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
19
+// can be a race between reading directory entries and then actually attempting
20
+// to remove everything in the directory.
21
+// These types of errors do not need to be returned since it's ok for the dir to
22
+// be gone we can just retry the remove operation.
23
+//
24
+// This should not return a `os.ErrNotExist` kind of error under any circumstances
25
+func EnsureRemoveAll(dir string) error {
26
+	notExistErr := make(map[string]bool)
27
+
28
+	// track retries
29
+	exitOnErr := make(map[string]int)
30
+	maxRetry := 50
31
+
32
+	// Attempt to unmount anything beneath this dir first
33
+	mount.RecursiveUnmount(dir)
34
+
35
+	for {
36
+		err := os.RemoveAll(dir)
37
+		if err == nil {
38
+			return nil
39
+		}
40
+
41
+		pe, ok := err.(*os.PathError)
42
+		if !ok {
43
+			return err
44
+		}
45
+
46
+		if os.IsNotExist(err) {
47
+			if notExistErr[pe.Path] {
48
+				return err
49
+			}
50
+			notExistErr[pe.Path] = true
51
+
52
+			// There is a race where some subdir can be removed but after the parent
53
+			//   dir entries have been read.
54
+			// So the path could be from `os.Remove(subdir)`
55
+			// If the reported non-existent path is not the passed in `dir` we
56
+			// should just retry, but otherwise return with no error.
57
+			if pe.Path == dir {
58
+				return nil
59
+			}
60
+			continue
61
+		}
62
+
63
+		if pe.Err != syscall.EBUSY {
64
+			return err
65
+		}
66
+
67
+		if e := mount.Unmount(pe.Path); e != nil {
68
+			return errors.Wrapf(e, "error while removing %s", dir)
69
+		}
70
+
71
+		if exitOnErr[pe.Path] == maxRetry {
72
+			return err
73
+		}
74
+		exitOnErr[pe.Path]++
75
+		time.Sleep(100 * time.Millisecond)
76
+	}
77
+}
0 78
new file mode 100644
... ...
@@ -0,0 +1,36 @@
0
+// +build !darwin
1
+
2
+package system // import "github.com/docker/docker/pkg/system"
3
+
4
+import (
5
+	"io/ioutil"
6
+	"testing"
7
+)
8
+
9
+func TestEnsureRemoveAllNotExist(t *testing.T) {
10
+	// should never return an error for a non-existent path
11
+	if err := EnsureRemoveAll("/non/existent/path"); err != nil {
12
+		t.Fatal(err)
13
+	}
14
+}
15
+
16
+func TestEnsureRemoveAllWithDir(t *testing.T) {
17
+	dir, err := ioutil.TempDir("", "test-ensure-removeall-with-dir")
18
+	if err != nil {
19
+		t.Fatal(err)
20
+	}
21
+	if err := EnsureRemoveAll(dir); err != nil {
22
+		t.Fatal(err)
23
+	}
24
+}
25
+
26
+func TestEnsureRemoveAllWithFile(t *testing.T) {
27
+	tmp, err := ioutil.TempFile("", "test-ensure-removeall-with-dir")
28
+	if err != nil {
29
+		t.Fatal(err)
30
+	}
31
+	tmp.Close()
32
+	if err := EnsureRemoveAll(tmp.Name()); err != nil {
33
+		t.Fatal(err)
34
+	}
35
+}
... ...
@@ -1,34 +1,56 @@
1
+// +build !darwin,!windows
2
+
1 3
 package system // import "github.com/docker/docker/pkg/system"
2 4
 
3 5
 import (
4 6
 	"io/ioutil"
7
+	"os"
8
+	"path/filepath"
5 9
 	"testing"
10
+	"time"
11
+
12
+	"github.com/moby/sys/mount"
13
+	"gotest.tools/v3/skip"
6 14
 )
7 15
 
8
-func TestEnsureRemoveAllNotExist(t *testing.T) {
9
-	// should never return an error for a non-existent path
10
-	if err := EnsureRemoveAll("/non/existent/path"); err != nil {
11
-		t.Fatal(err)
12
-	}
13
-}
16
+func TestEnsureRemoveAllWithMount(t *testing.T) {
17
+	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
14 18
 
15
-func TestEnsureRemoveAllWithDir(t *testing.T) {
16
-	dir, err := ioutil.TempDir("", "test-ensure-removeall-with-dir")
19
+	dir1, err := ioutil.TempDir("", "test-ensure-removeall-with-dir1")
17 20
 	if err != nil {
18 21
 		t.Fatal(err)
19 22
 	}
20
-	if err := EnsureRemoveAll(dir); err != nil {
23
+	dir2, err := ioutil.TempDir("", "test-ensure-removeall-with-dir2")
24
+	if err != nil {
21 25
 		t.Fatal(err)
22 26
 	}
23
-}
27
+	defer os.RemoveAll(dir2)
24 28
 
25
-func TestEnsureRemoveAllWithFile(t *testing.T) {
26
-	tmp, err := ioutil.TempFile("", "test-ensure-removeall-with-dir")
27
-	if err != nil {
29
+	bindDir := filepath.Join(dir1, "bind")
30
+	if err := os.MkdirAll(bindDir, 0755); err != nil {
28 31
 		t.Fatal(err)
29 32
 	}
30
-	tmp.Close()
31
-	if err := EnsureRemoveAll(tmp.Name()); err != nil {
33
+
34
+	if err := mount.Mount(dir2, bindDir, "none", "bind"); err != nil {
32 35
 		t.Fatal(err)
33 36
 	}
37
+
38
+	done := make(chan struct{}, 1)
39
+	go func() {
40
+		err = EnsureRemoveAll(dir1)
41
+		close(done)
42
+	}()
43
+
44
+	select {
45
+	case <-done:
46
+		if err != nil {
47
+			t.Fatal(err)
48
+		}
49
+	case <-time.After(5 * time.Second):
50
+		t.Fatal("timeout waiting for EnsureRemoveAll to finish")
51
+	}
52
+
53
+	if _, err := os.Stat(dir1); !os.IsNotExist(err) {
54
+		t.Fatalf("expected %q to not exist", dir1)
55
+	}
34 56
 }
35 57
deleted file mode 100644
... ...
@@ -1,78 +0,0 @@
1
-// +build !windows
2
-
3
-package system // import "github.com/docker/docker/pkg/system"
4
-
5
-import (
6
-	"os"
7
-	"syscall"
8
-	"time"
9
-
10
-	"github.com/moby/sys/mount"
11
-	"github.com/pkg/errors"
12
-)
13
-
14
-// EnsureRemoveAll wraps `os.RemoveAll` to check for specific errors that can
15
-// often be remedied.
16
-// Only use `EnsureRemoveAll` if you really want to make every effort to remove
17
-// a directory.
18
-//
19
-// Because of the way `os.Remove` (and by extension `os.RemoveAll`) works, there
20
-// can be a race between reading directory entries and then actually attempting
21
-// to remove everything in the directory.
22
-// These types of errors do not need to be returned since it's ok for the dir to
23
-// be gone we can just retry the remove operation.
24
-//
25
-// This should not return a `os.ErrNotExist` kind of error under any circumstances
26
-func EnsureRemoveAll(dir string) error {
27
-	notExistErr := make(map[string]bool)
28
-
29
-	// track retries
30
-	exitOnErr := make(map[string]int)
31
-	maxRetry := 50
32
-
33
-	// Attempt to unmount anything beneath this dir first
34
-	mount.RecursiveUnmount(dir)
35
-
36
-	for {
37
-		err := os.RemoveAll(dir)
38
-		if err == nil {
39
-			return nil
40
-		}
41
-
42
-		pe, ok := err.(*os.PathError)
43
-		if !ok {
44
-			return err
45
-		}
46
-
47
-		if os.IsNotExist(err) {
48
-			if notExistErr[pe.Path] {
49
-				return err
50
-			}
51
-			notExistErr[pe.Path] = true
52
-
53
-			// There is a race where some subdir can be removed but after the parent
54
-			//   dir entries have been read.
55
-			// So the path could be from `os.Remove(subdir)`
56
-			// If the reported non-existent path is not the passed in `dir` we
57
-			// should just retry, but otherwise return with no error.
58
-			if pe.Path == dir {
59
-				return nil
60
-			}
61
-			continue
62
-		}
63
-
64
-		if pe.Err != syscall.EBUSY {
65
-			return err
66
-		}
67
-
68
-		if e := mount.Unmount(pe.Path); e != nil {
69
-			return errors.Wrapf(e, "error while removing %s", dir)
70
-		}
71
-
72
-		if exitOnErr[pe.Path] == maxRetry {
73
-			return err
74
-		}
75
-		exitOnErr[pe.Path]++
76
-		time.Sleep(100 * time.Millisecond)
77
-	}
78
-}
79 1
deleted file mode 100644
... ...
@@ -1,58 +0,0 @@
1
-// +build !windows
2
-
3
-package system // import "github.com/docker/docker/pkg/system"
4
-
5
-import (
6
-	"io/ioutil"
7
-	"os"
8
-	"path/filepath"
9
-	"runtime"
10
-	"testing"
11
-	"time"
12
-
13
-	"github.com/moby/sys/mount"
14
-	"gotest.tools/v3/skip"
15
-)
16
-
17
-func TestEnsureRemoveAllWithMount(t *testing.T) {
18
-	skip.If(t, runtime.GOOS == "windows", "mount not supported on Windows")
19
-	skip.If(t, os.Getuid() != 0, "skipping test that requires root")
20
-
21
-	dir1, err := ioutil.TempDir("", "test-ensure-removeall-with-dir1")
22
-	if err != nil {
23
-		t.Fatal(err)
24
-	}
25
-	dir2, err := ioutil.TempDir("", "test-ensure-removeall-with-dir2")
26
-	if err != nil {
27
-		t.Fatal(err)
28
-	}
29
-	defer os.RemoveAll(dir2)
30
-
31
-	bindDir := filepath.Join(dir1, "bind")
32
-	if err := os.MkdirAll(bindDir, 0755); err != nil {
33
-		t.Fatal(err)
34
-	}
35
-
36
-	if err := mount.Mount(dir2, bindDir, "none", "bind"); err != nil {
37
-		t.Fatal(err)
38
-	}
39
-
40
-	done := make(chan struct{}, 1)
41
-	go func() {
42
-		err = EnsureRemoveAll(dir1)
43
-		close(done)
44
-	}()
45
-
46
-	select {
47
-	case <-done:
48
-		if err != nil {
49
-			t.Fatal(err)
50
-		}
51
-	case <-time.After(5 * time.Second):
52
-		t.Fatal("timeout waiting for EnsureRemoveAll to finish")
53
-	}
54
-
55
-	if _, err := os.Stat(dir1); !os.IsNotExist(err) {
56
-		t.Fatalf("expected %q to not exist", dir1)
57
-	}
58
-}