mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Convert docs to docsy
This commit is contained in:
375
site/content/en/guides/plugins/_index.md
Normal file
375
site/content/en/guides/plugins/_index.md
Normal file
@@ -0,0 +1,375 @@
|
||||
---
|
||||
title: "Kustomize Plugins"
|
||||
linkTitle: "Kustomize Plugins"
|
||||
type: docs
|
||||
weight: 98
|
||||
description: >
|
||||
Kustomize plugins guide
|
||||
---
|
||||
|
||||
Kustomize offers a plugin framework allowing people to write their own resource _generators_
|
||||
and _transformers_.
|
||||
|
||||
[generator options]: https://github.com/kubernetes-sigs/kustomize/tree/master/examples/generatorOptions.md
|
||||
[transformer configs]: https://github.com/kubernetes-sigs/kustomize/tree/master/examples/transformerconfigs
|
||||
|
||||
Write a plugin when changing [generator options]
|
||||
or [transformer configs] doesn't meet your needs.
|
||||
|
||||
[12-factor]: https://12factor.net
|
||||
|
||||
* A _generator_ plugin could be a helm chart
|
||||
inflator, or a plugin that emits all the
|
||||
components (deployment, service, scaler,
|
||||
ingress, etc.) needed by someone's [12-factor]
|
||||
application, based on a smaller number of free
|
||||
variables.
|
||||
|
||||
* A _transformer_ plugin might perform special
|
||||
container command line edits, or any other
|
||||
transformation beyond those provided by the
|
||||
builtin (`namePrefix`, `commonLabels`, etc.)
|
||||
transformers.
|
||||
|
||||
## Specification in `kustomization.yaml`
|
||||
|
||||
Start by adding a `generators` and/or `transformers`
|
||||
field to your kustomization.
|
||||
|
||||
Each field accepts a string list:
|
||||
|
||||
> ```
|
||||
> generators:
|
||||
> - relative/path/to/some/file.yaml
|
||||
> - relative/path/to/some/kustomization
|
||||
> - /absolute/path/to/some/kustomization
|
||||
> - https://github.com/org/repo/some/kustomization
|
||||
>
|
||||
> transformers:
|
||||
> - {as above}
|
||||
> ```
|
||||
|
||||
The value of each entry in a `generators` or
|
||||
`transformers` list must be a relative path to a
|
||||
YAML file, or a path or URL to a [kustomization].
|
||||
This is the same format as demanded by the
|
||||
`resources` field.
|
||||
|
||||
[kustomization]: /kustomize/api-reference/glossary#kustomization
|
||||
|
||||
YAML files are read from disk directly. Paths or
|
||||
URLs leading to kustomizations trigger an
|
||||
in-process kustomization run. Each of the
|
||||
resulting objects is now further interpreted by
|
||||
kustomize as a _plugin configuration_ object.
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
A kustomization file could have the following lines:
|
||||
|
||||
```
|
||||
generators:
|
||||
- chartInflator.yaml
|
||||
```
|
||||
|
||||
Given this, the kustomization process would expect
|
||||
to find a file called `chartInflator.yaml` in the
|
||||
kustomization [root](/kustomize/api-reference/glossary#kustomization-root).
|
||||
|
||||
This is the plugin's configuration file;
|
||||
it contains a YAML configuration object.
|
||||
|
||||
The file `chartInflator.yaml` could contain:
|
||||
|
||||
```
|
||||
apiVersion: someteam.example.com/v1
|
||||
kind: ChartInflator
|
||||
metadata:
|
||||
name: notImportantHere
|
||||
chartName: minecraft
|
||||
```
|
||||
|
||||
__The `apiVersion` and `kind` fields are
|
||||
used to locate the plugin.__
|
||||
|
||||
[k8s object]: /kustomize/api-reference/glossary#kubernetes-style-object
|
||||
|
||||
Thus, these fields are required. They are also
|
||||
required because a kustomize plugin configuration
|
||||
object is also a [k8s object].
|
||||
|
||||
To get the plugin ready to generate or transform,
|
||||
it is given the entire contents of the
|
||||
configuration file.
|
||||
|
||||
[NameTransformer]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/builtin/prefixsuffixtransformer/PrefixSuffixTransformer_test.go
|
||||
[ChartInflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go
|
||||
[plugins]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/builtin
|
||||
|
||||
For more examples of plugin configuration YAML,
|
||||
browse the unit tests below the [plugins] root,
|
||||
e.g. the tests for [ChartInflator] or
|
||||
[NameTransformer].
|
||||
|
||||
|
||||
## Placement
|
||||
|
||||
Each plugin gets its own dedicated directory named
|
||||
|
||||
[`XDG_CONFIG_HOME`]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
```
|
||||
$XDG_CONFIG_HOME/kustomize/plugin
|
||||
/${apiVersion}/LOWERCASE(${kind})
|
||||
```
|
||||
|
||||
The default value of [`XDG_CONFIG_HOME`] is
|
||||
`$HOME/.config`.
|
||||
|
||||
The one-plugin-per-directory requirement eases
|
||||
creation of a plugin bundle (source, tests, plugin
|
||||
data files, etc.) for sharing.
|
||||
|
||||
In the case of a [Go plugin](#go-plugins), it also
|
||||
allows one to provide a `go.mod` file for the
|
||||
single plugin, easing resolution of package
|
||||
version dependency skew.
|
||||
|
||||
When loading, kustomize will first look for an
|
||||
_executable_ file called
|
||||
|
||||
```
|
||||
$XDG_CONFIG_HOME/kustomize/plugin
|
||||
/${apiVersion}/LOWERCASE(${kind})/${kind}
|
||||
```
|
||||
|
||||
If this file is not found or is not executable,
|
||||
kustomize will look for a file called `${kind}.so`
|
||||
in the same directory and attempt to load it as a
|
||||
[Go plugin](#go-plugins).
|
||||
|
||||
If both checks fail, the plugin load fails the overall
|
||||
`kustomize build`.
|
||||
|
||||
## Execution
|
||||
|
||||
Plugins are only used during a run of the
|
||||
`kustomize build` command.
|
||||
|
||||
Generator plugins are run after processing the
|
||||
`resources` field (which itself can be viewed as a
|
||||
generator, simply reading objects from disk).
|
||||
|
||||
The full set of resources is then passed into the
|
||||
transformation pipeline, wherein builtin
|
||||
transformations like `namePrefix` and
|
||||
`commonLabel` are applied (if they were specified
|
||||
in the kustomization file), followed by the
|
||||
user-specified transformers in the `transformers`
|
||||
field.
|
||||
|
||||
The order specified in the `transformers` field is
|
||||
respected, as transformers cannot be expected to
|
||||
be commutative.
|
||||
|
||||
#### No Security
|
||||
|
||||
Kustomize plugins do not run in any kind of
|
||||
kustomize-provided sandbox. There's no notion
|
||||
of _"plugin security"_.
|
||||
|
||||
A `kustomize build` that tries to use plugins but
|
||||
omits the flag
|
||||
|
||||
> `--enable_alpha_plugins`
|
||||
|
||||
will not load plugins and will fail with a
|
||||
warning about plugin use.
|
||||
|
||||
The use of this flag is an opt-in acknowledging
|
||||
the unstable (alpha) plugin API, the absence of
|
||||
plugin provenance, and the fact that a plugin
|
||||
is not part of kustomize.
|
||||
|
||||
To be clear, some kustomize plugin downloaded
|
||||
from the internet might wonderfully transform
|
||||
k8s config in a desired manner, while also
|
||||
quietly doing anything the user could do to the
|
||||
system running `kustomize build`.
|
||||
|
||||
## Authoring
|
||||
|
||||
There are two kinds of plugins, [exec](#exec-plugins) and [Go](#go-plugins).
|
||||
|
||||
### Exec plugins
|
||||
|
||||
A _exec plugin_ is any executable that accepts a
|
||||
single argument on its command line - the name of
|
||||
a YAML file containing its configuration (the file name
|
||||
provided in the kustomization file).
|
||||
|
||||
> TODO: restrictions on plugin to allow the _same exec
|
||||
> plugin_ to be targeted by both the
|
||||
> `generators` and `transformers` fields.
|
||||
>
|
||||
> - first arg could be the fixed string
|
||||
> `generate` or `transform`,
|
||||
> (the name of the configuration file moves to
|
||||
> the 2nd arg), or
|
||||
> - or by default an exec plugin behaves as a tranformer
|
||||
> unless a flag `-g` is provided, switching the
|
||||
> exec plugin to behave as a generator.
|
||||
|
||||
[helm chart inflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/chartinflator
|
||||
[bashed config map]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/bashedconfigmap
|
||||
[sed transformer]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/sedtransformer
|
||||
[hashicorp go-getter]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/gogetter
|
||||
|
||||
#### Examples
|
||||
|
||||
* [helm chart inflator] - A generator that inflates a helm chart.
|
||||
* [bashed config map] - Super simple configMap generation from bash.
|
||||
* [sed transformer] - Define your unstructured edits using a
|
||||
plugin like this one.
|
||||
* [hashicorp go-getter] - Download kustomize layes and build it to generate resources
|
||||
|
||||
A generator plugin accepts nothing on `stdin`, but emits
|
||||
generated resources to `stdout`.
|
||||
|
||||
A transformer plugin accepts resource YAML on `stdin`,
|
||||
and emits those resources, presumably transformed, to
|
||||
`stdout`.
|
||||
|
||||
kustomize uses an exec plugin adapter to provide
|
||||
marshalled resources on `stdin` and capture
|
||||
`stdout` for further processing.
|
||||
|
||||
#### Generator Options
|
||||
|
||||
A generator exec plugin can adjust the generator options for the resources it emits by setting one of the following internal annotations.
|
||||
|
||||
> NOTE: These annotations are local to kustomize and will not be included in the final output.
|
||||
|
||||
**`kustomize.config.k8s.io/needs-hash`**
|
||||
|
||||
Resources can be marked as needing to be processed by the internal hash transformer by including the `needs-hash` annotation. When set valid values for the annotation are `"true"` and `"false"` which respectively enable or disable hash suffixing for the resource. Omitting the annotation is equivalent to setting the value `"false"`.
|
||||
|
||||
If this annotation is set on a resource not supported by the hash transformer the build will fail.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm-test
|
||||
annotations:
|
||||
kustomize.config.k8s.io/needs-hash: "true"
|
||||
data:
|
||||
foo: bar
|
||||
```
|
||||
|
||||
**`kustomize.config.k8s.io/behavior`**
|
||||
|
||||
The `behavior` annotation will influence how conflicts are handled for resources emitted by the plugin. Valid values include "create", "merge", and "replace" with "create" being the default.
|
||||
|
||||
Example:
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm-test
|
||||
annotations:
|
||||
kustomize.config.k8s.io/behavior: "merge"
|
||||
data:
|
||||
foo: bar
|
||||
```
|
||||
|
||||
### Go plugins
|
||||
|
||||
Be sure to read [Go plugin caveats](goPluginCaveats.md).
|
||||
|
||||
[Go plugin]: https://golang.org/pkg/plugin/
|
||||
|
||||
A `.go` file can be a [Go plugin] if it declares
|
||||
'main' as it's package, and exports a symbol to
|
||||
which useful functions are attached.
|
||||
|
||||
It can further be used as a _kustomize_ plugin if
|
||||
the symbol is named 'KustomizePlugin' and the
|
||||
attached functions implement the `Configurable`,
|
||||
`Generator` and `Transformer` interfaces.
|
||||
|
||||
A Go plugin for kustomize looks like this:
|
||||
|
||||
> ```
|
||||
> package main
|
||||
>
|
||||
> import (
|
||||
> "sigs.k8s.io/kustomize/api/ifc"
|
||||
> "sigs.k8s.io/kustomize/api/resmap"
|
||||
> ...
|
||||
> )
|
||||
>
|
||||
> type plugin struct {...}
|
||||
>
|
||||
> var KustomizePlugin plugin
|
||||
>
|
||||
> func (p *plugin) Config(
|
||||
> ldr ifc.Loader,
|
||||
> rf *resmap.Factory,
|
||||
> c []byte) error {...}
|
||||
>
|
||||
> func (p *plugin) Generate() (resmap.ResMap, error) {...}
|
||||
>
|
||||
> func (p *plugin) Transform(m resmap.ResMap) error {...}
|
||||
> ```
|
||||
|
||||
Use of the identifiers `plugin`, `KustomizePlugin`
|
||||
and implementation of the method signature
|
||||
`Config` is required.
|
||||
|
||||
Implementing the `Generator` or `Transformer`
|
||||
method allows (respectively) the plugin's config
|
||||
file to be added to the `generators` or
|
||||
`transformers` field in the kustomization file.
|
||||
Do one or the other or both as desired.
|
||||
|
||||
[secret generator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/secretsfromdatabase
|
||||
[service generator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/someservicegenerator
|
||||
[string prefixer]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/stringprefixer
|
||||
[date prefixer]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/dateprefixer
|
||||
[sops encoded secrets]: https://github.com/monopole/sopsencodedsecrets
|
||||
|
||||
#### Examples
|
||||
|
||||
* [service generator] - generate a service from a name and port argument.
|
||||
* [string prefixer] - uses the value in `metadata/name` as the prefix.
|
||||
This particular example exists to show how a plugin can
|
||||
transform the behavior of a plugin. See the
|
||||
`TestTransformedTransformers` test in the `target` package.
|
||||
* [date prefixer] - prefix the current date to resource names, a simple
|
||||
example used to modify the string prefixer plugin just mentioned.
|
||||
* [secret generator] - generate secrets from a toy database.
|
||||
* [sops encoded secrets] - a more complex secret generator.
|
||||
* [All the builtin plugins](https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/builtin).
|
||||
User authored plugins are
|
||||
on the same footing as builtin operations.
|
||||
|
||||
A Go plugin can be both a generator and a
|
||||
transformer. The `Generate` method will run along
|
||||
with all the other generators before the
|
||||
`Transform` method runs.
|
||||
|
||||
Here's a build command that sensibly assumes the
|
||||
plugin source code sits in the directory where
|
||||
kustomize expects to find `.so` files:
|
||||
|
||||
```
|
||||
d=$XDG_CONFIG_HOME/kustomize/plugin\
|
||||
/${apiVersion}/LOWERCASE(${kind})
|
||||
|
||||
go build -buildmode plugin \
|
||||
-o $d/${kind}.so $d/${kind}.go
|
||||
```
|
||||
|
||||
724
site/content/en/guides/plugins/builtins.md
Normal file
724
site/content/en/guides/plugins/builtins.md
Normal file
@@ -0,0 +1,724 @@
|
||||
---
|
||||
title: "Builtin Plugins"
|
||||
linkTitle: "Builtin Plugins"
|
||||
type: docs
|
||||
description: >
|
||||
Builtin Plugins
|
||||
---
|
||||
|
||||
|
||||
# Builtin Plugins
|
||||
|
||||
A list of kustomize's builtin plugins - both
|
||||
generators and transformers.
|
||||
|
||||
For each plugin, an example is given for
|
||||
|
||||
* implicitly triggering
|
||||
the plugin via a dedicated kustomization
|
||||
file field (e.g. the `AnnotationsTransformer` is
|
||||
triggered by the `commonAnnotations` field).
|
||||
|
||||
* explicitly triggering the plugin
|
||||
via the `generators` or `transformers` field
|
||||
(by providing a config file specifying the
|
||||
plugin).
|
||||
|
||||
The former method is convenient but limited in
|
||||
power as most of the plugins arguments must
|
||||
be defaulted. The latter method allows for
|
||||
complete plugin argument specification.
|
||||
|
||||
|
||||
[types.GeneratorOptions]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/generatoroptions.go
|
||||
[types.SecretArgs]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/secretargs.go
|
||||
[types.ConfigMapArgs]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/configmapargs.go
|
||||
[config.FieldSpec]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go
|
||||
[types.ObjectMeta]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/objectmeta.go
|
||||
[types.Selector]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/selector.go
|
||||
[types.Replica]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/replica.go
|
||||
[types.PatchStrategicMerge]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/patchstrategicmerge.go
|
||||
[types.PatchTarget]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/patchtarget.go
|
||||
[image.Image]: https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/image.go
|
||||
|
||||
## _AnnotationTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `commonAnnotations`
|
||||
|
||||
Adds annotions (non-identifying metadata) to add
|
||||
all resources. Like labels, these are key value
|
||||
pairs.
|
||||
|
||||
```
|
||||
commonAnnotations:
|
||||
oncallPager: 800-555-1212
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> Annotations map\[string\]string
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: AnnotationsTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> annotations:
|
||||
> app: myApp
|
||||
> greeting/morning: a string with blanks
|
||||
> fieldSpecs:
|
||||
> - path: metadata/annotations
|
||||
> create: true
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
## _ConfigMapGenerator_
|
||||
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `configMapGenerator`
|
||||
|
||||
Each entry in this list results in the creation of
|
||||
one ConfigMap resource (it's a generator of n maps).
|
||||
|
||||
The example below creates three ConfigMaps. One with the names and contents of
|
||||
the given files, one with key/value as data, and a third which sets an
|
||||
annotation and label via `options` for that single ConfigMap.
|
||||
|
||||
Each configMapGenerator item accepts a parameter of
|
||||
`behavior: [create|replace|merge]`.
|
||||
This allows an overlay to modify or
|
||||
replace an existing configMap from the parent.
|
||||
|
||||
Also, each entry has an `options` field, that has the
|
||||
same subfields as the kustomization file's `generatorOptions` field.
|
||||
|
||||
This `options` field allows one to add labels and/or
|
||||
annotations to the generated instance, or to individually
|
||||
disable the name suffix hash for that instance.
|
||||
Labels and annotations added here will not be overwritten
|
||||
by the global options associated with the kustomization
|
||||
file `generatorOptions` field. However, due to how
|
||||
booleans behave, if the global `generatorOptions` field
|
||||
specifies `disableNameSuffixHash: true`, this will
|
||||
trump any attempt to locally override it.
|
||||
|
||||
```
|
||||
# These labels are added to all configmaps and secrets.
|
||||
generatorOptions:
|
||||
labels:
|
||||
fruit: apple
|
||||
|
||||
configMapGenerator:
|
||||
- name: my-java-server-props
|
||||
behavior: merge
|
||||
files:
|
||||
- application.properties
|
||||
- more.properties
|
||||
- name: my-java-server-env-vars
|
||||
literals:
|
||||
- JAVA_HOME=/opt/java/jdk
|
||||
- JAVA_TOOL_OPTIONS=-agentlib:hprof
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
labels:
|
||||
pet: dog
|
||||
- name: dashboards
|
||||
files:
|
||||
- mydashboard.json
|
||||
options:
|
||||
annotations:
|
||||
dashboard: "1"
|
||||
labels:
|
||||
app.kubernetes.io/name: "app1"
|
||||
```
|
||||
|
||||
It is also possible to
|
||||
[define a key](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-the-key-to-use-when-creating-a-configmap-from-a-file)
|
||||
to set a name different than the filename.
|
||||
|
||||
The example below creates a ConfigMap
|
||||
with the name of file as `myFileName.ini`
|
||||
while the _actual_ filename from which the
|
||||
configmap is created is `whatever.ini`.
|
||||
|
||||
```
|
||||
configMapGenerator:
|
||||
- name: app-whatever
|
||||
files:
|
||||
- myFileName.ini=whatever.ini
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> [types.ConfigMapArgs]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: ConfigMapGenerator
|
||||
> metadata:
|
||||
> name: mymap
|
||||
> envs:
|
||||
> - devops.env
|
||||
> - uxteam.env
|
||||
> literals:
|
||||
> - FRUIT=apple
|
||||
> - VEGETABLE=carrot
|
||||
> ```
|
||||
|
||||
|
||||
## _ImageTagTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `images`
|
||||
|
||||
Images modify the name, tags and/or digest for images
|
||||
without creating patches. E.g. Given this
|
||||
kubernetes Deployment fragment:
|
||||
|
||||
```
|
||||
containers:
|
||||
- name: mypostgresdb
|
||||
image: postgres:8
|
||||
- name: nginxapp
|
||||
image: nginx:1.7.9
|
||||
- name: myapp
|
||||
image: my-demo-app:latest
|
||||
- name: alpine-app
|
||||
image: alpine:3.7
|
||||
```
|
||||
|
||||
one can change the `image` in the following ways:
|
||||
|
||||
- `postgres:8` to `my-registry/my-postgres:v1`,
|
||||
- nginx tag `1.7.9` to `1.8.0`,
|
||||
- image name `my-demo-app` to `my-app`,
|
||||
- alpine's tag `3.7` to a digest value
|
||||
|
||||
all with the following *kustomization*:
|
||||
|
||||
```
|
||||
images:
|
||||
- name: postgres
|
||||
newName: my-registry/my-postgres
|
||||
newTag: v1
|
||||
- name: nginx
|
||||
newTag: 1.8.0
|
||||
- name: my-demo-app
|
||||
newName: my-app
|
||||
- name: alpine
|
||||
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> ImageTag [image.Image]
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: ImageTagTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> imageTag:
|
||||
> name: nginx
|
||||
> newTag: v2
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
## _LabelTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `commonLabels`
|
||||
|
||||
Adds labels to all resources and selectors
|
||||
|
||||
```
|
||||
commonLabels:
|
||||
someName: someValue
|
||||
owner: alice
|
||||
app: bingo
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> Labels map\[string\]string
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: LabelTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> labels:
|
||||
> app: myApp
|
||||
> env: production
|
||||
> fieldSpecs:
|
||||
> - path: metadata/labels
|
||||
> create: true
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## _NamespaceTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `namespace`
|
||||
|
||||
Adds namespace to all resources
|
||||
|
||||
```
|
||||
namespace: my-namespace
|
||||
```
|
||||
Adds namespace to all resources
|
||||
|
||||
```
|
||||
namespace: my-namespace
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> [types.ObjectMeta]
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: NamespaceTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> namespace: test
|
||||
> fieldSpecs:
|
||||
> - path: metadata/namespace
|
||||
> create: true
|
||||
> - path: subjects
|
||||
> kind: RoleBinding
|
||||
> group: rbac.authorization.k8s.io
|
||||
> - path: subjects
|
||||
> kind: ClusterRoleBinding
|
||||
> group: rbac.authorization.k8s.io
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## _PatchesJson6902_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `patchesJson6902`
|
||||
|
||||
Each entry in this list should resolve to
|
||||
a kubernetes object and a JSON patch that will be applied
|
||||
to the object.
|
||||
The JSON patch is documented at https://tools.ietf.org/html/rfc6902
|
||||
|
||||
target field points to a kubernetes object within the same kustomization
|
||||
by the object's group, version, kind, name and namespace.
|
||||
path field is a relative file path of a JSON patch file.
|
||||
The content in this patch file can be either in JSON format as
|
||||
|
||||
```
|
||||
[
|
||||
{"op": "add", "path": "/some/new/path", "value": "value"},
|
||||
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
|
||||
]
|
||||
```
|
||||
|
||||
or in YAML format as
|
||||
|
||||
```
|
||||
- op: add
|
||||
path: /some/new/path
|
||||
value: value
|
||||
- op: replace
|
||||
path: /some/existing/path
|
||||
value: new value
|
||||
```
|
||||
|
||||
```
|
||||
patchesJson6902:
|
||||
- target:
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: my-deployment
|
||||
path: add_init_container.yaml
|
||||
- target:
|
||||
version: v1
|
||||
kind: Service
|
||||
name: my-service
|
||||
path: add_service_annotation.yaml
|
||||
```
|
||||
|
||||
The patch content can be an inline string as well:
|
||||
|
||||
```
|
||||
patchesJson6902:
|
||||
- target:
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: my-deployment
|
||||
patch: |-
|
||||
- op: add
|
||||
path: /some/new/path
|
||||
value: value
|
||||
- op: replace
|
||||
path: /some/existing/path
|
||||
value: "new value"
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
> Target [types.PatchTarget]
|
||||
>
|
||||
> Path string
|
||||
>
|
||||
> JsonOp string
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: PatchJson6902Transformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> target:
|
||||
> group: apps
|
||||
> version: v1
|
||||
> kind: Deployment
|
||||
> name: my-deploy
|
||||
> path: jsonpatch.json
|
||||
> ```
|
||||
|
||||
|
||||
## _PatchesStrategicMerge_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `patchesStrategicMerge`
|
||||
|
||||
Each entry in this list should be either a relative
|
||||
file path or an inline content
|
||||
resolving to a partial or complete resource
|
||||
definition.
|
||||
|
||||
The names in these (possibly partial) resource
|
||||
files must match names already loaded via the
|
||||
`resources` field. These entries are used to
|
||||
_patch_ (modify) the known resources.
|
||||
|
||||
Small patches that do one thing are best, e.g. modify
|
||||
a memory request/limit, change an env var in a
|
||||
ConfigMap, etc. Small patches are easy to review and
|
||||
easy to mix together in overlays.
|
||||
|
||||
```
|
||||
patchesStrategicMerge:
|
||||
- service_port_8888.yaml
|
||||
- deployment_increase_replicas.yaml
|
||||
- deployment_increase_memory.yaml
|
||||
```
|
||||
|
||||
The patch content can be a inline string as well.
|
||||
```
|
||||
patchesStrategicMerge:
|
||||
- |-
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nignx:latest
|
||||
```
|
||||
|
||||
Note that kustomize does not support more than one patch
|
||||
for the same object that contain a _delete_ directive. To remove
|
||||
several fields / slice elements from an object create a single
|
||||
patch that performs all the needed deletions.
|
||||
|
||||
### Usage via plugin
|
||||
|
||||
#### Arguments
|
||||
|
||||
> Paths \[\][types.PatchStrategicMerge]
|
||||
>
|
||||
> Patches string
|
||||
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: PatchStrategicMergeTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> paths:
|
||||
> - patch.yaml
|
||||
> ```
|
||||
|
||||
|
||||
## _PatchTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `patches`
|
||||
|
||||
Each entry in this list should resolve to an Patch
|
||||
object, which includes a patch and a target selector.
|
||||
The patch can be either a strategic merge patch or a
|
||||
JSON patch. it can be either a patch file or an inline
|
||||
string. The target selects
|
||||
resources by group, version, kind, name, namespace,
|
||||
labelSelector and annotationSelector. A resource
|
||||
which matches all the specified fields is selected
|
||||
to apply the patch.
|
||||
|
||||
```
|
||||
patches:
|
||||
- path: patch.yaml
|
||||
target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: deploy.*
|
||||
labelSelector: "env=dev"
|
||||
annotationSelector: "zone=west"
|
||||
- patch: |-
|
||||
- op: replace
|
||||
path: /some/existing/path
|
||||
value: new value
|
||||
target:
|
||||
kind: MyKind
|
||||
labelSelector: "env=dev"
|
||||
```
|
||||
|
||||
The `name` and `namespace` fields of the patch target selector are
|
||||
automatically anchored regular expressions. This means that the value `myapp`
|
||||
is equivalent to `^myapp$`.
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> Path string
|
||||
>
|
||||
> Patch string
|
||||
>
|
||||
> Target \*[types.Selector]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: PatchTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> patch: '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "nginx:latest"}]'
|
||||
> target:
|
||||
> name: .*Deploy
|
||||
> kind: Deployment
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
|
||||
## _PrefixSuffixTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field names: `namePrefix`, `nameSuffix`
|
||||
|
||||
Prepends or postfixes the value to the names
|
||||
of all resources.
|
||||
|
||||
E.g. a deployment named `wordpress` could
|
||||
become `alices-wordpress` or `wordpress-v2`
|
||||
or `alices-wordpress-v2`.
|
||||
|
||||
```
|
||||
namePrefix: alices-
|
||||
nameSuffix: -v2
|
||||
```
|
||||
|
||||
The suffix is appended before the content hash if
|
||||
the resource type is ConfigMap or Secret.
|
||||
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> Prefix string
|
||||
>
|
||||
> Suffix string
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: PrefixSuffixTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> prefix: baked-
|
||||
> suffix: -pie
|
||||
> fieldSpecs:
|
||||
> - path: metadata/name
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
## _ReplicaCountTransformer_
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `replicas`
|
||||
|
||||
Replicas modified the number of replicas for a resource.
|
||||
|
||||
E.g. Given this kubernetes Deployment fragment:
|
||||
|
||||
```
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-name
|
||||
spec:
|
||||
replicas: 3
|
||||
```
|
||||
|
||||
one can change the number of replicas to 5
|
||||
by adding the following to your kustomization:
|
||||
|
||||
```
|
||||
replicas:
|
||||
- name: deployment-name
|
||||
count: 5
|
||||
```
|
||||
|
||||
This field accepts a list, so many resources can
|
||||
be modified at the same time.
|
||||
|
||||
As this declaration does not take in a `kind:` nor a `group:`
|
||||
it will match any `group` and `kind` that has a matching name and
|
||||
that is one of:
|
||||
- `Deployment`
|
||||
- `ReplicationController`
|
||||
- `ReplicaSet`
|
||||
- `StatefulSet`
|
||||
|
||||
For more complex use cases, revert to using a patch.
|
||||
|
||||
### Usage via plugin
|
||||
|
||||
#### Arguments
|
||||
|
||||
> Replica [types.Replica]
|
||||
>
|
||||
> FieldSpecs \[\][config.FieldSpec]
|
||||
|
||||
#### Example
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: ReplicaCountTransformer
|
||||
> metadata:
|
||||
> name: not-important-to-example
|
||||
> replica:
|
||||
> name: myapp
|
||||
> count: 23
|
||||
> fieldSpecs:
|
||||
> - path: spec/replicas
|
||||
> create: true
|
||||
> kind: Deployment
|
||||
> - path: spec/replicas
|
||||
> create: true
|
||||
> kind: ReplicationController
|
||||
> ```
|
||||
|
||||
|
||||
|
||||
## _SecretGenerator_
|
||||
|
||||
### Usage via `kustomization.yaml`
|
||||
|
||||
#### field name: `secretGenerator`
|
||||
|
||||
Each entry in the argument list
|
||||
results in the creation of
|
||||
one Secret resource
|
||||
(it's a generator of n secrets).
|
||||
|
||||
This works like the `configMapGenerator` field
|
||||
described above.
|
||||
|
||||
```
|
||||
secretGenerator:
|
||||
- name: app-tls
|
||||
files:
|
||||
- secret/tls.cert
|
||||
- secret/tls.key
|
||||
type: "kubernetes.io/tls"
|
||||
- name: app-tls-namespaced
|
||||
# you can define a namespace to generate
|
||||
# a secret in, defaults to: "default"
|
||||
namespace: apps
|
||||
files:
|
||||
- tls.crt=catsecret/tls.cert
|
||||
- tls.key=secret/tls.key
|
||||
type: "kubernetes.io/tls"
|
||||
- name: env_file_secret
|
||||
envs:
|
||||
- env.txt
|
||||
type: Opaque
|
||||
- name: secret-with-annotation
|
||||
files:
|
||||
- app-config.yaml
|
||||
type: Opaque
|
||||
options:
|
||||
annotations:
|
||||
app_config: "true"
|
||||
labels:
|
||||
app.kubernetes.io/name: "app2"
|
||||
```
|
||||
|
||||
### Usage via plugin
|
||||
|
||||
#### Arguments
|
||||
|
||||
> [types.ObjectMeta]
|
||||
>
|
||||
> [types.SecretArgs]
|
||||
|
||||
#### Example
|
||||
|
||||
> ```
|
||||
> apiVersion: builtin
|
||||
> kind: SecretGenerator
|
||||
> metadata:
|
||||
> name: my-secret
|
||||
> namespace: whatever
|
||||
> behavior: merge
|
||||
> envs:
|
||||
> - a.env
|
||||
> - b.env
|
||||
> files:
|
||||
> - obscure=longsecret.txt
|
||||
> literals:
|
||||
> - FRUIT=apple
|
||||
> - VEGETABLE=carrot
|
||||
> ```
|
||||
234
site/content/en/guides/plugins/execPluginGuidedExample.md
Normal file
234
site/content/en/guides/plugins/execPluginGuidedExample.md
Normal file
@@ -0,0 +1,234 @@
|
||||
---
|
||||
title: "Exec plugin on linux"
|
||||
linkTitle: "Exec plugin on linux"
|
||||
type: docs
|
||||
description: >
|
||||
Exec plugin on linux in 60 seconds
|
||||
---
|
||||
|
||||
This is a (no reading allowed!) 60 second copy/paste guided
|
||||
example. Full plugin docs [here](README.md).
|
||||
|
||||
This demo writes and uses a somewhat ridiculous
|
||||
_exec_ plugin (written in bash) that generates a
|
||||
`ConfigMap`.
|
||||
|
||||
This is a guide to try it without damaging your
|
||||
current setup.
|
||||
|
||||
#### requirements
|
||||
|
||||
* linux, git, curl, Go 1.13
|
||||
|
||||
|
||||
## Make a place to work
|
||||
|
||||
```
|
||||
DEMO=$(mktemp -d)
|
||||
```
|
||||
|
||||
## Create a kustomization
|
||||
|
||||
Make a kustomization directory to
|
||||
hold all your config:
|
||||
|
||||
```
|
||||
MYAPP=$DEMO/myapp
|
||||
mkdir -p $MYAPP
|
||||
```
|
||||
|
||||
Make a deployment config:
|
||||
|
||||
```
|
||||
cat <<'EOF' >$MYAPP/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: the-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: the-container
|
||||
image: monopole/hello:1
|
||||
command: ["/hello",
|
||||
"--port=8080",
|
||||
"--date=$(THE_DATE)",
|
||||
"--enableRiskyFeature=$(ENABLE_RISKY)"]
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: THE_DATE
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: the-map
|
||||
key: today
|
||||
- name: ALT_GREETING
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: the-map
|
||||
key: altGreeting
|
||||
- name: ENABLE_RISKY
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: the-map
|
||||
key: enableRisky
|
||||
EOF
|
||||
```
|
||||
|
||||
Make a service config:
|
||||
|
||||
```
|
||||
cat <<EOF >$MYAPP/service.yaml
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: the-service
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8666
|
||||
targetPort: 8080
|
||||
EOF
|
||||
```
|
||||
|
||||
Now make a config file for the plugin
|
||||
you're about to write.
|
||||
|
||||
This config file is just another k8s resource
|
||||
object. The values of its `apiVersion` and `kind`
|
||||
fields are used to _find_ the plugin code on your
|
||||
filesystem (more on this later).
|
||||
|
||||
```
|
||||
cat <<'EOF' >$MYAPP/cmGenerator.yaml
|
||||
apiVersion: myDevOpsTeam
|
||||
kind: SillyConfigMapGenerator
|
||||
metadata:
|
||||
name: whatever
|
||||
argsOneLiner: Bienvenue true
|
||||
EOF
|
||||
```
|
||||
|
||||
Finally, make a kustomization file
|
||||
referencing all of the above:
|
||||
|
||||
```
|
||||
cat <<EOF >$MYAPP/kustomization.yaml
|
||||
commonLabels:
|
||||
app: hello
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
generators:
|
||||
- cmGenerator.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Review the files
|
||||
```
|
||||
ls -C1 $MYAPP
|
||||
```
|
||||
|
||||
|
||||
## Make a home for plugins
|
||||
|
||||
Plugins must live in a particular place for
|
||||
kustomize to find them.
|
||||
|
||||
This demo will use the ephemeral directory:
|
||||
|
||||
```
|
||||
PLUGIN_ROOT=$DEMO/kustomize/plugin
|
||||
```
|
||||
|
||||
The plugin config defined above in
|
||||
`$MYAPP/cmGenerator.yaml` specifies:
|
||||
|
||||
> ```
|
||||
> apiVersion: myDevOpsTeam
|
||||
> kind: SillyConfigMapGenerator
|
||||
> ```
|
||||
|
||||
This means the plugin must live in a directory
|
||||
named:
|
||||
|
||||
```
|
||||
MY_PLUGIN_DIR=$PLUGIN_ROOT/myDevOpsTeam/sillyconfigmapgenerator
|
||||
|
||||
mkdir -p $MY_PLUGIN_DIR
|
||||
```
|
||||
|
||||
The directory name is the plugin config's
|
||||
_apiVersion_ followed by its lower-cased _kind_.
|
||||
|
||||
A plugin gets its own directory to hold itself,
|
||||
its tests and any supplemental data files it
|
||||
might need.
|
||||
|
||||
## Create the plugin
|
||||
|
||||
There are two kinds of plugins, _exec_ and _Go_.
|
||||
|
||||
Make an _exec_ plugin, installing it to the
|
||||
correct directory and file name. The file name
|
||||
must match the plugin's _kind_ (in this case,
|
||||
`SillyConfigMapGenerator`):
|
||||
|
||||
```
|
||||
cat <<'EOF' >$MY_PLUGIN_DIR/SillyConfigMapGenerator
|
||||
#!/bin/bash
|
||||
# Skip the config file name argument.
|
||||
shift
|
||||
today=`date +%F`
|
||||
echo "
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: the-map
|
||||
data:
|
||||
today: $today
|
||||
altGreeting: "$1"
|
||||
enableRisky: "$2"
|
||||
"
|
||||
EOF
|
||||
```
|
||||
|
||||
By definition, an _exec_ plugin must be executable:
|
||||
|
||||
```
|
||||
chmod a+x $MY_PLUGIN_DIR/SillyConfigMapGenerator
|
||||
```
|
||||
|
||||
## Install kustomize
|
||||
|
||||
Per the [instructions](/kustomize/installation):
|
||||
```
|
||||
curl -s "https://raw.githubusercontent.com/\
|
||||
kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||
mkdir -p $DEMO/bin
|
||||
mv kustomize $DEMO/bin
|
||||
```
|
||||
|
||||
## Review the layout
|
||||
|
||||
```
|
||||
tree $DEMO
|
||||
```
|
||||
|
||||
## Build your app, using the plugin:
|
||||
|
||||
```
|
||||
XDG_CONFIG_HOME=$DEMO $DEMO/bin/kustomize build --enable_alpha_plugins $MYAPP
|
||||
```
|
||||
|
||||
Above, if you had set
|
||||
|
||||
> ```
|
||||
> PLUGIN_ROOT=$HOME/.config/kustomize/plugin
|
||||
> ```
|
||||
|
||||
there would be no need to use `XDG_CONFIG_HOME` in the
|
||||
_kustomize_ command above.
|
||||
123
site/content/en/guides/plugins/goPluginCaveats.md
Normal file
123
site/content/en/guides/plugins/goPluginCaveats.md
Normal file
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: "Go plugin Caveats"
|
||||
linkTitle: "Go plugin Caveats"
|
||||
type: docs
|
||||
description: >
|
||||
Go plugin Caveats
|
||||
---
|
||||
|
||||
[plugin package]: https://golang.org/pkg/plugin
|
||||
[Go modules]: https://github.com/golang/go/wiki/Modules
|
||||
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
[tensorflow plugin]: https://www.tensorflow.org/guide/extend/op
|
||||
|
||||
A _Go plugin_ is a compilation artifact described
|
||||
by the Go [plugin package]. It is built with
|
||||
special flags and cannot run on its own.
|
||||
It must be loaded into a running Go program.
|
||||
|
||||
> A normal program written in Go might be usable
|
||||
> as _exec plugin_, but is not a _Go plugin_.
|
||||
|
||||
Go plugins allow kustomize extensions that run
|
||||
without the cost marshalling/unmarshalling all
|
||||
resource data to/from a subprocess for each plugin
|
||||
run. The Go plugin API assures a certain level of
|
||||
consistency to avoid confusing downstream
|
||||
transformers.
|
||||
|
||||
Go plugins work as described in the [plugin
|
||||
package], but fall short of common notions
|
||||
associated with the word _plugin_.
|
||||
|
||||
## The skew problem
|
||||
|
||||
Go plugin compilation creates an [ELF] formatted
|
||||
`.so` file, which by definition has no information
|
||||
about the provenance of the object code.
|
||||
|
||||
Skew between the compilation conditions (versions
|
||||
of package dependencies, `GOOS`, `GOARCH`) of the
|
||||
main program ELF and the plugin ELF will cause
|
||||
plugin load failure, with non-helpful error
|
||||
messages.
|
||||
|
||||
Exec plugins also lack provenance, but won't fail
|
||||
due to compilation skew.
|
||||
|
||||
In either case, the only sensible way to share a
|
||||
plugin is as some kind of _bundle_ (a git repo
|
||||
URL, a git archive file, a tar file, etc.)
|
||||
containing source code, tests and associated data,
|
||||
unpackable under
|
||||
`$XDG_CONFIG_HOME/kustomize/plugin`.
|
||||
|
||||
In the case of a Go plugin, an _end user_
|
||||
accepting a shared plugin _must compile both
|
||||
kustomize and the plugin_.
|
||||
|
||||
This means a one-time run of
|
||||
```
|
||||
# Or whatever is appropriate at time of reading
|
||||
GOPATH=${whatever} GO111MODULE=on go get sigs.k8s.io/kustomize/api
|
||||
```
|
||||
|
||||
and then a normal development cycle using
|
||||
|
||||
```
|
||||
go build -buildmode plugin \
|
||||
-o ${wherever}/${kind}.so ${wherever}/${kind}.go
|
||||
```
|
||||
with paths and the release version tag (e.g. `v3.0.0`)
|
||||
adjusted as needed.
|
||||
|
||||
For comparison, consider what one
|
||||
must do to write a [tensorflow plugin].
|
||||
|
||||
## Why support Go plugins?
|
||||
|
||||
### Safety
|
||||
|
||||
The Go plugin developer sees the same API offered
|
||||
to native kustomize operations, assuring certain
|
||||
semantics, invariants, checks, etc. An exec
|
||||
plugin sub-process dealing with this via
|
||||
stdin/stdout will have an easier time screwing
|
||||
things up for downstream transformers and
|
||||
consumers.
|
||||
|
||||
Minor point: if the plugin reads files via
|
||||
the kustomize-provided file `Loader` interface, it
|
||||
will be constrained by kustomize file loading
|
||||
restrictions. Of course, nothing but a code audit
|
||||
prevents a Go plugin from importing the `io` package
|
||||
and doing whatever it wants.
|
||||
|
||||
### Debugging
|
||||
|
||||
A Go plugin developer can debug the plugin _in
|
||||
situ_, setting breakpoints inside the plugin and
|
||||
elsewhere while running a plugin in feature tests.
|
||||
|
||||
To get the best of both worlds (shareability and safety),
|
||||
a developer can write an `.go` program that functions
|
||||
as an _exec plugin_, but can be processed by `go generate`
|
||||
to emit a _Go plugin_ (or vice versa).
|
||||
|
||||
### Unit of contribution
|
||||
|
||||
All the builtin generators and transformers
|
||||
are themselves Go plugins. This means that
|
||||
the kustomize maintainers can promote a contributed
|
||||
plugin to a builtin without needing code changes
|
||||
(beyond those mandated by normal code review).
|
||||
|
||||
### Ecosystems grow through use
|
||||
|
||||
Tooling could ease Go plugin _sharing_, but this
|
||||
requires some critical mass of Go plugin
|
||||
_authoring_, which in turn is hampered by
|
||||
confusion around sharing. [Go modules], once they
|
||||
are more widely adopted, will solve the
|
||||
biggest plugin sharing difficulty: ambiguous
|
||||
plugin vs host dependencies.
|
||||
375
site/content/en/guides/plugins/goPluginGuidedExample.md
Normal file
375
site/content/en/guides/plugins/goPluginGuidedExample.md
Normal file
@@ -0,0 +1,375 @@
|
||||
---
|
||||
title: "Go plugin example"
|
||||
linkTitle: "Go plugin example"
|
||||
type: docs
|
||||
description: >
|
||||
Go plugin example
|
||||
---
|
||||
|
||||
# Go Plugin Guided Example for Linux
|
||||
|
||||
[SopsEncodedSecrets repository]: https://github.com/monopole/sopsencodedsecrets
|
||||
[Go plugin]: https://golang.org/pkg/plugin
|
||||
[Go plugin caveats]: goPluginCaveats.md
|
||||
|
||||
This is a (no reading allowed!) 60 second copy/paste guided
|
||||
example.
|
||||
|
||||
Full plugin docs [here](README.md).
|
||||
Be sure to read the [Go plugin caveats].
|
||||
|
||||
This demo uses a Go plugin, `SopsEncodedSecrets`,
|
||||
that lives in the [sopsencodedsecrets repository].
|
||||
This is an inprocess [Go plugin], not an
|
||||
sub-process exec plugin that happens to be written
|
||||
in Go (which is another option for Go authors).
|
||||
|
||||
This is a guide to try it without damaging your
|
||||
current setup.
|
||||
|
||||
#### requirements
|
||||
|
||||
* linux, git, curl, Go 1.13
|
||||
|
||||
For encryption
|
||||
|
||||
* gpg
|
||||
|
||||
Or
|
||||
|
||||
* Google cloud (gcloud) install
|
||||
* a Google account with KMS permission
|
||||
|
||||
## Make a place to work
|
||||
|
||||
```shell
|
||||
# Keeping these separate to avoid cluttering the DEMO dir.
|
||||
DEMO=$(mktemp -d)
|
||||
tmpGoPath=$(mktemp -d)
|
||||
```
|
||||
|
||||
## Install kustomize
|
||||
|
||||
Need v3.0.0 for what follows, and you must _compile_
|
||||
it (not download the binary from the release page):
|
||||
|
||||
```shell
|
||||
GOPATH=$tmpGoPath go install sigs.k8s.io/kustomize/kustomize
|
||||
```
|
||||
|
||||
## Make a home for plugins
|
||||
|
||||
A kustomize plugin is fully determined by
|
||||
its configuration file and source code.
|
||||
|
||||
[required fields]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
|
||||
|
||||
Kustomize plugin configuration files are formatted
|
||||
as kubernetes resource objects, meaning
|
||||
`apiVersion`, `kind` and `metadata` are [required
|
||||
fields] in these config files.
|
||||
|
||||
The kustomize program reads the config file
|
||||
(because the config file name appears in the
|
||||
`generators` or `transformers` field in the
|
||||
kustomization file), then locates the Go plugin's
|
||||
object code at the following location:
|
||||
|
||||
> ```shell
|
||||
> $XDG_CONFIG_HOME/kustomize/plugin/$apiVersion/$lKind/$kind.so
|
||||
> ```
|
||||
|
||||
where `lKind` holds the lowercased kind. The
|
||||
plugin is then loaded and fed its config, and the
|
||||
plugin's output becomes part of the overall
|
||||
`kustomize build` process.
|
||||
|
||||
The same plugin might be used multiple times in
|
||||
one kustomize build, but with different config
|
||||
files. Also, kustomize might customize config
|
||||
data before sending it to the plugin, for whatever
|
||||
reason. For these reasons, kustomize owns the
|
||||
mapping between plugins and config data; it's not
|
||||
left to plugins to find their own config.
|
||||
|
||||
This demo will house the plugin it uses at the
|
||||
ephemeral directory
|
||||
|
||||
```shell
|
||||
PLUGIN_ROOT=$DEMO/kustomize/plugin
|
||||
```
|
||||
|
||||
and ephemerally set `XDG_CONFIG_HOME` on a command
|
||||
line below.
|
||||
|
||||
### What apiVersion and kind?
|
||||
|
||||
At this stage in the development of kustomize
|
||||
plugins, plugin code doesn't know or care what
|
||||
`apiVersion` or `kind` appears in the config file
|
||||
sent to it.
|
||||
|
||||
The plugin could check these fields, but it's the
|
||||
remaining fields that provide actual configuration
|
||||
data, and at this point the successful parsing of
|
||||
these other fields are the only thing that matters
|
||||
to a plugin.
|
||||
|
||||
This demo uses a plugin called _SopsEncodedSecrets_,
|
||||
and it lives in the [SopsEncodedSecrets repository].
|
||||
|
||||
Somewhat arbitrarily, we'll chose to install
|
||||
this plugin with
|
||||
|
||||
```shell
|
||||
apiVersion=mygenerators
|
||||
kind=SopsEncodedSecrets
|
||||
```
|
||||
|
||||
### Define the plugin's home dir
|
||||
|
||||
By convention, the ultimate home of the plugin
|
||||
code and supplemental data, tests, documentation,
|
||||
etc. is the lowercase form of its kind.
|
||||
|
||||
```shell
|
||||
lKind=$(echo $kind | awk '{print tolower($0)}')
|
||||
```
|
||||
|
||||
### Download the SopsEncodedSecrets plugin
|
||||
|
||||
In this case, the repo name matches the lowercase
|
||||
kind already, so we just clone the repo and get
|
||||
the proper directory name automatically:
|
||||
|
||||
```shell
|
||||
mkdir -p $PLUGIN_ROOT/${apiVersion}
|
||||
cd $PLUGIN_ROOT/${apiVersion}
|
||||
git clone git@github.com:monopole/sopsencodedsecrets.git
|
||||
```
|
||||
|
||||
Remember this directory:
|
||||
|
||||
```shell
|
||||
MY_PLUGIN_DIR=$PLUGIN_ROOT/${apiVersion}/${lKind}
|
||||
```
|
||||
|
||||
### Try the plugin's own test
|
||||
|
||||
Plugins may come with their own tests.
|
||||
This one does, and it hopefully passes:
|
||||
|
||||
```shell
|
||||
cd $MY_PLUGIN_DIR
|
||||
go test SopsEncodedSecrets_test.go
|
||||
```
|
||||
|
||||
Build the object code for use by kustomize:
|
||||
|
||||
```shell
|
||||
cd $MY_PLUGIN_DIR
|
||||
GOPATH=$tmpGoPath go build -buildmode plugin -o ${kind}.so ${kind}.go
|
||||
```
|
||||
|
||||
This step may succeed, but kustomize might
|
||||
ultimately fail to load the plugin because of
|
||||
dependency [skew].
|
||||
|
||||
[skew]: /docs/plugins/README.md#caveats
|
||||
[used in this demo]: #install-kustomize
|
||||
|
||||
On load failure
|
||||
|
||||
* be sure to build the plugin with the same
|
||||
version of Go (_go1.13_) on the same `$GOOS`
|
||||
(_linux_) and `$GOARCH` (_amd64_) used to build
|
||||
the kustomize being [used in this demo].
|
||||
|
||||
* change the plugin's dependencies in its `go.mod`
|
||||
to match the versions used by kustomize (check
|
||||
kustomize's `go.mod` used in its tagged commit).
|
||||
|
||||
Lacking tools and metadata to allow this to be
|
||||
automated, there won't be a Go plugin ecosystem.
|
||||
|
||||
Kustomize has adopted a Go plugin architecture as
|
||||
to ease accept new generators and transformers
|
||||
(just write a plugin), and to be sure that native
|
||||
operations (also constructed and tested as
|
||||
plugins) are compartmentalized, orderable and
|
||||
reusable instead of bizarrely woven throughout the
|
||||
code as a individual special cases.
|
||||
|
||||
## Create a kustomization
|
||||
|
||||
Make a kustomization directory to
|
||||
hold all your config:
|
||||
|
||||
```shell
|
||||
MYAPP=$DEMO/myapp
|
||||
mkdir -p $MYAPP
|
||||
```
|
||||
|
||||
Make a config file for the SopsEncodedSecrets plugin.
|
||||
|
||||
Its `apiVersion` and `kind` allow the plugin to be
|
||||
found:
|
||||
|
||||
```shell
|
||||
cat <<EOF >$MYAPP/secGenerator.yaml
|
||||
apiVersion: ${apiVersion}
|
||||
kind: ${kind}
|
||||
metadata:
|
||||
name: mySecretGenerator
|
||||
name: forbiddenValues
|
||||
namespace: production
|
||||
file: myEncryptedData.yaml
|
||||
keys:
|
||||
- ROCKET
|
||||
- CAR
|
||||
EOF
|
||||
```
|
||||
|
||||
This plugin expects to find more data in
|
||||
`myEncryptedData.yaml`; we'll get to that shortly.
|
||||
|
||||
Make a kustomization file referencing the plugin
|
||||
config:
|
||||
|
||||
```shell
|
||||
cat <<EOF >$MYAPP/kustomization.yaml
|
||||
commonLabels:
|
||||
app: hello
|
||||
generators:
|
||||
- secGenerator.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Now generate the real encrypted data.
|
||||
|
||||
### Assure you have an encryption tool installed
|
||||
|
||||
We're going to use [sops](https://github.com/mozilla/sops) to encode a file. Choose either GPG or Google Cloud KMS as the secret provider to continue.
|
||||
|
||||
#### GPG
|
||||
|
||||
Try this:
|
||||
|
||||
```shell
|
||||
gpg --list-keys
|
||||
```
|
||||
|
||||
If it returns a list, presumably you've already created keys. If not, try import test keys from sops for dev.
|
||||
|
||||
```shell
|
||||
curl https://raw.githubusercontent.com/mozilla/sops/master/pgp/sops_functional_tests_key.asc | gpg --import
|
||||
SOPS_PGP_FP="1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A"
|
||||
```
|
||||
|
||||
#### Google Cloude KMS
|
||||
|
||||
Try this:
|
||||
|
||||
```shell
|
||||
gcloud kms keys list --location global --keyring sops
|
||||
```
|
||||
|
||||
If it succeeds, presumably you've already created keys and placed them in a keyring called sops. If not, do this:
|
||||
|
||||
```shell
|
||||
gcloud kms keyrings create sops --location global
|
||||
gcloud kms keys create sops-key --location global \
|
||||
--keyring sops --purpose encryption
|
||||
```
|
||||
|
||||
Extract your keyLocation for use below:
|
||||
|
||||
```shell
|
||||
keyLocation=$(\
|
||||
gcloud kms keys list --location global --keyring sops |\
|
||||
grep GOOGLE | cut -d " " -f1)
|
||||
echo $keyLocation
|
||||
```
|
||||
|
||||
### Install `sops`
|
||||
|
||||
```shell
|
||||
GOPATH=$tmpGoPath go install go.mozilla.org/sops/cmd/sops
|
||||
```
|
||||
|
||||
### Create data encrypted with your private key
|
||||
|
||||
Create raw data to encrypt:
|
||||
|
||||
```shell
|
||||
cat <<EOF >$MYAPP/myClearData.yaml
|
||||
VEGETABLE: carrot
|
||||
ROCKET: saturn-v
|
||||
FRUIT: apple
|
||||
CAR: dymaxion
|
||||
EOF
|
||||
```
|
||||
|
||||
Encrypt the data into file the plugin wants to read:
|
||||
|
||||
With PGP
|
||||
|
||||
```shell
|
||||
$tmpGoPath/bin/sops --encrypt \
|
||||
--pgp $SOPS_PGP_FP \
|
||||
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
|
||||
```
|
||||
|
||||
Or GCP KMS
|
||||
|
||||
```shell
|
||||
$tmpGoPath/bin/sops --encrypt \
|
||||
--gcp-kms $keyLocation \
|
||||
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
|
||||
```
|
||||
|
||||
Review the files
|
||||
|
||||
```shell
|
||||
tree $DEMO
|
||||
```
|
||||
|
||||
This should look something like:
|
||||
|
||||
> ```shell
|
||||
> /tmp/tmp.0kIE9VclPt
|
||||
> ├── kustomize
|
||||
> │ └── plugin
|
||||
> │ └── mygenerators
|
||||
> │ └── sopsencodedsecrets
|
||||
> │ ├── go.mod
|
||||
> │ ├── go.sum
|
||||
> │ ├── LICENSE
|
||||
> │ ├── README.md
|
||||
> │ ├── SopsEncodedSecrets.go
|
||||
> │ ├── SopsEncodedSecrets.so
|
||||
> │ └── SopsEncodedSecrets_test.go
|
||||
> └── myapp
|
||||
> ├── kustomization.yaml
|
||||
> ├── myClearData.yaml
|
||||
> ├── myEncryptedData.yaml
|
||||
> └── secGenerator.yaml
|
||||
> ```
|
||||
|
||||
## Build your app, using the plugin:
|
||||
|
||||
```shell
|
||||
XDG_CONFIG_HOME=$DEMO $tmpGoPath/bin/kustomize build --enable_alpha_plugins $MYAPP
|
||||
```
|
||||
|
||||
This should emit a kubernetes secret, with
|
||||
encrypted data for the names `ROCKET` and `CAR`.
|
||||
|
||||
Above, if you had set
|
||||
|
||||
> ```shell
|
||||
> PLUGIN_ROOT=$HOME/.config/kustomize/plugin
|
||||
> ```
|
||||
|
||||
there would be no need to use `XDG_CONFIG_HOME` in the
|
||||
_kustomize_ command above.
|
||||
Reference in New Issue
Block a user