Merge pull request #4113 from KnVerey/kyaml_container_patch_kinds

Enable fn framework container patches to work with more kinds
This commit is contained in:
Kubernetes Prow Robot
2021-08-17 11:31:13 -07:00
committed by GitHub
15 changed files with 338 additions and 7 deletions

View File

@@ -168,8 +168,7 @@ func (cpt ContainerPatchTemplate) apply(matches []*yaml.RNode) error {
}
for i := range matches {
// TODO(knverey): Make this work for more Kinds and expose the helper for doing so.
containers, err := matches[i].Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
containers, err := matches[i].Pipe(yaml.LookupFirstMatch(yaml.ConventionalContainerPaths))
if err != nil {
return errors.Wrap(err)
}

View File

@@ -5,6 +5,7 @@ package framework_test
import (
"bytes"
"regexp"
"strings"
"testing"
@@ -276,6 +277,57 @@ functionConfig:
`), strings.TrimSpace(out.String()))
}
func TestTemplateProcessor_ContainerPatchTemplates_MultipleWorkloadKinds(t *testing.T) {
type API struct {
Spec struct {
Key string `json:"key" yaml:"key"`
Value string `json:"value" yaml:"value"`
A string `json:"a" yaml:"a"`
}
}
config := &API{}
p := framework.TemplateProcessor{
TemplateData: config,
ResourceTemplates: []framework.ResourceTemplate{{
Templates: parser.TemplateFiles("testdata/template-processor/templates/container-sources"),
}},
PatchTemplates: []framework.PatchTemplate{
&framework.ContainerPatchTemplate{
Templates: parser.TemplateFiles("testdata/template-processor/container-patches"),
},
},
}
out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Writer: out, Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items: []
functionConfig:
spec:
key: Hello
value: World
a: bar
`)}
require.NoError(t, framework.Execute(p, rw))
resources, err := (&kio.ByteReader{Reader: out}).Read()
require.NoError(t, err)
envRegex := regexp.MustCompile(strings.TrimSpace(`
\s+ env:
\s+ - name: EXISTING
\s+ value: variable
\s+ - name: Hello
\s+ value: World
`))
require.Equal(t, 9, len(resources))
for i, r := range resources {
t.Run(r.GetKind(), func(t *testing.T) {
assert.Regexp(t, envRegex, resources[i].MustString())
})
}
}
func TestSimpleProcessor_Process_loads_config(t *testing.T) {
cfg := new(struct {
Value string `yaml:"value"`

View File

@@ -0,0 +1,19 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,23 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: hello
spec:
selector:
matchLabels:
name: hello
template:
metadata:
labels:
name: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,16 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,14 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,17 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: PodTemplate
metadata:
name: hello
labels:
tier: hello
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: ReplicationController
metadata:
name: hello
spec:
selector:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,24 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: hello
spec:
serviceName: "nginx"
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -6,13 +6,13 @@
package kubernetesapi
import (
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1212"
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1212"
)
const Info = "{title:Kubernetes,version:v1.21.2}"
var OpenAPIMustAsset = map[string]func(string)[]byte{
"v1212": v1212.MustAsset,
var OpenAPIMustAsset = map[string]func(string) []byte{
"v1212": v1212.MustAsset,
}
const DefaultOpenAPI = "v1212"

View File

@@ -1,6 +1,6 @@
// Code generated by go-bindata. (@generated) DO NOT EDIT.
//Package v1212 generated by go-bindata.// sources:
//Package v1212 generated by go-bindata.// sources:
// kubernetesapi/v1212/swagger.json
package v1212

View File

@@ -424,12 +424,46 @@ func Lookup(path ...string) PathGetter {
return PathGetter{Path: path}
}
// Lookup returns a PathGetter to lookup a field by its path and create it if it doesn't already
// LookupCreate returns a PathGetter to lookup a field by its path and create it if it doesn't already
// exist.
func LookupCreate(kind yaml.Kind, path ...string) PathGetter {
return PathGetter{Path: path, Create: kind}
}
// ConventionalContainerPaths is a list of paths at which containers typically appear in workload APIs.
// It is intended for use with LookupFirstMatch.
var ConventionalContainerPaths = [][]string{
// e.g. Deployment, ReplicaSet, DaemonSet, Job, StatefulSet
{"spec", "template", "spec", "containers"},
// e.g. CronJob
{"spec", "jobTemplate", "spec", "template", "spec", "containers"},
// e.g. Pod
{"spec", "containers"},
// e.g. PodTemplate
{"template", "spec", "containers"},
}
// LookupFirstMatch returns a Filter for locating a value that may exist at one of several possible paths.
// For example, it can be used with ConventionalContainerPaths to find the containers field in a standard workload resource.
// If more than one of the paths exists in the resource, the first will be returned. If none exist,
// nil will be returned. If an error is encountered during lookup, it will be returned.
func LookupFirstMatch(paths [][]string) Filter {
return FilterFunc(func(object *RNode) (*RNode, error) {
var result *RNode
var err error
for _, path := range paths {
result, err = object.Pipe(PathGetter{Path: path})
if err != nil {
return nil, errors.Wrap(err)
}
if result != nil {
return result, nil
}
}
return nil, nil
})
}
// PathGetter returns the RNode under Path.
type PathGetter struct {
Kind string `yaml:"kind,omitempty"`

View File

@@ -8,6 +8,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml"
. "sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -724,6 +725,72 @@ j: k
assert.Nil(t, rn)
}
func TestLookupFirstMatch(t *testing.T) {
tests := []struct {
name string
paths [][]string
wantPath []string
}{
{
name: "finds path that exists",
paths: [][]string{{"spec", "jobTemplate", "spec", "template", "spec", "containers"}},
wantPath: []string{"spec", "jobTemplate", "spec", "template", "spec", "containers"},
},
{
name: "chooses first path when multiple exist: containers example",
paths: ConventionalContainerPaths,
wantPath: []string{"spec", "template", "spec", "containers"},
},
{
name: "chooses first path when multiple exist: annotations example",
paths: [][]string{
{"metadata", "annotations", "example.kustomize.io/new"},
{"metadata", "annotations", "example.kustomize.io/deprecated"},
},
wantPath: []string{"metadata", "annotations", "example.kustomize.io/new"},
},
{
name: "returns nil when path does not exist",
paths: [][]string{
{"metadata", "annotations", "example.kustomize.io/does-not-exist"},
{"metadata", "annotations", "example.kustomize.io/also-not-exist"},
},
wantPath: nil,
},
}
for _, tt := range tests {
s := `
apiVersion: example.kustomize.io/v1
kind: Custom
metadata:
annotations:
example.kustomize.io/deprecated: foo
example.kustomize.io/new: foo
spec:
template:
spec:
containers:
- name: foo
jobTemplate:
spec:
template:
spec:
containers:
- name: foo
`
resource := MustParse(s)
t.Run(tt.name, func(t *testing.T) {
result, err := LookupFirstMatch(tt.paths).Filter(resource)
require.NoError(t, err)
if tt.wantPath != nil {
assert.Equal(t, tt.wantPath, result.FieldPath())
} else {
assert.Nil(t, result)
}
})
}
}
func TestFieldSetter(t *testing.T) {
// Change field
node, err := Parse(`