From 964c49a69e78b46cf27b1447d60046d56f30f757 Mon Sep 17 00:00:00 2001 From: Arash Deshmeh Date: Sat, 24 Mar 2018 21:43:57 -0400 Subject: [PATCH] add exec option to API TmpfsOptions and the related volume functions Signed-off-by: Arash Deshmeh --- api/swagger.yaml | 3 ++ api/types/mount/mount.go | 3 +- docs/api/version-history.md | 1 + volume/mounts/linux_parser.go | 25 ++++++++++++++ volume/mounts/parser_test.go | 65 +++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 1 deletion(-) diff --git a/api/swagger.yaml b/api/swagger.yaml index 5677340dbd..e4ec9c3741 100644 --- a/api/swagger.yaml +++ b/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: | diff --git a/api/types/mount/mount.go b/api/types/mount/mount.go index 6fe04da257..cf4953a0ce 100644 --- a/api/types/mount/mount.go +++ b/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. // diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 6942c65e79..9ff9a0bd82 100644 --- a/docs/api/version-history.md +++ b/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 diff --git a/volume/mounts/linux_parser.go b/volume/mounts/linux_parser.go index 1532187c77..0d086090de 100644 --- a/volume/mounts/linux_parser.go +++ b/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 } diff --git a/volume/mounts/parser_test.go b/volume/mounts/parser_test.go index c4af79055c..f356fe8e36 100644 --- a/volume/mounts/parser_test.go +++ b/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) {