mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
1067 lines
24 KiB
Go
1067 lines
24 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package framework_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
|
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
|
|
"sigs.k8s.io/kustomize/kyaml/fn/framework/parser"
|
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
const service = "Service"
|
|
|
|
// ExampleSimpleProcessor_modify implements a function that sets an annotation on each resource.
|
|
func ExampleSimpleProcessor_modify() {
|
|
input := bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
- apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: foo
|
|
functionConfig:
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
data:
|
|
value: baz
|
|
`)
|
|
config := new(struct {
|
|
Data map[string]string `yaml:"data" json:"data"`
|
|
})
|
|
fn := func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
for i := range items {
|
|
// set the annotation on each resource item
|
|
if err := items[i].PipeE(yaml.SetAnnotation("value", config.Data["value"])); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
err := framework.Execute(framework.SimpleProcessor{Config: config, Filter: kio.FilterFunc(fn)}, &kio.ByteReadWriter{Reader: input})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// annotations:
|
|
// value: 'baz'
|
|
// - apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: foo
|
|
// annotations:
|
|
// value: 'baz'
|
|
// functionConfig:
|
|
// apiVersion: v1
|
|
// kind: ConfigMap
|
|
// data:
|
|
// value: baz
|
|
}
|
|
|
|
// ExampleSimpleProcessor_generateReplace generates a resource from a FunctionConfig.
|
|
// If the resource already exists, it replaces the resource with a new copy.
|
|
func ExampleSimpleProcessor_generateReplace() {
|
|
input := bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
functionConfig:
|
|
apiVersion: example.com/v1alpha1
|
|
kind: ExampleServiceGenerator
|
|
spec:
|
|
name: bar
|
|
`)
|
|
|
|
// function API definition which will be parsed from the ResourceList.FunctionConfig
|
|
// read from stdin
|
|
type Spec struct {
|
|
Name string `yaml:"name,omitempty"`
|
|
}
|
|
type ExampleServiceGenerator struct {
|
|
Spec Spec `yaml:"spec,omitempty"`
|
|
}
|
|
|
|
functionConfig := &ExampleServiceGenerator{}
|
|
|
|
fn := func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
// remove the last generated resource
|
|
var newNodes []*yaml.RNode
|
|
for i := range items {
|
|
meta, err := items[i].GetMeta()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// something we already generated, remove it from the list so we regenerate it
|
|
if meta.Name == functionConfig.Spec.Name &&
|
|
meta.Kind == service &&
|
|
meta.APIVersion == "v1" {
|
|
continue
|
|
}
|
|
newNodes = append(newNodes, items[i])
|
|
}
|
|
items = newNodes
|
|
|
|
// generate the resource again
|
|
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: %s
|
|
`, functionConfig.Spec.Name))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, n)
|
|
return items, nil
|
|
}
|
|
|
|
p := framework.SimpleProcessor{Config: functionConfig, Filter: kio.FilterFunc(fn)}
|
|
err := framework.Execute(p, &kio.ByteReadWriter{Reader: input})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// - apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: bar
|
|
// functionConfig:
|
|
// apiVersion: example.com/v1alpha1
|
|
// kind: ExampleServiceGenerator
|
|
// spec:
|
|
// name: bar
|
|
}
|
|
|
|
// ExampleTemplateProcessor provides an example for using the TemplateProcessor to add resources
|
|
// from templates defined inline
|
|
func ExampleTemplateProcessor_generate_inline() {
|
|
api := new(struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
})
|
|
// create the template
|
|
fn := framework.TemplateProcessor{
|
|
// Templates input
|
|
TemplateData: api,
|
|
// Templates
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
`)}},
|
|
}
|
|
cmd := command.Build(fn, command.StandaloneEnabled, false)
|
|
|
|
// mimic standalone mode: testdata/template/config.yaml will be parsed into `api`
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "example", "template", "config.yaml")})
|
|
if err := cmd.Execute(); err != nil {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
}
|
|
|
|
// ExampleTemplateProcessor_files provides an example for using the TemplateProcessor to add
|
|
// resources from templates defined in files.
|
|
func ExampleTemplateProcessor_generate_files() {
|
|
api := new(struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
})
|
|
// create the template
|
|
templateFn := framework.TemplateProcessor{
|
|
// Templates input
|
|
TemplateData: api,
|
|
// Templates
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
Templates: parser.TemplateFiles("testdata/example/templatefiles/deployment.template.yaml"),
|
|
}},
|
|
}
|
|
cmd := command.Build(templateFn, command.StandaloneEnabled, false)
|
|
// mimic standalone mode: testdata/template/config.yaml will be parsed into `api`
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "example", "templatefiles", "config.yaml")})
|
|
if err := cmd.Execute(); err != nil {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err)
|
|
}
|
|
|
|
// Output:
|
|
// # Copyright 2021 The Kubernetes Authors.
|
|
// # SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
}
|
|
|
|
// ExampleTemplateProcessor_preprocess provides an example for using the TemplateProcessor
|
|
// with PreProcess to configure the template based on the input resources observed.
|
|
func ExampleTemplateProcessor_preprocess() {
|
|
config := new(struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
Short bool
|
|
})
|
|
|
|
// create the template
|
|
fn := framework.TemplateProcessor{
|
|
// Templates input
|
|
TemplateData: config,
|
|
PreProcessFilters: []kio.Filter{
|
|
kio.FilterFunc(func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
config.Short = len(items) < 3
|
|
return items, nil
|
|
}),
|
|
},
|
|
// Templates
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
{{- if .Short }}
|
|
short: 'true'
|
|
{{- end }}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
{{- if .Short }}
|
|
short: 'true'
|
|
{{- end }}
|
|
`),
|
|
}},
|
|
}
|
|
|
|
cmd := command.Build(fn, command.StandaloneEnabled, false)
|
|
// mimic standalone mode: testdata/template/config.yaml will be parsed into `api`
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "example", "template", "config.yaml")})
|
|
if err := cmd.Execute(); err != nil {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
// short: 'true'
|
|
// ---
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
// short: 'true'
|
|
}
|
|
|
|
// ExampleTemplateProcessor_postprocess provides an example for using the TemplateProcessor
|
|
// with PostProcess to modify the results.
|
|
func ExampleTemplateProcessor_postprocess() {
|
|
config := new(struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
})
|
|
|
|
// create the template
|
|
fn := framework.TemplateProcessor{
|
|
// Templates input
|
|
TemplateData: config,
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
`),
|
|
}},
|
|
PostProcessFilters: []kio.Filter{
|
|
kio.FilterFunc(func(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
items = items[1:]
|
|
return items, nil
|
|
}),
|
|
},
|
|
}
|
|
cmd := command.Build(fn, command.StandaloneEnabled, false)
|
|
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "example", "template", "config.yaml")})
|
|
if err := cmd.Execute(); err != nil {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
}
|
|
|
|
// ExampleTemplateProcessor_patch provides an example for using the TemplateProcessor to
|
|
// create a function that patches resources.
|
|
func ExampleTemplateProcessor_patch() {
|
|
fn := framework.TemplateProcessor{
|
|
TemplateData: new(struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
}),
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
`),
|
|
}},
|
|
// PatchTemplates are applied to BOTH ResourceList input resources AND templated resources
|
|
PatchTemplates: []framework.PatchTemplate{
|
|
&framework.ResourcePatchTemplate{
|
|
// patch the foo resource only
|
|
Selector: &framework.Selector{Names: []string{"foo"}},
|
|
Templates: parser.TemplateStrings(`
|
|
metadata:
|
|
annotations:
|
|
patched: 'true'
|
|
`),
|
|
}},
|
|
}
|
|
cmd := command.Build(fn, command.StandaloneEnabled, false)
|
|
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "example", "template", "config.yaml")})
|
|
if err := cmd.Execute(); err != nil {
|
|
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
// patched: 'true'
|
|
// ---
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// namespace: default
|
|
// annotations:
|
|
// a: b
|
|
}
|
|
|
|
// ExampleTemplateProcessor_MergeResources provides an example for using the TemplateProcessor to
|
|
// create a function that treats incoming resources as potential patches
|
|
// for the resources the function generates itself.
|
|
func ExampleTemplateProcessor_MergeResources() {
|
|
p := framework.TemplateProcessor{
|
|
TemplateData: new(struct {
|
|
Name string `json:"name" yaml:"name"`
|
|
}),
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
// This is the generated resource the input will patch
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ .Name }}
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
app: foo
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
image: example.io/team/app
|
|
`),
|
|
}},
|
|
MergeResources: true,
|
|
}
|
|
|
|
// The second resource will be treated as a patch since its metadata matches the resource
|
|
// generated by ResourceTemplates and MergeResources is true.
|
|
rw := kio.ByteReadWriter{Reader: bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
items:
|
|
- kind: Deployment
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: custom
|
|
spec:
|
|
replicas: 6
|
|
selector:
|
|
app: custom
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
image: example.io/team/custom
|
|
- kind: Deployment
|
|
apiVersion: apps/v1
|
|
metadata:
|
|
name: mergeTest
|
|
spec:
|
|
replicas: 6
|
|
functionConfig:
|
|
name: mergeTest
|
|
`)}
|
|
if err := framework.Execute(p, &rw); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: mergeTest
|
|
// spec:
|
|
// replicas: 6
|
|
// selector:
|
|
// app: foo
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: app
|
|
// image: example.io/team/app
|
|
// - kind: Deployment
|
|
// apiVersion: apps/v1
|
|
// metadata:
|
|
// name: custom
|
|
// spec:
|
|
// replicas: 6
|
|
// selector:
|
|
// app: custom
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: app
|
|
// image: example.io/team/custom
|
|
// functionConfig:
|
|
// name: mergeTest
|
|
}
|
|
|
|
// ExampleSelector_templatizeKinds provides an example of using a template as a selector value,
|
|
// to dynamically match resources based on the functionConfig input. It also shows how Selector
|
|
// can be used with SimpleProcessor to implement a ResourceListProcessor the filters the input.
|
|
func ExampleSelector_templatizeKinds() {
|
|
type api struct {
|
|
KindName string `yaml:"kindName"`
|
|
}
|
|
rw := &kio.ByteReadWriter{
|
|
Reader: bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
functionConfig:
|
|
kindName: Deployment
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
- apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: bar
|
|
namespace: default
|
|
`),
|
|
}
|
|
config := &api{}
|
|
p := framework.SimpleProcessor{
|
|
Config: config,
|
|
Filter: &framework.Selector{
|
|
TemplateData: config,
|
|
Kinds: []string{"{{ .KindName }}"},
|
|
},
|
|
}
|
|
|
|
err := framework.Execute(p, rw)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// namespace: default
|
|
// functionConfig:
|
|
// kindName: Deployment
|
|
}
|
|
|
|
// ExampleSelector_templatizeKinds provides an example of using a template as a selector value,
|
|
// to dynamically match resources based on the functionConfig input. It also shows how Selector
|
|
// can be used with SimpleProcessor to implement a ResourceListProcessor the filters the input.
|
|
func ExampleSelector_templatizeAnnotations() {
|
|
type api struct {
|
|
Value string `yaml:"value"`
|
|
}
|
|
rw := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
functionConfig:
|
|
value: bar
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
key: foo
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
namespace: default
|
|
annotations:
|
|
key: bar
|
|
`)}
|
|
config := &api{}
|
|
p := framework.SimpleProcessor{
|
|
Config: config,
|
|
Filter: &framework.Selector{
|
|
TemplateData: config,
|
|
Annotations: map[string]string{"key": "{{ .Value }}"},
|
|
},
|
|
}
|
|
|
|
if err := framework.Execute(p, rw); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// namespace: default
|
|
// annotations:
|
|
// key: bar
|
|
// functionConfig:
|
|
// value: bar
|
|
}
|
|
|
|
// ExampleTemplateProcessor_container_patch provides an example for using TemplateProcessor to
|
|
// patch all of the containers in the input.
|
|
func ExampleTemplateProcessor_container_patch() {
|
|
input := `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: foo
|
|
image: a
|
|
- name: bar
|
|
image: b
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: foo
|
|
spec:
|
|
selector:
|
|
foo: bar
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: foo
|
|
image: a
|
|
- name: baz
|
|
image: b
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bar
|
|
spec:
|
|
selector:
|
|
foo: bar
|
|
`
|
|
p := framework.TemplateProcessor{
|
|
PatchTemplates: []framework.PatchTemplate{
|
|
&framework.ContainerPatchTemplate{
|
|
Templates: parser.TemplateStrings(`
|
|
env:
|
|
- name: KEY
|
|
value: {{ .Value }}
|
|
`),
|
|
TemplateData: struct{ Value string }{Value: "new-value"},
|
|
}},
|
|
}
|
|
err := framework.Execute(p, &kio.ByteReadWriter{Reader: bytes.NewBufferString(input)})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// spec:
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: foo
|
|
// image: a
|
|
// env:
|
|
// - name: KEY
|
|
// value: new-value
|
|
// - name: bar
|
|
// image: b
|
|
// env:
|
|
// - name: KEY
|
|
// value: new-value
|
|
// ---
|
|
// apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: foo
|
|
// spec:
|
|
// selector:
|
|
// foo: bar
|
|
// ---
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// spec:
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: foo
|
|
// image: a
|
|
// env:
|
|
// - name: KEY
|
|
// value: new-value
|
|
// - name: baz
|
|
// image: b
|
|
// env:
|
|
// - name: KEY
|
|
// value: new-value
|
|
// ---
|
|
// apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: bar
|
|
// spec:
|
|
// selector:
|
|
// foo: bar
|
|
}
|
|
|
|
// PatchTemplateContainersWithString patches containers matching
|
|
// a specific name.
|
|
func ExampleTemplateProcessor_container_patch_by_name() {
|
|
input := `
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: foo
|
|
image: a
|
|
env:
|
|
- name: EXISTING
|
|
value: variable
|
|
- name: bar
|
|
image: b
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: foo
|
|
spec:
|
|
selector:
|
|
foo: bar
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: bar
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: foo
|
|
image: a
|
|
- name: baz
|
|
image: b
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bar
|
|
spec:
|
|
selector:
|
|
foo: bar
|
|
`
|
|
p := framework.TemplateProcessor{
|
|
TemplateData: struct{ Value string }{Value: "new-value"},
|
|
PatchTemplates: []framework.PatchTemplate{
|
|
&framework.ContainerPatchTemplate{
|
|
// Only patch containers named "foo"
|
|
ContainerMatcher: framework.ContainerNameMatcher("foo"),
|
|
Templates: parser.TemplateStrings(`
|
|
env:
|
|
- name: KEY
|
|
value: {{ .Value }}
|
|
`),
|
|
}},
|
|
}
|
|
|
|
err := framework.Execute(p, &kio.ByteReadWriter{Reader: bytes.NewBufferString(input)})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// spec:
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: foo
|
|
// image: a
|
|
// env:
|
|
// - name: EXISTING
|
|
// value: variable
|
|
// - name: KEY
|
|
// value: new-value
|
|
// - name: bar
|
|
// image: b
|
|
// ---
|
|
// apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: foo
|
|
// spec:
|
|
// selector:
|
|
// foo: bar
|
|
// ---
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: bar
|
|
// spec:
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: foo
|
|
// image: a
|
|
// env:
|
|
// - name: KEY
|
|
// value: new-value
|
|
// - name: baz
|
|
// image: b
|
|
// ---
|
|
// apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: bar
|
|
// spec:
|
|
// selector:
|
|
// foo: bar
|
|
}
|
|
|
|
type v1alpha1JavaSpringBoot struct {
|
|
Metadata Metadata `yaml:"metadata" json:"metadata"`
|
|
Spec v1alpha1JavaSpringBootSpec `yaml:"spec" json:"spec"`
|
|
}
|
|
|
|
type Metadata struct {
|
|
Name string `yaml:"name" json:"name"`
|
|
}
|
|
|
|
type v1alpha1JavaSpringBootSpec struct {
|
|
Replicas int `yaml:"replicas" json:"replicas"`
|
|
Domain string `yaml:"domain" json:"domain"`
|
|
Image string `yaml:"image" json:"image"`
|
|
}
|
|
|
|
func (a v1alpha1JavaSpringBoot) Filter(items []*yaml.RNode) ([]*yaml.RNode, error) {
|
|
filter := framework.TemplateProcessor{
|
|
ResourceTemplates: []framework.ResourceTemplate{{
|
|
TemplateData: &a,
|
|
Templates: parser.TemplateStrings(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: {{ .Metadata.Name }}
|
|
selector:
|
|
app: {{ .Metadata.Name }}
|
|
spec:
|
|
replicas: {{ .Spec.Replicas }}
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: app
|
|
image: {{ .Spec.Image }}
|
|
{{ if .Spec.Domain }}
|
|
ports:
|
|
- containerPort: 80
|
|
name: http
|
|
{{ end }}
|
|
|
|
{{ if .Spec.Domain }}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: {{ .Metadata.Name }}-svc
|
|
spec:
|
|
selector:
|
|
app: {{ .Metadata.Name }}
|
|
ports:
|
|
- protocol: TCP
|
|
port: 80
|
|
targetPort: 80
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: {{ .Metadata.Name }}-ingress
|
|
spec:
|
|
tls:
|
|
- hosts:
|
|
- {{ .Spec.Domain }}
|
|
secretName: secret-tls
|
|
defaultBackend:
|
|
service:
|
|
name: {{ .Metadata.Name }}
|
|
port:
|
|
number: 80
|
|
{{ end }}
|
|
`),
|
|
}},
|
|
}
|
|
return filter.Filter(items)
|
|
}
|
|
|
|
func (a *v1alpha1JavaSpringBoot) Default() error {
|
|
if a.Spec.Replicas == 0 {
|
|
a.Spec.Replicas = 3
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *v1alpha1JavaSpringBoot) Validate() error {
|
|
var messages []string
|
|
if a.Metadata.Name == "" {
|
|
messages = append(messages, "name is required")
|
|
}
|
|
if a.Spec.Replicas > 10 {
|
|
messages = append(messages, "replicas must be less than 10")
|
|
}
|
|
if !strings.HasSuffix(a.Spec.Domain, "example.com") {
|
|
messages = append(messages, "domain must be a subdomain of example.com")
|
|
}
|
|
if strings.HasSuffix(a.Spec.Image, ":latest") {
|
|
messages = append(messages, "image should not have latest tag")
|
|
}
|
|
if len(messages) == 0 {
|
|
return nil
|
|
}
|
|
errMsg := fmt.Sprintf("JavaSpringBoot had %d errors:\n", len(messages))
|
|
for i, msg := range messages {
|
|
errMsg += fmt.Sprintf(" [%d] %s\n", i+1, msg)
|
|
}
|
|
return errors.Errorf(errMsg)
|
|
}
|
|
|
|
// ExampleVersionedAPIProcessor shows how to use the VersionedAPIProcessor and TemplateProcessor to
|
|
// build functions that implement complex multi-version APIs that require defaulting and validation.
|
|
func ExampleVersionedAPIProcessor() {
|
|
p := &framework.VersionedAPIProcessor{FilterProvider: framework.GVKFilterMap{
|
|
"JavaSpringBoot": {
|
|
"example.com/v1alpha1": &v1alpha1JavaSpringBoot{},
|
|
}}}
|
|
|
|
source := &kio.ByteReadWriter{Reader: bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1
|
|
kind: ResourceList
|
|
functionConfig:
|
|
apiVersion: example.com/v1alpha1
|
|
kind: JavaSpringBoot
|
|
metadata:
|
|
name: my-app
|
|
spec:
|
|
image: example.docker.com/team/app:1.0
|
|
domain: demo.example.com
|
|
`)}
|
|
if err := framework.Execute(p, source); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: my-app
|
|
// selector:
|
|
// app: my-app
|
|
// spec:
|
|
// replicas: 3
|
|
// template:
|
|
// spec:
|
|
// containers:
|
|
// - name: app
|
|
// image: example.docker.com/team/app:1.0
|
|
// ports:
|
|
// - containerPort: 80
|
|
// name: http
|
|
// - apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: my-app-svc
|
|
// spec:
|
|
// selector:
|
|
// app: my-app
|
|
// ports:
|
|
// - protocol: TCP
|
|
// port: 80
|
|
// targetPort: 80
|
|
// - apiVersion: networking.k8s.io/v1
|
|
// kind: Ingress
|
|
// metadata:
|
|
// name: my-app-ingress
|
|
// spec:
|
|
// tls:
|
|
// - hosts:
|
|
// - demo.example.com
|
|
// secretName: secret-tls
|
|
// defaultBackend:
|
|
// service:
|
|
// name: my-app
|
|
// port:
|
|
// number: 80
|
|
// functionConfig:
|
|
// apiVersion: example.com/v1alpha1
|
|
// kind: JavaSpringBoot
|
|
// metadata:
|
|
// name: my-app
|
|
// spec:
|
|
// image: example.docker.com/team/app:1.0
|
|
// domain: demo.example.com
|
|
}
|