add exec option to API TmpfsOptions and the related volume functions

Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>
This commit is contained in:
Arash Deshmeh 2018-03-24 21:43:57 -04:00 committed by Drew Erny
parent d25b0bd7ea
commit 964c49a69e
5 changed files with 96 additions and 1 deletions

View file

@ -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: |

View file

@ -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.
//

View file

@ -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

View file

@ -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
}

View file

@ -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) {