mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
606 lines
14 KiB
Go
606 lines
14 KiB
Go
// Copyright 2019 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package framework_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"path/filepath"
|
|
"text/template"
|
|
|
|
"github.com/spf13/pflag"
|
|
"sigs.k8s.io/kustomize/kyaml/fn/framework"
|
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
|
)
|
|
|
|
const service = "Service"
|
|
|
|
// ExampleResourceList_modify implements a function that sets an annotation on each resource.
|
|
// The annotation value is configured via a flag value parsed from ResourceList.functionConfig.data
|
|
func ExampleResourceList_modify() {
|
|
// for testing purposes only -- normally read from stdin when Executing
|
|
input := bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
- apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: foo
|
|
# functionConfig is parsed into flags by framework.Command
|
|
functionConfig:
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
data:
|
|
value: baz
|
|
`)
|
|
|
|
// configure the annotation value using a flag parsed from
|
|
// ResourceList.functionConfig.data.value
|
|
fs := pflag.NewFlagSet("tests", pflag.ContinueOnError)
|
|
value := fs.String("value", "", "annotation value")
|
|
rl := framework.ResourceList{
|
|
Flags: fs,
|
|
Reader: input, // for testing only
|
|
}
|
|
if err := rl.Read(); err != nil {
|
|
panic(err)
|
|
}
|
|
for i := range rl.Items {
|
|
// set the annotation on each resource item
|
|
if err := rl.Items[i].PipeE(yaml.SetAnnotation("value", *value)); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
if err := rl.Write(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// 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
|
|
}
|
|
|
|
// ExampleCommand_modify implements a function that sets an annotation on each resource.
|
|
// The annotation value is configured via a flag value parsed from
|
|
// ResourceList.functionConfig.data
|
|
func ExampleCommand_modify() {
|
|
// configure the annotation value using a flag parsed from
|
|
// ResourceList.functionConfig.data.value
|
|
resourceList := framework.ResourceList{}
|
|
var value string
|
|
cmd := framework.Command(&resourceList, func() error {
|
|
for i := range resourceList.Items {
|
|
// set the annotation on each resource item
|
|
err := resourceList.Items[i].PipeE(yaml.SetAnnotation("value", value))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
cmd.Flags().StringVar(&value, "value", "", "annotation value")
|
|
|
|
// for testing purposes only -- normally read from stdin when Executing
|
|
cmd.SetIn(bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
- apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: foo
|
|
# functionConfig is parsed into flags by framework.Command
|
|
functionConfig:
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
data:
|
|
value: baz
|
|
`))
|
|
// run the command
|
|
if err := cmd.Execute(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// 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
|
|
}
|
|
|
|
// ExampleCommand_generateReplace generates a resource from a functionConfig.
|
|
// If the resource already exist s, it replaces the resource with a new copy.
|
|
func ExampleCommand_generateReplace() {
|
|
// 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{}
|
|
|
|
// function implementation -- generate a Service resource
|
|
resourceList := &framework.ResourceList{FunctionConfig: functionConfig}
|
|
cmd := framework.Command(resourceList, func() error {
|
|
var newNodes []*yaml.RNode
|
|
for i := range resourceList.Items {
|
|
meta, err := resourceList.Items[i].GetMeta()
|
|
if err != nil {
|
|
return 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, resourceList.Items[i])
|
|
}
|
|
|
|
// generate the resource
|
|
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: %s
|
|
`, functionConfig.Spec.Name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newNodes = append(newNodes, n)
|
|
resourceList.Items = newNodes
|
|
return nil
|
|
})
|
|
|
|
// for testing purposes only -- normally read from stdin when Executing
|
|
cmd.SetIn(bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
# functionConfig is parsed into flags by framework.Command
|
|
functionConfig:
|
|
apiVersion: example.com/v1alpha1
|
|
kind: ExampleServiceGenerator
|
|
spec:
|
|
name: bar
|
|
`))
|
|
|
|
// run the command
|
|
if err := cmd.Execute(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// 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
|
|
}
|
|
|
|
// ExampleResourceList_generateReplace generates a resource from a functionConfig.
|
|
// If the resource already exist s, it replaces the resource with a new copy.
|
|
func ExampleResourceList_generateReplace() {
|
|
input := bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
# functionConfig is parsed into flags by framework.Command
|
|
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{}
|
|
|
|
rl := framework.ResourceList{
|
|
FunctionConfig: functionConfig,
|
|
Reader: input, // for testing only
|
|
}
|
|
if err := rl.Read(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// remove the last generated resource
|
|
var newNodes []*yaml.RNode
|
|
for i := range rl.Items {
|
|
meta, err := rl.Items[i].GetMeta()
|
|
if err != nil {
|
|
panic(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, rl.Items[i])
|
|
}
|
|
rl.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 {
|
|
panic(err)
|
|
}
|
|
rl.Items = append(rl.Items, n)
|
|
|
|
if err := rl.Write(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// 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
|
|
}
|
|
|
|
// ExampleCommand_generateUpdate generates a resource, updating the previously generated
|
|
// copy rather than replacing it.
|
|
//
|
|
// Note: This will keep manual edits to the previously generated copy.
|
|
func ExampleCommand_generateUpdate() {
|
|
// function API definition which will be parsed from the ResourceList.functionConfig
|
|
// read from stdin
|
|
type Spec struct {
|
|
Name string `yaml:"name,omitempty"`
|
|
Annotations map[string]string `yaml:"annotations,omitempty"`
|
|
}
|
|
type ExampleServiceGenerator struct {
|
|
Spec Spec `yaml:"spec,omitempty"`
|
|
}
|
|
functionConfig := &ExampleServiceGenerator{}
|
|
|
|
// function implementation -- generate or update a Service resource
|
|
resourceList := &framework.ResourceList{FunctionConfig: functionConfig}
|
|
cmd := framework.Command(resourceList, func() error {
|
|
var found bool
|
|
for i := range resourceList.Items {
|
|
meta, err := resourceList.Items[i].GetMeta()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// something we already generated, reconcile it to make sure it matches what
|
|
// is specified by the functionConfig
|
|
if meta.Name == functionConfig.Spec.Name &&
|
|
meta.Kind == service &&
|
|
meta.APIVersion == "v1" {
|
|
// set some values
|
|
for k, v := range functionConfig.Spec.Annotations {
|
|
err := resourceList.Items[i].PipeE(yaml.SetAnnotation(k, v))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
return nil
|
|
}
|
|
|
|
// generate the resource if not found
|
|
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: %s
|
|
`, functionConfig.Spec.Name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for k, v := range functionConfig.Spec.Annotations {
|
|
err := n.PipeE(yaml.SetAnnotation(k, v))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
resourceList.Items = append(resourceList.Items, n)
|
|
|
|
return nil
|
|
})
|
|
|
|
// for testing purposes only -- normally read from stdin when Executing
|
|
cmd.SetIn(bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
- apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: bar
|
|
# functionConfig is parsed into flags by framework.Command
|
|
functionConfig:
|
|
apiVersion: example.com/v1alpha1
|
|
kind: ExampleServiceGenerator
|
|
spec:
|
|
name: bar
|
|
annotations:
|
|
a: b
|
|
`))
|
|
|
|
// run the command
|
|
if err := cmd.Execute(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// - apiVersion: v1
|
|
// kind: Service
|
|
// metadata:
|
|
// name: bar
|
|
// annotations:
|
|
// a: 'b'
|
|
// functionConfig:
|
|
// apiVersion: example.com/v1alpha1
|
|
// kind: ExampleServiceGenerator
|
|
// spec:
|
|
// name: bar
|
|
// annotations:
|
|
// a: b
|
|
}
|
|
|
|
// ExampleCommand_validate validates that all Deployment resources have the replicas field set.
|
|
// If any Deployments do not contain spec.replicas, then the function will return results
|
|
// which will be set on ResourceList.results
|
|
func ExampleCommand_validate() {
|
|
resourceList := &framework.ResourceList{}
|
|
cmd := framework.Command(resourceList, func() error {
|
|
// validation results
|
|
var validationResults []framework.Item
|
|
|
|
// validate that each Deployment resource has spec.replicas set
|
|
for i := range resourceList.Items {
|
|
// only check Deployment resources
|
|
meta, err := resourceList.Items[i].GetMeta()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if meta.Kind != "Deployment" {
|
|
continue
|
|
}
|
|
|
|
// lookup replicas field
|
|
r, err := resourceList.Items[i].Pipe(yaml.Lookup("spec", "replicas"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check replicas not specified
|
|
if r != nil {
|
|
continue
|
|
}
|
|
validationResults = append(validationResults, framework.Item{
|
|
Severity: framework.Error,
|
|
Message: "missing replicas",
|
|
ResourceRef: meta,
|
|
Field: framework.Field{
|
|
Path: "spec.field",
|
|
SuggestedValue: "1",
|
|
},
|
|
})
|
|
}
|
|
|
|
if len(validationResults) > 0 {
|
|
resourceList.Result = &framework.Result{
|
|
Name: "replicas-validator",
|
|
Items: validationResults,
|
|
}
|
|
}
|
|
|
|
return resourceList.Result
|
|
})
|
|
|
|
// for testing purposes only -- normally read from stdin when Executing
|
|
cmd.SetIn(bytes.NewBufferString(`
|
|
apiVersion: config.kubernetes.io/v1alpha1
|
|
kind: ResourceList
|
|
# items are provided as nodes
|
|
items:
|
|
- apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
`))
|
|
|
|
// run the command
|
|
if err := cmd.Execute(); err != nil {
|
|
// normally exit 1 here
|
|
}
|
|
|
|
// Output:
|
|
// apiVersion: config.kubernetes.io/v1alpha1
|
|
// kind: ResourceList
|
|
// items:
|
|
// - apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// results:
|
|
// name: replicas-validator
|
|
// items:
|
|
// - message: missing replicas
|
|
// severity: error
|
|
// resourceRef:
|
|
// apiVersion: apps/v1
|
|
// kind: Deployment
|
|
// metadata:
|
|
// name: foo
|
|
// field:
|
|
// path: spec.field
|
|
// suggestedValue: "1"
|
|
}
|
|
|
|
// ExampleTemplateCommand provides an example for using the TemplateCommand
|
|
func ExampleTemplateCommand() {
|
|
// create the template
|
|
cmd := framework.TemplateCommand{
|
|
// Template input
|
|
API: &struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
}{},
|
|
// Template
|
|
Template: template.Must(template.New("example").Parse(`
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: foo
|
|
namespace: default
|
|
annotations:
|
|
{{ .Key }}: {{ .Value }}
|
|
`)),
|
|
}.GetCommand()
|
|
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "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
|
|
}
|
|
|
|
// ExampleTemplateCommand_files provides an example for using the TemplateCommand
|
|
func ExampleTemplateCommand_files() {
|
|
// create the template
|
|
cmd := framework.TemplateCommand{
|
|
// Template input
|
|
API: &struct {
|
|
Key string `json:"key" yaml:"key"`
|
|
Value string `json:"value" yaml:"value"`
|
|
}{},
|
|
// Template
|
|
TemplatesFiles: []string{filepath.Join("testdata", "templatefiles", "deployment.template")},
|
|
}.GetCommand()
|
|
|
|
cmd.SetArgs([]string{filepath.Join("testdata", "templatefiles", "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
|
|
}
|