Add kyaml command for invoking container filters

This commit is contained in:
Phillip Wittrock
2019-11-12 17:45:20 -08:00
parent 912a9c3baa
commit c99278c67b
24 changed files with 1578 additions and 95 deletions

View File

@@ -17,7 +17,7 @@ import (
const (
ResourceListKind = "ResourceList"
ResourceListApiVersion = "kyaml.kustomize.dev/v1alpha1"
ResourceListApiVersion = "config.kubernetes.io/v1alpha1"
)
// ByteReadWriter reads from an input and writes to an output.

View File

@@ -34,7 +34,7 @@ i: j
}
func TestByteReader_Read_wrappedResourceßßList(t *testing.T) {
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: kyaml.kustomize.dev/v1alpha1
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
functionConfig:
foo: bar

View File

@@ -45,7 +45,7 @@ g:
if !assert.NoError(t, err) {
return
}
assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- c: d # second

View File

@@ -5,12 +5,13 @@ package filters
import (
"bytes"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -24,6 +25,8 @@ 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"`
@@ -38,6 +41,10 @@ type ContainerFilter struct {
checkInput func(string)
}
func (c *ContainerFilter) SetMountPath(path string) {
c.mountPath = path
}
// GrepFilter implements kio.GrepFilter
func (c *ContainerFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
// get the command to filter the Resources
@@ -89,6 +96,10 @@ 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))
}
// export the local environment vars to the container
for _, pair := range os.Environ() {
@@ -141,7 +152,8 @@ type IsReconcilerFilter struct {
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
var out []*yaml.RNode
for i := range inputs {
isContainerResource := GetContainerName(inputs[i]) != ""
img, _ := GetContainerName(inputs[i])
isContainerResource := img != ""
if isContainerResource && !c.ExcludeReconcilers {
out = append(out, inputs[i])
}
@@ -153,19 +165,20 @@ func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error)
}
// GetContainerName returns the container image for an API if one exists
func GetContainerName(n *yaml.RNode) string {
func GetContainerName(n *yaml.RNode) (string, string) {
meta, _ := n.GetMeta()
container := meta.Annotations["kyaml.kustomize.dev/container"]
// path to the function, this will be mounted into the container
path := meta.Annotations[kioutil.PathAnnotation]
container := meta.Annotations["config.kubernetes.io/container"]
if container != "" {
return container
return container, path
}
if match.MatchString(meta.ApiVersion) {
return meta.ApiVersion
image, err := n.Pipe(yaml.Lookup("metadata", "configFn", "container", "image"))
if err != nil || yaml.IsMissingOrNull(image) {
return "", path
}
return ""
return image.YNode().Value, path
}
// match specifies the set of apiVersions to recognize as being container images
var match = regexp.MustCompile(`(docker\.io|.*\.?gcr\.io)/.*(:.*)?`)

View File

@@ -5,7 +5,9 @@ package filters
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
@@ -60,6 +62,54 @@ metadata:
assert.True(t, foundKyaml)
}
func TestFilter_commandMountPath(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,
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)
foundKyaml := false
for _, e := range cmd.Env {
// verify the command has the right environment variables to pass to the container
split := strings.Split(e, "=")
if split[0] == "KYAML_TEST" {
assert.Equal(t, "FOO", split[1])
foundKyaml = true
}
}
assert.True(t, foundKyaml)
}
func TestFilter_Filter(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment
@@ -92,7 +142,7 @@ metadata:
args: []string{"sed", "s/Deployment/StatefulSet/g"},
checkInput: func(s string) {
called = true
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiVersion: apps/v1
@@ -174,7 +224,7 @@ metadata:
args: []string{"sh", "-c", "cat <&0"},
checkInput: func(s string) {
called = true
if !assert.Equal(t, `apiVersion: kyaml.kustomize.dev/v1alpha1
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- apiversion: apps/v1
@@ -226,36 +276,30 @@ metadata:
func Test_GetContainerName(t *testing.T) {
// make sure gcr.io works
n, err := yaml.Parse(`apiVersion: gcr.io/foo/bar:something
n, err := yaml.Parse(`apiVersion: v1beta1
kind: MyThing
metadata:
configFn:
container:
image: gcr.io/foo/bar:something
`)
if !assert.NoError(t, err) {
return
}
c := GetContainerName(n)
c, _ := GetContainerName(n)
assert.Equal(t, "gcr.io/foo/bar:something", c)
// make sure regional gcr.io works
n, err = yaml.Parse(`apiVersion: us.gcr.io/foo/bar:something
kind: MyThing
`)
if !assert.NoError(t, err) {
return
}
c = GetContainerName(n)
assert.Equal(t, "us.gcr.io/foo/bar:something", c)
// container from annotation
n, err = yaml.Parse(`apiVersion: v1
kind: MyThing
metadata:
annotations:
kyaml.kustomize.dev/container: gcr.io/foo/bar:something
config.kubernetes.io/container: gcr.io/foo/bar:something
`)
if !assert.NoError(t, err) {
return
}
c = GetContainerName(n)
c, _ = GetContainerName(n)
assert.Equal(t, "gcr.io/foo/bar:something", c)
// doesn't have a container
@@ -266,16 +310,6 @@ metadata:
if !assert.NoError(t, err) {
return
}
c = GetContainerName(n)
c, _ = GetContainerName(n)
assert.Equal(t, "", c)
// make sure docker.io works
n, err = yaml.Parse(`apiVersion: docker.io/foo/bar:something
kind: MyThing
`)
if !assert.NoError(t, err) {
return
}
c = GetContainerName(n)
assert.Equal(t, "docker.io/foo/bar:something", c)
}

View File

@@ -0,0 +1,38 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filters
import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const LocalConfigAnnotation = "config.kubernetes.io/local-config"
// IsLocalConfig filters Resources using the config.kubernetes.io/local-config annotation
type IsLocalConfig struct {
// IncludeLocalConfig will include local-config if set to true
IncludeLocalConfig bool `yaml:"includeLocalConfig,omitempty"`
// ExcludeNonLocalConfig will exclude non local-config if set to true
ExcludeNonLocalConfig bool `yaml:"excludeNonLocalConfig,omitempty"`
}
// Filter implements kio.Filter
func (c *IsLocalConfig) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
var out []*yaml.RNode
for i := range inputs {
meta, err := inputs[i].GetMeta()
if err != nil {
return nil, err
}
_, local := meta.Annotations[LocalConfigAnnotation]
if local && c.IncludeLocalConfig {
out = append(out, inputs[i])
} else if !local && !c.ExcludeNonLocalConfig {
out = append(out, inputs[i])
}
}
return out, nil
}

View File

@@ -12,7 +12,7 @@ import (
)
const (
mergeSourceAnnotation = "kyaml.kustomize.dev/merge-source"
mergeSourceAnnotation = "config.kubernetes.io/merge-source"
mergeSourceOriginal = "original"
mergeSourceUpdated = "updated"
mergeSourceDest = "dest"