Use mount flag to pass storage mounts to functions

This commit is contained in:
Prachi Pendse
2020-04-01 15:03:57 -07:00
parent a4ee1c2e72
commit 38973a80c3
7 changed files with 132 additions and 297 deletions

View File

@@ -134,11 +134,8 @@ type ContainerFilter struct {
// Network is the container network to use.
Network string `yaml:"network,omitempty"`
// Volumes are the directories to mount as container volumes.
Volumes []string `yaml:"volumes,omitempty"`
// StorageMounts is a list of storage options that the container will have mounted.
StorageMounts []StorageMount
StorageMounts []StorageMount `yaml:"mounts,omitempty"`
// Config is the API configuration for the container and passed through the
// API_CONFIG env var to the container.
@@ -159,25 +156,31 @@ func (c ContainerFilter) String() string {
return c.Image
}
// StorageMount represents a container's mounted storage option(s)
type StorageMount struct {
// Type of mount e.g. bind mount, local volume, etc.
MountType string
// Source for the storage to be mounted.
// For named volumes, this is the name of the volume.
// For anonymous volumes, this field is omitted (empty string).
// For bind mounts, this is the path to the file or directory on the host.
Src string
// The path where the file or directory is mounted in the container.
DstPath string
}
func (s *StorageMount) String() string {
return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath)
}
func StringToStorageMount(s string) StorageMount {
m := make(map[string]string)
options := strings.Split(s, ";")
for _, option := range options {
keyVal := strings.Split(option, "=")
m[keyVal[0]] = keyVal[1]
}
var sm StorageMount
for key, value := range m {
switch {
case key == "type":
sm.MountType = value
case key == "src":
sm.Src = value
case key == "dst":
sm.DstPath = value
}
}
return sm
}
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
const functionsDirectoryName = "functions"
@@ -338,11 +341,6 @@ func (c *ContainerFilter) getArgs() []string {
args = append(args, "--mount", storageMount.String())
}
// export volumes to the container
for _, volume := range c.Volumes {
args = append(args, "--volume", volume)
}
// export the local environment vars to the container
for _, pair := range os.Environ() {
tokens := strings.Split(pair, "=")

View File

@@ -141,87 +141,6 @@ metadata:
assert.Equal(t, expected, cmd.Args)
}
func TestFilter_command_volume(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`)
if !assert.NoError(t, err) {
return
}
instance := &ContainerFilter{
Image: "example.com:version",
Volumes: []string{"/host-src:/container-dest:ro"},
Config: cfg,
}
cmd, err := instance.getCommand()
if !assert.NoError(t, err) {
return
}
expected := []string{
"docker", "run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "none",
"--user", "nobody",
"--security-opt=no-new-privileges",
"--volume", "/host-src:/container-dest:ro",
}
for _, e := range os.Environ() {
// the process env
tokens := strings.Split(e, "=")
if tokens[0] == "" {
continue
}
expected = append(expected, "-e", tokens[0])
}
expected = append(expected, "example.com:version")
assert.Equal(t, expected, cmd.Args)
}
func TestFilter_command_volumes(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`)
if !assert.NoError(t, err) {
return
}
instance := &ContainerFilter{
Image: "example.com:version",
Volumes: []string{"/host-src1:/container-dest1:ro", "/host-src2:/container-dest2:rw"},
Config: cfg,
}
cmd, err := instance.getCommand()
if !assert.NoError(t, err) {
return
}
expected := []string{
"docker", "run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "none",
"--user", "nobody",
"--security-opt=no-new-privileges",
"--volume", "/host-src1:/container-dest1:ro",
"--volume", "/host-src2:/container-dest2:rw",
}
for _, e := range os.Environ() {
// the process env
tokens := strings.Split(e, "=")
if tokens[0] == "" {
continue
}
expected = append(expected, "-e", tokens[0])
}
expected = append(expected, "example.com:version")
assert.Equal(t, expected, cmd.Args)
}
func TestFilter_Filter(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment
@@ -415,6 +334,68 @@ container:
image: foo:v1.0.0`,
},
{
name: "storage mounts json style",
resource: `
apiVersion: v1beta1
kind: Example
metadata:
annotations:
config.kubernetes.io/function: |-
container:
image: foo:v1.0.0
mounts: [ {type: bind, src: /mount/path, dst: /local/}, {src: myvol, dst: /local/, type: volume}, {dst: /local/, type: tmpfs} ]
`,
expectedFn: `
container:
image: foo:v1.0.0
mounts:
- type: bind
src: /mount/path
dst: /local/
- type: volume
src: myvol
dst: /local/
- type: tmpfs
dst: /local/
`,
},
{
name: "storage mounts yaml style",
resource: `
apiVersion: v1beta1
kind: Example
metadata:
annotations:
config.kubernetes.io/function: |-
container:
image: foo:v1.0.0
mounts:
- src: /mount/path
type: bind
dst: /local/
- dst: /local/
src: myvol
type: volume
- type: tmpfs
dst: /local/
`,
expectedFn: `
container:
image: foo:v1.0.0
mounts:
- type: bind
src: /mount/path
dst: /local/
- type: volume
src: myvol
dst: /local/
- type: tmpfs
dst: /local/
`,
},
{
name: "network",
resource: `
@@ -436,70 +417,6 @@ container:
`,
},
{
name: "volume",
resource: `
apiVersion: v1beta1
kind: Example
metadata:
annotations:
config.kubernetes.io/function: |-
container:
image: foo:v1.0.0
volumes: ["/host-src:/container-dest:ro"]
`,
expectedFn: `
container:
image: foo:v1.0.0
volumes:
- /host-src:/container-dest:ro
`,
},
{
name: "volumes as array",
resource: `
apiVersion: v1beta1
kind: Example
metadata:
annotations:
config.kubernetes.io/function: |-
container:
image: foo:v1.0.0
volumes: ["/host-src1:/container-dest1:ro", "/host-src2:/container-dest2:rw"]
`,
expectedFn: `
container:
image: foo:v1.0.0
volumes:
- /host-src1:/container-dest1:ro
- /host-src2:/container-dest2:rw
`,
},
{
name: "volumes as list",
resource: `
apiVersion: v1beta1
kind: Example
metadata:
annotations:
config.kubernetes.io/function: |-
container:
image: foo:v1.0.0
volumes:
- "/host-src1:/container-dest1:ro"
- "/host-src2:/container-dest2:rw"
`,
expectedFn: `
container:
image: foo:v1.0.0
volumes:
- /host-src1:/container-dest1:ro
- /host-src2:/container-dest2:rw
`,
},
{
name: "path",
resource: `
@@ -661,76 +578,6 @@ metadata:
}
}
func Test_GetContainerVolumeRequired(t *testing.T) {
tests := []struct {
input string
volumes []string
}{
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo
configFn:
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
volumes: [ /host-src:/container-dest:ro ]
`,
volumes: []string{"/host-src:/container-dest:ro"},
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo
configFn:
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
`,
volumes: []string(nil),
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
volumes: [ "/host-src1:/container-dest1:ro", "/host-src2:/container-dest2:rw" ]
`,
volumes: []string{"/host-src1:/container-dest1:ro", "/host-src2:/container-dest2:rw"},
},
{
input: `apiVersion: v1
kind: Foo
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
volumes:
- /host-src1:/container-dest1:ro
- /host-src2:/container-dest2:rw
`,
volumes: []string{"/host-src1:/container-dest1:ro", "/host-src2:/container-dest2:rw"},
},
}
for _, tc := range tests {
cfg, err := yaml.Parse(tc.input)
if !assert.NoError(t, err) {
return
}
fn := GetFunctionSpec(cfg)
assert.Equal(t, tc.volumes, fn.Container.Volumes)
}
}
func TestFilter_Filter_defaultNaming(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment

View File

@@ -29,8 +29,8 @@ type FunctionSpec struct {
// Starlark is the spec for running a function as a starlark script
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
// Volumes are the directories to mount as container volumes
Volumes []string `json:"volumes,omitempty" yaml:"volumes,omitempty"`
// Mounts are the storage or directories to mount into the container
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
}
// ContainerSpec defines a spec for running a function as a container
@@ -41,8 +41,8 @@ type ContainerSpec struct {
// Network defines network specific configuration
Network ContainerNetwork `json:"network,omitempty" yaml:"network,omitempty"`
// Volumes are the directories to mount as container volumes
Volumes []string `json:"volumes,omitempty" yaml:"volumes,omitempty"`
// Mounts are the storage or directories to mount into the container
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
}
// ContainerNetwork
@@ -59,6 +59,21 @@ type StarlarkSpec struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}
// StorageMount represents a container's mounted storage option(s)
type StorageMount struct {
// Type of mount e.g. bind mount, local volume, etc.
MountType string `json:"type,omitempty" yaml:"type,omitempty"`
// Source for the storage to be mounted.
// For named volumes, this is the name of the volume.
// For anonymous volumes, this field is omitted (empty string).
// For bind mounts, this is the path to the file or directory on the host.
Src string `json:"src,omitempty" yaml:"src,omitempty"`
// The path where the file or directory is mounted in the container.
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
}
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
// nil if the resource does not have a FunctionSpec.
//
@@ -74,7 +89,7 @@ func GetFunctionSpec(n *yaml.RNode) *FunctionSpec {
path := meta.Annotations[kioutil.PathAnnotation]
if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil {
fn.Network = ""
fn.Volumes = []string{}
fn.StorageMounts = []StorageMount{}
fn.Path = path
return fn
}