mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Add kyaml command for invoking container filters
This commit is contained in:
98
kyaml/runfn/runfn.go
Normal file
98
kyaml/runfn/runfn.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runfn
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// RunFns runs the set of configuration functions in a local directory against
|
||||
// the Resources in that directory
|
||||
type RunFns struct {
|
||||
// Path is the path to the directory containing functions
|
||||
Path string
|
||||
|
||||
// FunctionPaths Paths allows functions to be specified outside the configuration
|
||||
// directory
|
||||
FunctionPaths []string
|
||||
|
||||
// Output can be set to write the result to Output rather than back to the directory
|
||||
Output io.Writer
|
||||
|
||||
// containerFilterProvider may be override by tests to fake invoking containers
|
||||
containerFilterProvider func(string, string, *yaml.RNode) kio.Filter
|
||||
}
|
||||
|
||||
// Execute runs the command
|
||||
func (r RunFns) Execute() error {
|
||||
// make the path absolute so it works on mac
|
||||
var err error
|
||||
r.Path, err = filepath.Abs(r.Path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// default the containerFilterProvider if it hasn't been override. Split out for testing.
|
||||
(&r).init()
|
||||
|
||||
// identify the configuration functions in the directory
|
||||
buff := &kio.PackageBuffer{}
|
||||
err = kio.Pipeline{
|
||||
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: r.Path}},
|
||||
Filters: []kio.Filter{&filters.IsReconcilerFilter{}},
|
||||
Outputs: []kio.Writer{buff},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// accept a
|
||||
for i := range r.FunctionPaths {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: r.FunctionPaths[i]}},
|
||||
Outputs: []kio.Writer{buff},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// reconcile each local API
|
||||
var fltrs []kio.Filter
|
||||
for i := range buff.Nodes {
|
||||
api := buff.Nodes[i]
|
||||
img, path := filters.GetContainerName(api)
|
||||
fltrs = append(fltrs, r.containerFilterProvider(img, path, api))
|
||||
}
|
||||
|
||||
pkgIO := &kio.LocalPackageReadWriter{PackagePath: r.Path}
|
||||
inputs := []kio.Reader{pkgIO}
|
||||
var outputs []kio.Writer
|
||||
if r.Output == nil {
|
||||
// write back to the package
|
||||
outputs = append(outputs, pkgIO)
|
||||
} else {
|
||||
// write to the output instead of the directory
|
||||
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
|
||||
}
|
||||
return kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: outputs}.Execute()
|
||||
}
|
||||
|
||||
// init initializes the RunFns with a containerFilterProvider.
|
||||
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}
|
||||
cf.SetMountPath(filepath.Join(r.Path, path))
|
||||
return cf
|
||||
}
|
||||
}
|
||||
}
|
||||
248
kyaml/runfn/runfn_test.go
Normal file
248
kyaml/runfn/runfn_test.go
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package runfn
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestRunFns_Execute(t *testing.T) {
|
||||
instance := RunFns{}
|
||||
instance.init()
|
||||
api, err := yaml.Parse(`apiVersion: apps/v1
|
||||
kind:
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
filter := instance.containerFilterProvider("example.com:version", "", api)
|
||||
assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter)
|
||||
}
|
||||
|
||||
func TestCmd_Execute(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||
return
|
||||
}
|
||||
|
||||
// write a test filter
|
||||
f := `apiVersion: v1
|
||||
kind: ValueReplacer
|
||||
metadata:
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example.com/image:version
|
||||
stringMatch: Deployment
|
||||
replace: StatefulSet
|
||||
`
|
||||
if !assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(dir, "filter.yaml"), []byte(f), 0600)) {
|
||||
return
|
||||
}
|
||||
|
||||
instance := RunFns{
|
||||
Path: dir,
|
||||
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||
// parse the filter from the input
|
||||
filter := yaml.YFilter{}
|
||||
b := &bytes.Buffer{}
|
||||
e := yaml.NewEncoder(b)
|
||||
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||
t.FailNow()
|
||||
}
|
||||
e.Close()
|
||||
d := yaml.NewDecoder(b)
|
||||
if !assert.NoError(t, d.Decode(&filter)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
return filters.Modifier{
|
||||
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||
}
|
||||
},
|
||||
}
|
||||
if !assert.NoError(t, instance.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
b, err := ioutil.ReadFile(
|
||||
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Contains(t, string(b), "kind: StatefulSet")
|
||||
}
|
||||
|
||||
func TestCmd_Execute_APIs(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||
return
|
||||
}
|
||||
|
||||
// write a test filter
|
||||
f := `apiVersion: v1
|
||||
kind: ValueReplacer
|
||||
metadata:
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example.com/image:version
|
||||
stringMatch: Deployment
|
||||
replace: StatefulSet
|
||||
`
|
||||
tmpF, err := ioutil.TempFile("", "filter*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
os.RemoveAll(tmpF.Name())
|
||||
if !assert.NoError(t, ioutil.WriteFile(tmpF.Name(), []byte(f), 0600)) {
|
||||
return
|
||||
}
|
||||
|
||||
instance := RunFns{
|
||||
FunctionPaths: []string{tmpF.Name()},
|
||||
Path: dir,
|
||||
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||
// parse the filter from the input
|
||||
filter := yaml.YFilter{}
|
||||
b := &bytes.Buffer{}
|
||||
e := yaml.NewEncoder(b)
|
||||
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||
t.FailNow()
|
||||
}
|
||||
e.Close()
|
||||
d := yaml.NewDecoder(b)
|
||||
if !assert.NoError(t, d.Decode(&filter)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
return filters.Modifier{
|
||||
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||
}
|
||||
},
|
||||
}
|
||||
err = instance.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
b, err := ioutil.ReadFile(
|
||||
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Contains(t, string(b), "kind: StatefulSet")
|
||||
}
|
||||
|
||||
func TestCmd_Execute_Stdout(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
ds, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "test", "testdata"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, copyutil.CopyDir(ds, dir)) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.NoError(t, os.Chdir(filepath.Dir(dir))) {
|
||||
return
|
||||
}
|
||||
|
||||
// write a test filter
|
||||
f := `apiVersion: v1
|
||||
kind: ValueReplacer
|
||||
metadata:
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example.com/image:version
|
||||
stringMatch: Deployment
|
||||
replace: StatefulSet
|
||||
`
|
||||
if !assert.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(dir, "filter.yaml"), []byte(f), 0600)) {
|
||||
return
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
instance := RunFns{
|
||||
Output: out,
|
||||
Path: dir,
|
||||
containerFilterProvider: func(s, _ string, node *yaml.RNode) kio.Filter {
|
||||
// parse the filter from the input
|
||||
filter := yaml.YFilter{}
|
||||
b := &bytes.Buffer{}
|
||||
e := yaml.NewEncoder(b)
|
||||
if !assert.NoError(t, e.Encode(node.YNode())) {
|
||||
t.FailNow()
|
||||
}
|
||||
e.Close()
|
||||
d := yaml.NewDecoder(b)
|
||||
if !assert.NoError(t, d.Decode(&filter)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
return filters.Modifier{
|
||||
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||
}
|
||||
},
|
||||
}
|
||||
if !assert.NoError(t, instance.Execute()) {
|
||||
return
|
||||
}
|
||||
b, err := ioutil.ReadFile(
|
||||
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.NotContains(t, string(b), "kind: StatefulSet")
|
||||
assert.Contains(t, out.String(), "kind: StatefulSet")
|
||||
}
|
||||
10
kyaml/runfn/test/testdata/java/java-configmap.resource.yaml
vendored
Normal file
10
kyaml/runfn/test/testdata/java/java-configmap.resource.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: app-config
|
||||
labels:
|
||||
app.kubernetes.io/component: undefined
|
||||
app.kubernetes.io/instance: undefined
|
||||
data: {}
|
||||
36
kyaml/runfn/test/testdata/java/java-deployment.resource.yaml
vendored
Normal file
36
kyaml/runfn/test/testdata/java/java-deployment.resource.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: app
|
||||
labels:
|
||||
app: java
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: java
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: java
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: app
|
||||
image: gcr.io/project/app:version
|
||||
command:
|
||||
- java
|
||||
- -jar
|
||||
- /app.jar
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: app-config
|
||||
env:
|
||||
- name: JAVA_OPTS
|
||||
value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
|
||||
-Djava.security.egd=file:/dev/./urandom
|
||||
imagePullPolicy: Always
|
||||
minReadySeconds: 5
|
||||
15
kyaml/runfn/test/testdata/java/java-service.resource.yaml
vendored
Normal file
15
kyaml/runfn/test/testdata/java/java-service.resource.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: app
|
||||
labels:
|
||||
app: java
|
||||
spec:
|
||||
selector:
|
||||
app: java
|
||||
ports:
|
||||
- name: "8080"
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
Reference in New Issue
Block a user