Documentation ¶
Overview ¶
Package command contains a builder for creating cobra.Commands based on configuration functions written using the kyaml function framework. The commands this package generates can be used as standalone executables or as part of a configuration management pipeline that complies with the Configuration Functions Specification (e.g. Kustomize generators or transformers): https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
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 $ ./example-fn config.yaml patch.yaml
Go implementation
// example-fn/main.go func main() { // Define the template used to generate resources p := framework.TemplateProcessor{ MergeResources: true, // apply inputs as patches to the template output TemplateData: new(struct { Key string `json:"key" yaml:"key"` Value string `json:"value" yaml:"value"` }), ResourceTemplates: []framework.ResourceTemplate{{ Templates: framework.StringTemplates(` apiVersion: apps/v1 kind: Deployment metadata: name: foo namespace: default annotations: {{ .Key }}: {{ .Value }} `)}}, } // Run the command if err := command.Build(p, command.StandaloneEnabled, true).Execute(); err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "%v\n", err) os.Exit(1) } }
Example function implementation using command.Build with flag input
func main() { var value string fn := func(rl *framework.ResourceList) error { for i := range rl.Items { // set the annotation on each resource item if err := rl.Items[i].PipeE(yaml.SetAnnotation("value", value)); err != nil { return err } } return nil } cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneEnabled, false) cmd.Flags().StringVar(&value, "value", "", "annotation value") if err := cmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) } }
Index ¶
Examples ¶
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 into a container image. The gen command takes one argument: the directory where the Dockerfile will be created.
go run main.go gen DIR/
func Build ¶
Build returns a cobra.Command to run a function.
The cobra.Command reads the input from STDIN, invokes the provided processor, and then writes the output to STDOUT.
The cobra.Command has a boolean `--stack` flag to print stack traces on failure.
By default, invoking the returned cobra.Command with arguments triggers "standalone" mode. In this mode: - The first argument must be the name of a file containing the FunctionConfig. - The remaining arguments must be filenames containing input resources for ResourceList.Items. - The argument "-", if present, will cause resources to be read from STDIN as well. The output will be a raw stream of resources (not wrapped in a List type). Example usage: `cat input1.yaml | go run main.go config.yaml input2.yaml input3.yaml -`
If mode is `StandaloneDisabled`, all arguments are ignored, and STDIN must contain a Kubernetes List type. To pass a function config in this mode, use a ResourceList as the input. The output will be of the same type as the input (e.g. ResourceList). Example usage: `cat resource_list.yaml | go run main.go`
By default, any error returned by the ResourceListProcessor will be printed to STDERR. Set noPrintError to true to suppress this.
Example (GenerateReplace) ¶
ExampleBuild_generateReplace generates a resource from a FunctionConfig. If the resource already exists, 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/fn/framework/command" "sigs.k8s.io/kustomize/kyaml/kio" "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 p := &framework.SimpleProcessor{ Config: functionConfig, Filter: kio.FilterFunc(func(items []*yaml.RNode) ([]*yaml.RNode, error) { 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]) } // generate the resource n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1 kind: Service metadata: name: %s `, functionConfig.Spec.Name)) if err != nil { return nil, err } newNodes = append(newNodes, n) return newNodes, nil }), } cmd := command.Build(p, command.StandaloneDisabled, false) // for testing purposes only -- normally read from stdin when Executing cmd.SetIn(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 `)) // run the command if err := cmd.Execute(); 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
Example (GenerateUpdate) ¶
ExampleBuild_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/fn/framework/command" "sigs.k8s.io/kustomize/kyaml/kio" "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 fn := func(items []*yaml.RNode) ([]*yaml.RNode, error) { var found bool for i := range items { meta, err := items[i].GetMeta() if err != nil { return nil, 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 := items[i].PipeE(yaml.SetAnnotation(k, v)) if err != nil { return nil, err } } found = true break } } if found { return items, 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 nil, err } for k, v := range functionConfig.Spec.Annotations { err := n.PipeE(yaml.SetAnnotation(k, v)) if err != nil { return nil, err } } items = append(items, n) return items, nil } p := &framework.SimpleProcessor{Config: functionConfig, Filter: kio.FilterFunc(fn)} cmd := command.Build(p, command.StandaloneDisabled, false) // for testing purposes only -- normally read from stdin when Executing cmd.SetIn(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: bar 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/v1 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) ¶
ExampleBuild_modify implements a function that sets an annotation on each resource. The annotation value is configured via ResourceList.FunctionConfig.
package main import ( "bytes" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/fn/framework/command" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/yaml" ) func main() { // create a struct matching the structure of ResourceList.FunctionConfig to hold its data var config struct { Data map[string]string `yaml:"data"` } fn := func(items []*yaml.RNode) ([]*yaml.RNode, error) { for i := range items { // set the annotation on each resource item err := items[i].PipeE(yaml.SetAnnotation("value", config.Data["value"])) if err != nil { return nil, err } } return items, nil } p := framework.SimpleProcessor{Filter: kio.FilterFunc(fn), Config: &config} cmd := command.Build(p, command.StandaloneDisabled, false) // for testing purposes only -- normally read from stdin when Executing cmd.SetIn(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 `)) // run the command if err := cmd.Execute(); 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
Example (Validate) ¶
ExampleBuild_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/fn/framework/command" "sigs.k8s.io/kustomize/kyaml/yaml" ) func main() { fn := func(rl *framework.ResourceList) error { // validation results var validationResults framework.Results // validate that each Deployment resource has spec.replicas set for i := range rl.Items { // only check Deployment resources meta, err := rl.Items[i].GetMeta() if err != nil { return err } if meta.Kind != "Deployment" { continue } // lookup replicas field r, err := rl.Items[i].Pipe(yaml.Lookup("spec", "replicas")) if err != nil { return err } // check replicas not specified if r != nil { continue } validationResults = append(validationResults, &framework.Result{ Severity: framework.Error, Message: "field is required", ResourceRef: &yaml.ResourceIdentifier{ TypeMeta: meta.TypeMeta, NameMeta: meta.ObjectMeta.NameMeta, }, Field: &framework.Field{ Path: "spec.replicas", ProposedValue: "1", }, }) } if len(validationResults) > 0 { rl.Results = validationResults } return rl.Results } cmd := command.Build(framework.ResourceListProcessorFunc(fn), command.StandaloneDisabled, true) // for testing purposes only -- normally read from stdin when Executing cmd.SetIn(bytes.NewBufferString(` apiVersion: config.kubernetes.io/v1 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/v1 kind: ResourceList items: - apiVersion: apps/v1 kind: Deployment metadata: name: foo results: - message: field is required severity: error resourceRef: apiVersion: apps/v1 kind: Deployment name: foo field: path: spec.replicas proposedValue: "1"