Go plugin example
Go plugin example
Go Plugin Guided Example for Linux
This is a (no reading allowed!) 60 second copy/paste guided example.
Full plugin docs here. 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
# 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):
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.
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:
$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
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
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
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.
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’sgo.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:
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: 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:
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 to encode a file. Choose either GPG or Google Cloud KMS as the secret provider to continue.
GPG
Try this:
gpg --list-keys
If it returns a list, presumably you’ve already created keys. If not, try import test keys from sops for dev.
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:
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
GOPATH=$tmpGoPath go install go.mozilla.org/sops/cmd/sops
Create data encrypted with your private 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:
With PGP
$tmpGoPath/bin/sops --encrypt \
--pgp $SOPS_PGP_FP \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
Or GCP KMS
$tmpGoPath/bin/sops --encrypt \
--gcp-kms $keyLocation \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
Review the files
tree $DEMO
This should look something like:
/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
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
PLUGIN_ROOT=$HOME/.config/kustomize/plugin
there would be no need to use XDG_CONFIG_HOME
in the kustomize command above.