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.
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 ¶
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" # {"$kpt-set":"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" # {"$kpt-set":"skip-grant-tables"}
- "mysql-user=" # {"$kpt-set":"mysql-user"}
- "mysql-database=" # {"$kpt-set":"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: "" # {"$kpt-set":"namespace"}
configMapGenerator:
- name: mysql
behavior: merge
literals:
# for bootstrapping the root table grants -- set to false after bootstrapped
- "skip-grant-tables=true" # {"$kpt-set":"skip-grant-tables"}
- "mysql-user=" # {"$kpt-set":"mysql-user"}
- "mysql-database=" # {"$kpt-set":"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 # {"$kpt-set":"port"}
resources:
requests:
cpu: 100m # {"$kpt-set":"cpu"}
memory: 256Mi # {"$kpt-set":"memory"}
# mysql/instance/service.yaml
apiVersion: v1
kind: Service
...
spec:
ports:
- name: mysql
port: 3306 # {"$kpt-set":"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.
`
var BootstrapGuide = `` /* 1208-byte string literal not displayed */
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. All local environment variables
except the ` + "`" + `TMPDIR` + "`" + ` variable are loaded into the container by default. See the
[fn command reference] for more details.
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
and the [go libraries] provide utilities for writing them in golang.
## 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 created from the contents of the package directory, and
finally read from it's STDOUT a ResourceList used to write resource back to
the package directory.
kpt fn run DIR/ --image gcr.io/a/b:v1
## 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.
## Next Steps
- Explore other ways to run functions from the [Functions Developer Guide].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
- Consult the [fn command reference].
`
var DevelopGuide = `
This guide will walk you through developing a config function using the
Typescript SDK.
## Setup
### System Requirements
Currently supported platforms: amd64 Linux/Mac
### Setting Up Your Local Environment
- Install [node][download-node]
- The SDK requires ` + "`" + `npm` + "`" + ` version 6 or higher.
- If installing node from binaries (i.e. without a package manager), follow
these [installation instructions][install-node].
- Install [docker][install-docker]
### Your Kubernetes Cluster
For the type generation functionality to work, you need a Kubernetes cluster
with the [CRD OpenAPI Publishing][crd-openapi] feature which is beta with
Kubernetes 1.15.
Alternatively, you can use an existing NPM package with pre-generated types
such as the ` + "`" + `hello-world` + "`" + ` package discussed in the [Quickstart] and skip to
[implementing the function](#implement-the-function).
#### Using a ` + "`" + `kind` + "`" + ` Cluster
The easiest way to get developing is to use ` + "`" + `kind` + "`" + ` to bring up a cluster
running in a local container.
1. Download the [kind binary][download-kind] version 0.5.1 or higher
1. Use this config file:
` + "`" + `` + "`" + `` + "`" + `sh
cat > kind.yaml <<EOF
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
kubeadmConfigPatches:
- |
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
metadata:
name: config
apiServer:
extraArgs:
"feature-gates": "CustomResourcePublishOpenAPI=true"
nodes:
- role: control-plane
EOF
` + "`" + `` + "`" + `` + "`" + `
Note the use of the beta feature.
1. Create the cluster:
` + "`" + `` + "`" + `` + "`" + `sh
kind create cluster --name=kpt-functions --config=kind.yaml --image=kindest/node:v1.15.7
` + "`" + `` + "`" + `` + "`" + `
#### Using a GKE Cluster
You can also use a deployed cluster in GKE. The beta k8s feature is avilable
only when using GKE's ` + "`" + `--enable-kubernetes-alpha` + "`" + ` flag, as seen here:
gcloud container clusters create $USER-1-15 --cluster-version=latest --region=us-central1-a --project <PROJECT>
gcloud container clusters get-credentials $USER-1-15 --zone us-central1-a --project <PROJECT>
### Working with CRDs
The SDK uses the k8s server to generate the typescript classes. If your
function uses a Custom Resource Definition, make sure you apply it to the
cluster used for type generation:
kubectl apply -f /path/to/my/crd.yaml
## Create the NPM Package
To initialize a new NPM package, first create a package directory:
mkdir my-package
cd my-package
> **Note:** All subsequent commands are run from the ` + "`" + `my-package/` + "`" + ` directory.
Run the interactive initializer:
npm init kpt-functions
Follow the instructions and respond to all prompts.
This process will create the following:
1. ` + "`" + `package.json` + "`" + `: The ` + "`" + `kpt-functions` + "`" + ` framework library is the only item
declared in ` + "`" + `dependencies` + "`" + `. Everything required to compile and test your
config function is declared as ` + "`" + `devDependencies` + "`" + `, including the
` + "`" + `create-kpt-functions` + "`" + ` CLI discussed later in the ` + "`" + `README` + "`" + `.
1. ` + "`" + `src/` + "`" + `: Directory containing the source files for all your functions, e.g.:
- ` + "`" + `my_func.ts` + "`" + `: Implement your function's interface here.
- ` + "`" + `my_func_test.ts` + "`" + `: Unit tests for your function.
- ` + "`" + `my_func_run.ts` + "`" + `: The entry point from which your function is run.
1. ` + "`" + `src/gen/` + "`" + `: Contains Kubernetes core and CRD types generated from the
OpenAPI spec published by the cluster you selected.
1. ` + "`" + `build/` + "`" + `: Contains Dockerfile for each function, e.g.:
- ` + "`" + `my_func.Dockerfile` + "`" + `
Next, install all package dependencies:
npm install
In addition to installation, ` + "`" + `install` + "`" + ` compiles your function into the ` + "`" + `dist/` + "`" + `
directory.
You can run your function directly:
node dist/my_func_run.js --help
Currently, it simply passes through the input configuration data. Let's remedy
this.
## Implement the Function
You can now start implementing the function using your favorite IDE, e.g.
code .
In ` + "`" + `src/my_func.ts` + "`" + `, implement the ` + "`" + `KptFunc` + "`" + ` interface from the [TS SDK API].
Take a look at these [demo functions] to better understand how
to use the typescript library. These functions are available as container
images documented in the [catalog].
Once you've written some code, build the package with:
npm run build
Alternatively, run the following in a separate terminal. It will continuously
build your function as you make changes:
npm run watch
To run the tests, use:
npm test
## Debug and Test the Function
You may want to run a function developed with one of the config function SDKs
using the exec runtime in order to avoid the overhead associated with running
a container. To run your function in the exec runtime, you will first need to
package your function as an executable.
The below example shows how to run a typescript function using the kpt exec
runtime.
### Prerequisites
- Install the pkg CLI.
` + "`" + `` + "`" + `` + "`" + `sh
npm install -g pkg
` + "`" + `` + "`" + `` + "`" + `
- Install your kpt-functions package module to create your function's
distributable file.
` + "`" + `` + "`" + `` + "`" + `sh
npm i
` + "`" + `` + "`" + `` + "`" + `
### Steps
1. Use the pkg CLI to create an executable from your function's distributable
file. For a ` + "`" + `my_fn` + "`" + ` function built using the typescript SDK, this is
` + "`" + `dist/my_fn_run.js` + "`" + `.
` + "`" + `` + "`" + `` + "`" + `sh
npx pkg dist/my_fn_run.js
` + "`" + `` + "`" + `` + "`" + `
1. Pass the path to the appropriate executable for your OS when running kpt
using the exec runtime.
` + "`" + `` + "`" + `` + "`" + `sh
kpt fn run DIR/ --enable-exec --exec-path /path/to/my_fn_run-macos -- a=b
` + "`" + `` + "`" + `` + "`" + `
## Build and push container images
With your working function in-hand, it's time to package your function into an
executable container image.
To build the docker image:
npm run kpt:docker-build
You can now run the function container, e.g.:
docker run gcr.io/kpt-functions-demo/my-func:dev --help
To push the image to your container registry of choice:
npm run kpt:docker-push
You'll need proper authentication/authorization to push to your registry.
` + "`" + `kpt:docker-push` + "`" + ` pushes to the registry specified in the
` + "`" + `kpt.docker_repo_base` + "`" + ` field in ` + "`" + `package.json` + "`" + `. You can manually edit this
field at any time.
The default value for the container image tag is ` + "`" + `dev` + "`" + `. This can be overridden
using` + "`" + `--tag` + "`" + ` flag:
npm run kpt:docker-build -- --tag=latest
npm run kpt:docker-push -- --tag=latest
## Use the SDK CLI
The ` + "`" + `create-kpt-functions` + "`" + ` package (installed as ` + "`" + `devDependencies` + "`" + `), provides
a CLI for managing the NPM package you created above. The CLI sub-commands can
be invoked via ` + "`" + `npm run` + "`" + `. For example, to add a new function to the package:
npm run kpt:function-create
These sub-commands are available:
kpt:docker-create Generate Dockerfiles for all functions. Overwrite
files if they exist.
kpt:docker-build Build container images for all functions.
kpt:docker-push Push container images to the registry for all
functions.
kpt:function-create Generate stubs for a new function. Overwrites files
if they exist.
kpt:type-create Generate classes for core and CRD types. Overwrite
files if they exist.
Flags are passed to the CLI after the ` + "`" + `--` + "`" + ` separator. For example, to pass a
tag when building a container image:
npm run kpt:docker-build -- --tag=latest
## Next Steps
- Learn how to [run functions].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
- Take a look at these [demo functions] to better understand how to use the
typescript SDK.
`
var ExecGuide = `
{{% pageinfo color="warning" %}}
The Exec runtime is Alpha. It is disabled by default, and must be enabled with
the ` + "`" + `--enable-exec` + "`" + ` flag.
{{% /pageinfo %}}
Config functions may be run as executables outside of containers. Exec
functions read input and write output like container functions, but are run
outside of a container.
Running functions as executables can be useful for function development, or for
running trusted executables.
{{% pageinfo color="info" %}}
Exec functions may be converted to container functions by building the
executable into a container and invoking it as the container's ` + "`" + `CMD` + "`" + ` or
` + "`" + `ENTRYPOINT` + "`" + `.
{{% /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.
## Next Steps
- Explore other ways to run functions from the [Functions Developer Guide].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
- Consult the [fn command reference].
`
var FunctionsGuide = `
## Functions Developer Guide
Config 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, config 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 |
| [Exec] | STDIN | STDOUT | STDERR | Exit Code | Alpha |
| [Starlark] | ` + "`" + `ctx.resource_list` + "`" + ` | ` + "`" + `ctx.resource_list` + "`" + ` | ` + "`" + `log` + "`" + ` | Exit Code | Alpha |
Additionally, the following libraries are available to create config functions:
| Library | Link |
| ---------- | ----------- |
| Golang | [Go Fn Lib] |
| Typescript | [TS SDK] |
## Input / Output
Functions read a ` + "`" + `ResourceList` + "`" + `, modify it, and write it back out. The
` + "`" + `ResourceList` + "`" + ` contains:
- (input+output) a list of resource ` + "`" + `items` + "`" + `
- (input) configuration for the function
- (output) validation results
### ResourceList.items
Items are resources read from some source -- such as a package directory --
using a source function -- such as [` + "`" + `kpt fn source` + "`" + `] or [` + "`" + `helm-template` + "`" + `].
After a function adds, deletes or modifies items, the items will be written to
a sink directory using a sink function -- such as [` + "`" + `kpt fn sink` + "`" + `]. In most
cases the sink directory 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.
kind: ResourceList
functionConfig:
apiVersion: example.com/v1alpha1
kind: Foo
spec:
foo: bar
...
items:
...
{{% pageinfo color="primary" %}}
Some functions introduce a new resource type bespoke to the function instead of
using a ConfigMap as the functionConfig kind.
{{% /pageinfo %}}
### 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
...
## Next Steps
- Learn how to [run functions].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
- Consult the [fn command reference].
`
var GolangGuide = `
Function developers can write exec and container functions in Golang using the
following libraries:
| Library | Purpose |
| ------------------------------------------ | ---------------------- |
| [sigs.k8s.io/kustomize/kyaml/fn/framework] | Setup function command |
| [sigs.k8s.io/kustomize/kyaml/yaml] | Modify resources |
Develop using the two examples of writing functions in Go or consult the
kyaml libraries' reference below.
## Hello World Go Function
### 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:
go build -o my-fn .
Test it by running imperatively as an executable function:
# run the my-fn function against the configuration in PACKAGE_DIR/
kpt fn run PACKAGE_DIR/ --enable-exec --exec-path ./my-fn -- value=foo
Or declaratively as an executable function:
# PACKAGE_DIR/example.yaml
apiVersion: example.com/v1alpha1
kind: ConfigMap
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: /path/to/my-fn
data:
value: foo
kpt fn run PACKAGE_DIR/ --enable-exec
### 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 imperatively as a container function:
kpt fn run PACKAGE_DIR/ --image gcr.io/project/fn-name:tag -- value=foo
Or run declaratively as a container function:
# PACKAGE_DIR/example.yaml
apiVersion: example.com/v1alpha1
kind: ConfigMap
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/project/fn-name:tag
data:
value: foo
kpt fn run PACKAGE_DIR/
## Advanced Go Function
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 Libraries Reference
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
- [RNode link]
- The ` + "`" + `Pipe` + "`" + ` and ` + "`" + `PipeE` + "`" + ` functions, which apply a series of pipelined
operations to the ` + "`" + `*RNode` + "`" + `.
- [Pipe link]
### 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 Results
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
}
...
})
## Next Steps
- Learn how to [run functions].
- Consult the [fn command reference].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
`
var InitGuide = `
A kpt package is published as a git subdirectory containing configuration
files (YAML). Publishers 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)
So package example that exists in the example folder of the repo, can
be individually versioned (as version v1.0.2) by creating the tag ` + "`" + `example/v1.0.2` + "`" + `.
{{% /pageinfo %}}
{{< svg src="images/producer-guide" >}}
## Steps
1. [Create a git repo](#create-a-git-repo)
2. [Create the package contents](#create-the-package)
3. [Create configuration](#create-configuration)
4. [Publish package to git](#publish-package-to-git)
5. [Fetch the released package](#fetch-the-released-package)
## 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
## Fetch the released package
kpt pkg get https://<REPO>/nginx@v0.1.0 nginx
`
var PackagesGuide = `
{{% pageinfo color="warning" %}}
### Notice: Under Development
{{% /pageinfo %}}
*Reusable, customizable components can be built and shared as packages.*
## Overview
Packages are a **pattern for developing reusable, customizable
configuration**. Packages are typically published and consumed by
different teams.
{{% pageinfo color="primary" %}}
Because packages can be updated to new versions, consumers
can pull in changes to a package after fetching it.
{{% /pageinfo %}}
### Example use cases for packages
- **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/package" >}}
# Optional: copy the mysql-kustomize package to follow along
kpt pkg get https://github.com/GoogleContainerTools/kpt.git/package-examples/mysql-kustomize mysql
## Factor / denormalize the configuration data
Structuring packages 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" # {"$kpt-set":"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" # {"$kpt-set":"skip-grant-tables"}
- "mysql-user=" # {"$kpt-set":"mysql-user"}
- "mysql-database=" # {"$kpt-set":"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
package
- 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: "" # {"$kpt-set":"namespace"}
configMapGenerator:
- name: mysql
behavior: merge
literals:
# for bootstrapping the root table grants -- set to false after bootstrapped
- "skip-grant-tables=true" # {"$kpt-set":"skip-grant-tables"}
- "mysql-user=" # {"$kpt-set":"mysql-user"}
- "mysql-database=" # {"$kpt-set":"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 # {"$kpt-set":"port"}
resources:
requests:
cpu: 100m # {"$kpt-set":"cpu"}
memory: 256Mi # {"$kpt-set":"memory"}
# mysql/instance/service.yaml
apiVersion: v1
kind: Service
...
spec:
ports:
- name: mysql
port: 3306 # {"$kpt-set":"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.
`
var ProducerGuide = `
{{< svg src="images/producer-guide" >}}`
var QuickstartGuide = `
## Developer Quickstart
This quickstart will get you started developing a config function with the
TypeScript SDK, using an existing kpt-functions package. Config functions are
any functions which implement the ` + "`" + `KptFunc` + "`" + ` interface documented in the
[TS SDK API].
### Prerequisites
#### System Requirements
Currently supported platforms: amd64 Linux/Mac
#### Setting Up Your Local Environment
- Install [node][download-node]
- Install [docker][install-docker]
- Install [kpt][download-kpt] and add it to your $PATH
### Explore the Demo Functions Package
1. Get the ` + "`" + `demo-functions` + "`" + ` package:
` + "`" + `` + "`" + `` + "`" + `sh
git clone --depth 1 https://github.com/GoogleContainerTools/kpt-functions-sdk.git
` + "`" + `` + "`" + `` + "`" + `
All subsequent commands are run from the ` + "`" + `demo-functions` + "`" + ` directory:
` + "`" + `` + "`" + `` + "`" + `sh
cd kpt-functions-sdk/ts/demo-functions
` + "`" + `` + "`" + `` + "`" + `
1. Install all dependencies:
` + "`" + `` + "`" + `` + "`" + `sh
npm install
` + "`" + `` + "`" + `` + "`" + `
1. Run the following in a separate terminal to continuously build your
function as you make changes:
` + "`" + `` + "`" + `` + "`" + `sh
npm run watch
` + "`" + `` + "`" + `` + "`" + `
1. Explore the [` + "`" + `label_namespace` + "`" + `][label-namespace] transformer function. This
function takes a given ` + "`" + `label_name` + "`" + ` and ` + "`" + `label_value` + "`" + ` to add the
appropriate label to ` + "`" + `Namespace` + "`" + ` objects using the SDK's ` + "`" + `addLabel` + "`" + `
function.
` + "`" + `` + "`" + `` + "`" + `typescript
import { addLabel, Configs } from 'kpt-functions';
import { isNamespace } from './gen/io.k8s.api.core.v1';
export const LABEL_NAME = 'label_name';
export const LABEL_VALUE = 'label_value';
export async function labelNamespace(configs: Configs) {
const labelName = configs.getFunctionConfigValueOrThrow(LABEL_NAME);
const labelValue = configs.getFunctionConfigValueOrThrow(LABEL_VALUE);
configs.get(isNamespace).forEach(n => addLabel(n, labelName, labelValue));
}
` + "`" + `` + "`" + `` + "`" + `
1. Run the ` + "`" + `label_namespace` + "`" + ` function on the ` + "`" + `example-configs` + "`" + ` directory:
` + "`" + `` + "`" + `` + "`" + `sh
export CONFIGS=../../example-configs
kpt fn source $CONFIGS |
node dist/label_namespace_run.js -d label_name=color -d label_value=orange |
kpt fn sink $CONFIGS
` + "`" + `` + "`" + `` + "`" + `
As the name suggests, this function added the given label to all
` + "`" + `Namespace` + "`" + ` objects in the ` + "`" + `example-configs` + "`" + ` directory:
` + "`" + `` + "`" + `` + "`" + `sh
git diff $CONFIGS
` + "`" + `` + "`" + `` + "`" + `
1. Try modifying the function in ` + "`" + `src/label_namespace.ts` + "`" + ` to perform other
operations and rerun the function on ` + "`" + `example-configs` + "`" + ` to see the changes.
1. Explore validation functions like [validate-rolebinding]. Instead of
transforming configuration, this function searches RoleBindings and returns
a results field containing details about invalid subject names.
` + "`" + `` + "`" + `` + "`" + `typescript
import { Configs, kubernetesObjectResult } from 'kpt-functions';
import { isRoleBinding } from './gen/io.k8s.api.rbac.v1';
const SUBJECT_NAME = 'subject_name';
export async function validateRolebinding(configs: Configs) {
// Get the subject name parameter.
const subjectName = configs.getFunctionConfigValueOrThrow(SUBJECT_NAME);
// Iterate over all RoleBinding objects in the input
// Filter those that have a subject we're looking for.
const results = configs
.get(isRoleBinding)
.filter(
(rb) =>
rb && rb.subjects && rb.subjects.find((s) => s.name === subjectName)
)
.map((rb) =>
kubernetesObjectResult(` + "`" + `Found banned subject '${subjectName}'` + "`" + `, rb)
);
configs.addResults(...results);
}
` + "`" + `` + "`" + `` + "`" + `
1. Run ` + "`" + `validate-rolebinding` + "`" + ` on ` + "`" + `example-configs` + "`" + `.
` + "`" + `` + "`" + `` + "`" + `sh
kpt fn source $CONFIGS |
node dist/validate_rolebinding_run.js -d subject_name=alice@foo-corp.com |
kpt fn sink $CONFIGS
git diff $CONFIGS
` + "`" + `` + "`" + `` + "`" + `
1. Explore generator functions like [expand-team-cr]. This function generates
a per-environment Namespace and RoleBinding object for each custom resource
(CR) of type Team.
` + "`" + `` + "`" + `` + "`" + `typescript
import { Configs } from 'kpt-functions';
import { isTeam, Team } from './gen/dev.cft.anthos.v1alpha1';
import { Namespace } from './gen/io.k8s.api.core.v1';
import { RoleBinding, Subject } from './gen/io.k8s.api.rbac.v1';
const ENVIRONMENTS = ['dev', 'prod'];
export async function expandTeamCr(configs: Configs) {
// For each 'Team' custom resource in the input:
// 1. Generate a per-enviroment Namespace.
// 2. Generate RoleBindings in each Namespace.
configs.get(isTeam).forEach((team) => {
const name = team.metadata.name;
ENVIRONMENTS.forEach((suffix) => {
const ns = ` + "`" + `${name}-${suffix}` + "`" + `;
configs.insert(Namespace.named(ns));
configs.insert(...createRoleBindings(team, ns));
});
});
}
function createRoleBindings(team: Team, namespace: string): RoleBinding[] {
return (team.spec.roles || []).map((item) => {
return new RoleBinding({
metadata: {
name: item.role,
namespace,
},
subjects: roleSubjects(item),
roleRef: {
kind: 'ClusterRole',
name: item.role,
apiGroup: 'rbac.authorization.k8s.io',
},
});
});
}
function roleSubjects(item: Team.Spec.Item): Subject[] {
const userSubjects: Subject[] = (item.users || []).map(
(user) =>
new Subject({
kind: 'User',
name: user,
})
);
const groupSubjects: Subject[] = (item.groups || []).map(
(group) =>
new Subject({
kind: 'Group',
name: group,
})
);
return userSubjects.concat(groupSubjects);
}
` + "`" + `` + "`" + `` + "`" + `
1. Run ` + "`" + `expand-team-cr` + "`" + ` on ` + "`" + `example-configs` + "`" + `.
` + "`" + `` + "`" + `` + "`" + `sh
kpt fn source $CONFIGS |
node dist/expand_team_cr_run.js |
kpt fn sink $CONFIGS
git diff $CONFIGS
` + "`" + `` + "`" + `` + "`" + `
## Next Steps
- Take a look at these [demo functions] to better understand
how to use the typescript SDK.
- Read the complete [Typescript Developer Guide].
- Learn how to [run functions].
`
var SettersGuide = `
{{% 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. ` + "`" + `# { "$kpt-set": "replicas-setter" }` + "`" + `
- 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" >}}
This guide walks you through an end-to-end example to create, invoke and delete setters.
## Steps
1. [Creating a Setter](#creating-a-setter)
2. [Targeting fields using the path](#targeting-fields-using-the-path)
3. [Invoking a Setter](#invoking-a-setter)
4. [Deleting a Setter](#deleting-a-setter)
5. [OpenAPI Validations](#openapi-validations)
6. [Required Setters](#required-setters)
7. [Setting Lists](#setting-lists)
#### 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
cat <<EOF >deployment.yaml
# deployment.yaml -- original
kind: Deployment
metadata:
name: foo
spec:
replicas: 3
EOF
# create or update a setter named "replicas"
# match fields with the value "3"
kpt cfg create-setter . 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 # {"$kpt-set":"replicas"}
#### Targeting fields using the path
The basic way to create a setter only matches fields based on the value. But
in some situations there might be several fields with the same value, but not all
of them should be targeted by the setter. In these situations, use the ` + "`" + `--field` + "`" + `
flag to provide either the name of the field, the full path to the field, or a
partial (suffix) path to the field. Only fields that match both the path and
the value will be targeted by the setter.
# create a setter named "replicas" and but only target the field name replicas in the spec
kpt cfg create-setter hello-world/ replicas 3 --field="spec.replicas"
# deployment-foo.yaml
kind: Deployment
metadata:
name: foo
annotations:
replicas: 3
spec:
replicas: 3 # {"$kpt-set":"replicas"}
The path is always just the names of the properties on the path to the field,
regardless whether the field is nested inside a sequence. Targeting specific
elements inside a sequence is not supported.
# create a setter named "mountName" and only target the name of the volume mount
kpt cfg create-setter hello-world/ mountName nginx --field="spec.containers.volumeMounts.name"
# deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx # {"$kpt-set":"mountName"}
mountPath: /usr/share/nginx
- name: temp
mountPath: /tmp
#### Invoking a Setter
kind: Deployment
metadata:
name: foo
spec:
replicas: 3 # {"$kpt-set":"replicas"}
# set the replicas field to 5
kpt cfg set . replicas 5
# deployment.yaml -- updated
kind: Deployment
metadata:
name: foo
spec:
replicas: 3 # {"$kpt-set":"replicas"}
#### Deleting a Setter
Setters may be deleted either manually (by editing the Kptfile directly), or
programmatically (through the ` + "`" + `delete-setter` + "`" + ` command). The ` + "`" + `delete-setter` + "`" + `
command will:
1. delete an OpenAPI definition for a setter in the Kptfile
2. remove references to the setter definition on the resource fields
# Kptfile -- original
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: "replicas"
value: "3"
# deployment.yaml -- original
kind: Deployment
metadata:
name: foo
spec:
replicas: 3 # {"$kpt-set":"replicas"}
# delete a setter named "replicas"
kpt cfg delete-setter . replicas
# Kptfile -- updated
openAPI:
# deployment.yaml -- updated
kind: Deployment
metadata:
name: foo
spec:
replicas: 3
#### 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. This example walks
you through the steps to work with openAPI validations.
cat <<EOF >schema.json
{"maximum": 10, "type": "integer"}
EOF
# create setter with openAPI property constraints
kpt cfg create-setter . replicas 5 --schema-path schema.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: "5"
# 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
Example schema for integer validation
{
"maximum": 10,
"type": "integer",
"minimum": 3,
"format": "int64",
"multipleOf": 2
}
Example schema for string validation
{
"maxLength": 10,
"type": "string",
"minLength": 3,
"pattern": "^[A-Za-z]+$",
"enum": [
"nginx",
"ubuntu"
]
}
Example schema for array validation
{
"maxItems": 10,
"type": "array",
"minItems": 3,
"uniqueItems": true,
"items": {
"type": "string",
"maxLength": 4
}
}
Relevant resources for more information: [OpenAPI types]
#### Required setters
Package publisher can mark a setter as required to convey the consumer that the
setter value must be set before triggering live apply/preview operation
on the package. This example walks you through the steps to work with required setters.
# create a setter named "replicas" and mark it as required
kpt cfg create-setter . replicas 3 --required
# deployment-foo.yaml
kind: Deployment
metadata:
name: foo
spec:
replicas: 3 # {"$kpt-set":"replicas"}
# Kptfile
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: "replicas"
value: "3"
required: true
# if you live apply/preview without setting the value
kpt live apply .
error: setter replicas is required but not set, please set it and try again
# set the replicas value
kpt cfg set hello-world/ replicas 4
kpt live apply hello-world/
# Success
#### Setting Lists
It is possible to create setters for fields which are a list of strings/integers.
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:** You should skip passing the value arg while creating array setters. ` + "`" + `field` + "`" + `
flag is required for array setters.
cat <<EOF >example.yaml
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list:
- "a"
- "b"
EOF
# Kptfile
kind: Kptfile
kpt cfg create-setter . list --type array --field spec.list
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list: # {"$kpt-set":"list"}
- "a"
- "b"
# Kptfile
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.list:
type: array
x-k8s-cli:
setter:
name: list
listValues:
- a
- b
kpt cfg set . list c d e
# example.yaml
apiVersion: example.com/v1beta1
kind: Example
spec:
list: # {"$kpt-set":"list"}
- "c"
- "d"
- "e"
# Kptfile
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.list:
type: array
x-k8s-cli:
setter:
name: list
listValues:
- "c"
- "d"
- "e"
`
var StarlarkGuide = `
{{% pageinfo color="warning" %}}
The Starlark runtime is Alpha. It is disabled by default, 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}
data:
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"]["data"]["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.
## Next Steps
- Explore other ways to run functions from the [Functions Developer Guide].
- Find out how to structure a pipeline of functions from the
[functions concepts] page.
- Consult the [fn command reference].
`
var SubpackagesGuide = `
{{% pageinfo color="warning" %}}
#### Notice: Subpackages support is available with kpt version v0.34.0+ for [cfg] commands only
{{% /pageinfo %}}
This guide walks you through an example to create, parameterize and publish a
kpt package with a [subpackage] in it.
## Steps
1. [Create the package](#create-the-package)
2. [Add setters and substitutions](#add-setters-and-substitutions)
3. [Publish the package](#publish-the-package)
## Create the package
Initialize a ` + "`" + `kpt` + "`" + ` package with a [subpackage] in its directory tree.
mkdir wordpress
kpt pkg init wordpress
mkdir wordpress/mysql
kpt pkg init wordpress/mysql
Download the ` + "`" + `Wordpress` + "`" + ` deployment configuration file.
cd wordpress
curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
Download the ` + "`" + `MySQL` + "`" + ` deployment configuration file.
cd mysql
curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
cd ../../
## Add setters and substitutions
### Add annotations
As a package publisher you might want to add [annotations] to kubernetes objects.
You can leverage ` + "`" + `--recurse-subpackages(-R)` + "`" + ` flag to create it recursively in both
` + "`" + `wordpress` + "`" + ` and ` + "`" + `mysql` + "`" + ` packages.
kpt cfg annotate wordpress/ --kv teamname=YOURTEAM -R
Output:
wordpress/
added annotations in the package
wordpress/mysql/
added annotations in the package
Similarly add ` + "`" + `projectId` + "`" + ` annotation to both the packages.
kpt cfg annotate wordpress/ --kv projectId=PROJECT_ID -R
Output:
wordpress/
added annotations in the package
wordpress/mysql/
added annotations in the package
### Create setters
Create [Setters] for the annotations values which you just added
kpt cfg create-setter wordpress/ teamname YOURTEAM -R --required
Output:
wordpress/
created setter "teamname"
wordpress/mysql/
created setter "teamname"
Similarly create an [auto-setter] with name ` + "`" + `gcloud.core.project` + "`" + `.
kpt cfg create-setter wordpress/ gcloud.core.project PROJECT_ID -R
Output:
wordpress/
created setter "gcloud.core.project"
wordpress/mysql/
created setter "gcloud.core.project"
### Create substitutions
Create [Substitutions] so that package consumers can substitute values,
using ` + "`" + `kpt cfg set` + "`" + `
kpt cfg create-subst wordpress image-tag \
--field-value wordpress:4.8-apache \
--pattern \${image}:\${tag}-apache
Output:
wordpress/
unable to find setter with name image, creating new setter with value wordpress
unable to find setter with name tag, creating new setter with value 4.8
created substitution "image-tag"
kpt cfg create-subst wordpress/mysql image-tag \
--field-value mysql:5.6 \
--pattern \${image}:\${tag}
Output:
wordpress/mysql/
unable to find setter with name image, creating new setter with value wordpress
unable to find setter with name tag, creating new setter with value 4.8
created substitution "image-tag"
### List and verify setters/substitutions
Use list-setters command to verify that the setters and substitutions are created as expected
kpt cfg list-setters wordpress/ --include-subst
Output:
wordpress/
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
gcloud.core.project PROJECT_ID 3 No
image wordpress 1 No
tag 4.8 1 No
teamname YOURTEAM 3 Yes
SUBSTITUTION PATTERN REFERENCES
image-tag ${image}:${tag} [image,tag]
## Publish the package
Now that as a package creator, you have created and parameterized a ` + "`" + `kpt` + "`" + ` package,
publish it so that package consumers can consume it.
Create a [git repo] in your profile with name ` + "`" + `wordpress` + "`" + `
cd wordpress/
git init; git add .; git commit -am "Publish package";
git remote add origin <YOUR_GIT_REPO_LINK>
git push origin master
## Next steps
Go through the [consumer guide] to consume the published package
`
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. ` + "`" + `# { "$kpt-set": "substitution" }` + "`" + `
- 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
# deployment.yaml -- original
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$kpt-set":"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 # {"$kpt-set":"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 # {"$kpt-set":"image-value"}
## Nested Substitutions
In addition to referring to setters, a substitution may also refer to another
substitution forming a tree structure. Upon invoking ` + "`" + `kpt cfg set` + "`" + ` on a setter,
the value will be set if a substitution is an ancestor/parent of the setter.
Here is the example of a simple setter and a substitution to start with
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: myspace # {"$kpt-set":"namespace-setter"}
spec:
replicas: 3
template:
spec:
containers:
- name: sidecar
image: nginx:1.7.9 # {"$kpt-set":"image-subst"}
- name: nginx
image: myspace/nginx:1.7.9
apiVersion: v1alpha1
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.namespace-setter:
x-k8s-cli:
setter:
name: namespace-setter
value: myspace
io.k8s.cli.setters.image-setter:
x-k8s-cli:
setter:
name: image-setter
value: nginx
io.k8s.cli.setters.tag-setter:
x-k8s-cli:
setter:
name: tag-setter
value: 1.7.9
io.k8s.cli.substitutions.image-subst:
x-k8s-cli:
substitution:
name: image-subst
pattern: ${image-setter}:${tag-setter}
values:
- marker: ${image-setter}
ref: '#/definitions/io.k8s.cli.setters.image-setter'
- marker: ${tag-setter}
ref: '#/definitions/io.k8s.cli.setters.tag-setter'
Now create a nested substitution for the value ` + "`" + `myspace/nginx:1.7.9` + "`" + ` which is
a combination of ` + "`" + `namespace-setter` + "`" + ` and ` + "`" + `image-subst` + "`" + `
kpt cfg create-subst hello-world/ nested-subst --field-value myspace/nginx:1.7.9 \
--pattern \${namespace-setter}/\${image-subst}
# deployment.yaml -- updated
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: myspace # {"$kpt-set":"namespace-setter"}
spec:
replicas: 3
template:
spec:
containers:
- name: sidecar
image: nginx:1.7.9 # {"$kpt-set":"image-subst"}
- name: nginx
image: myspace/nginx:1.7.9 # {"$kpt-set":"nested-subst"}
# Kptfile -- updated
apiVersion: v1alpha1
kind: Kptfile
openAPI:
definitions:
io.k8s.cli.setters.namespace-setter:
x-k8s-cli:
setter:
name: namespace-setter
value: myspace
io.k8s.cli.setters.image-setter:
x-k8s-cli:
setter:
name: image-setter
value: nginx
io.k8s.cli.setters.tag-setter:
x-k8s-cli:
setter:
name: tag-setter
value: 1.7.9
io.k8s.cli.substitutions.image-subst:
x-k8s-cli:
substitution:
name: image-subst
pattern: ${image-setter}:${tag-setter}
values:
- marker: ${image-setter}
ref: '#/definitions/io.k8s.cli.setters.image-setter'
- marker: ${tag-setter}
ref: '#/definitions/io.k8s.cli.setters.tag-setter'
io.k8s.cli.substitutions.nested-subst:
x-k8s-cli:
substitution:
name: nested-subst
pattern: ${namespace-setter}/${image-subst}
values:
- marker: ${image-subst}
ref: '#/definitions/io.k8s.cli.substitutions.image-subst'
- marker: ${namespace-setter}
ref: '#/definitions/io.k8s.cli.setters.namespace-setter'
kpt cfg set hello-world/ namespace-setter otherspace
# deployment.yaml -- updated
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: otherspace # {"$kpt-set":"namespace-setter"}
spec:
replicas: 3
template:
spec:
containers:
- name: sidecar
image: nginx:1.7.9 # {"$kpt-set":"image-subst"}
- name: nginx
image: otherspace/nginx:1.7.9 # {"$kpt-set":"nested-subst"}
{{% 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 %}}
`
var TsGuide = `` /* 1444-byte string literal not displayed */
var VariantGuide = `
{{% pageinfo color="warning" %}}
# Notice: Under Development
{{% /pageinfo %}}`
Functions ¶
This section is empty.
Types ¶
This section is empty.