Merge pull request #25830 from tiborvass/cherry-pick-25825

[1.12] Fix volume not working after daemon restart
diff --git a/volume/local/local.go b/volume/local/local.go
index 30aa9de..498f4db 100644
--- a/volume/local/local.go
+++ b/volume/local/local.go
@@ -9,6 +9,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"sync"
 
 	"github.com/Sirupsen/logrus"
@@ -90,10 +91,13 @@
 		r.volumes[name] = v
 		optsFilePath := filepath.Join(rootDirectory, name, "opts.json")
 		if b, err := ioutil.ReadFile(optsFilePath); err == nil {
-			v.opts = &optsConfig{}
-			if err := json.Unmarshal(b, v.opts); err != nil {
+			opts := optsConfig{}
+			if err := json.Unmarshal(b, &opts); err != nil {
 				return nil, err
 			}
+			if !reflect.DeepEqual(opts, optsConfig{}) {
+				v.opts = &opts
+			}
 
 			// unmount anything that may still be mounted (for example, from an unclean shutdown)
 			for _, info := range mountInfos {
@@ -178,7 +182,7 @@
 		path:       path,
 	}
 
-	if opts != nil {
+	if len(opts) != 0 {
 		if err = setOpts(v, opts); err != nil {
 			return nil, err
 		}
diff --git a/volume/local/local_test.go b/volume/local/local_test.go
index 6b9ce55..a21f5f7 100644
--- a/volume/local/local_test.go
+++ b/volume/local/local_test.go
@@ -3,6 +3,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"reflect"
 	"runtime"
 	"strings"
@@ -133,6 +134,11 @@
 			}
 		}
 	}
+
+	r, err = New(rootDir, 0, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
 }
 
 func TestValidateName(t *testing.T) {
@@ -262,3 +268,61 @@
 		t.Fatal("missing volume options on restart")
 	}
 }
+
+func TestRealodNoOpts(t *testing.T) {
+	rootDir, err := ioutil.TempDir("", "volume-test-reload-no-opts")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(rootDir)
+
+	r, err := New(rootDir, 0, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := r.Create("test1", nil); err != nil {
+		t.Fatal(err)
+	}
+	if _, err := r.Create("test2", nil); err != nil {
+		t.Fatal(err)
+	}
+	// make sure a file with `null` (.e.g. empty opts map from older daemon) is ok
+	if err := ioutil.WriteFile(filepath.Join(rootDir, "test2"), []byte("null"), 600); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := r.Create("test3", nil); err != nil {
+		t.Fatal(err)
+	}
+	// make sure an empty opts file doesn't break us too
+	if err := ioutil.WriteFile(filepath.Join(rootDir, "test3"), nil, 600); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err := r.Create("test4", map[string]string{}); err != nil {
+		t.Fatal(err)
+	}
+
+	r, err = New(rootDir, 0, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, name := range []string{"test1", "test2", "test3", "test4"} {
+		v, err := r.Get(name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		lv, ok := v.(*localVolume)
+		if !ok {
+			t.Fatalf("expected *localVolume got: %v", reflect.TypeOf(v))
+		}
+		if lv.opts != nil {
+			t.Fatalf("expected opts to be nil, got: %v", lv.opts)
+		}
+		if _, err := lv.Mount("1234"); err != nil {
+			t.Fatal(err)
+		}
+	}
+}