Browse code

Skip empty directories on prior graphdriver detection

When starting the daemon, the `/var/lib/docker` directory
is scanned for existing directories, so that the previously
selected graphdriver will automatically be used.

In some situations, empty directories are present (those
directories can be created during feature detection of
graph-drivers), in which case the daemon refuses to start.

This patch improves detection, and skips empty directories,
so that leftover directories don't cause the daemon to
fail.

Before this change:

$ mkdir /var/lib/docker /var/lib/docker/aufs /var/lib/docker/overlay2
$ dockerd
...
Error starting daemon: error initializing graphdriver: /var/lib/docker contains several valid graphdrivers: overlay2, aufs; Please cleanup or explicitly choose storage driver (-s <DRIVER>)

With this patch applied:

$ mkdir /var/lib/docker /var/lib/docker/aufs /var/lib/docker/overlay2
$ dockerd
...
INFO[2017-11-16T17:26:43.207739140Z] Docker daemon commit=ab90bc296 graphdriver(s)=overlay2 version=dev
INFO[2017-11-16T17:26:43.208033095Z] Daemon has completed initialization

And on restart (prior graphdriver is still picked up):

$ dockerd
...
INFO[2017-11-16T17:27:52.260361465Z] [graphdriver] using prior storage driver: overlay2

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

Sebastiaan van Stijn authored on 2017/11/17 02:22:22
Showing 2 changed files
... ...
@@ -283,8 +283,27 @@ func scanPriorDrivers(root string) map[string]bool {
283 283
 	for driver := range drivers {
284 284
 		p := filepath.Join(root, driver)
285 285
 		if _, err := os.Stat(p); err == nil && driver != "vfs" {
286
-			driversMap[driver] = true
286
+			if !isEmptyDir(p) {
287
+				driversMap[driver] = true
288
+			}
287 289
 		}
288 290
 	}
289 291
 	return driversMap
290 292
 }
293
+
294
+// isEmptyDir checks if a directory is empty. It is used to check if prior
295
+// storage-driver directories exist. If an error occurs, it also assumes the
296
+// directory is not empty (which preserves the behavior _before_ this check
297
+// was added)
298
+func isEmptyDir(name string) bool {
299
+	f, err := os.Open(name)
300
+	if err != nil {
301
+		return false
302
+	}
303
+	defer f.Close()
304
+
305
+	if _, err = f.Readdirnames(1); err == io.EOF {
306
+		return true
307
+	}
308
+	return false
309
+}
291 310
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+package graphdriver
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"path/filepath"
6
+	"testing"
7
+
8
+	"github.com/stretchr/testify/assert"
9
+	"github.com/stretchr/testify/require"
10
+)
11
+
12
+func TestIsEmptyDir(t *testing.T) {
13
+	tmp, err := ioutil.TempDir("", "test-is-empty-dir")
14
+	require.NoError(t, err)
15
+	defer os.RemoveAll(tmp)
16
+
17
+	d := filepath.Join(tmp, "empty-dir")
18
+	err = os.Mkdir(d, 0755)
19
+	require.NoError(t, err)
20
+	empty := isEmptyDir(d)
21
+	assert.True(t, empty)
22
+
23
+	d = filepath.Join(tmp, "dir-with-subdir")
24
+	err = os.MkdirAll(filepath.Join(d, "subdir"), 0755)
25
+	require.NoError(t, err)
26
+	empty = isEmptyDir(d)
27
+	assert.False(t, empty)
28
+
29
+	d = filepath.Join(tmp, "dir-with-empty-file")
30
+	err = os.Mkdir(d, 0755)
31
+	require.NoError(t, err)
32
+	_, err = ioutil.TempFile(d, "file")
33
+	require.NoError(t, err)
34
+	empty = isEmptyDir(d)
35
+	assert.False(t, empty)
36
+}