Skip to content

Commit 75f78b8

Browse files
feat: Support other volume mount modes
Signed-off-by: Magnus Karlsson <magnus.karlsson@sectra.com>
1 parent 5a62bb4 commit 75f78b8

8 files changed

+114
-81
lines changed

pkg/cnab/provider/action.go

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"get.porter.sh/porter/pkg/tracing"
1313
cnabaction "github.com/cnabio/cnab-go/action"
1414
"github.com/cnabio/cnab-go/driver"
15+
"github.com/docker/docker/api/types/mount"
1516
"github.com/hashicorp/go-multierror"
1617
"go.opentelemetry.io/otel/attribute"
1718
"go.uber.org/zap/zapcore"
@@ -21,6 +22,7 @@ type HostVolumeMountSpec struct {
2122
Source string
2223
Target string
2324
ReadOnly bool
25+
Type mount.Type
2426
}
2527

2628
// ActionArguments are the shared arguments for all bundle runs.

pkg/cnab/provider/docker_linux.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ func (r *Runtime) mountDockerSocket(cfg *container.Config, hostCfg *container.Ho
4141
dockerSockMount := mount.Mount{
4242
Source: "/var/run/docker.sock",
4343
Target: "/var/run/docker.sock",
44-
Type: "bind",
44+
Type: mount.TypeBind,
4545
ReadOnly: false,
4646
}
4747
hostCfg.Mounts = append(hostCfg.Mounts, dockerSockMount)
4848
return nil
4949
}
5050

51-
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool) error {
51+
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool, mountType mount.Type) error {
5252
mount := mount.Mount{
5353
Source: source,
5454
Target: target,
55-
Type: "bind",
55+
Type: mountType,
5656
ReadOnly: readOnly,
5757
}
5858
hostConfig.Mounts = append(hostConfig.Mounts, mount)

pkg/cnab/provider/driver.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (r *Runtime) dockerDriverWithHostAccess(config cnab.Docker, d *docker.Drive
115115

116116
func (r *Runtime) addVolumeMountsToHostConfig(hostConfig *container.HostConfig, mounts []HostVolumeMountSpec) error {
117117
for _, mount := range mounts {
118-
err := r.addVolumeMountToHostConfig(hostConfig, mount.Source, mount.Target, mount.ReadOnly)
118+
err := r.addVolumeMountToHostConfig(hostConfig, mount.Source, mount.Target, mount.ReadOnly, mount.Type)
119119
if err != nil {
120120
return err
121121
}

pkg/cnab/provider/driver_darwin.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ func (r *Runtime) mountDockerSocket(cfg *container.Config, hostCfg *container.Ho
2020
return nil
2121
}
2222

23-
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool) error {
23+
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool, mountType mount.Type) error {
2424
mount := mount.Mount{
2525
Source: source,
2626
Target: target,
27-
Type: "bind",
27+
Type: mountType,
2828
ReadOnly: readOnly,
2929
}
3030
hostConfig.Mounts = append(hostConfig.Mounts, mount)

pkg/cnab/provider/driver_test.go

+63-68
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"get.porter.sh/porter/pkg/cnab"
77
"github.com/cnabio/cnab-go/driver/docker"
8+
"github.com/docker/docker/api/types/mount"
89
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/require"
1011
)
@@ -131,34 +132,36 @@ func TestNewDriver_Docker(t *testing.T) {
131132
r.MockGetDockerGroupId()
132133
defer r.Close()
133134

134-
r.Extensions[cnab.DockerExtensionKey] = cnab.Docker{}
135-
_, err := r.FileSystem.Create("/var/run/docker.sock")
136-
require.NoError(t, err)
137-
_, err = r.FileSystem.Create("/sourceFolder")
138-
require.NoError(t, err)
139-
_, err = r.FileSystem.Create("/sourceFolder2")
140-
require.NoError(t, err)
141-
_, err = r.FileSystem.Create("/sourceFolder3")
142-
require.NoError(t, err)
135+
var hostVolumeMounts = []HostVolumeMountSpec{
136+
{
137+
Source: "/sourceFolder",
138+
Target: "/targetFolder",
139+
ReadOnly: false,
140+
Type: mount.TypeBind,
141+
},
142+
{
143+
Source: "/sourceFolder2",
144+
Target: "/targetFolder2",
145+
ReadOnly: true,
146+
Type: mount.TypeBind,
147+
},
148+
{
149+
Source: "/sourceFolder3",
150+
Target: "/targetFolder3",
151+
ReadOnly: false,
152+
Type: mount.TypeBind,
153+
},
154+
{
155+
Source: "volume",
156+
Target: "/targetFolder4",
157+
ReadOnly: true,
158+
Type: mount.TypeVolume,
159+
},
160+
}
143161

144162
args := ActionArguments{
145163
AllowDockerHostAccess: true,
146-
HostVolumeMounts: []HostVolumeMountSpec{
147-
{
148-
Source: "/sourceFolder",
149-
Target: "/targetFolder",
150-
},
151-
{
152-
Source: "/sourceFolder2",
153-
Target: "/targetFolder2",
154-
ReadOnly: true,
155-
},
156-
{
157-
Source: "/sourceFolder3",
158-
Target: "/targetFolder3",
159-
ReadOnly: false,
160-
},
161-
},
164+
HostVolumeMounts: hostVolumeMounts,
162165
}
163166

164167
driver, err := r.newDriver(DriverNameDocker, args)
@@ -175,16 +178,14 @@ func TestNewDriver_Docker(t *testing.T) {
175178
require.NoError(t, err)
176179
require.Equal(t, false, containerHostCfg.Privileged)
177180

178-
require.Len(t, containerHostCfg.Mounts, 4) //includes the docker socket mount
179-
assert.Equal(t, "/sourceFolder", containerHostCfg.Mounts[1].Source)
180-
assert.Equal(t, "/targetFolder", containerHostCfg.Mounts[1].Target)
181-
assert.Equal(t, false, containerHostCfg.Mounts[1].ReadOnly)
182-
assert.Equal(t, "/sourceFolder2", containerHostCfg.Mounts[2].Source)
183-
assert.Equal(t, "/targetFolder2", containerHostCfg.Mounts[2].Target)
184-
assert.Equal(t, true, containerHostCfg.Mounts[2].ReadOnly)
185-
assert.Equal(t, "/sourceFolder3", containerHostCfg.Mounts[3].Source)
186-
assert.Equal(t, "/targetFolder3", containerHostCfg.Mounts[3].Target)
187-
assert.Equal(t, false, containerHostCfg.Mounts[3].ReadOnly)
181+
require.Len(t, containerHostCfg.Mounts, 5) //includes the docker socket mount
182+
183+
for i, hostMount := range hostVolumeMounts {
184+
assert.Equal(t, hostMount.Source, containerHostCfg.Mounts[i+1].Source)
185+
assert.Equal(t, hostMount.Target, containerHostCfg.Mounts[i+1].Target)
186+
assert.Equal(t, hostMount.ReadOnly, containerHostCfg.Mounts[i+1].ReadOnly)
187+
assert.Equal(t, hostMount.Type, containerHostCfg.Mounts[i+1].Type)
188+
}
188189
})
189190

190191
t.Run("host volume mount, docker driver, with multiple mounts", func(t *testing.T) {
@@ -193,30 +194,25 @@ func TestNewDriver_Docker(t *testing.T) {
193194
r := NewTestRuntime(t)
194195
defer r.Close()
195196

196-
_, err := r.FileSystem.Create("/sourceFolder")
197-
require.NoError(t, err)
198-
_, err = r.FileSystem.Create("/sourceFolder2")
199-
require.NoError(t, err)
200-
_, err = r.FileSystem.Create("/sourceFolder3")
201-
require.NoError(t, err)
197+
var hostVolumeMounts = []HostVolumeMountSpec{
198+
{
199+
Source: "/sourceFolder",
200+
Target: "/targetFolder",
201+
},
202+
{
203+
Source: "/sourceFolder2",
204+
Target: "/targetFolder2",
205+
ReadOnly: true,
206+
},
207+
{
208+
Source: "/sourceFolder3",
209+
Target: "/targetFolder3",
210+
ReadOnly: false,
211+
},
212+
}
202213

203214
args := ActionArguments{
204-
HostVolumeMounts: []HostVolumeMountSpec{
205-
{
206-
Source: "/sourceFolder",
207-
Target: "/targetFolder",
208-
},
209-
{
210-
Source: "/sourceFolder2",
211-
Target: "/targetFolder2",
212-
ReadOnly: true,
213-
},
214-
{
215-
Source: "/sourceFolder3",
216-
Target: "/targetFolder3",
217-
ReadOnly: false,
218-
},
219-
},
215+
HostVolumeMounts: hostVolumeMounts,
220216
}
221217

222218
driver, err := r.newDriver(DriverNameDocker, args)
@@ -234,16 +230,13 @@ func TestNewDriver_Docker(t *testing.T) {
234230
require.NoError(t, err)
235231

236232
require.Len(t, containerHostCfg.Mounts, 3)
237-
assert.Equal(t, "/sourceFolder", containerHostCfg.Mounts[0].Source)
238-
assert.Equal(t, "/targetFolder", containerHostCfg.Mounts[0].Target)
239-
assert.Equal(t, false, containerHostCfg.Mounts[0].ReadOnly)
240-
assert.Equal(t, "/sourceFolder2", containerHostCfg.Mounts[1].Source)
241-
assert.Equal(t, "/targetFolder2", containerHostCfg.Mounts[1].Target)
242-
assert.Equal(t, true, containerHostCfg.Mounts[1].ReadOnly)
243-
assert.Equal(t, "/sourceFolder3", containerHostCfg.Mounts[2].Source)
244-
assert.Equal(t, "/targetFolder3", containerHostCfg.Mounts[2].Target)
245-
assert.Equal(t, false, containerHostCfg.Mounts[2].ReadOnly)
246233

234+
for i, hostMount := range hostVolumeMounts {
235+
assert.Equal(t, hostMount.Source, containerHostCfg.Mounts[i].Source)
236+
assert.Equal(t, hostMount.Target, containerHostCfg.Mounts[i].Target)
237+
assert.Equal(t, hostMount.ReadOnly, containerHostCfg.Mounts[i].ReadOnly)
238+
assert.Equal(t, hostMount.Type, containerHostCfg.Mounts[i].Type)
239+
}
247240
})
248241

249242
t.Run("host volume mount, docker driver, with single mount", func(t *testing.T) {
@@ -252,14 +245,15 @@ func TestNewDriver_Docker(t *testing.T) {
252245
r := NewTestRuntime(t)
253246
defer r.Close()
254247

255-
_, err := r.FileSystem.Create("/sourceFolder")
256-
require.NoError(t, err)
248+
// _, err := r.FileSystem.Create("/sourceFolder")
249+
//require.NoError(t, err)
257250

258251
args := ActionArguments{
259252
HostVolumeMounts: []HostVolumeMountSpec{
260253
{
261254
Source: "/sourceFolder",
262255
Target: "/targetFolder",
256+
Type: mount.TypeBind,
263257
},
264258
},
265259
}
@@ -282,6 +276,7 @@ func TestNewDriver_Docker(t *testing.T) {
282276
assert.Equal(t, "/sourceFolder", containerHostCfg.Mounts[0].Source)
283277
assert.Equal(t, "/targetFolder", containerHostCfg.Mounts[0].Target)
284278
assert.Equal(t, false, containerHostCfg.Mounts[0].ReadOnly)
279+
assert.Equal(t, mount.TypeBind, containerHostCfg.Mounts[0].Type)
285280
})
286281

287282
t.Run("host volume mount, mismatch driver name", func(t *testing.T) {

pkg/cnab/provider/driver_windows.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ func (r *Runtime) mountDockerSocket(cfg *container.Config, hostCfg *container.Ho
1818
return nil
1919
}
2020

21-
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool) error {
21+
func (r *Runtime) addVolumeMountToHostConfig(hostConfig *container.HostConfig, source string, target string, readOnly bool, mountType mount.Type) error {
2222
mount := mount.Mount{
2323
Source: source,
2424
Target: target,
25-
Type: "bind",
25+
Type: mountType,
2626
ReadOnly: readOnly,
2727
}
2828
hostConfig.Mounts = append(hostConfig.Mounts, mount)

pkg/porter/lifecycle.go

+17-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"get.porter.sh/porter/pkg/secrets"
1919
"get.porter.sh/porter/pkg/storage"
2020
"get.porter.sh/porter/pkg/tracing"
21+
"github.com/docker/docker/api/types/mount"
2122
"github.com/opencontainers/go-digest"
2223
"go.mongodb.org/mongo-driver/bson"
2324
)
@@ -48,8 +49,9 @@ type BundleExecutionOptions struct {
4849
AllowDockerHostAccess bool
4950

5051
// MountHostVolume mounts provides the bundle access to a host volume.
51-
// This is the unparsed list of HOST_PATH:TARGET_PATH:OPTION
52+
// This is the unparsed list of HOST_PATH:TARGET_PATH:OPTION:MOUNT_TYPE
5253
// OPTION can be ro (read-only) or rw (read-write). Defaults to ro.
54+
// MOUNT_TYPE can be bind, volume, or tmpfs. Defaults to bind.
5355
HostVolumeMounts []string
5456

5557
// DebugMode indicates if the bundle should be run in debug mode.
@@ -106,10 +108,10 @@ func (o *BundleExecutionOptions) GetParameters() map[string]interface{} {
106108
// Sets the final resolved set of host volumes to be made availabe to the bundle
107109
func (o *BundleExecutionOptions) GetHostVolumeMounts() []cnabprovider.HostVolumeMountSpec {
108110
var hostVolumeMounts []cnabprovider.HostVolumeMountSpec
109-
for _, mount := range o.HostVolumeMounts {
111+
for _, mountString := range o.HostVolumeMounts {
112+
var mountType mount.Type = mount.TypeBind
110113
var isReadOnlyMount bool
111-
parts := strings.Split(mount, ":") // HOST_PATH:TARGET_PATH:OPTION
112-
114+
parts := strings.Split(mountString, ":") // HOST_PATH:TARGET_PATH:OPTION:MOUNT_TYPE
113115
// if parts[0] is a single character, it's a drive letter on Windows
114116
// so we need to join it with the next part
115117
if runtime.GOOS == "windows" && len(parts) > 1 && len(parts[0]) == 1 && unicode.IsLetter(rune(parts[0][0])) {
@@ -118,7 +120,8 @@ func (o *BundleExecutionOptions) GetHostVolumeMounts() []cnabprovider.HostVolume
118120
}
119121

120122
l := len(parts)
121-
if l < 2 || l > 3 {
123+
if l < 2 || l > 4 {
124+
fmt.Printf("ERROR: invalid mount: %s\n", mountString)
122125
continue
123126
}
124127

@@ -134,10 +137,19 @@ func (o *BundleExecutionOptions) GetHostVolumeMounts() []cnabprovider.HostVolume
134137
isReadOnlyMount = true
135138
}
136139

140+
if l == 4 {
141+
mountType = mount.Type(parts[3])
142+
if mountType != mount.TypeBind && mountType != mount.TypeVolume && mountType != mount.TypeTmpfs {
143+
fmt.Printf("ERROR: invalid mount type: %s\n", mountType)
144+
continue
145+
}
146+
}
147+
137148
hostVolumeMounts = append(hostVolumeMounts, cnabprovider.HostVolumeMountSpec{
138149
Source: parts[0],
139150
Target: parts[1],
140151
ReadOnly: isReadOnlyMount,
152+
Type: mountType,
141153
})
142154
}
143155

pkg/porter/lifecycle_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"get.porter.sh/porter/tests"
2121
"github.com/cnabio/cnab-go/secrets/host"
2222
"github.com/cnabio/cnab-to-oci/relocation"
23+
"github.com/docker/docker/api/types/mount"
2324
"github.com/stretchr/testify/assert"
2425
"github.com/stretchr/testify/require"
2526
)
@@ -712,6 +713,7 @@ func TestBundleExecutionOptions_GetHostVolumeMounts(t *testing.T) {
712713
"/host/path:/target/path:ro",
713714
"/host/path:/target/path:rw",
714715
"/host/path:/target/path",
716+
"myvolume:/target/path:ro:volume",
715717
},
716718
}
717719

@@ -720,16 +722,25 @@ func TestBundleExecutionOptions_GetHostVolumeMounts(t *testing.T) {
720722
Source: "/host/path",
721723
Target: "/target/path",
722724
ReadOnly: true,
725+
Type: mount.TypeBind,
723726
},
724727
{
725728
Source: "/host/path",
726729
Target: "/target/path",
727730
ReadOnly: false,
731+
Type: mount.TypeBind,
728732
},
729733
{
730734
Source: "/host/path",
731735
Target: "/target/path",
732736
ReadOnly: true,
737+
Type: mount.TypeBind,
738+
},
739+
{
740+
Source: "myvolume",
741+
Target: "/target/path",
742+
ReadOnly: true,
743+
Type: mount.TypeVolume,
733744
},
734745
}
735746

@@ -748,6 +759,7 @@ func TestBundleExecutionOptions_GetHostVolumeMounts(t *testing.T) {
748759
if expected[i].ReadOnly != actual[i].ReadOnly {
749760
t.Errorf("expected %v but got %v", expected[i].ReadOnly, actual[i].ReadOnly)
750761
}
762+
751763
}
752764
})
753765

@@ -781,6 +793,18 @@ func TestBundleExecutionOptions_GetHostVolumeMounts(t *testing.T) {
781793
}
782794

783795
})
796+
797+
t.Run("invalid host volume mount type option value", func(t *testing.T) {
798+
opts := &BundleExecutionOptions{
799+
HostVolumeMounts: []string{
800+
"/host/path:/target/path:ro:invalid-option",
801+
},
802+
}
803+
804+
actual := opts.GetHostVolumeMounts()
805+
806+
assert.Equal(t, 0, len(actual), "expected no host volume mounts")
807+
})
784808
}
785809

786810
func TestBundleExecutionOptions_GetHostVolumeMountsWindows(t *testing.T) {

0 commit comments

Comments
 (0)