mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 16:42:51 +00:00
Add another detailed plugin example.
This commit is contained in:
353
docs/plugins/README.md
Normal file
353
docs/plugins/README.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# kustomize plugins
|
||||
|
||||
Quick guides:
|
||||
|
||||
* [linux exec plugin in 60 sec](execPluginGuidedExample.md)
|
||||
* [linux Go plugin in 60 sec](goPluginGuidedExample.md)
|
||||
|
||||
Kustomize offers a plugin framework allowing
|
||||
people to write their own resource _generators_
|
||||
and _transformers_.
|
||||
|
||||
[generator options]: ../../examples/generatorOptions.md
|
||||
[transformer configs]: ../../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 that exceeds the power of the
|
||||
builtin transformations (`namePrefix`,
|
||||
`commonLabels`, etc.).
|
||||
|
||||
## 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}
|
||||
> ```
|
||||
|
||||
This is exactly like the syntax of the `resources`
|
||||
field.
|
||||
|
||||
The value of each entry in a `resources`,
|
||||
`generators` or `transformers` list must be a
|
||||
relative path to a YAML file, or a path or URL
|
||||
to a [kustomization].
|
||||
|
||||
[kustomization]: ../glossary.md#kustomization
|
||||
|
||||
In the former case the YAML is read from disk directly,
|
||||
and in the latter case a kustomization is performed,
|
||||
and its YAML output is merged with the YAML read
|
||||
directly from files. The net result in all three cases
|
||||
is a set of YAML objects.
|
||||
|
||||
Each object resulting from a `generators` or
|
||||
`transformers` field 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](../glossary.md#kustomization-root).
|
||||
|
||||
This is the _plugin's configuration file_.
|
||||
|
||||
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]: ../glossary.md#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 generator or transform,
|
||||
it is given the entire contents of the
|
||||
configuration file.
|
||||
|
||||
[NameTransformer]: ../../plugin/builtin/prefixsuffixtransformer/PrefixSuffixTransformer_test.go
|
||||
[ChartInflator]: ../../plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go
|
||||
[plugins]: ../../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/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 tarball (source, test, 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 is in some sense a
|
||||
generator in that it emits resources for further
|
||||
processing).
|
||||
|
||||
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 specified order of transformers in the
|
||||
`transformers` field should be respected, as
|
||||
transformers cannot be expected to be commutative.
|
||||
|
||||
A `kustomize build` that tries to use plugins but
|
||||
omits the flag
|
||||
|
||||
> `--enable_alpha_plugins`
|
||||
|
||||
will fail with a warning about plugin use.
|
||||
|
||||
Flag use is an opt-in acknowledging the absence of
|
||||
plugin provenance. It's meant to give pause to
|
||||
someone who blindly downloads a kustomization from
|
||||
the internet and attempts to run it, without
|
||||
realizing that it might attempt to run 3rd party
|
||||
code in plugin form. The plugin would have to be
|
||||
installed already, but nevertheless the flag is a
|
||||
reminder.
|
||||
|
||||
|
||||
## Writing 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: more restrictions on plugin to allow the same exec
|
||||
> plugin to be specified in a config under 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
|
||||
> - 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]: ../../plugin/someteam.example.com/v1/chartinflator
|
||||
[bashed config map]: ../../plugin/someteam.example.com/v1/bashedconfigmap
|
||||
[sed transformer]: ../../plugin/someteam.example.com/v1/sedtransformer
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
### Go plugins
|
||||
|
||||
[Go plugin]: https://golang.org/pkg/plugin/
|
||||
|
||||
A [Go plugin] for kustomize looks like this:
|
||||
|
||||
> ```
|
||||
> package main
|
||||
>
|
||||
> import (
|
||||
> "sigs.k8s.io/kustomize/v3/pkg/ifc"
|
||||
> "sigs.k8s.io/kustomize/v3/pkg/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]: ../../plugin/someteam.example.com/v1/secretsfromdatabase
|
||||
[service generator]: ../../plugin/someteam.example.com/v1/someservicegenerator
|
||||
[string prefixer]: ../../plugin/someteam.example.com/v1/stringprefixer
|
||||
[date prefixer]: ../../plugin/someteam.example.com/v1/dateprefixer
|
||||
|
||||
|
||||
#### Examples
|
||||
|
||||
* [secret generator] - Generate secrets from a database.
|
||||
* [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.
|
||||
* All the builtin plugins [here](../../plugin/builtin).
|
||||
User authored plugins are
|
||||
on the same footing as builtin operations.
|
||||
|
||||
A 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
|
||||
```
|
||||
|
||||
#### Caveats
|
||||
|
||||
Go plugins allow kustomize extensions that run
|
||||
without the cost marshalling/unmarshalling all
|
||||
resource data to/from a subprocess for each plugin
|
||||
run.
|
||||
|
||||
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
|
||||
|
||||
Go plugins work as [defined][Go plugin], but fall
|
||||
short of common notions associated with the word
|
||||
_plugin_. 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.
|
||||
|
||||
Exec plugins also lack provenance, but won't
|
||||
complain about compilation skew.
|
||||
|
||||
In either case, a sensible way to share a plugin
|
||||
is as a tar file of source code, tests and
|
||||
associated data, unpackable under
|
||||
`$XDG_CONFIG_HOME/kustomize/plugin` (exactly where
|
||||
one would develop a plugin).
|
||||
|
||||
[Go modules]: https://github.com/golang/go/wiki/Modules
|
||||
|
||||
In the case of a Go plugin, an end user accepting
|
||||
a shared plugin must compile both kustomize and
|
||||
the plugin. Tooling could be built to make Go
|
||||
_plugin sharing_ easier, but this requires some
|
||||
critical mass of _plugin authoring_, which in turn
|
||||
is hampered by confusion around sharing.
|
||||
[Go modules], once they are more widely adopted,
|
||||
will solve one of the biggest plugin sharing
|
||||
difficulties - ambiguous plugin vs host
|
||||
dependencies.
|
||||
229
docs/plugins/execPluginGuidedExample.md
Normal file
229
docs/plugins/execPluginGuidedExample.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# Exec plugin on linux in 60 seconds
|
||||
|
||||
This is a (no reading) 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.12
|
||||
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Download kustomize 3.0.0
|
||||
|
||||
```
|
||||
mkdir -p $DEMO/bin
|
||||
gh=https://github.com/kubernetes-sigs/kustomize/releases/download
|
||||
url=$gh/v3.0.0-pre/kustomize_3.0.0-pre_linux_amd64
|
||||
curl -o $DEMO/bin/kustomize -L $url
|
||||
chmod u+x $DEMO/bin/kustomize
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
308
docs/plugins/goPluginGuidedExample.md
Normal file
308
docs/plugins/goPluginGuidedExample.md
Normal file
@@ -0,0 +1,308 @@
|
||||
# Go Plugin Guided Example for Linux
|
||||
|
||||
This is a (no reading) 60 second copy/paste guided
|
||||
example. Full plugin docs [here](README.md).
|
||||
|
||||
[SopsEncodedSecrets repository]: https://github.com/monopole/sopsencodedsecrets
|
||||
[Go plugin]: https://golang.org/pkg/plugin
|
||||
|
||||
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.12
|
||||
* Google cloud (gcloud) install
|
||||
* a Google account (will use Google kms -
|
||||
volunteers needed to convert to a GPG example).
|
||||
|
||||
## Make a place to work
|
||||
|
||||
```
|
||||
DEMO=$(mktemp -d)
|
||||
```
|
||||
|
||||
## Install kustomize
|
||||
|
||||
Need v3.0.0 for what follows:
|
||||
|
||||
```
|
||||
GOBIN=$DEMO/bin go get sigs.k8s.io/kustomize/v3/cmd/kustomize@v3.0.0-pre
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
> ```
|
||||
> $XGD_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
|
||||
|
||||
```
|
||||
PLUGIN_ROOT=$DEMO/kustomize/plugin
|
||||
```
|
||||
|
||||
and ephemerally set `XGD_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
|
||||
|
||||
```
|
||||
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.
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
mkdir -p $PLUGIN_ROOT/${apiVersion}
|
||||
cd $PLUGIN_ROOT/${apiVersion}
|
||||
git clone git@github.com:monopole/sopsencodedsecrets.git
|
||||
```
|
||||
|
||||
Remember this directory:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
cd $MY_PLUGIN_DIR
|
||||
go test SopsEncodedSecrets_test.go
|
||||
```
|
||||
|
||||
Build the object code for use by kustomize:
|
||||
|
||||
```
|
||||
cd $MY_PLUGIN_DIR
|
||||
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]: https://github.com/kubernetes-sigs/kustomize/blob/master/docs/plugins.md#caveats
|
||||
[used in this demo]: #install-kustomize
|
||||
|
||||
On load failure
|
||||
|
||||
* be sure to build the plugin with the same
|
||||
version of Go (`go1.12`) on the same `$GOOS`
|
||||
(`linux`) and `$GOARCH` (`amd64`) used to build
|
||||
the kustomize being [used in this demo].
|
||||
|
||||
* change the plugin's dependencies in `go.mod` to
|
||||
match those used to build kustomize (check the
|
||||
`go.mod` used in the 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:
|
||||
|
||||
```
|
||||
MYAPP=$DEMO/myapp
|
||||
mkdir -p $MYAPP
|
||||
```
|
||||
|
||||
Make a config file for the SopsEncodedSecrets plugin.
|
||||
|
||||
Its `apiVersion` and `kind` allow the plugin to be
|
||||
found:
|
||||
|
||||
```
|
||||
cat <<EOF >$MYAPP/secGenerator.yaml
|
||||
apiVersion: ${apiVersion}
|
||||
kind: ${kind}
|
||||
metadata:
|
||||
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:
|
||||
|
||||
```
|
||||
cat <<EOF >$MYAPP/kustomization.yaml
|
||||
commonLabels:
|
||||
app: hello
|
||||
generators:
|
||||
- secGenerator.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Now for the hard part. Generate the real encrypted data.
|
||||
|
||||
|
||||
### Assure you have a Google Cloud sops key ring.
|
||||
|
||||
We're going to use [sops](https://github.com/mozilla/sops) to encode a file.
|
||||
|
||||
Try this:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
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:
|
||||
```
|
||||
keyLocation=$(\
|
||||
gcloud kms keys list --location global --keyring sops |\
|
||||
grep GOOGLE | cut -d " " -f1)
|
||||
echo $keyLocation
|
||||
```
|
||||
|
||||
### Install `sops`
|
||||
|
||||
```
|
||||
GOBIN=$DEMO/bin go install go.mozilla.org/sops/cmd/sops
|
||||
```
|
||||
|
||||
### Create data encrypted with your Google Cloud key
|
||||
|
||||
Create raw data to encrypt:
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
$DEMO/bin/sops --encrypt \
|
||||
--gcp-kms $keyLocation \
|
||||
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
|
||||
```
|
||||
|
||||
|
||||
Review the files
|
||||
```
|
||||
tree $DEMO
|
||||
```
|
||||
|
||||
|
||||
## Build your app, using the plugin:
|
||||
|
||||
```
|
||||
XDG_CONFIG_HOME=$DEMO $DEMO/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
|
||||
|
||||
> ```
|
||||
> 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