diff --git a/cmd/config/internal/commands/run-fns.go b/cmd/config/internal/commands/run-fns.go index f31d2fe7f..523cf9015 100644 --- a/cmd/config/internal/commands/run-fns.go +++ b/cmd/config/internal/commands/run-fns.go @@ -11,6 +11,7 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/kyaml/errors" + "sigs.k8s.io/kustomize/kyaml/kio/filters" "sigs.k8s.io/kustomize/kyaml/runfn" "sigs.k8s.io/kustomize/kyaml/yaml" ) @@ -55,8 +56,8 @@ func GetRunFnRunner(name string) *RunFnRunner { r.Command.Flags().StringVar( &r.NetworkName, "network-name", "bridge", "the docker network to run the container in") r.Command.Flags().StringSliceVar( - &r.Volumes, "volume", []string{}, - "the volumes to bind mount to the container.") + &r.Mounts, "mount", []string{}, + "a list of storage options read from the filesystem") return r } @@ -78,7 +79,7 @@ type RunFnRunner struct { RunFns runfn.RunFns Network bool NetworkName string - Volumes []string + Mounts []string } func (r *RunFnRunner) runE(c *cobra.Command, args []string) error { @@ -203,6 +204,14 @@ data: {} return []*yaml.RNode{rc}, nil } +func toStorageMounts(mounts []string) []filters.StorageMount { + var sms []filters.StorageMount + for _, mount := range mounts { + sms = append(sms, filters.StringToStorageMount(mount)) + } + return sms +} + func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error { if r.EnableStar != (r.StarPath != "") { return errors.Errorf("must specify --star-path with --enable-star") @@ -244,6 +253,9 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error { path = args[0] } + // parse mounts to set storageMounts + storageMounts := toStorageMounts(r.Mounts) + r.RunFns = runfn.RunFns{ FunctionPaths: r.FnPaths, GlobalScope: r.GlobalScope, @@ -254,7 +266,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error { Network: r.Network, NetworkName: r.NetworkName, EnableStarlark: r.EnableStar, - Volumes: r.Volumes, + StorageMounts: storageMounts, } // don't consider args for the function diff --git a/cmd/config/internal/commands/run_test.go b/cmd/config/internal/commands/run_test.go index 887451035..a3e8f492d 100644 --- a/cmd/config/internal/commands/run_test.go +++ b/cmd/config/internal/commands/run_test.go @@ -27,7 +27,7 @@ func TestRunFnCommand_preRunE(t *testing.T) { functionPaths []string network bool networkName string - volumes []string + mount []string }{ { name: "config map", @@ -217,17 +217,14 @@ apiVersion: v1 `, }, { - name: "volumes", - args: []string{"run", "dir", "--volume", "vol1", "--volume", "vol2"}, - path: "dir", - volumes: []string{"vol1", "vol2"}, - }, - { - name: "custom kind with volumes", + name: "custom kind with storage mounts", args: []string{ - "run", "dir", "--volume", "vol", "--image", "foo:bar", "--", "Foo", "g=h", "i=j=k"}, - path: "dir", - volumes: []string{"vol"}, + "run", "dir", "--mount", "type=bind;src=/mount/path;dst=/local/", + "--mount", "type=volume;src=myvol;dst=/local/", + "--mount", "type=tmpfs;dst=/local/", + "--image", "foo:bar", "--", "Foo", "g=h", "i=j=k"}, + path: "dir", + mount: []string{"type=bind;src=/mount/path;dst=/local/", "type=volume;src=myvol;dst=/local/", "type=tmpfs;dst=/local/"}, expected: ` metadata: name: function-input @@ -327,12 +324,7 @@ apiVersion: v1 t.FailNow() } - // check if Volumes were set - if tt.volumes == nil { - // make Equal work against flag default - tt.volumes = []string{} - } - if !assert.Equal(t, tt.volumes, r.RunFns.Volumes) { + if !assert.Equal(t, toStorageMounts(tt.mount), r.RunFns.StorageMounts) { t.FailNow() } diff --git a/kyaml/kio/filters/container.go b/kyaml/kio/filters/container.go index 663774356..54dfdec57 100644 --- a/kyaml/kio/filters/container.go +++ b/kyaml/kio/filters/container.go @@ -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, "=") diff --git a/kyaml/kio/filters/container_test.go b/kyaml/kio/filters/container_test.go index 5d37ee55e..319a1a535 100644 --- a/kyaml/kio/filters/container_test.go +++ b/kyaml/kio/filters/container_test.go @@ -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 diff --git a/kyaml/kio/filters/functiontypes.go b/kyaml/kio/filters/functiontypes.go index 4096cd952..db5a8adb3 100644 --- a/kyaml/kio/filters/functiontypes.go +++ b/kyaml/kio/filters/functiontypes.go @@ -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 } diff --git a/kyaml/runfn/runfn.go b/kyaml/runfn/runfn.go index 23ccd00f6..19d348486 100644 --- a/kyaml/runfn/runfn.go +++ b/kyaml/runfn/runfn.go @@ -51,10 +51,6 @@ type RunFns struct { // NetworkName is the name of the docker network to use for the container NetworkName string - // Volumes Volumes allows directories to be specified outside the configuration - // directory. - Volumes []string - // Output can be set to write the result to Output rather than back to the directory Output io.Writer @@ -137,13 +133,6 @@ func (r RunFns) getFilters(nodes []*yaml.RNode) ([]kio.Filter, error) { } fltrs = append(fltrs, f...) - // directories from volumes specified on the struct - f, err = r.getDirectoriesFromVolumes() - if err != nil { - return nil, err - } - fltrs = append(fltrs, f...) - // explicit fns specified on the struct f, err = r.getFunctionsFromFunctions() if err != nil { @@ -207,24 +196,6 @@ func (r RunFns) getFunctionsFromFunctionPaths() ([]kio.Filter, error) { return r.getFunctionFilters(true, buff.Nodes...) } -// getDirectoriesFromVolumes returns the set of directories read from r.Volumes -// as a slice of Filters -func (r RunFns) getDirectoriesFromVolumes() ([]kio.Filter, error) { - buff := &kio.PackageBuffer{} - for i := range r.Volumes { - err := kio.Pipeline{ - Inputs: []kio.Reader{ - kio.LocalPackageReader{PackagePath: r.Volumes[i]}, - }, - Outputs: []kio.Writer{buff}, - }.Execute() - if err != nil { - return nil, err - } - } - return r.getFunctionFilters(true, buff.Nodes...) -} - // getFunctionsFromFunctions returns the set of explicitly provided functions as // Filters func (r RunFns) getFunctionsFromFunctions() ([]kio.Filter, error) { diff --git a/kyaml/runfn/runfn_test.go b/kyaml/runfn/runfn_test.go index b3018129f..236f5610c 100644 --- a/kyaml/runfn/runfn_test.go +++ b/kyaml/runfn/runfn_test.go @@ -141,13 +141,13 @@ func TestRunFns_Execute__initDefault(t *testing.T) { }, }, { - name: "explicit directories in volumes", - instance: RunFns{Volumes: []string{"vol"}}, + name: "explicit directories in mounts", + instance: RunFns{StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}}}, expected: RunFns{ Output: os.Stdout, Input: os.Stdin, NoFunctionsFromInput: getFalse(), - Volumes: []string{"vol"}, + StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}}, }, }, }