Browse Source

add exec option to API TmpfsOptions and the related volume functions

Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>
Arash Deshmeh 7 years ago
parent
commit
964c49a69e

+ 3 - 0
api/swagger.yaml

@@ -442,6 +442,9 @@ definitions:
           Mode:
             description: "The permission mode for the tmpfs mount in an integer."
             type: "integer"
+          Options:
+            description: "The list of options to be passed to the tmpfs mount in a string."
+            type: "string"
 
   RestartPolicy:
     description: |

+ 2 - 1
api/types/mount/mount.go

@@ -119,7 +119,8 @@ type TmpfsOptions struct {
 	SizeBytes int64 `json:",omitempty"`
 	// Mode of the tmpfs upon creation
 	Mode os.FileMode `json:",omitempty"`
-
+	// Options passed directly to the tmpfs mount
+	Options string `json:",omitempty"`
 	// TODO(stevvooe): There are several more tmpfs flags, specified in the
 	// daemon, that are accepted. Only the most basic are added for now.
 	//

+ 1 - 0
docs/api/version-history.md

@@ -312,6 +312,7 @@ keywords: "API, Docker, rcli, REST, documentation"
 * `GET /services/{id}` now returns `Ulimits` as part of `ContainerSpec`.
 * `POST /services/create` now accepts `Ulimits` as part of `ContainerSpec`.
 * `POST /services/{id}/update` now accepts `Ulimits` as part of `ContainerSpec`.
+* `POST /containers/create` now takes `Options` as part of `HostConfig.Mounts` to set options for tmpfs mounts.
 
 ## v1.40 API changes
 

+ 25 - 0
volume/mounts/linux_parser.go

@@ -204,6 +204,22 @@ func linuxValidMountMode(mode string) bool {
 	return true
 }
 
+var validTmpfsOptions = map[string]bool{
+	"exec":   true,
+	"noexec": true,
+}
+
+func validateTmpfsOptions(rawOptions string) ([]string, error) {
+	var options []string
+	for _, opt := range strings.Split(rawOptions, ",") {
+		if _, ok := validTmpfsOptions[opt]; !ok {
+			return nil, errors.New("invalid option: " + opt)
+		}
+		options = append(options, opt)
+	}
+	return options, nil
+}
+
 func (p *linuxParser) ReadWrite(mode string) bool {
 	if !linuxValidMountMode(mode) {
 		return false
@@ -406,6 +422,15 @@ func (p *linuxParser) ConvertTmpfsOptions(opt *mount.TmpfsOptions, readOnly bool
 
 		rawOpts = append(rawOpts, fmt.Sprintf("size=%d%s", size, suffix))
 	}
+
+	if opt != nil && len(opt.Options) > 0 {
+		tmpfsOpts, err := validateTmpfsOptions(opt.Options)
+		if err != nil {
+			return "", err
+		}
+		rawOpts = append(rawOpts, tmpfsOpts...)
+	}
+
 	return strings.Join(rawOpts, ","), nil
 }
 

+ 65 - 0
volume/mounts/parser_test.go

@@ -2,6 +2,7 @@ package mounts // import "github.com/docker/docker/volume/mounts"
 
 import (
 	"os"
+	"strings"
 	"testing"
 
 	"github.com/docker/docker/api/types/mount"
@@ -9,6 +10,70 @@ import (
 	is "gotest.tools/v3/assert/cmp"
 )
 
+type parseMountRawTestSet struct {
+	valid   []string
+	invalid map[string]string
+}
+
+func TestConvertTmpfsOptions(t *testing.T) {
+	type testCase struct {
+		opt                  mount.TmpfsOptions
+		readOnly             bool
+		expectedSubstrings   []string
+		unexpectedSubstrings []string
+		err                  bool
+	}
+	cases := []testCase{
+		{
+			opt:                  mount.TmpfsOptions{SizeBytes: 1024 * 1024, Mode: 0700},
+			readOnly:             false,
+			expectedSubstrings:   []string{"size=1m", "mode=700"},
+			unexpectedSubstrings: []string{"ro"},
+		},
+		{
+			opt:                  mount.TmpfsOptions{},
+			readOnly:             true,
+			expectedSubstrings:   []string{"ro"},
+			unexpectedSubstrings: []string{},
+		},
+		{
+			opt:                  mount.TmpfsOptions{Options: "exec"},
+			readOnly:             true,
+			expectedSubstrings:   []string{"ro", "exec"},
+			unexpectedSubstrings: []string{"noexec"},
+		},
+		{
+			opt: mount.TmpfsOptions{Options: "INVALID"},
+			err: true,
+		},
+	}
+	p := &linuxParser{}
+	for _, c := range cases {
+		data, err := p.ConvertTmpfsOptions(&c.opt, c.readOnly)
+		if c.err {
+			if err == nil {
+				t.Fatalf("expected error for %+v, got nil", c.opt)
+			}
+			continue
+		}
+		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)
+			}
+		}
+	}
+}
+
 type mockFiProvider struct{}
 
 func (mockFiProvider) fileInfo(path string) (exists, isDir bool, err error) {