Merge 2922732df5
into ee8b788538
This commit is contained in:
commit
84495cb4bb
8 changed files with 126 additions and 1 deletions
|
@ -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: |
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -136,6 +136,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
|||
mount.TmpfsOptions = &mounttypes.TmpfsOptions{
|
||||
SizeBytes: m.TmpfsOptions.SizeBytes,
|
||||
Mode: m.TmpfsOptions.Mode,
|
||||
Options: m.TmpfsOptions.Options,
|
||||
}
|
||||
}
|
||||
containerSpec.Mounts = append(containerSpec.Mounts, mount)
|
||||
|
@ -423,6 +424,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
|||
mount.TmpfsOptions = &swarmapi.Mount_TmpfsOptions{
|
||||
SizeBytes: m.TmpfsOptions.SizeBytes,
|
||||
Mode: m.TmpfsOptions.Mode,
|
||||
Options: m.TmpfsOptions.Options,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -364,6 +364,7 @@ func convertMount(m api.Mount) enginemount.Mount {
|
|||
mount.TmpfsOptions = &enginemount.TmpfsOptions{
|
||||
SizeBytes: m.TmpfsOptions.SizeBytes,
|
||||
Mode: m.TmpfsOptions.Mode,
|
||||
Options: m.TmpfsOptions.Options,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
swarmapi "github.com/moby/swarmkit/v2/api"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
func TestIsolationConversion(t *testing.T) {
|
||||
|
@ -117,6 +119,7 @@ func TestCredentialSpecConversion(t *testing.T) {
|
|||
to: []string{"credentialspec=registry://testing"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
@ -139,3 +142,75 @@ func TestCredentialSpecConversion(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTmpfsConversion(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
from []swarmapi.Mount
|
||||
to []mount.Mount
|
||||
}{
|
||||
{
|
||||
name: "tmpfs-exec",
|
||||
from: []swarmapi.Mount{
|
||||
{
|
||||
Source: "/foo",
|
||||
Target: "/bar",
|
||||
Type: swarmapi.MountTypeTmpfs,
|
||||
TmpfsOptions: &swarmapi.Mount_TmpfsOptions{
|
||||
Options: "exec",
|
||||
},
|
||||
},
|
||||
},
|
||||
to: []mount.Mount{
|
||||
{
|
||||
Source: "/foo",
|
||||
Target: "/bar",
|
||||
Type: mount.TypeTmpfs,
|
||||
TmpfsOptions: &mount.TmpfsOptions{
|
||||
Options: "exec",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tmpfs-noexec",
|
||||
from: []swarmapi.Mount{
|
||||
{
|
||||
Source: "/foo",
|
||||
Target: "/bar",
|
||||
Type: swarmapi.MountTypeTmpfs,
|
||||
TmpfsOptions: &swarmapi.Mount_TmpfsOptions{
|
||||
Options: "noexec",
|
||||
},
|
||||
},
|
||||
},
|
||||
to: []mount.Mount{
|
||||
{
|
||||
Source: "/foo",
|
||||
Target: "/bar",
|
||||
Type: mount.TypeTmpfs,
|
||||
TmpfsOptions: &mount.TmpfsOptions{
|
||||
Options: "noexec",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
task := swarmapi.Task{
|
||||
Spec: swarmapi.TaskSpec{
|
||||
Runtime: &swarmapi.TaskSpec_Container{
|
||||
Container: &swarmapi.ContainerSpec{
|
||||
Image: "alpine:latest",
|
||||
Mounts: c.from,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
config := containerConfig{task: &task}
|
||||
assert.Check(t, is.DeepEqual(c.to, config.hostConfig(nil).Mounts))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
values originally submitted to the `POST /containers/create` endpoint. The
|
||||
newly introduced `DNSNames` should now be used instead when short container
|
||||
IDs are needed.
|
||||
* `POST /containers/create` now takes `Options` as part of `HostConfig.Mounts` to set options for tmpfs mounts.
|
||||
|
||||
## v1.44 API changes
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -238,6 +238,7 @@ func TestConvertTmpfsOptions(t *testing.T) {
|
|||
readOnly bool
|
||||
expectedSubstrings []string
|
||||
unexpectedSubstrings []string
|
||||
err bool
|
||||
}
|
||||
cases := []testCase{
|
||||
{
|
||||
|
@ -252,10 +253,26 @@ func TestConvertTmpfsOptions(t *testing.T) {
|
|||
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 := NewLinuxParser()
|
||||
for _, tc := range cases {
|
||||
data, err := p.ConvertTmpfsOptions(&tc.opt, tc.readOnly)
|
||||
if tc.err {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error for %+v, got nil", tc.opt)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("could not convert %+v (readOnly: %v) to string: %v",
|
||||
tc.opt, tc.readOnly, err)
|
||||
|
|
Loading…
Add table
Reference in a new issue