Documentation ¶
Overview ¶
Package framework contains a framework for writing functions in go. The function spec is defined at: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
Functions are executables which generate, modify, delete or validate Kubernetes resources. They are often used used to implement abstractions ("kind: JavaSpringBoot") and cross-cutting logic ("kind: SidecarInjector").
Functions may be run as standalone executables or invoked as part of an orchestrated pipeline (e.g. kustomize).
Example standalone usage ¶
Function template input:
# config.yaml -- this is the input to the template apiVersion: example.com/v1alpha1 kind: Example Key: a Value: b
Additional function inputs:
# patch.yaml -- this will be applied as a patch apiVersion: apps/v1 kind: Deployment metadata: name: foo namespace: default annotations: patch-key: patch-value
Manually run the function:
# build the function $ go build example-fn/ # run the function using the $ ./example-fn config.yaml patch.yaml
Go implementation
// example-fn/main.go func main() { // Define the template used to generate resources tc := framework.TemplateCommand{ Merge: true, // apply inputs as patches to the template output API: &struct { Key string `json:"key" yaml:"key"` Value string `json:"value" yaml:"value"` }{}, Template: template.Must(template.New("example").Parse(` apiVersion: apps/v1 kind: Deployment metadata: name: foo namespace: default annotations: {{ .Key }}: {{ .Value }} `))} // Run the command if err := tc.GetCommand().Execute(); err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err) os.Exit(1) } }
More Examples ¶
Example function implementation using framework.Command with flag input
var value string resourceList := &framework.ResourceList{} cmd := framework.Command(resourceList, func() error { for i := range resourceList.Items { // modify the items... } return nil }) cmd.Flags().StringVar(&value, "value", "", "annotation value") if err := cmd.Execute(); err != nil { return err }
Example function implementation using framework.ResourceList with a struct input
type Spec struct { Value string `yaml:"value,omitempty"` } type Example struct { Spec Spec `yaml:"spec,omitempty"` } functionConfig := &Example{} rl := framework.ResourceList{FunctionConfig: functionConfig} if err := rl.Read(); err != nil { return err } for i := range rl.Items { // modify the items... } if err := rl.Write(); err != nil { return err }
Architecture ¶
Functions modify a slice of resources (ResourceList.Items) which are read as input and written as output. The function itself may be configured through a functionConfig (ResourceList.FunctionConfig).
Example Function Input:
kind: ResourceList items: - kind: Deployment ... - kind: Service .... functionConfig: kind: Example spec: value: foo
The functionConfig may be specified declaratively and run with
config run DIR/
Declarative function declaration:
kind: Example metadata: annotations: # run the function by creating this container and providing this # Example as the functionConfig config.kubernetes.io/function: | image: image/containing/function:impl spec: value: foo
The framework takes care of serializing and deserializing the ResourceList.
Generated ResourceList.functionConfig -- ConfigMaps ¶
Functions may also be specified imperatively and run using:
config run DIR/ --image image/containing/function:impl -- value=foo
When run imperatively, a ConfigMap is generated for the functionConfig, and the command arguments are set as ConfigMap data entries.
kind: ConfigMap data: value: foo
To write a function that can be run imperatively on the commandline, have it take a ConfigMap as its functionConfig.
Mutator and Generator Functions ¶
Functions may add, delete or modify resources by modifying the ResourceList.Items slice.
Validator Functions ¶
A function may emit validation results by setting the ResourceList.Result ¶
Configuring Functions ¶
Functions may be configured through a functionConfig (i.e. a client side custom resource), or through flags (which the framework parses from a ConfigMap provided as input).
When using framework.Command, any flags registered on the cobra.Command will be parsed from the functionConfig input if they are defined as functionConfig.data entries.
When using framework.ResourceList, any flags set on the ResourceList.Flags will be parsed from the functionConfig input if they are defined as functionConfig.data entries.
Functions may also access environment variables set by the caller.
Building a container image for the function ¶
The go program may be built into a container and run as a function. The framework can be used to generate a Dockerfile to build the function container.
# create the ./Dockerfile for the container $ go run ./main.go gen ./ # build the function's container $ docker build . -t gcr.io/my-project/my-image:my-version
Index ¶
- func AddGenerateDockerfile(cmd *cobra.Command)
- func Command(resourceList *ResourceList, function Function) *cobra.Command
- func Filters(fltrs ...kio.Filter) func(*ResourceList) []kio.Filter
- func PatchContainers(resources []*yaml.RNode, patch *yaml.RNode, containers ...string) error
- func PatchContainersWithString(resources []*yaml.RNode, t string, input interface{}, containers ...string) error
- func PatchContainersWithTemplate(resources []*yaml.RNode, t *template.Template, input interface{}, ...) error
- type Applier
- type CPT
- type ContainerPatchTemplate
- type ContainerPatchTemplateFn
- type Defaulter
- type Field
- type File
- type Function
- type Item
- type PT
- type PatchTemplate
- type PatchTemplatesFn
- type ResourceList
- type Result
- type Selector
- type Severity
- type TemplateCommand
- type TemplatesFn
- type Validator
Examples ¶
- Command (GenerateReplace)
- Command (GenerateUpdate)
- Command (Modify)
- Command (Validate)
- PatchContainersWithString
- PatchContainersWithString (Names)
- ResourceList (GenerateReplace)
- ResourceList (Modify)
- Selector (TemplatizeAnnotations)
- Selector (TemplatizeKinds)
- TemplateCommand
- TemplateCommand (Files)
- TemplateCommand (Patch)
- TemplateCommand (Postprocess)
- TemplateCommand (Preprocess)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddGenerateDockerfile ¶
AddGenerateDockerfile adds a "gen" subcommand to create a Dockerfile for building the function as a container.
func Command ¶
func Command(resourceList *ResourceList, function Function) *cobra.Command
Command returns a cobra.Command to run a function.
The cobra.Command will use the provided ResourceList to Read() the input, run the provided function, and then Write() the output.
The returned cobra.Command will have a "gen" subcommand which can be used to generate a Dockerfile to build the function into a container image
go run main.go gen DIR/
Example (GenerateReplace) ¶
ExampleCommand_generateReplace generates a resource from a functionConfig. If the resource already exist s, it replaces the resource with a new copy.
package main import ( "bytes" "fmt" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) const service = "Service" func main() { // 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
Example (GenerateUpdate) ¶
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.
package main import ( "bytes" "fmt" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) const service = "Service" func main() { // 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
Example (Modify) ¶
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
package main import ( "bytes" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) func main() { // 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
Example (Validate) ¶
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
package main import ( "bytes" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) func main() { 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"
func Filters ¶ added in v0.10.2
func Filters(fltrs ...kio.Filter) func(*ResourceList) []kio.Filter
Filters returns a function which returns the provided Filters
func PatchContainers ¶ added in v0.10.1
PatchContainers applies patch to each container in each resource.
func PatchContainersWithString ¶ added in v0.10.1
func PatchContainersWithString(resources []*yaml.RNode, t string, input interface{}, containers ...string) error
PatchContainersWithString executes t as a template and patches each container in each resource with the result.
Example ¶
ExamplePatchContainersWithString patches all containers.
package main import ( "fmt" "log" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/kio" ) func main() { resources, err := kio.ParseAll(` 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 `) if err != nil { log.Fatal(err) } input := struct{ Value string }{Value: "new-value"} err = framework.PatchContainersWithString(resources, ` env: KEY: {{ .Value }} `, input) if err != nil { log.Fatal(err) } fmt.Println(kio.StringAll(resources)) }
Output: apiVersion: apps/v1 kind: Deployment metadata: name: foo spec: template: spec: containers: - name: foo image: a env: KEY: new-value - name: bar image: b env: KEY: 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: KEY: new-value - name: baz image: b env: KEY: new-value --- apiVersion: v1 kind: Service metadata: name: bar spec: selector: foo: bar <nil>
Example (Names) ¶
PatchTemplateContainersWithString patches containers matching a specific name.
package main import ( "fmt" "log" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/kio" ) func main() { resources, err := kio.ParseAll(` 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 `) if err != nil { log.Fatal(err) } input := struct{ Value string }{Value: "new-value"} err = framework.PatchContainersWithString(resources, ` env: KEY: {{ .Value }} `, input, "foo") if err != nil { log.Fatal(err) } fmt.Println(kio.StringAll(resources)) }
Output: apiVersion: apps/v1 kind: Deployment metadata: name: foo spec: template: spec: containers: - name: foo image: a env: KEY: 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: KEY: new-value - name: baz image: b --- apiVersion: v1 kind: Service metadata: name: bar spec: selector: foo: bar <nil>
Types ¶
type Applier ¶ added in v0.10.0
type Applier interface {
Apply(rl *ResourceList) error
}
Applier applies some modification to a ResourceList
type ContainerPatchTemplate ¶ added in v0.10.1
type ContainerPatchTemplate struct { PatchTemplate ContainerNames []string }
ContainerPatchTemplate defines a patch to be applied to containers
type ContainerPatchTemplateFn ¶ added in v0.10.2
type ContainerPatchTemplateFn func(*ResourceList) ([]ContainerPatchTemplate, error)
ContainerPatchTemplateFn returns a slice of ContainerPatchTemplate
func ContainerPatchTemplatesFromDir ¶ added in v0.10.2
func ContainerPatchTemplatesFromDir(templates ...CPT) ContainerPatchTemplateFn
ContainerPatchTemplatesFromDir applies a directory of templates as container patches.
type Defaulter ¶ added in v0.10.2
type Defaulter interface {
Default() error
}
Defaulter is implemented by APIs to have Default invoked
type Field ¶
type Field struct { // Path is the field path Path string `yaml:"path,omitempty"` // CurrentValue is the current field value CurrentValue string `yaml:"currentValue,omitempty"` // SuggestedValue is the suggested field value SuggestedValue string `yaml:"suggestedValue,omitempty"` }
Field references a field in a resource
type File ¶
type File struct { // Path is relative path to the file containing the resource Path string `yaml:"path,omitempty"` // Index is the index into the file containing the resource // (i.e. if there are multiple resources in a single file) Index int `yaml:"index,omitempty"` }
File references a file containing a resource
type Function ¶
type Function func() error
Function defines a function which mutates or validates a collection of configuration To create a structured validation result, return a Result as the error.
func WrapInResourceListIO ¶ added in v0.10.7
func WrapInResourceListIO(rl *ResourceList, function Function) Function
type Item ¶
type Item struct { // Message is a human readable message Message string `yaml:"message,omitempty"` // Severity is the severity of the Severity Severity `yaml:"severity,omitempty"` // ResourceRef is a reference to a resource ResourceRef yaml.ResourceMeta `yaml:"resourceRef,omitempty"` Field Field `yaml:"field,omitempty"` File File `yaml:"file,omitempty"` }
Item defines a validation result
type PatchTemplate ¶ added in v0.10.0
type PatchTemplate struct { // Template is a template to render into one or more patches. Template *template.Template // Selector targets the rendered patch to specific resources. Selector *Selector }
PatchTemplate applies a patch to a collection of Resources
func (PatchTemplate) Apply ¶ added in v0.10.0
func (p PatchTemplate) Apply(rl *ResourceList) error
Apply applies the patch to all matching resources in the list. The rl.FunctionConfig is provided to the template as input.
type PatchTemplatesFn ¶ added in v0.10.2
type PatchTemplatesFn func(*ResourceList) ([]PatchTemplate, error)
PatchTemplatesFn returns a slice of PatchTemplate
func PatchTemplatesFromDir ¶ added in v0.10.2
func PatchTemplatesFromDir(templates ...PT) PatchTemplatesFn
PatchTemplatesFromDir applies a directory of templates as patches.
type ResourceList ¶
type ResourceList struct { // FunctionConfig is the ResourceList.functionConfig input value. If FunctionConfig // is set to a value such as a struct or map[string]interface{} before ResourceList.Read() // is called, then the functionConfig will be parsed into that value. // If it is nil, the functionConfig will be set to a map[string]interface{} // before it is parsed. // // e.g. given the function input: // // kind: ResourceList // functionConfig: // kind: Example // spec: // foo: var // // FunctionConfig will contain the Example unmarshalled into its value. FunctionConfig interface{} // Items is the ResourceList.items input and output value. Items will be set by // ResourceList.Read() and written by ResourceList.Write(). // // e.g. given the function input: // // kind: ResourceList // items: // - kind: Deployment // ... // - kind: Service // ... // // Items will be a slice containing the Deployment and Service resources Items []*yaml.RNode // Result is ResourceList.result output value. Result will be written by // ResourceList.Write() Result *Result // DisableStandalone if set will not support standalone mode DisableStandalone bool // Args are the command args used for standalone mode Args []string // Flags are an optional set of flags to parse the ResourceList.functionConfig.data. // If non-nil, ResourceList.Read() will set the flag value for each flag name matching // a ResourceList.functionConfig.data map entry. // // e.g. given the function input: // // kind: ResourceList // functionConfig: // data: // foo: bar // a: b // // The flags --a=b and --foo=bar will be set in Flags. Flags *pflag.FlagSet // Reader is used to read the function input (ResourceList). // Defaults to os.Stdin. Reader io.Reader // Writer is used to write the function output (ResourceList) // Defaults to os.Stdout. Writer io.Writer // ReadWriter reads function input and writes function output // If set, it will take precedence over the Reader and Writer fields ReadWriter *kio.ByteReadWriter // NoPrintError if set will prevent the error from being printed NoPrintError bool Command *cobra.Command }
ResourceList reads the function input and writes the function output.
Adheres to the spec: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
Example (GenerateReplace) ¶
ExampleResourceList_generateReplace generates a resource from a functionConfig. If the resource already exist s, it replaces the resource with a new copy.
package main import ( "bytes" "fmt" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) const service = "Service" func main() { 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
Example (Modify) ¶
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
package main import ( "bytes" "github.com/spf13/pflag" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/yaml" ) func main() { // 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
type Result ¶
type Result struct { // Name is the name of the function creating the result Name string `yaml:"name,omitempty"` // Items are the individual results Items []Item `yaml:"items,omitempty"` }
Result defines a function result which will be set on the emitted ResourceList
type Selector ¶ added in v0.10.0
type Selector struct { // Names is a list of metadata.names to match. If empty match all names. // e.g. Names: ["foo", "bar"] matches if `metadata.name` is either "foo" or "bar". Names []string `json:"names" yaml:"names"` // Namespaces is a list of metadata.namespaces to match. If empty match all namespaces. // e.g. Namespaces: ["foo", "bar"] matches if `metadata.namespace` is either "foo" or "bar". Namespaces []string `json:"namespaces" yaml:"namespaces"` // Kinds is a list of kinds to match. If empty match all kinds. // e.g. Kinds: ["foo", "bar"] matches if `kind` is either "foo" or "bar". Kinds []string `json:"kinds" yaml:"kinds"` // APIVersions is a list of apiVersions to match. If empty apply match all apiVersions. // e.g. APIVersions: ["foo/v1", "bar/v1"] matches if `apiVersion` is either "foo/v1" or "bar/v1". APIVersions []string `json:"apiVersions" yaml:"apiVersions"` // Labels is a collection of labels to match. All labels must match exactly. // e.g. Labels: {"foo": "bar", "baz": "buz"] matches if BOTH "foo" and "baz" labels match. Labels map[string]string `json:"labels" yaml:"labels"` // Annotations is a collection of annotations to match. All annotations must match exactly. // e.g. Annotations: {"foo": "bar", "baz": "buz"] matches if BOTH "foo" and "baz" annotations match. Annotations map[string]string `json:"annotations" yaml:"annotations"` // Filter is an arbitrary filter function to match a resource. // Selector matches if the function returns true. Filter func(*yaml.RNode) bool // TemplatizeValues if set to true will parse the selector values as templates // and execute them with the functionConfig TemplatizeValues bool // contains filtered or unexported fields }
Selector matches resources. A resource matches if and only if ALL of the Selector fields match the resource. An empty Selector matches all resources.
Example (TemplatizeAnnotations) ¶
package main import ( "bytes" "os" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { type api struct { Value string `yaml:"vaue"` } rl := &framework.ResourceList{ FunctionConfig: &api{Value: "bar"}, Reader: bytes.NewBufferString(` 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 `), Writer: os.Stdout, } if err := rl.Read(); err != nil { panic(err) } var err error s := &framework.Selector{ TemplatizeValues: true, Annotations: map[string]string{"key": "{{ .Value }}"}, } rl.Items, err = s.GetMatches(rl) if err != nil { panic(err) } if err := rl.Write(); err != nil { panic(err) } }
Output: apiVersion: apps/v1 kind: Deployment metadata: name: bar namespace: default annotations: key: bar config.kubernetes.io/index: '1'
Example (TemplatizeKinds) ¶
package main import ( "bytes" "os" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { type api struct { KindName string `yaml:"kindName"` } rl := &framework.ResourceList{ FunctionConfig: &api{KindName: "Deployment"}, Reader: bytes.NewBufferString(` apiVersion: apps/v1 kind: Deployment metadata: name: foo namespace: default --- apiVersion: apps/v1 kind: StatefulSet metadata: name: bar namespace: default `), Writer: os.Stdout, } if err := rl.Read(); err != nil { panic(err) } var err error s := &framework.Selector{ TemplatizeValues: true, Kinds: []string{"{{ .KindName }}"}, } rl.Items, err = s.GetMatches(rl) if err != nil { panic(err) } if err := rl.Write(); err != nil { panic(err) } }
Output: apiVersion: apps/v1 kind: Deployment metadata: name: foo namespace: default annotations: config.kubernetes.io/index: '0'
func (*Selector) GetMatches ¶ added in v0.10.0
func (s *Selector) GetMatches(rl *ResourceList) ([]*yaml.RNode, error)
GetMatches returns them matching resources from rl
type TemplateCommand ¶ added in v0.10.0
type TemplateCommand struct { // API is the function API provide to the template as input API interface{} // Template is a go template to render and is appended to Templates. Template *template.Template // Templates is a list of templates to render. Templates []*template.Template // TemplatesFn returns a list of templates TemplatesFn func(*ResourceList) ([]*template.Template, error) // PatchTemplates is a list of templates to render into Patches and apply. 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 // to Templates. TemplatesFiles []string // MergeResources if set to true will apply input resources // as patches to the templates MergeResources bool // PreProcess is run on the ResourceList before the template is invoked PreProcess func(*ResourceList) error PreProcessFilters []kio.Filter // PostProcess is run on the ResourceList after the template is invoked PostProcess func(*ResourceList) error PostProcessFilters []kio.Filter }
TemplateCommand provides a cobra command to invoke a template
Example ¶
ExampleTemplateCommand provides an example for using the TemplateCommand
package main import ( "fmt" "path/filepath" "text/template" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { // 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
Example (Files) ¶
ExampleTemplateCommand_files provides an example for using the TemplateCommand
package main import ( "fmt" "path/filepath" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { // 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
Example (Patch) ¶
ExampleTemplateCommand_patch provides an example for using the TemplateCommand to create a function which patches resources.
package main import ( "fmt" "path/filepath" "text/template" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { // patch the foo resource only s := framework.Selector{Names: []string{"foo"}} cmd := framework.TemplateCommand{ API: &struct { Key string `json:"key" yaml:"key"` Value string `json:"value" yaml:"value"` }{}, Template: template.Must(template.New("example").Parse(` 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{{ Selector: &s, Template: template.Must(template.New("test").Parse(` metadata: annotations: patched: 'true' `)), }}, }.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 patched: 'true' --- apiVersion: apps/v1 kind: Deployment metadata: name: bar namespace: default annotations: a: b
Example (Postprocess) ¶
ExampleTemplateCommand_postprocess provides an example for using the TemplateCommand with PostProcess to modify the results.
package main import ( "fmt" "path/filepath" "text/template" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { config := &struct { Key string `json:"key" yaml:"key"` Value string `json:"value" yaml:"value"` }{} // create the template cmd := framework.TemplateCommand{ // Template input API: config, // Template Template: template.Must(template.New("example").Parse(` 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 }} `)), PostProcess: func(rl *framework.ResourceList) error { // trim the first resources rl.Items = rl.Items[1:] return nil }, }.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: bar namespace: default annotations: a: b
Example (Preprocess) ¶
ExampleTemplateCommand_preprocess provides an example for using the TemplateCommand with PreProcess to configure the template based on the input resources observed.
package main import ( "fmt" "path/filepath" "text/template" "sigs.k8s.io/kustomize/kyaml/fn/framework" ) func main() { config := &struct { Key string `json:"key" yaml:"key"` Value string `json:"value" yaml:"value"` Short bool }{} // create the template cmd := framework.TemplateCommand{ // Template input API: config, PreProcess: func(rl *framework.ResourceList) error { config.Short = len(rl.Items) < 3 return nil }, // Template Template: template.Must(template.New("example").Parse(` 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 }} `)), }.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 short: 'true' --- apiVersion: apps/v1 kind: Deployment metadata: name: bar namespace: default annotations: a: b short: 'true'
func (TemplateCommand) Execute ¶ added in v0.10.7
func (tc TemplateCommand) Execute(rl *ResourceList) error
func (TemplateCommand) GetCommand ¶ added in v0.10.0
func (tc TemplateCommand) GetCommand() *cobra.Command
GetCommand returns a new cobra command
type TemplatesFn ¶ added in v0.10.2
type TemplatesFn func(*ResourceList) ([]*template.Template, error)
func TemplatesFromDir ¶ added in v0.10.2
func TemplatesFromDir(dirs ...pkger.Dir) TemplatesFn
TemplatesFromDir applies a directory of templates as generated resources.
Directories ¶
Path | Synopsis |
---|---|
Package main contains an example using the the framework.
|
Package main contains an example using the the framework. |
Package frameworktestutil contains utilities for testing functions written using the framework.
|
Package frameworktestutil contains utilities for testing functions written using the framework. |