From e6306f60f44b9a3fc52485c94c7ad9115955c472 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Sun, 17 Nov 2019 19:51:19 -0800 Subject: [PATCH 1/6] Add docs command boilerplate --- cmd/kyaml/cmd/docs.go | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 cmd/kyaml/cmd/docs.go diff --git a/cmd/kyaml/cmd/docs.go b/cmd/kyaml/cmd/docs.go new file mode 100644 index 000000000..8ba1d8e40 --- /dev/null +++ b/cmd/kyaml/cmd/docs.go @@ -0,0 +1,58 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 +// +package cmd + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func GetDocsRunner() *DocsRunner { + r := &DocsRunner{} + c := &cobra.Command{ + Use: "docs [API_TYPE]", + Short: "Print out documentation for API resource", + Long: `Print out documentation for API resource + +The Docs command reads a JSON schema from the Kustomize API and outputs a pretiffied version of the documentation. + +TODO: + + +For information on merge rules, run: + + kyaml help docs +`, + Example: `kyaml docs kustomization`, + PreRunE: r.preRunE, + RunE: r.runE, + } + r.Command = c + r.Command.Flags().StringVar(&r.apiType, "api-type", "", + "API type to print out") + return r +} + +// DocsCommand ... +func DocsCommand() *cobra.Command { + return GetDocsRunner().Command +} + +// DocsRunner contains the run function +type DocsRunner struct { + Command *cobra.Command + apiType string +} + +func (r *DocsRunner) preRunE(c *cobra.Command, args []string) error { + fmt.Println("Docs pre run. :)") + return nil +} + +func (r *DocsRunner) runE(c *cobra.Command, args []string) error { + fmt.Println("Docs actual run. :-)") + fmt.Println(args[0]) + return nil +} From a7cff1c75b3ee0330b9fbc1b9afae01cdcd94752 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Thu, 21 Nov 2019 12:16:09 -0800 Subject: [PATCH 2/6] Add local volume support to container filters --- cmd/config/cmd/docs.go | 58 ----------------------------- kyaml/kio/filters/container.go | 7 ++++ kyaml/kio/filters/container_test.go | 36 ++++++++++++++++++ 3 files changed, 43 insertions(+), 58 deletions(-) delete mode 100644 cmd/config/cmd/docs.go diff --git a/cmd/config/cmd/docs.go b/cmd/config/cmd/docs.go deleted file mode 100644 index 8ba1d8e40..000000000 --- a/cmd/config/cmd/docs.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 -// -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -func GetDocsRunner() *DocsRunner { - r := &DocsRunner{} - c := &cobra.Command{ - Use: "docs [API_TYPE]", - Short: "Print out documentation for API resource", - Long: `Print out documentation for API resource - -The Docs command reads a JSON schema from the Kustomize API and outputs a pretiffied version of the documentation. - -TODO: - - -For information on merge rules, run: - - kyaml help docs -`, - Example: `kyaml docs kustomization`, - PreRunE: r.preRunE, - RunE: r.runE, - } - r.Command = c - r.Command.Flags().StringVar(&r.apiType, "api-type", "", - "API type to print out") - return r -} - -// DocsCommand ... -func DocsCommand() *cobra.Command { - return GetDocsRunner().Command -} - -// DocsRunner contains the run function -type DocsRunner struct { - Command *cobra.Command - apiType string -} - -func (r *DocsRunner) preRunE(c *cobra.Command, args []string) error { - fmt.Println("Docs pre run. :)") - return nil -} - -func (r *DocsRunner) runE(c *cobra.Command, args []string) error { - fmt.Println("Docs actual run. :-)") - fmt.Println(args[0]) - return nil -} diff --git a/kyaml/kio/filters/container.go b/kyaml/kio/filters/container.go index c3a60cf98..53fc8178b 100644 --- a/kyaml/kio/filters/container.go +++ b/kyaml/kio/filters/container.go @@ -33,6 +33,9 @@ type ContainerFilter struct { // Network is the container network to use. Network string `yaml:"network,omitempty"` + // LocalVolume is the volume the container uses. + LocalVolume string `yaml:"localVolume,omitempty"` + // Config is the API configuration for the container and passed through the // API_CONFIG env var to the container. // Typically a Kubernetes style Resource Config. @@ -110,6 +113,10 @@ func (c *ContainerFilter) getArgs() []string { args = append(args, "-v", fmt.Sprintf("%s:/local/:ro", c.mountPath)) } + if c.LocalVolume != "" { + args = append(args, "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/'", c.LocalVolume)) + } + // export the local environment vars to the container for _, pair := range os.Environ() { args = append(args, "-e", strings.Split(pair, "=")[0]) diff --git a/kyaml/kio/filters/container_test.go b/kyaml/kio/filters/container_test.go index 76410788a..67cbf6028 100644 --- a/kyaml/kio/filters/container_test.go +++ b/kyaml/kio/filters/container_test.go @@ -99,6 +99,42 @@ metadata: assert.Equal(t, expected, cmd.Args) } +func TestFilter_command_LocalVolume(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", + Config: cfg, + LocalVolume: "myvol", + } + 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", + "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/'", "myvol"), + } + for _, e := range os.Environ() { + // the process env + expected = append(expected, "-e", strings.Split(e, "=")[0]) + } + expected = append(expected, "example.com:version") + assert.Equal(t, expected, cmd.Args) +} + func TestFilter_command_network(t *testing.T) { cfg, err := yaml.Parse(`apiversion: apps/v1 kind: Deployment From 7eaaedf9f6db84589913c2974fdb7225b933ba3d Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Tue, 26 Nov 2019 13:29:50 -0800 Subject: [PATCH 3/6] Make LocalVolume read only --- kyaml/kio/filters/container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyaml/kio/filters/container.go b/kyaml/kio/filters/container.go index 53fc8178b..797dc2cad 100644 --- a/kyaml/kio/filters/container.go +++ b/kyaml/kio/filters/container.go @@ -114,7 +114,7 @@ func (c *ContainerFilter) getArgs() []string { } if c.LocalVolume != "" { - args = append(args, "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/'", c.LocalVolume)) + args = append(args, "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/:ro'", c.LocalVolume)) } // export the local environment vars to the container From eccef3bb0d95c8450e248efe871a2ad3d2c0e9dd Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Tue, 26 Nov 2019 13:52:27 -0800 Subject: [PATCH 4/6] Add appropriate test for read only LocalVolume --- kyaml/kio/filters/container_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyaml/kio/filters/container_test.go b/kyaml/kio/filters/container_test.go index 67cbf6028..6dbc88beb 100644 --- a/kyaml/kio/filters/container_test.go +++ b/kyaml/kio/filters/container_test.go @@ -125,7 +125,7 @@ metadata: "--network", "none", "--user", "nobody", "--security-opt=no-new-privileges", - "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/'", "myvol"), + "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/:ro'", "myvol"), } for _, e := range os.Environ() { // the process env From e46108ada0c22c011d8d66f64969a84b6fccbbe4 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Thu, 28 Nov 2019 15:43:17 -0800 Subject: [PATCH 5/6] Add in struct for mounted storage options --- kyaml/kio/filters/container.go | 39 +++++++++++++++------ kyaml/kio/filters/container_test.go | 53 ++++++----------------------- kyaml/runfn/runfn.go | 1 - 3 files changed, 38 insertions(+), 55 deletions(-) diff --git a/kyaml/kio/filters/container.go b/kyaml/kio/filters/container.go index 797dc2cad..8d327ccf2 100644 --- a/kyaml/kio/filters/container.go +++ b/kyaml/kio/filters/container.go @@ -25,7 +25,6 @@ import ( // The full set of environment variables from the parent process // are passed to the container. type ContainerFilter struct { - mountPath string // Image is the container image to use to create a container. Image string `yaml:"image,omitempty"` @@ -33,8 +32,8 @@ type ContainerFilter struct { // Network is the container network to use. Network string `yaml:"network,omitempty"` - // LocalVolume is the volume the container uses. - LocalVolume string `yaml:"localVolume,omitempty"` + // List of storage options that container will have mounted. + StorageMounts []StorageMount // Config is the API configuration for the container and passed through the // API_CONFIG env var to the container. @@ -47,8 +46,25 @@ type ContainerFilter struct { checkInput func(string) } -func (c *ContainerFilter) SetMountPath(path string) { - c.mountPath = path +// 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 +} + +// AddStorageMount adds a mounted storage option to the Container +func (c *ContainerFilter) AddStorageMount(mountType, src, dstPath string) { + storageMount := StorageMount{mountType, src, dstPath} + c.StorageMounts = append(c.StorageMounts, storageMount) } // GrepFilter implements kio.GrepFilter @@ -108,13 +124,14 @@ func (c *ContainerFilter) getArgs() []string { // don't make fs readonly because things like heredoc rely on writing tmp files "--security-opt=no-new-privileges", // don't allow the user to escalate privileges } - // mount the directory containing the function as read-only - if c.mountPath != "" { - args = append(args, "-v", fmt.Sprintf("%s:/local/:ro", c.mountPath)) - } - if c.LocalVolume != "" { - args = append(args, "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/:ro'", c.LocalVolume)) + // TODO(joncwong): Allow StorageMount fields to have default values. + for _, storageMount := range c.StorageMounts { + mountType := storageMount.mountType + src := storageMount.src + dstPath := storageMount.dstPath + + args = append(args, "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", mountType, src, dstPath)) } // export the local environment vars to the container diff --git a/kyaml/kio/filters/container_test.go b/kyaml/kio/filters/container_test.go index 6dbc88beb..f64b38f6d 100644 --- a/kyaml/kio/filters/container_test.go +++ b/kyaml/kio/filters/container_test.go @@ -7,7 +7,6 @@ import ( "bytes" "fmt" "os" - "path/filepath" "strings" "testing" @@ -62,7 +61,7 @@ metadata: assert.True(t, foundKyaml) } -func TestFilter_commandMountPath(t *testing.T) { +func TestFilter_command_StorageMount(t *testing.T) { cfg, err := yaml.Parse(`apiversion: apps/v1 kind: Deployment metadata: @@ -71,47 +70,13 @@ metadata: if !assert.NoError(t, err) { return } + bindMount := StorageMount{"bind", "/mount/path", "/local/"} + localVol := StorageMount{"volume", "myvol", "/local/"} + tmpfs := StorageMount{"tmpfs", "", "/local/"} instance := &ContainerFilter{ - Image: "example.com:version", - Config: cfg, - mountPath: filepath.Join("mount", "path"), - } - os.Setenv("KYAML_TEST", "FOO") - 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", - "-v", fmt.Sprintf("%s:/local/:ro", filepath.Join("mount", "path")), - } - for _, e := range os.Environ() { - // the process env - expected = append(expected, "-e", strings.Split(e, "=")[0]) - } - expected = append(expected, "example.com:version") - assert.Equal(t, expected, cmd.Args) -} - -func TestFilter_command_LocalVolume(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", - Config: cfg, - LocalVolume: "myvol", + Image: "example.com:version", + Config: cfg, + StorageMounts: []StorageMount{bindMount, localVol, tmpfs}, } cmd, err := instance.getCommand() if !assert.NoError(t, err) { @@ -125,7 +90,9 @@ metadata: "--network", "none", "--user", "nobody", "--security-opt=no-new-privileges", - "--mount", fmt.Sprintf("'type=volume,src=%s,dst=/local/:ro'", "myvol"), + "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "bind", "/mount/path", "/local/"), + "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "volume", "myvol", "/local/"), + "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "tmpfs", "", "/local/"), } for _, e := range os.Environ() { // the process env diff --git a/kyaml/runfn/runfn.go b/kyaml/runfn/runfn.go index 1a587485d..dd7faabb2 100644 --- a/kyaml/runfn/runfn.go +++ b/kyaml/runfn/runfn.go @@ -91,7 +91,6 @@ func (r *RunFns) init() { if r.containerFilterProvider == nil { r.containerFilterProvider = func(image, path string, api *yaml.RNode) kio.Filter { cf := &filters.ContainerFilter{Image: image, Config: api} - cf.SetMountPath(filepath.Join(r.Path, path)) return cf } } From dff30b926e18acbe8c6be1e620896d90f78293d3 Mon Sep 17 00:00:00 2001 From: Jonathan Wong Date: Tue, 3 Dec 2019 00:17:12 -0800 Subject: [PATCH 6/6] Nit fixes and proper RunFns integration --- kyaml/kio/filters/container.go | 20 +++++++------------- kyaml/kio/filters/container_test.go | 6 +++--- kyaml/runfn/runfn.go | 6 +++++- kyaml/runfn/runfn_test.go | 5 ++++- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/kyaml/kio/filters/container.go b/kyaml/kio/filters/container.go index 8d327ccf2..aa929a644 100644 --- a/kyaml/kio/filters/container.go +++ b/kyaml/kio/filters/container.go @@ -32,7 +32,7 @@ type ContainerFilter struct { // Network is the container network to use. Network string `yaml:"network,omitempty"` - // List of storage options that container will have mounted. + // StorageMounts is a list of storage options that the container will have mounted. StorageMounts []StorageMount // Config is the API configuration for the container and passed through the @@ -49,22 +49,20 @@ type ContainerFilter struct { // StorageMount represents a container's mounted storage option(s) type StorageMount struct { // Type of mount e.g. bind mount, local volume, etc. - mountType string + 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 + Src string // The path where the file or directory is mounted in the container. - dstPath string + DstPath string } -// AddStorageMount adds a mounted storage option to the Container -func (c *ContainerFilter) AddStorageMount(mountType, src, dstPath string) { - storageMount := StorageMount{mountType, src, dstPath} - c.StorageMounts = append(c.StorageMounts, storageMount) +func (s *StorageMount) String() string { + return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath) } // GrepFilter implements kio.GrepFilter @@ -127,11 +125,7 @@ func (c *ContainerFilter) getArgs() []string { // TODO(joncwong): Allow StorageMount fields to have default values. for _, storageMount := range c.StorageMounts { - mountType := storageMount.mountType - src := storageMount.src - dstPath := storageMount.dstPath - - args = append(args, "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", mountType, src, dstPath)) + args = append(args, "--mount", storageMount.String()) } // export the local environment vars to the container diff --git a/kyaml/kio/filters/container_test.go b/kyaml/kio/filters/container_test.go index f64b38f6d..336ba0e8e 100644 --- a/kyaml/kio/filters/container_test.go +++ b/kyaml/kio/filters/container_test.go @@ -90,9 +90,9 @@ metadata: "--network", "none", "--user", "nobody", "--security-opt=no-new-privileges", - "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "bind", "/mount/path", "/local/"), - "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "volume", "myvol", "/local/"), - "--mount", fmt.Sprintf("'type=%s,src=%s,dst=%s:ro'", "tmpfs", "", "/local/"), + "--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "bind", "/mount/path", "/local/"), + "--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "volume", "myvol", "/local/"), + "--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "tmpfs", "", "/local/"), } for _, e := range os.Environ() { // the process env diff --git a/kyaml/runfn/runfn.go b/kyaml/runfn/runfn.go index dd7faabb2..366aaa519 100644 --- a/kyaml/runfn/runfn.go +++ b/kyaml/runfn/runfn.go @@ -16,6 +16,8 @@ import ( // RunFns runs the set of configuration functions in a local directory against // the Resources in that directory type RunFns struct { + StorageMounts []filters.StorageMount + // Path is the path to the directory containing functions Path string @@ -90,7 +92,9 @@ func (r *RunFns) init() { // if containerFilterProvider hasn't been set, use the default if r.containerFilterProvider == nil { r.containerFilterProvider = func(image, path string, api *yaml.RNode) kio.Filter { - cf := &filters.ContainerFilter{Image: image, Config: api} + defaultMount := filters.StorageMount{} + r.StorageMounts = append(r.StorageMounts, defaultMount) + cf := &filters.ContainerFilter{Image: image, Config: api, StorageMounts: r.StorageMounts} return cf } } diff --git a/kyaml/runfn/runfn_test.go b/kyaml/runfn/runfn_test.go index ef55a401d..5ca3ce76a 100644 --- a/kyaml/runfn/runfn_test.go +++ b/kyaml/runfn/runfn_test.go @@ -28,7 +28,10 @@ kind: return } filter := instance.containerFilterProvider("example.com:version", "", api) - assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter) + defaultMount:= filters.StorageMount{} + mounts := []filters.StorageMount{} + mounts = append(mounts, defaultMount) + assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api, StorageMounts: mounts}, filter) } func TestCmd_Execute(t *testing.T) {