diff --git a/container/container_unix.go b/container/container_unix.go index b62dce8bed..c38f750667 100644 --- a/container/container_unix.go +++ b/container/container_unix.go @@ -414,7 +414,7 @@ func (container *Container) TmpfsMounts() ([]Mount, error) { } for dest, mnt := range container.MountPoints { if mnt.Type == mounttypes.TypeTmpfs { - data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions) + data, err := volume.ConvertTmpfsOptions(mnt.Spec.TmpfsOptions, mnt.Spec.ReadOnly) if err != nil { return nil, err } diff --git a/volume/validate.go b/volume/validate.go index de41e0bf1e..27a8c5d5b0 100644 --- a/volume/validate.go +++ b/volume/validate.go @@ -91,7 +91,7 @@ func validateMountConfig(mnt *mount.Mount, options ...func(*validateOpts)) error if len(mnt.Source) != 0 { return &errMountConfig{mnt, errExtraField("Source")} } - if _, err := ConvertTmpfsOptions(mnt.TmpfsOptions); err != nil { + if _, err := ConvertTmpfsOptions(mnt.TmpfsOptions, mnt.ReadOnly); err != nil { return &errMountConfig{mnt, err} } default: diff --git a/volume/volume_linux.go b/volume/volume_linux.go index 991aa66123..2176301767 100644 --- a/volume/volume_linux.go +++ b/volume/volume_linux.go @@ -13,16 +13,17 @@ import ( // for mount(2). // The logic is copy-pasted from daemon/cluster/executer/container.getMountMask. // It will be deduplicated when we migrated the cluster to the new mount scheme. -func ConvertTmpfsOptions(opt *mounttypes.TmpfsOptions) (string, error) { - if opt == nil { - return "", nil - } +func ConvertTmpfsOptions(opt *mounttypes.TmpfsOptions, readOnly bool) (string, error) { var rawOpts []string - if opt.Mode != 0 { + if readOnly { + rawOpts = append(rawOpts, "ro") + } + + if opt != nil && opt.Mode != 0 { rawOpts = append(rawOpts, fmt.Sprintf("mode=%o", opt.Mode)) } - if opt.SizeBytes != 0 { + if opt != nil && opt.SizeBytes != 0 { // calculate suffix here, making this linux specific, but that is // okay, since API is that way anyways. diff --git a/volume/volume_linux_test.go b/volume/volume_linux_test.go index 05e5e16a71..40ce5525a3 100644 --- a/volume/volume_linux_test.go +++ b/volume/volume_linux_test.go @@ -3,6 +3,7 @@ package volume import ( + "strings" "testing" mounttypes "github.com/docker/docker/api/types/mount" @@ -10,14 +11,41 @@ import ( func TestConvertTmpfsOptions(t *testing.T) { type testCase struct { - opt mounttypes.TmpfsOptions + opt mounttypes.TmpfsOptions + readOnly bool + expectedSubstrings []string + unexpectedSubstrings []string } cases := []testCase{ - {mounttypes.TmpfsOptions{SizeBytes: 1024 * 1024, Mode: 0700}}, + { + opt: mounttypes.TmpfsOptions{SizeBytes: 1024 * 1024, Mode: 0700}, + readOnly: false, + expectedSubstrings: []string{"size=1m", "mode=700"}, + unexpectedSubstrings: []string{"ro"}, + }, + { + opt: mounttypes.TmpfsOptions{}, + readOnly: true, + expectedSubstrings: []string{"ro"}, + unexpectedSubstrings: []string{}, + }, } for _, c := range cases { - if _, err := ConvertTmpfsOptions(&c.opt); err != nil { - t.Fatalf("could not convert %+v to string: %v", c.opt, err) + data, err := ConvertTmpfsOptions(&c.opt, c.readOnly) + if err != nil { + t.Fatalf("could not convert %+v (readOnly: %v) to string: %v", + c.opt, c.readOnly, err) + } + t.Logf("data=%q", data) + for _, s := range c.expectedSubstrings { + if !strings.Contains(data, s) { + t.Fatalf("expected substring: %s, got %v (case=%+v)", s, data, c) + } + } + for _, s := range c.unexpectedSubstrings { + if strings.Contains(data, s) { + t.Fatalf("unexpected substring: %s, got %v (case=%+v)", s, data, c) + } } } } diff --git a/volume/volume_unsupported.go b/volume/volume_unsupported.go index ca7445c7de..ff9d6afa27 100644 --- a/volume/volume_unsupported.go +++ b/volume/volume_unsupported.go @@ -11,6 +11,6 @@ import ( // ConvertTmpfsOptions converts *mounttypes.TmpfsOptions to the raw option string // for mount(2). -func ConvertTmpfsOptions(opt *mounttypes.TmpfsOptions) (string, error) { +func ConvertTmpfsOptions(opt *mounttypes.TmpfsOptions, readOnly bool) (string, error) { return "", fmt.Errorf("%s does not support tmpfs", runtime.GOOS) }