Documentation ¶
Overview ¶
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Code generated by "mdtogo"; DO NOT EDIT.
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var BlueprintGuide = `
{{% pageinfo color="warning" %}}
# Notice: Under Development
{{% /pageinfo %}}
*Reusable, customizable components can be built and shared as blueprints.*
## Overview
Blueprints are a **pattern for developing reusable, customizable
configuration**. Blueprints are typically published and consumed by
different teams.
{{% pageinfo color="primary" %}}
Because packages can be updated to new versions, blueprint consumers
can pull in changes to a blueprint after fetching it.
{{% /pageinfo %}}
### Example use cases for blueprints
- **Languages**: Java / Node / Ruby / Python / Golang application
- **Frameworks**: Spring, Express, Rails, Django
- **Platforms**: Kubeflow, Spark
- **Applications / Stacks**:
- Rails Backend + Node Frontend + Prometheus
- Spring Cloud Microservices (discovery-server, config-server, api-gateway,
admin-server, hystrix, various backends)
- **Infrastructure Stacks**: CloudSQL + Pubsub + GKE
{{< svg src="images/blueprint" >}}
# Optional: copy the mysql-kustomize blueprint to follow along
kpt pkg get https://github.com/GoogleContainerTools/kpt.git/package-examples/mysql-kustomize mysql
## Factor / denormalize the configuration data
Structuring blueprints into separate publisher and consumer focused pieces
provides a clean UX for consumers to modifys the package.
Example: provide separate directories with pieces consumers are expected to
edit (replicas) vs publisher implementation (health check command).
As a package publisher, it is important to think about **where and how you
want to promote customization.**
We will use [kustomize] to structure the package:
1. **Factoring out a common field value**
- Example: ` + "`" + `namespace` + "`" + `, ` + "`" + `commonLabels` + "`" + `, ` + "`" + `commonAnnotations` + "`" + `
2. **Factoring a single resource into multiple files**
- Example: ` + "`" + `resources` + "`" + ` + ` + "`" + `patches` + "`" + `
{{% pageinfo color="info" %}}
Remote kustomize bases may be used to reference the publisher focused pieces
directly from a git repository rather than including them in the package.
One disadvantage of this approach is that it creates a dependency on the
remote package being accessible in order to push -- if you can't fetch the
remote package, then you can't push changes.
{{% /pageinfo %}}
Example package structure:
$ tree mysql/
mysql/
├── Kptfile
├── README.md
├── instance
│ ├── kustomization.yaml
│ ├── service.yaml
│ └── statefulset.yaml
└── upstream
├── kustomization.yaml
├── service.yaml
└── statefulset.yaml
The ` + "`" + `upstream` + "`" + ` directory acts as a kustomize base to the ` + "`" + `instance` + "`" + ` directory.
Upstream contains things most **consumers are unlikely to modify** --
e.g. the image (for off the shelf software), health check endpoints, etc.
The ` + "`" + `instance` + "`" + ` directory contains patches with fields populated for things
most **consumers are expected to modify** -- e.g. namespace, cpu, memory,
user, password, etc.
{{% pageinfo color="warning" %}}
While the package is structured into publisher and consumer focused pieces,
it is still possible for the package consumer to modify (via direct edits)
or override (via patches) any part of the package.
**Factoring is for UX, not for enforcement of specific configuration values.**
{{% /pageinfo %}}
## Commands, Args and Environment Variables
How do you configure applications in a way that can be extended or overridden --
how can consumers of a package specify new args, flags, environment variables
or configuration files and merge those with those defined by the package
publisher?
### Notes
- Commands and Args are non-associative arrays so it is not possible to
target specific elements -- any changes replace the entire list of elements.
- Commands and Args are separate fields that are concatenated
- Commands and Args can use values from environment variables
- Environment variables are associative arrays, so it is possible to target
specific elements within the list to be overridden or added.
- Environment variables can be pulled from ConfigMaps and Secrets
- Kustomize merges ConfigMaps and Secrets per-key (deep merges of the
values is not supported).
- ConfigMaps and Secrets can be read from apps via environment variables or
volumes.
Flags and arguments may be factored into publisher and consumer focused pieces
by **specifying the ` + "`" + `command` + "`" + ` in the ` + "`" + `upstream` + "`" + ` base dir and the ` + "`" + `args` + "`" + ` in the
` + "`" + `instance` + "`" + ` dir**. This allows consumers to set and add flags using ` + "`" + `args` + "`" + `
without erasing those defined by the publisher in the ` + "`" + `command` + "`" + `.
When **specifying values for arguments or flag values, it is best to use an
environment variable read from a generated ConfigMap.** This enables overriding
the value using kustomize's generators.
Example: Enable setting ` + "`" + `--skip-grant-tables` + "`" + ` as a flag on mysql.
*` + "`" + `# {"$ref": ...` + "`" + ` comments are setter references, defined in the next section.*
# mysql/instance/statefulset.yaml
# Wire ConfigMap value from kustomization.yaml to
# an environment variable used by an arg
apiVersion: apps/v1
kind: StatefulSet
...
spec:
template:
metadata:
labels:
app: release-name-mysql
spec:
containers:
- name: mysql
...
args:
- --skip-grant-tables=$(SKIP_GRANT_TABLES)
...
env:
- name: SKIP_GRANT_TABLES
valueFrom:
configMapKeyRef:
name: mysql
key: skip-grant-tables
# mysql/instance/kustomization.yaml
# Changing the literal changes the StatefulSet behavior
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: mysql
behavior: merge
literals:
# for bootstrapping the root table grants -- set to false after bootstrapped
- "skip-grant-tables=true" # {"$ref":"#/definitions/io.k8s.cli.substitutions.skip-grant-tables"}
### Generating ConfigMaps and Secrets
Kustomize supports generating ConfigMaps and Secrets from the
kustomization.yaml.
- Generated objects have a suffix applied so that the name is unique for
the data. This ensures a rollout of Deployments and StatefulSets occurs.
- Generated object may have their values overridden by downstream consumers.
Example Upstream:
# mysql/upstream
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: mysql
literals:
- skip-grant-tables=true
Example Instance:
# mysql/instance
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: mysql
behavior: merge
literals:
- "skip-grant-tables=true" # {"$ref":"#/definitions/io.k8s.cli.substitutions.skip-grant-tables"}
- "mysql-user=" # {"$ref":"#/definitions/io.k8s.cli.substitutions.mysql-user"}
- "mysql-database=" # {"$ref":"#/definitions/io.k8s.cli.substitutions.mysql-database"}
## Setters and Substitutions
It may be desirable to provide user friendly commands for customizing the
package rather than exclusively through text editors and sed:
- Setting a value in several different patches at once
- Setting common or required values -- e.g. the image name for a Java app
blueprint
- Setting the image tag to match the digest of an image that was just build.
- Setting a value from the environment when the package is fetched the first
time -- e.g. GCP project.
Setters and substitutions are a way to define user and automation friendly
commands for performing structured edits of a configuration.
Combined with the preceding techniques, setters or substitutions can be used
to modify generated ConfigMaps and patches in the ` + "`" + `instance` + "`" + ` dir.
See the [setter] and [substitution] guides for details.
# mysql/instance/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
#
# namespace is the namespace the mysql instance is run in
namespace: "" # {"$ref":"#/definitions/io.k8s.cli.setters.namespace"}
configMapGenerator:
- name: mysql
behavior: merge
literals:
# for bootstrapping the root table grants -- set to false after bootstrapped
- "skip-grant-tables=true" # {"$ref":"#/definitions/io.k8s.cli.substitutions.skip-grant-tables"}
- "mysql-user=" # {"$ref":"#/definitions/io.k8s.cli.substitutions.mysql-user"}
- "mysql-database=" # {"$ref":"#/definitions/io.k8s.cli.substitutions.mysql-database"}
...
# mysql/instance/statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
template:
spec:
containers:
- name: mysql
...
ports:
- name: mysql
containerPort: 3306 # {"$ref":"#/definitions/io.k8s.cli.setters.port"}
resources:
requests:
cpu: 100m # {"$ref":"#/definitions/io.k8s.cli.setters.cpu"}
memory: 256Mi # {"$ref":"#/definitions/io.k8s.cli.setters.memory"}
# mysql/instance/service.yaml
apiVersion: v1
kind: Service
...
spec:
ports:
- name: mysql
port: 3306 # {"$ref":"#/definitions/io.k8s.cli.setters.port"}
targetPort: mysql
## Updates
Individual directories may have their own package versions by prefixing the
version with the directory path -- e.g.
` + "`" + `package-examples/mysql-kustomize/v0.1.0` + "`" + `.
When publishing a new version of a package, publishers should think about how
their changes will be merged into existing packages.
Changing values in the instance package is not recommended, but adding them
may be ok -- changes to fields will overwrite user changes to those same
fields, whereas adds will only conflict if the user added the same field.
`
View Source
var BootstrapGuide = `` /* 1208-byte string literal not displayed */
View Source
var ContainerGuide = `
Functions may be written as container images. The container must run a program which:
- Reads a ResourceList from STDIN
- Writes a ResourceList to STDOUT
- Exits non-0 on failure
- Writes error messages to users on STDERR
By default containers are run without network access, and without the ability to write to volumes
outside the container.
While container functions may be written in any language so long as they adhere to the io
specification (read / write ResourceList), the [kpt-functions-sdk] provides a solution for
writing functions using typescript.
## Imperative Run
Container functions may be run imperatively using the ` + "`" + `--image` + "`" + ` flag with ` + "`" + `kpt fn run` + "`" + `. This
will create a container from the image, then write to its STDIN a ResourceList and read from
it's STDOUT a ResourceList.
## Declarative Run
Container functions may be run by declaring the function in a resource using the
` + "`" + `config.kubernetes.io/function` + "`" + ` annotation.
apiVersion: example.com/v1beta1
kind: ExampleKind
metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/a/b:v1
To run the declared function use ` + "`" + `kpt fn run DIR/` + "`" + ` on the directory containing the example.
## Network
By default, container functions cannot access network or volumes. Functions may enable network
access using the ` + "`" + `--network` + "`" + ` flag, and specifying that a network is required in the functionConfig.
apiVersion: example.com/v1beta1
kind: ExampleKind
metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/a/b:v1
network:
required: true
kpt fn run DIR/ --network
`
View Source
var ExecGuide = `
Functions may alternatively be run as executables outside of containers. Exec
functions read input and write output the same as container functions, but are
run outside of a container.
Running functions as executables can be useful for function development, or for
running trusted executables.
Exec functions are disabled by default, and must be enabled with ` + "`" + `--enable-exec` + "`" + `.
{{% pageinfo color="info" %}}
Exec functions may be converted to container functions by building the executable
into a container and invoking it as the container ` + "`" + `CMD` + "`" + `.
{{% /pageinfo %}}
## Imperative Run
Exec functions may be run imperatively using the ` + "`" + `--exec-path` + "`" + ` flag with ` + "`" + `kpt fn run` + "`" + `.
kpt fn run DIR/ --enable-exec --exec-path /path/to/executable
This is similar to building ` + "`" + `/path/to/executable` + "`" + ` into the container image
` + "`" + `gcr.io/project/image:tag` + "`" + ` and running -- except that the executable has access
to the local machine.
kpt fn run DIR/ --image gcr.io/project/image:tag
Just like container functions, exec functions accept input as arguments after ` + "`" + `--` + "`" + `
kpt fn run DIR/ --enable-exec --exec-path /path/to/executable -- foo=bar
## Declarative Run
Exec functions may also be run by declaring the function in a resource using the
` + "`" + `config.kubernetes.io/function` + "`" + ` annotation.
To run the declared function use ` + "`" + `kpt fn run DIR/ --enable-exec` + "`" + ` on the directory containing
the example.
apiVersion: example.com/v1beta1
kind: ExampleKind
metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
exec:
path: /path/to/executable
Note: if the ` + "`" + `--enable-exec` + "`" + ` flag is not provided, ` + "`" + `kpt fn run DIR/` + "`" + ` will ignore the exec
function and exit 0.
`
View Source
var FunctionsGuide = `
Kpt functions are conceptually similar to Kubernetes *controllers* and *validating webhooks* --
they are programs which read resources as input, then write resources as output (creating,
modifying, deleting, or validating resources).
Unlike controllers and validating webhooks, kpt functions can be run outside of the Kubernetes
control plane. This allows them to run in more contexts or embedded in other systems.
For example, functions could be:
- manually run locally
- automatically run locally as part of *make*, *mvn*, *go generate*, etc
- automatically run in CICD systems
- run by controllers as reconcile implementations
{{< svg src="images/fn" >}}
{{% pageinfo color="primary" %}}
Unlikely pure-templating and DSL approaches, functions must be able to both *read* and *write*
resources, and specifically should be able to read resources they have previously written --
updating the inputs rather generating new resources.
This mirrors the level-triggered controller architecture used in the Kubernetes control plane.
A function reconciles the desired state of the resource configuration to match the declared state
specified by the functionConfig.
Functions that implement abstractions should update resources they have generated in the past
by reading them from the input.
{{% /pageinfo %}}
The following function runtimes are available in kpt:
| Runtime | Read Resources From | Write Resources To | Write Error Messages To | Validation Failure | Maturity |
|------------|---------------------|--------------------|-------------------------|--------------------|----------|
| Containers | STDIN | STDOUT | STDERR | Exit Code | Beta |
| Starlark | ` + "`" + `ctx.resource_list` + "`" + ` | ` + "`" + `ctx.resource_list` + "`" + `| ` + "`" + `log` + "`" + ` | Exit Code | Alpha |
## Input / Output
Functions read a ` + "`" + `ResourceList` + "`" + `, modify it, and write it back out.
### ResourceList.items
The ResourceList contains:
- (input+output) a list of resource ` + "`" + `items` + "`" + `
- (input) configuration for the function
- (output) validation results
Items are resources read from some source -- such as a package directory.
After a function adds, deletes or modifies items, the items will be written to a sink.
In most cases the sink will be the same as the source (directory).
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
spec:
...
- apiVersion: v1
kind: Service
spec:
...
### ResourceList.functionConfig
Functions may optionally be configured using the ` + "`" + `ResourceList.functionConfig` + "`" + `
field. ` + "`" + `functionConfig` + "`" + ` is analogous to a Deployment, and ` + "`" + `items` + "`" + `
is analogous to the set of all resources in the Deployment controller in-memory cache
(e.g. all the resources in the cluster) -- this includes the ReplicaSets created, updated
and deleted for that Deployment.
*Functions are responsible for identifying which resources from the ` + "`" + `items` + "`" + ` that they own.*
kind: ResourceList
functionConfig:
apiVersion: example.com/v1alpha1
kind: Foo
spec:
foo: bar
...
items:
...
{{% pageinfo color="primary" %}}
Some functions use a ConfigMap as the functionConfig instead of introducing a new resource type
bespoke to the function.
{{% /pageinfo %}}
## Running Functions
Functions may be run either imperatively using the form ` + "`" + `kpt fn run DIR/ --image fn` + "`" + `, or they
may be run declaratively using the form ` + "`" + `kpt fn run DIR/` + "`" + `.
Either way, ` + "`" + `kpt fn run` + "`" + ` will
1. read the package directory as input
2. encapsulate the package resources in a ` + "`" + `ResourceList` + "`" + `
3. run the function(s), providing the ResourceList as input
4. write the function(s) output back to the package directory; creating, deleting, or updating
resources
### Imperative Run
Functions can be run imperatively by specifying the ` + "`" + `--image` + "`" + ` flag.
` + "`" + `kpt fn run DIR/ --image some-image:version` + "`" + ` will:
- create a container from the image
- read all resources from the package directory
- provide the resources as input to the function (container)
- write the output items back to the package directory
kpt pkg get https://github.com/GoogleContainerTools/kpt-functions-sdk.git/example-configs example-configs
mkdir results/
kpt fn run example-configs/ --results-dir results/ --image gcr.io/kpt-functions/validate-rolebinding:results -- subject_name=bob@foo-corp.com
{{% pageinfo color="primary" %}}
If key-value pairs are provided after a ` + "`" + `--` + "`" + ` argument, then a ConfigMap will be generated
with these values as ` + "`" + `data` + "`" + ` elements, and the ConfigMap will be set as the functionConfig
field.
{{% /pageinfo %}}
### Declarative Run
Functions can be specified declaratively using the ` + "`" + `config.kubernetes.io/function` + "`" + `
annotation on a resource serving as the functionConfig.
` + "`" + `kpt fn run DIR/` + "`" + ` (without ` + "`" + `--image` + "`" + `) will:
- read all resources from the package directory
- for each resource with this annotation, kpt will run the specified function (using the resource as
the functionConfig)
- functions are run sequentially, with the output of each function provided as input
to the next
- write the output items back to the package directory
{{% pageinfo color="primary" %}}
If multiple functions are specified in the same yaml file (e.g. separated by ` + "`" + `---` + "`" + `),
then they are run in the order they are specified.
{{% /pageinfo %}}
## Declaring Functions
Functions may be run by declaratively through the ` + "`" + `config.kubernetes.io/function` + "`" + ` annotation.
The following is a declaration to run the function implemented by the
` + "`" + `gcr.io/example.com/image:v1.0.0` + "`" + ` image, and to provide this ExampleFunction as the functionConfig.
apiVersion: example.com/v1alpha1
kind: ExampleFunction
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example.com/image:v1.0.0
## Function Scoping
Functions are scoped to resources that live in their same directory, or subdirectories of their
directory.
` + "`" + `kpt fn run DIR/` + "`" + ` will recursively traverse DIR/ looking for declared functions and invoking
them -- passing in only those resources scoped to the function.
## Validation
### ResourceList.results
Functions may define validation results through the ` + "`" + `results` + "`" + ` field.
When functions are run using the ` + "`" + `--results-dir` + "`" + `, each function's results field will be written
to a file under the specified directory.
kind: ResourceList
functionConfig:
apiVersion: example.com/v1alpha1
kind: Foo
spec:
foo: bar
...
results:
- name: "kubeval"
items:
- severity: error # one of ["error", "warn", "info"] -- error code should be non-0 if there are 1 or more errors
tags: # arbitrary metadata about the result
error-type: "field"
message: "Value exceeds the namespace quota, reduce the value to make the pod schedulable"
resourceRef: # key to lookup the resource
apiVersion: apps/v1
kind: Deployment
name: foo
namespace: bar
file:
# optional if present as annotation
path: deploy.yaml # read from annotation if present
# optional if present as annotation
index: 0 # read from annotation if present
field:
path: "spec.template.spec.containers[3].resources.limits.cpu"
currentValue: "200" # number | string | boolean
suggestedValue: "2" # number | string | boolean
- severity: warn
...
- name: "something else"
items:
- severity: info
...
## Deferring Failure
When running multiple validation functions, it may be desired to defer failures until all functions
have been run so that the results of all functions are written to the results directory.
Functions can specify that failures should be deferred by specifying ` + "`" + `deferFailure` + "`" + ` in the
declaration.
apiVersion: example.com/v1alpha1
kind: ExampleFunction
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/example.com/image:v1.0.0
deferFailure: true # continue running functions if this fails, and fail at the end
## FunctionConfig for imperative runs
When functions are run imperatively, the functionConfig may be generated from commandline arguments.
When functions are run using the form ` + "`" + `kpt fn run DIR/ --image foo:v1 -- a=b c=d` + "`" + `, the arguments
following ` + "`" + `--` + "`" + ` are parsed into a ConfigMap which is set as the functionConfig.
kind: ResourceList
functionConfig:
apiVersion: v1
kind: ConfigMap
spec:
a: b
...
items:
...
If the first argument after ` + "`" + `--` + "`" + ` is *not* a key=value pair, it will be used as the
functionConfig type.
` + "`" + `kpt fn run DIR/ --image foo:v1 -- Foo a=b c=d` + "`" + `
kind: ResourceList
functionConfig:
kind: Foo
spec:
a: b
...
items:
...`
View Source
var GolangGuide = `
Writing exec and container functions in Golang.
### Hello World Go Function
Go libraries:
| Library | Purpose |
|---|---|
| [sigs.k8s.io/kustomize/kyaml/fn/framework] | Setup function command |
| [sigs.k8s.io/kustomize/kyaml/yaml] | Modify resources |
#### Create the go module
go mod init github.com/user/repo
go get sigs.k8s.io/kustomize/kyaml
#### Create the ` + "`" + `main.go` + "`" + `
// main.go
package main
import (
"os"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var value string
func main() {
resourceList := &framework.ResourceList{}
cmd := framework.Command(resourceList, func() error {
// cmd.Execute() will parse the ResourceList.functionConfig into cmd.Flags from
// the ResourceList.functionConfig.data field.
for i := range resourceList.Items {
// modify the resources using the kyaml/yaml library:
// https://pkg.go.dev/sigs.k8s.io/kustomize/kyaml/yaml
if err := resourceList.Items[i].PipeE(yaml.SetAnnotation("value", value)); err != nil {
return err
}
}
return nil
})
cmd.Flags().StringVar(&value, "value", "", "flag value")
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
### Build and test the function
Build the go binary and test it by running it as an executable function.
go build -o my-fn .
# run the my-fn function against the configuration in PACKAGE_DIR/
kpt fn run PACKAGE_DIR/ --enable-exec --exec-path ./my-fn -- value=foo
### Publish the function
Build the function into a container image.
# optional: generate a Dockerfile to contain the function
go run ./main.go gen ./
# build the function into an image
docker build . -t gcr.io/project/fn-name:tag
# optional: push the image to a container registry
docker push gcr.io/project/fn-name:tag
Run the function as a container
kpt fn run PACKAGE_DIR/ --image gcr.io/project/fn-name:tag -- value=foo
### Declarative function configuration
#### Run the function declaratively
Run as a container function:
# PACKAGE_DIR/example.yaml
apiVersion: example.com/v1alpha1
kind: Example
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/project/fn-name:tag
data:
value: a
kpt fn run PACKAGE_DIR/
Or as an exec function:
# PACKAGE_DIR/example.yaml
apiVersion: example.com/v1alpha1
kind: Example
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: /path/to/my-fn
data:
value: a
kpt fn run PACKAGE_DIR/ --enable-exec
#### Implement the function using declarative input
Functions may alternatively be written using a struct for parsing the functionConfig rather than
flags. The example shown below explicitly implements what the preceding example implements
implicitly.
package main
import (
"os"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func main() {
type Data struct {
Value string ` + "`" + `yaml:"value,omitempty"` + "`" + `
}
type Example struct {
// Data contains the function configuration (e.g. client-side CRD). Using "data"
// as the field name to contain key-value pairs enables the function to be invoked
// imperatively via ` + "`" + `kpt fn run DIR/ --image img:tag -- key=value` + "`" + ` and the
// key=value arguments will be parsed into the functionConfig.data field.
// If the function does not need to be invoked imperatively, other field names
// may be used.
Data Data ` + "`" + `yaml:"data,omitempty"` + "`" + `
}
functionConfig := &Example{}
resourceList := &framework.ResourceList{FunctionConfig: functionConfig}
cmd := framework.Command(resourceList, func() error {
for i := range resourceList.Items {
// use the kyaml libraries to modify each resource by applying transformations
err := resourceList.Items[i].PipeE(
yaml.SetAnnotation("value", functionConfig.Data.Value),
)
if err != nil {
return nil, err
}
}
return items, nil
})
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
Note: functionConfig need not read from the ` + "`" + `data` + "`" + ` field if it is not going to be run
imperatively with ` + "`" + `kpt fn run DIR/ --image gcr.io/some/image -- foo=bar` + "`" + ` or
` + "`" + `kpt fn run DIR/ --exec-path /some/bin --enable-exec -- foo=bar` + "`" + `. This is more appropriate
for functions implementing abstractions (e.g. client-side CRD equivalents).
...
type NestedValue struct {
Value string ` + "`" + `yaml:"value,omitempty"` + "`" + `
}
type Spec struct {
NestedValue string ` + "`" + `yaml:"nestedValue,omitempty"` + "`" + `
MapValues map[string]string ` + "`" + `yaml:"mapValues,omitempty"` + "`" + `
ListItems []string ` + "`" + `yaml:"listItems,omitempty"` + "`" + `
}
type Example struct {
Spec Spec ` + "`" + `yaml:"spec,omitempty"` + "`" + `
}
functionConfig := &Example{}
...
# PACKAGE_DIR/example.yaml
apiVersion: example.com/v1alpha1
kind: Example
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: /path/to/my-fn
spec:
nestedValue:
value: something
mapValues:
key: value
listItems:
- a
- b
### kyaml
Functions written in go should use the [sigs.k8s.io/kustomize/kyaml] libraries for modifying
resource configuration.
The [sigs.k8s.io/kustomize/kyaml/yaml] library offers utilities for reading and modifying
yaml configuration, while retaining comments and structure.
To use the kyaml/yaml library, become familiar with:
- The ` + "`" + `*yaml.RNode` + "`" + ` type, which represents a configuration object or field
- [link](https://pkg.go.dev/sigs.k8s.io/kustomize/kyaml/yaml?tab=doc#RNode)
- The ` + "`" + `Pipe` + "`" + ` and ` + "`" + `PipeE` + "`" + ` functions, which apply a series of pipelined operations to the ` + "`" + `*RNode` + "`" + `.
- [link](https://pkg.go.dev/sigs.k8s.io/kustomize/kyaml/yaml?tab=doc#RNode.Pipe)
#### Workflow
To modify a *yaml.RNode call PipeE() on the *RNode, passing in the operations to be performed.
// Set the spec.replicas field to 3 if it exists
var node *yaml.RNode
...
err := node.PipeE(yaml.Lookup("spec", "replicas"), yaml.FieldSetter{StringValue: "3"})
// Set the spec.replicas field to 3, creating it if it doesn't exist
var node *yaml.RNode
...
// pass in the type of the node to create if it doesn't exist (e.g. Sequence, Map, Scalar)
err := node.PipeE(yaml.LookupCreate(yaml.ScalarNode, "spec", "replicas"), yaml.FieldSetter{StringValue: "3"})
To read a value from a *yaml.RNode call Pipe() on the RNode, passing in the operations to
lookup a field.
// Read the spec.replicas field
var node *yaml.RNode
...
replicas, err := node.Pipe(yaml.Lookup("spec", "replicas"))
{{% pageinfo color="info" %}}
Operations are any types implementing the ` + "`" + `yaml.Filter` + "`" + ` interface, so it is simple to
define custom operations and provide them to ` + "`" + `Pipe` + "`" + `, combining them with the built-in operations.
{{% /pageinfo %}}
#### Visiting Fields and Elements
Maps (i.e. Objects) and Sequences (i.e. Lists) support functions for visiting their fields and
elements.
// Visit each of the elements in a Sequence (i.e. a List)
err := node.VisitElements(func(elem *yaml.RNode) error {
// do something with each element in the list
return nil
})
// Visit each of the fields in a Map (i.e. an Object)
err := node.VisitFields(func(n *yaml.MapNode) error {
// do something with each field in the map / object
return nil
})
### Validation
Go functions can implement high fidelity validation results by setting a ` + "`" + `framework.Result` + "`" + `
on the ` + "`" + `ResourceList` + "`" + `.
If run using ` + "`" + `kpt fn run --results-dir SOME_DIR/` + "`" + `, the result will be written to a file
in the specified directory.
If the result is returned and contains an item with severity of ` + "`" + `framework.Error` + "`" + `, the function
will exit non-0. Otherwise it will exit 0.
cmd := framework.Command(resourceList, func() error {
...
if ... {
// return validation results to be written under the results dir
resourceList.Result = framework.Result{...}
// return the results as an error if desireds
return resourceList.Result
}
...
})
`
View Source
var InitGuide = `
A kpt package is published as a git subdirectory containing configuration
files (YAML). Publishes of kpt packages can create or generate YAML files
however they like using the tool of their choice.
Publishing a package is done by pushing the git directory
(and optionally tagging it with a version).
{{% pageinfo color="primary" %}}
Multiple packages may exist in a single repo under separate subdirectories.
Packages may be nested -- both parent (composite) and child
(component) directories may be fetched as a kpt package.
A package is versioned by tagging the git repo as one of:
- ` + "`" + `package-subdirectory/package-version` + "`" + ` (directory scoped versioning)
- ` + "`" + `package-version` + "`" + ` (repo scoped versioning)
{{% /pageinfo %}}
{{< svg src="images/producer-guide" >}}
## Steps
1. [Create a git repo](#create-a-git-repo)
2. [Create the package contents](#create-the-package)
2. [Create configuration](#create-configuration)
3. [Publish package to git](#publish-package-to-git)
## Create a git repo
git clone REPO_URL # or create a new repo with ` + "`" + `git init` + "`" + `
cd REPO_NAME
## Create the package
mkdir nginx
Recommended: initialize the package with metadata
kpt pkg init nginx --tag kpt.dev/app=nginx --description "kpt nginx package"
## Create configuration
$ curl https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/controllers/nginx-deployment.yaml --output nginx/nginx-deployment.yaml
## Publish package to git
git add .
git commit -m "Add nginx package"
Recommended: tag the commit as a release
# tag as DIR/VERSION for per-directory versioning
git tag nginx/v0.1.0
git push nginx/v0.1.0`
View Source
var ProducerGuide = `{{% pageinfo color="warning" %}}
# Notice: Under Development
{{% /pageinfo %}}
{{< svg src="images/producer-guide" >}}`
View Source
var SettersGuide = `Setters provide a solution for template-free setting or substitution of field
values through package metadata (OpenAPI). They are a safer alternative to
other substitution techniques which do not have the context of the
structured data -- e.g. using ` + "`" + `sed` + "`" + ` to replace values.
The OpenAPI definitions for setters are defined in a Kptfile and referenced by
a fields through comments on the fields.
Setters may be invoked to programmatically modify the configuration
using ` + "`" + `kpt cfg set` + "`" + ` to set and/or substitute values.
{{% pageinfo color="primary" %}}
Creating a setter requires that the package has a Kptfile. If one does
not exist for the package, run ` + "`" + `kpt pkg init DIR/` + "`" + ` to create one.
{{% /pageinfo %}}
## Setters explained
Following is a short explanation of the command that will be demonstrated
in this guide.
### Data model
- Fields reference setters through OpenAPI definitions specified as
line comments -- e.g. ` + "`" + `# { "$ref": "#/definitions/..." }` + "`" + `
- OpenAPI definitions are provided through the Kptfile
### Command control flow
1. Read the package Kptfile and resources.
2. Change the setter OpenAPI value in the Kptfile
3. Locate all fields which reference the setter and change their values.
4. Write both the modified Kptfile and resources back to the package.
{{< svg src="images/set-command" >}}
#### Creating a Setter
Setters may be created either manually (by editing the Kptfile directly), or
programmatically (through the ` + "`" + `create-setter` + "`" + ` command). The ` + "`" + `create-setter` + "`" + `
command will:
1. create a new OpenAPI definition for a setter in the Kptfile
2. create references to the setter definition on the resource fields
# Kptfile -- original
openAPI:
definitions: {}
# deployment.yaml -- original
kind: Deployment
metadata:
name: foo
spec:
replicas: 3
# create or update a setter named "replicas"
# match fields with the value "3"
kpt cfg create-setter hello-world/ replicas 3
# Kptfile -- updated
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: "replicas"
value: "3"
# deployment.yaml -- updated
kind: Deployment
metadata:
name: foo
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
#### Invoking a Setter
# deployment.yaml -- original
kind: Deployment
metadata:
name: helloworld-gke
labels:
app: hello
spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
# set the replicas field to 5
kpt cfg set DIR/ replicas 5
# deployment.yaml -- updated
kind: Deployment
metadata:
name: helloworld-gke
labels:
app: hello
spec:
replicas: 5 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
#### Types
Setters may have types specified which ensure that the configuration is always
serialized correctly as yaml 1.1 -- e.g. if a string field such as an
annotation or arg has the value "on", then it would need to be quoted otherwise
it will be parsed as a bool by yaml 1.1.
This may be done by modifying the Kptfile OpenAPI definitions as shown here:
openAPI:
definitions:
io.k8s.cli.setters.version:
x-k8s-cli:
setter:
name: "version"
value: "3"
type: string
Set would change the configuration like this:
kind: Deployment
metadata:
name: foo
annotations:
version: "3" # {"$ref":"#/definitions/io.k8s.cli.setters.version"}
#### OpenAPI Validations
Users can input any additional validation constraints during ` + "`" + `create-setter` + "`" + `
operation in the form of openAPI schema. Relevant openAPI specification
constraints can be provided in json file format. The ` + "`" + `set` + "`" + ` operation validates
the input value against provided schema during setter creation and throws an
error if the input value doesn't meet any of the constraints.
$ cat /path/to/file.json
{"maximum": 10, "type": "integer"}
# create setter with openAPI property constraints
kpt cfg create-setter replicas 3 --schema-path /path/to/file.json
The command creates setter with the following definition
openAPI:
definitions:
io.k8s.cli.setters.replicas:
maximum: 10
type: integer
x-k8s-cli:
setter:
name: replicas
value: "3"
# try to set value not adhering to the constraints
kpt cfg set replicas 11
error: The input value doesn't validate against provided OpenAPI schema:
validation failure list: replicas in body should be less than or equal to 10
Relevant fixtures from openAPI JSON schema suite
{"minLength", "maxLength", "pattern", "type", "minimum", "maximum", "multipleOf",
"enum", "maxItems", "minItems", "format", "patternProperties", "uniqueItems",
"allOf", "not", "oneOf", "anyOf"}
#### Setting Lists
It is possible to create setters for fields which are a list of strings.
The setter type must be ` + "`" + `array` + "`" + `, and the reference must be on the list field.
The list setter will take variable args for its value rather than a single value.
**Note:** Currently ` + "`" + `create-setter` + "`" + ` will not directly create a setter reference for a
list field. The simplest way to create a list setter is to create a setter for one of
the elements, and then move the reference to the list field.
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list:
- "a"
# Kptfile
kind: Kptfile
` + "`" + `$ kpt cfg create-setter --type array . list a` + "`" + `
**Note:** Move the setter reference from the element (` + "`" + `- "a"` + "`" + `) to the list (` + "`" + `list: ` + "`" + `)
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"}
- "a"
# Kptfile
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.list:
type: array
x-k8s-cli:
setter:
name: list
listValues:
- "a"
` + "`" + `$ kpt cfg set . list a b c` + "`" + `
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list: # {"$ref":"#/definitions/io.k8s.cli.setters.list"}
- "a"
- "b"
- "c"
# Kptfile
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.list:
type: array
x-k8s-cli:
setter:
name: list
listValues:
- "a"
- "b"
- "c"
#### Enumerations
Setters may be configured to map an enum input to a different value set
in the configuration.
e.g. users set ` + "`" + `small` + "`" + `, ` + "`" + `medium` + "`" + `, ` + "`" + `large` + "`" + ` cpu sizes, and these are mapped
to numeric values set in the configuration.
This may be done by modifying the Kptfile OpenAPI definitions as shown here:
openAPI:
definitions:
io.k8s.cli.setters.cpu:
x-k8s-cli:
setter:
name: "cpu"
value: "small"
# enumValues will replace the user provided key with the
# map value when setting fields.
enumValues:
small: "0.5"
medium: "2"
large: "4"
Set would change the configuration like this:
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- name: foo
resources:
requests:
cpu: "0.5" # {"$ref":"#/definitions/io.k8s.cli.setters.cpu"}`
View Source
var StarlarkGuide = `
{{% pageinfo color="warning" %}}
The Starlark runtime is Alpha, and must be enabled with the ` + "`" + `--enable-star` + "`" + ` flag.
{{% /pageinfo %}}
Functions may be written as Starlark scripts which modify a ResourceList provided as
a variable.
#### Imperative Run
Starlark functions can be run imperatively by providing the Starlark script as a flag
on ` + "`" + `kpt fn run` + "`" + `. Following is an example of a Starlark function which adds a "foo" annotation
to every resource in the package.
# c.star
# set the foo annotation on each resource
def run(r, an):
for resource in r:
# mutate the resource
resource["metadata"]["annotations"]["foo"] = an
# get the value of the annotation to add
an = ctx.resource_list["functionConfig"]["data"]["value"]
run(ctx.resource_list["items"], an)
Run the Starlark function with:
# run c.star as a function, generating a ConfigMap with value=bar as the functionConfig
kpt fn run . --enable-star --star-path c.star -- value=bar
Any resource under ` + "`" + `.` + "`" + ` will have the ` + "`" + `foo: bar` + "`" + ` annotation added.
#### Declarative Run
Starlark functions can also be run declaratively using the ` + "`" + `config.kubernetes.io/function` + "`" + `
annotation. This annotation indicates that the resource is functionConfig that should
be provided to a function.
Following is an example of a Starlark function which adds a "foo" annotation to
each resource in its package. The ExampleKind resource will be set as the
ResourceList.functionConfig.
# example.yaml
apiVersion: example.com/v1beta1
kind: ExampleKind
metadata:
name: function-input
annotations:
config.kubernetes.io/function: |
starlark: {path: c.star, name: example-name}
spec:
value: "hello world"
Example Starlark function to which will add an annotation to each resource scoped to ` + "`" + `example.yaml` + "`" + `
(those under the directory containing ` + "`" + `example.yaml` + "`" + `):
# c.star
# set the foo annotation on each resource
def run(r, an):
for resource in r:
resource["metadata"]["annotations"]["foo"] = an
an = ctx.resource_list["functionConfig"]["spec"]["value"]
run(ctx.resource_list["items"], an)
Run them on the directory containing ` + "`" + `example.yaml` + "`" + ` using:
kpt fn run DIR/ --enable-star
## Debugging Functions
It is possible to debug Starlark functions using ` + "`" + `print` + "`" + `
# c.star
print(ctx.resource_list["items"][0]["metadata"]["name"])
kpt fn run . --enable-star --star-path c.star
> foo
## OpenAPI
The OpenAPI known to kpt is provided to the Starlark program through the ` + "`" + `ctx.open_api` + "`" + ` variable.
This may contain metadata about the resources and their types.
#c.star
print(ctx.open_api["definitions"]["io.k8s.api.apps.v1.Deployment"]["description"])
kpt fn run . --enable-star --star-path c.star
> Deployment enables declarative updates for Pods and ReplicaSets.
## Retaining YAML Comments
While Starlark programs are unable to retain comments on resources, kpt will
attempt to retain comments by copying them from the function inputs to the function outputs.
It is not possible at this time to add, modify or delete comments from Starlark scripts.`
View Source
var SubstitutionsGuide = `
Substitutions provide a solution for template-free substitution of field values
built on top of [setters]. They enable substituting values into part of a
field, including combining multiple setters into a single value.
Much like setters, substitutions are defined using OpenAPI.
Substitutions may be invoked to programmatically modify the configuration
using ` + "`" + `kpt cfg set` + "`" + ` to substitute values which are derived from the setter.
Substitutions are computed by substituting setter values into a pattern.
They are composed of 2 parts: a pattern and a list of values.
- The pattern is a string containing markers which will be replaced with
1 or more setter values.
- The values are pairs of markers and setter references. The *set* command
retrieves the values from the referenced setters, and replaces the markers
with the setter values.
{{% pageinfo color="primary" %}}
Creating a substitution requires that the package has a Kptfile. If one does
not exist for the package, run ` + "`" + `kpt pkg init DIR/` + "`" + ` to create one.
{{% /pageinfo %}}
## Substitutions explained
Following is a short explanation of the command that will be demonstrated
in this guide.
### Data model
- Fields reference substitutions through OpenAPI definitions specified as
line comments -- e.g. ` + "`" + `# { "$ref": "#/definitions/..." }` + "`" + `
- OpenAPI definitions are provided through the Kptfile
- Substitution OpenAPI definitions contain patterns and values to compute
the field value
### Command control flow
1. Read the package Kptfile and resources.
2. Change the setter OpenAPI value in the Kptfile
3. Locate all fields which reference the setter indirectly through a
substitution.
4. Compute the new substitution value by substituting the setter values into
the pattern.
5. Write both the modified Kptfile and resources back to the package.
{{< svg src="images/substitute-command" >}}
## Creating a Substitution
Substitution may be created either manually (by editing the Kptfile directly),
or programmatically (with ` + "`" + `create-subst` + "`" + `). The ` + "`" + `create-subst` + "`" + ` command will:
1. Create a new OpenAPI definition for a substitution in the Kptfile
2. Create references to the substitution OpenAPI definition on the resource
fields
### Example
# Kptfile -- original
openAPI:
definitions: {}
# deployment.yaml -- original
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image-value"}
# create an image substitution and a setter that populates it
kpt cfg create-subst hello-world/ image-value --field-value nginx:1.7.9 \
--pattern nginx:\${TAG_SETTER}
# Kptfile -- updated
openAPI:
definitions:
io.k8s.cli.setters.TAG_SETTER:
x-k8s-cli:
setter:
name: TAG_SETTER
value: 1.7.9
io.k8s.cli.substitutions.image-value:
x-k8s-cli:
substitution:
name: image-value
pattern: nginx:${TAG_SETTER}
values:
- marker: ${TAG_SETTER}
ref: '#/definitions/io.k8s.cli.setters.TAG_SETTER'
# deployment.yaml -- updated
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image-value"}
This substitution defines how the value for a field may be produced by
substituting the ` + "`" + `tag` + "`" + ` setter value into the pattern.
Any time the ` + "`" + `tag` + "`" + ` value is changed via the *set* command, then then
substitution value will be re-calculated for referencing fields.
## Creation semantics
By default create-subst will create referenced setters if they do not already
exist. It will infer the current setter value from the pattern and value.
If setters already exist before running the create-subst command, then those
setters are used and left unmodified.
If a setter does not exist and create-subst cannot infer the setter value,
then it will throw and error, and the setter must be manually created.
## Invoking a Substitution
Substitutions are invoked by running ` + "`" + `kpt cfg set` + "`" + ` on a setter used by the
substitution.
kpt cfg set hello-world/ TAG_SETTER 1.8.1
# deployment.yaml -- updated
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.8.1 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image-value"}
{{% pageinfo color="primary" %}}
When setting a field through a substitution, the names of the setters
are used *not* the name of the substitution. The name of the substitution is
*only used in the configuration field references*.
{{% /pageinfo %}}
`
View Source
var TsGuide = `` /* 145-byte string literal not displayed */
View Source
var VariantGuide = `
{{% pageinfo color="warning" %}}
# Notice: Under Development
{{% /pageinfo %}}`
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.