mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Improved fn framework support for patching
- Generate patches with a func - Generate patches for containers
This commit is contained in:
@@ -13,12 +13,18 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PatchTemplateContainers executes t as a template and patches each container in each resource
|
// PatchContainersWithString executes t as a template and patches each container in each resource
|
||||||
// with the result.
|
// with the result.
|
||||||
func PatchTemplateContainers(resources []*yaml.RNode, t string, input interface{}, containers ...string) error {
|
func PatchContainersWithString(resources []*yaml.RNode, t string, input interface{}, containers ...string) error {
|
||||||
resourcePatch := template.Must(template.New("containers").Parse(t))
|
resourcePatch := template.Must(template.New("containers").Parse(t))
|
||||||
|
return PatchContainersWithTemplate(resources, resourcePatch, input, containers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PatchContainersWithTemplate executes t and patches each container in each resource
|
||||||
|
// with the result.
|
||||||
|
func PatchContainersWithTemplate(resources []*yaml.RNode, t *template.Template, input interface{}, containers ...string) error {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
if err := resourcePatch.Execute(&b, input); err != nil {
|
if err := t.Execute(&b, input); err != nil {
|
||||||
return errors.Wrap(err)
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
patch, err := yaml.Parse(b.String())
|
patch, err := yaml.Parse(b.String())
|
||||||
|
|||||||
@@ -893,8 +893,8 @@ metadata:
|
|||||||
// config.kubernetes.io/index: '1'
|
// config.kubernetes.io/index: '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExamplePatchTemplateContainers_names patches all containers.
|
// ExamplePatchContainersWithString patches all containers.
|
||||||
func ExamplePatchTemplateContainers() {
|
func ExamplePatchContainersWithString() {
|
||||||
resources, err := kio.ParseAll(`
|
resources, err := kio.ParseAll(`
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -943,7 +943,7 @@ spec:
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := struct{ Value string }{Value: "new-value"}
|
input := struct{ Value string }{Value: "new-value"}
|
||||||
err = framework.PatchTemplateContainers(resources, `
|
err = framework.PatchContainersWithString(resources, `
|
||||||
env:
|
env:
|
||||||
KEY: {{ .Value }}
|
KEY: {{ .Value }}
|
||||||
`, input)
|
`, input)
|
||||||
@@ -1006,9 +1006,9 @@ env:
|
|||||||
// <nil>
|
// <nil>
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExamplePatchTemplateContainers_names patches containers matching
|
// PatchTemplateContainersWithString patches containers matching
|
||||||
// a specific name.
|
// a specific name.
|
||||||
func ExamplePatchTemplateContainers_names() {
|
func ExamplePatchContainersWithString_names() {
|
||||||
resources, err := kio.ParseAll(`
|
resources, err := kio.ParseAll(`
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -1057,7 +1057,7 @@ spec:
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := struct{ Value string }{Value: "new-value"}
|
input := struct{ Value string }{Value: "new-value"}
|
||||||
err = framework.PatchTemplateContainers(resources, `
|
err = framework.PatchContainersWithString(resources, `
|
||||||
env:
|
env:
|
||||||
KEY: {{ .Value }}
|
KEY: {{ .Value }}
|
||||||
`, input, "foo")
|
`, input, "foo")
|
||||||
|
|||||||
@@ -286,6 +286,16 @@ type TemplateCommand struct {
|
|||||||
// PatchTemplates is a list of templates to render into Patches and apply.
|
// PatchTemplates is a list of templates to render into Patches and apply.
|
||||||
PatchTemplates []PatchTemplate
|
PatchTemplates []PatchTemplate
|
||||||
|
|
||||||
|
// PatchTemplateFn returns a list of templates to render into Patches and apply.
|
||||||
|
// PatchTemplateFn is called after the ResourceList has been parsed.
|
||||||
|
PatchTemplatesFn func(*ResourceList) ([]PatchTemplate, error)
|
||||||
|
|
||||||
|
// PatchContainerTemplates applies patches to matching container fields
|
||||||
|
PatchContainerTemplates []ContainerPatchTemplate
|
||||||
|
|
||||||
|
// PatchContainerTemplates returns a list of PatchContainerTemplates
|
||||||
|
PatchContainerTemplatesFn func(*ResourceList) ([]ContainerPatchTemplate, error)
|
||||||
|
|
||||||
// TemplateFiles list of templates to read from disk which are appended
|
// TemplateFiles list of templates to read from disk which are appended
|
||||||
// to Templates.
|
// to Templates.
|
||||||
TemplatesFiles []string
|
TemplatesFiles []string
|
||||||
@@ -301,6 +311,13 @@ type TemplateCommand struct {
|
|||||||
PostProcess func(*ResourceList) error
|
PostProcess func(*ResourceList) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerPatchTemplate defines a patch to be applied to containers
|
||||||
|
type ContainerPatchTemplate struct {
|
||||||
|
PatchTemplate
|
||||||
|
|
||||||
|
ContainerNames []string
|
||||||
|
}
|
||||||
|
|
||||||
func (tc TemplateCommand) doTemplate(t *template.Template, rl *ResourceList) error {
|
func (tc TemplateCommand) doTemplate(t *template.Template, rl *ResourceList) error {
|
||||||
// invoke the template
|
// invoke the template
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
@@ -372,12 +389,39 @@ func (tc TemplateCommand) GetCommand() *cobra.Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.PatchTemplatesFn != nil {
|
||||||
|
pt, err := tc.PatchTemplatesFn(&rl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tc.PatchTemplates = append(tc.PatchTemplates, pt...)
|
||||||
|
}
|
||||||
|
|
||||||
for i := range tc.PatchTemplates {
|
for i := range tc.PatchTemplates {
|
||||||
if err := tc.PatchTemplates[i].Apply(&rl); err != nil {
|
if err := tc.PatchTemplates[i].Apply(&rl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tc.PatchContainerTemplatesFn != nil {
|
||||||
|
ct, err := tc.PatchContainerTemplatesFn(&rl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tc.PatchContainerTemplates = append(tc.PatchContainerTemplates, ct...)
|
||||||
|
}
|
||||||
|
for i := range tc.PatchContainerTemplates {
|
||||||
|
ct := tc.PatchContainerTemplates[i]
|
||||||
|
matches, err := ct.Selector.GetMatches(&rl)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = PatchContainersWithTemplate(matches, ct.Template, rl.FunctionConfig, ct.ContainerNames...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if tc.MergeResources {
|
if tc.MergeResources {
|
||||||
rl.Items, err = filters.MergeFilter{}.Filter(rl.Items)
|
rl.Items, err = filters.MergeFilter{}.Filter(rl.Items)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -162,3 +163,204 @@ metadata:
|
|||||||
a: 'b'
|
a: 'b'
|
||||||
`), strings.TrimSpace(out.String()))
|
`), strings.TrimSpace(out.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommand_PatchTemplateFn(t *testing.T) {
|
||||||
|
// TODO: make this test pass on windows -- currently failure seems spurious
|
||||||
|
testutil.SkipWindows(t)
|
||||||
|
|
||||||
|
type api = struct {
|
||||||
|
Spec struct {
|
||||||
|
A string `json:"a" yaml:"a"`
|
||||||
|
} `json:"spec" yaml:"spec"`
|
||||||
|
}
|
||||||
|
var config api
|
||||||
|
|
||||||
|
cmd := framework.TemplateCommand{
|
||||||
|
API: &config,
|
||||||
|
PatchTemplatesFn: func(_ *framework.ResourceList) ([]framework.PatchTemplate, error) {
|
||||||
|
return []framework.PatchTemplate{{
|
||||||
|
Selector: &framework.Selector{Names: []string{config.Spec.A}},
|
||||||
|
Template: template.Must(template.New("test").Parse(`
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baz: buz
|
||||||
|
`)),
|
||||||
|
}}, nil
|
||||||
|
},
|
||||||
|
}.GetCommand()
|
||||||
|
|
||||||
|
cmd.SetIn(bytes.NewBufferString(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar1
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar1
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar2
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Example
|
||||||
|
spec:
|
||||||
|
a: "bar1"
|
||||||
|
`))
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.SetOut(&out)
|
||||||
|
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
|
||||||
|
require.Equal(t, strings.TrimSpace(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar1
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar1
|
||||||
|
baz: buz
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar2
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Example
|
||||||
|
spec:
|
||||||
|
a: "bar1"
|
||||||
|
`), strings.TrimSpace(out.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommand_PatchContainerTemplatesFn(t *testing.T) {
|
||||||
|
// TODO: make this test pass on windows -- currently failure seems spurious
|
||||||
|
testutil.SkipWindows(t)
|
||||||
|
|
||||||
|
type api = struct {
|
||||||
|
Spec struct {
|
||||||
|
A string `json:"a" yaml:"a"`
|
||||||
|
} `json:"spec" yaml:"spec"`
|
||||||
|
}
|
||||||
|
var config api
|
||||||
|
|
||||||
|
cmd := framework.TemplateCommand{
|
||||||
|
API: &config,
|
||||||
|
PatchContainerTemplatesFn: func(_ *framework.ResourceList) ([]framework.ContainerPatchTemplate, error) {
|
||||||
|
return []framework.ContainerPatchTemplate{{
|
||||||
|
PatchTemplate: framework.PatchTemplate{
|
||||||
|
Selector: &framework.Selector{Names: []string{config.Spec.A}},
|
||||||
|
Template: template.Must(template.New("test").Parse(`
|
||||||
|
env:
|
||||||
|
key: Foo
|
||||||
|
value: Bar
|
||||||
|
`))},
|
||||||
|
}}, nil
|
||||||
|
},
|
||||||
|
}.GetCommand()
|
||||||
|
|
||||||
|
cmd.SetIn(bytes.NewBufferString(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar1
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar1
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar2
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar2
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Example
|
||||||
|
spec:
|
||||||
|
a: "bar1"
|
||||||
|
`))
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.SetOut(&out)
|
||||||
|
|
||||||
|
require.NoError(t, cmd.Execute())
|
||||||
|
|
||||||
|
require.Equal(t, strings.TrimSpace(`
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar1
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar1
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: foo
|
||||||
|
env:
|
||||||
|
key: Foo
|
||||||
|
value: Bar
|
||||||
|
- name: bar
|
||||||
|
env:
|
||||||
|
key: Foo
|
||||||
|
value: Bar
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bar2
|
||||||
|
namespace: default
|
||||||
|
annotations:
|
||||||
|
foo: bar2
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: foo
|
||||||
|
- name: bar
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1alpha1
|
||||||
|
kind: Example
|
||||||
|
spec:
|
||||||
|
a: "bar1"
|
||||||
|
`), strings.TrimSpace(out.String()))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user