Kustomize – Kustomize Plugins https://kubernetes-sigs.github.io/kustomize/guides/plugins/ Recent content in Kustomize Plugins on Kustomize Hugo -- gohugo.io Guides: Builtin Plugins https://kubernetes-sigs.github.io/kustomize/guides/plugins/builtins/ Mon, 01 Jan 0001 00:00:00 +0000 https://kubernetes-sigs.github.io/kustomize/guides/plugins/builtins/ <h1 id="builtin-plugins">Builtin Plugins</h1> <p>A list of kustomize&rsquo;s builtin plugins - both generators and transformers.</p> <p>For each plugin, an example is given for</p> <ul> <li> <p>implicitly triggering the plugin via a dedicated kustomization file field (e.g. the <code>AnnotationsTransformer</code> is triggered by the <code>commonAnnotations</code> field).</p> </li> <li> <p>explicitly triggering the plugin via the <code>generators</code> or <code>transformers</code> field (by providing a config file specifying the plugin).</p> </li> </ul> <p>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.</p> <h2 id="_annotationtransformer_"><em>AnnotationTransformer</em></h2> <h3 id="usage-via-kustomizationyaml">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-commonannotations">field name: <code>commonAnnotations</code></h4> <p>Adds annotions (non-identifying metadata) to add all resources. Like labels, these are key value pairs.</p> <pre><code>commonAnnotations: oncallPager: 800-555-1212 </code></pre><h3 id="usage-via-plugin">Usage via plugin</h3> <h4 id="arguments">Arguments</h4> <blockquote> <p>Annotations map[string]string</p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example">Example</h4> <blockquote> <pre><code>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 </code></pre></blockquote> <h2 id="_configmapgenerator_"><em>ConfigMapGenerator</em></h2> <h3 id="usage-via-kustomizationyaml-1">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-configmapgenerator">field name: <code>configMapGenerator</code></h4> <p>Each entry in this list results in the creation of one ConfigMap resource (it&rsquo;s a generator of n maps).</p> <p>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 <code>options</code> for that single ConfigMap.</p> <p>Each configMapGenerator item accepts a parameter of <code>behavior: [create|replace|merge]</code>. This allows an overlay to modify or replace an existing configMap from the parent.</p> <p>Also, each entry has an <code>options</code> field, that has the same subfields as the kustomization file&rsquo;s <code>generatorOptions</code> field.</p> <p>This <code>options</code> 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 <code>generatorOptions</code> field. However, due to how booleans behave, if the global <code>generatorOptions</code> field specifies <code>disableNameSuffixHash: true</code>, this will trump any attempt to locally override it.</p> <pre><code># 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: &quot;1&quot; labels: app.kubernetes.io/name: &quot;app1&quot; </code></pre><p>It is also possible to <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-the-key-to-use-when-creating-a-configmap-from-a-file">define a key</a> to set a name different than the filename.</p> <p>The example below creates a ConfigMap with the name of file as <code>myFileName.ini</code> while the <em>actual</em> filename from which the configmap is created is <code>whatever.ini</code>.</p> <pre><code>configMapGenerator: - name: app-whatever files: - myFileName.ini=whatever.ini </code></pre><h3 id="usage-via-plugin-1">Usage via plugin</h3> <h4 id="arguments-1">Arguments</h4> <blockquote> <p><a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/configmapargs.go">types.ConfigMapArgs</a></p> </blockquote> <h4 id="example-1">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: ConfigMapGenerator metadata: name: mymap envs: - devops.env - uxteam.env literals: - FRUIT=apple - VEGETABLE=carrot </code></pre></blockquote> <h2 id="_imagetagtransformer_"><em>ImageTagTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-2">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-images">field name: <code>images</code></h4> <p>Images modify the name, tags and/or digest for images without creating patches. E.g. Given this kubernetes Deployment fragment:</p> <pre><code>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 </code></pre><p>one can change the <code>image</code> in the following ways:</p> <ul> <li><code>postgres:8</code> to <code>my-registry/my-postgres:v1</code>,</li> <li>nginx tag <code>1.7.9</code> to <code>1.8.0</code>,</li> <li>image name <code>my-demo-app</code> to <code>my-app</code>,</li> <li>alpine&rsquo;s tag <code>3.7</code> to a digest value</li> </ul> <p>all with the following <em>kustomization</em>:</p> <pre><code>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 </code></pre><h3 id="usage-via-plugin-2">Usage via plugin</h3> <h4 id="arguments-2">Arguments</h4> <blockquote> <p>ImageTag <a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/image.go">image.Image</a></p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example-2">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: ImageTagTransformer metadata: name: not-important-to-example imageTag: name: nginx newTag: v2 </code></pre></blockquote> <h2 id="_labeltransformer_"><em>LabelTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-3">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-commonlabels">field name: <code>commonLabels</code></h4> <p>Adds labels to all resources and selectors</p> <pre><code>commonLabels: someName: someValue owner: alice app: bingo </code></pre><h3 id="usage-via-plugin-3">Usage via plugin</h3> <h4 id="arguments-3">Arguments</h4> <blockquote> <p>Labels map[string]string</p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example-3">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: LabelTransformer metadata: name: not-important-to-example labels: app: myApp env: production fieldSpecs: - path: metadata/labels create: true </code></pre></blockquote> <h2 id="_namespacetransformer_"><em>NamespaceTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-4">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-namespace">field name: <code>namespace</code></h4> <p>Adds namespace to all resources</p> <pre><code>namespace: my-namespace </code></pre><h3 id="usage-via-plugin-4">Usage via plugin</h3> <h4 id="arguments-4">Arguments</h4> <blockquote> <p><a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/objectmeta.go">types.ObjectMeta</a></p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example-4">Example</h4> <blockquote> <pre><code>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 </code></pre></blockquote> <h2 id="_patchesjson6902_"><em>PatchesJson6902</em></h2> <h3 id="usage-via-kustomizationyaml-5">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-patchesjson6902">field name: <code>patchesJson6902</code></h4> <p>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 <a href="https://tools.ietf.org/html/rfc6902">https://tools.ietf.org/html/rfc6902</a></p> <p>target field points to a kubernetes object within the same kustomization by the object&rsquo;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</p> <pre><code> [ {&quot;op&quot;: &quot;add&quot;, &quot;path&quot;: &quot;/some/new/path&quot;, &quot;value&quot;: &quot;value&quot;}, {&quot;op&quot;: &quot;replace&quot;, &quot;path&quot;: &quot;/some/existing/path&quot;, &quot;value&quot;: &quot;new value&quot;} ] </code></pre><p>or in YAML format as</p> <pre><code>- op: add path: /some/new/path value: value - op: replace path: /some/existing/path value: new value </code></pre><pre><code>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 </code></pre><p>The patch content can be an inline string as well:</p> <pre><code>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: &quot;new value&quot; </code></pre><h3 id="usage-via-plugin-5">Usage via plugin</h3> <h4 id="arguments-5">Arguments</h4> <blockquote> <p>Target <a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/patchtarget.go">types.PatchTarget</a></p> <p>Path string</p> <p>JsonOp string</p> </blockquote> <h4 id="example-5">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: PatchJson6902Transformer metadata: name: not-important-to-example target: group: apps version: v1 kind: Deployment name: my-deploy path: jsonpatch.json </code></pre></blockquote> <h2 id="_patchesstrategicmerge_"><em>PatchesStrategicMerge</em></h2> <h3 id="usage-via-kustomizationyaml-6">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-patchesstrategicmerge">field name: <code>patchesStrategicMerge</code></h4> <p>Each entry in this list should be either a relative file path or an inline content resolving to a partial or complete resource definition.</p> <p>The names in these (possibly partial) resource files must match names already loaded via the <code>resources</code> field. These entries are used to <em>patch</em> (modify) the known resources.</p> <p>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.</p> <pre><code>patchesStrategicMerge: - service_port_8888.yaml - deployment_increase_replicas.yaml - deployment_increase_memory.yaml </code></pre><p>The patch content can be a inline string as well.</p> <pre><code>patchesStrategicMerge: - |- apiVersion: apps/v1 kind: Deployment metadata: name: nginx spec: template: spec: containers: - name: nginx image: nignx:latest </code></pre><p>Note that kustomize does not support more than one patch for the same object that contain a <em>delete</em> directive. To remove several fields / slice elements from an object create a single patch that performs all the needed deletions.</p> <h3 id="usage-via-plugin-6">Usage via plugin</h3> <h4 id="arguments-6">Arguments</h4> <blockquote> <p>Paths []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/patchstrategicmerge.go">types.PatchStrategicMerge</a></p> <p>Patches string</p> </blockquote> <h4 id="example-6">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: PatchStrategicMergeTransformer metadata: name: not-important-to-example paths: - patch.yaml </code></pre></blockquote> <h2 id="_patchtransformer_"><em>PatchTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-7">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-patches">field name: <code>patches</code></h4> <p>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.</p> <pre><code>patches: - path: patch.yaml target: group: apps version: v1 kind: Deployment name: deploy.* labelSelector: &quot;env=dev&quot; annotationSelector: &quot;zone=west&quot; - patch: |- - op: replace path: /some/existing/path value: new value target: kind: MyKind labelSelector: &quot;env=dev&quot; </code></pre><p>The <code>name</code> and <code>namespace</code> fields of the patch target selector are automatically anchored regular expressions. This means that the value <code>myapp</code> is equivalent to <code>^myapp$</code>.</p> <h3 id="usage-via-plugin-7">Usage via plugin</h3> <h4 id="arguments-7">Arguments</h4> <blockquote> <p>Path string</p> <p>Patch string</p> <p>Target *<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/selector.go">types.Selector</a></p> </blockquote> <h4 id="example-7">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: PatchTransformer metadata: name: not-important-to-example patch: '[{&quot;op&quot;: &quot;replace&quot;, &quot;path&quot;: &quot;/spec/template/spec/containers/0/image&quot;, &quot;value&quot;: &quot;nginx:latest&quot;}]' target: name: .*Deploy kind: Deployment </code></pre></blockquote> <h2 id="_prefixsuffixtransformer_"><em>PrefixSuffixTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-8">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-names-nameprefix-namesuffix">field names: <code>namePrefix</code>, <code>nameSuffix</code></h4> <p>Prepends or postfixes the value to the names of all resources.</p> <p>E.g. a deployment named <code>wordpress</code> could become <code>alices-wordpress</code> or <code>wordpress-v2</code> or <code>alices-wordpress-v2</code>.</p> <pre><code>namePrefix: alices- nameSuffix: -v2 </code></pre><p>The suffix is appended before the content hash if the resource type is ConfigMap or Secret.</p> <h3 id="usage-via-plugin-8">Usage via plugin</h3> <h4 id="arguments-8">Arguments</h4> <blockquote> <p>Prefix string</p> <p>Suffix string</p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example-8">Example</h4> <blockquote> <pre><code>apiVersion: builtin kind: PrefixSuffixTransformer metadata: name: not-important-to-example prefix: baked- suffix: -pie fieldSpecs: - path: metadata/name </code></pre></blockquote> <h2 id="_replicacounttransformer_"><em>ReplicaCountTransformer</em></h2> <h3 id="usage-via-kustomizationyaml-9">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-replicas">field name: <code>replicas</code></h4> <p>Replicas modified the number of replicas for a resource.</p> <p>E.g. Given this kubernetes Deployment fragment:</p> <pre><code>kind: Deployment metadata: name: deployment-name spec: replicas: 3 </code></pre><p>one can change the number of replicas to 5 by adding the following to your kustomization:</p> <pre><code>replicas: - name: deployment-name count: 5 </code></pre><p>This field accepts a list, so many resources can be modified at the same time.</p> <p>As this declaration does not take in a <code>kind:</code> nor a <code>group:</code> it will match any <code>group</code> and <code>kind</code> that has a matching name and that is one of:</p> <ul> <li><code>Deployment</code></li> <li><code>ReplicationController</code></li> <li><code>ReplicaSet</code></li> <li><code>StatefulSet</code></li> </ul> <p>For more complex use cases, revert to using a patch.</p> <h3 id="usage-via-plugin-9">Usage via plugin</h3> <h4 id="arguments-9">Arguments</h4> <blockquote> <p>Replica <a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/replica.go">types.Replica</a></p> <p>FieldSpecs []<a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/fieldspec.go">config.FieldSpec</a></p> </blockquote> <h4 id="example-9">Example</h4> <blockquote> <pre><code>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 </code></pre></blockquote> <h2 id="_secretgenerator_"><em>SecretGenerator</em></h2> <h3 id="usage-via-kustomizationyaml-10">Usage via <code>kustomization.yaml</code></h3> <h4 id="field-name-secretgenerator">field name: <code>secretGenerator</code></h4> <p>Each entry in the argument list results in the creation of one Secret resource (it&rsquo;s a generator of n secrets).</p> <p>This works like the <code>configMapGenerator</code> field described above.</p> <pre><code>secretGenerator: - name: app-tls files: - secret/tls.cert - secret/tls.key type: &quot;kubernetes.io/tls&quot; - name: app-tls-namespaced # you can define a namespace to generate # a secret in, defaults to: &quot;default&quot; namespace: apps files: - tls.crt=catsecret/tls.cert - tls.key=secret/tls.key type: &quot;kubernetes.io/tls&quot; - name: env_file_secret envs: - env.txt type: Opaque - name: secret-with-annotation files: - app-config.yaml type: Opaque options: annotations: app_config: &quot;true&quot; labels: app.kubernetes.io/name: &quot;app2&quot; </code></pre><h3 id="usage-via-plugin-10">Usage via plugin</h3> <h4 id="arguments-10">Arguments</h4> <blockquote> <p><a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/objectmeta.go">types.ObjectMeta</a></p> <p><a href="https://github.com/kubernetes-sigs/kustomize/tree/master/api/types/secretargs.go">types.SecretArgs</a></p> </blockquote> <h4 id="example-10">Example</h4> <blockquote> <pre><code>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 </code></pre></blockquote> Guides: Exec plugin on linux https://kubernetes-sigs.github.io/kustomize/guides/plugins/execpluginguidedexample/ Mon, 01 Jan 0001 00:00:00 +0000 https://kubernetes-sigs.github.io/kustomize/guides/plugins/execpluginguidedexample/ <p>This is a (no reading allowed!) 60 second copy/paste guided example. Full plugin docs <a href="..">here</a>.</p> <p>This demo writes and uses a somewhat ridiculous <em>exec</em> plugin (written in bash) that generates a <code>ConfigMap</code>.</p> <p>This is a guide to try it without damaging your current setup.</p> <h4 id="requirements">requirements</h4> <ul> <li>linux, git, curl, Go 1.13</li> </ul> <h2 id="make-a-place-to-work">Make a place to work</h2> <pre><code>DEMO=$(mktemp -d) </code></pre><h2 id="create-a-kustomization">Create a kustomization</h2> <p>Make a kustomization directory to hold all your config:</p> <pre><code>MYAPP=$DEMO/myapp mkdir -p $MYAPP </code></pre><p>Make a deployment config:</p> <pre><code>cat &lt;&lt;'EOF' &gt;$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: [&quot;/hello&quot;, &quot;--port=8080&quot;, &quot;--date=$(THE_DATE)&quot;, &quot;--enableRiskyFeature=$(ENABLE_RISKY)&quot;] 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 </code></pre><p>Make a service config:</p> <pre><code>cat &lt;&lt;EOF &gt;$MYAPP/service.yaml kind: Service apiVersion: v1 metadata: name: the-service spec: type: LoadBalancer ports: - protocol: TCP port: 8666 targetPort: 8080 EOF </code></pre><p>Now make a config file for the plugin you&rsquo;re about to write.</p> <p>This config file is just another k8s resource object. The values of its <code>apiVersion</code> and <code>kind</code> fields are used to <em>find</em> the plugin code on your filesystem (more on this later).</p> <pre><code>cat &lt;&lt;'EOF' &gt;$MYAPP/cmGenerator.yaml apiVersion: myDevOpsTeam kind: SillyConfigMapGenerator metadata: name: whatever argsOneLiner: Bienvenue true EOF </code></pre><p>Finally, make a kustomization file referencing all of the above:</p> <pre><code>cat &lt;&lt;EOF &gt;$MYAPP/kustomization.yaml commonLabels: app: hello resources: - deployment.yaml - service.yaml generators: - cmGenerator.yaml EOF </code></pre><p>Review the files</p> <pre><code>ls -C1 $MYAPP </code></pre><h2 id="make-a-home-for-plugins">Make a home for plugins</h2> <p>Plugins must live in a particular place for kustomize to find them.</p> <p>This demo will use the ephemeral directory:</p> <pre><code>PLUGIN_ROOT=$DEMO/kustomize/plugin </code></pre><p>The plugin config defined above in <code>$MYAPP/cmGenerator.yaml</code> specifies:</p> <blockquote> <pre><code>apiVersion: myDevOpsTeam kind: SillyConfigMapGenerator </code></pre></blockquote> <p>This means the plugin must live in a directory named:</p> <pre><code>MY_PLUGIN_DIR=$PLUGIN_ROOT/myDevOpsTeam/sillyconfigmapgenerator mkdir -p $MY_PLUGIN_DIR </code></pre><p>The directory name is the plugin config&rsquo;s <em>apiVersion</em> followed by its lower-cased <em>kind</em>.</p> <p>A plugin gets its own directory to hold itself, its tests and any supplemental data files it might need.</p> <h2 id="create-the-plugin">Create the plugin</h2> <p>There are two kinds of plugins, <em>exec</em> and <em>Go</em>.</p> <p>Make an <em>exec</em> plugin, installing it to the correct directory and file name. The file name must match the plugin&rsquo;s <em>kind</em> (in this case, <code>SillyConfigMapGenerator</code>):</p> <pre><code>cat &lt;&lt;'EOF' &gt;$MY_PLUGIN_DIR/SillyConfigMapGenerator #!/bin/bash # Skip the config file name argument. shift today=`date +%F` echo &quot; kind: ConfigMap apiVersion: v1 metadata: name: the-map data: today: $today altGreeting: &quot;$1&quot; enableRisky: &quot;$2&quot; &quot; EOF </code></pre><p>By definition, an <em>exec</em> plugin must be executable:</p> <pre><code>chmod a+x $MY_PLUGIN_DIR/SillyConfigMapGenerator </code></pre><h2 id="install-kustomize">Install kustomize</h2> <p>Per the <a href="https://kubernetes-sigs.github.io/kustomize/kustomize/installation">instructions</a>:</p> <pre><code>curl -s &quot;https://raw.githubusercontent.com/\ kubernetes-sigs/kustomize/master/hack/install_kustomize.sh&quot; | bash mkdir -p $DEMO/bin mv kustomize $DEMO/bin </code></pre><h2 id="review-the-layout">Review the layout</h2> <pre><code>tree $DEMO </code></pre><h2 id="build-your-app-using-the-plugin">Build your app, using the plugin</h2> <pre><code>XDG_CONFIG_HOME=$DEMO $DEMO/bin/kustomize build --enable_alpha_plugins $MYAPP </code></pre><p>Above, if you had set</p> <blockquote> <pre><code>PLUGIN_ROOT=$HOME/.config/kustomize/plugin </code></pre></blockquote> <p>there would be no need to use <code>XDG_CONFIG_HOME</code> in the <em>kustomize</em> command above.</p> Guides: Go plugin Caveats https://kubernetes-sigs.github.io/kustomize/guides/plugins/goplugincaveats/ Mon, 01 Jan 0001 00:00:00 +0000 https://kubernetes-sigs.github.io/kustomize/guides/plugins/goplugincaveats/ <p>A <em>Go plugin</em> is a compilation artifact described by the Go <a href="https://golang.org/pkg/plugin">plugin package</a>. It is built with special flags and cannot run on its own. It must be loaded into a running Go program.</p> <blockquote> <p>A normal program written in Go might be usable as <em>exec plugin</em>, but is not a <em>Go plugin</em>.</p> </blockquote> <p>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.</p> <p>Go plugins work as described in the <a href="https://golang.org/pkg/plugin">plugin package</a>, but fall short of common notions associated with the word <em>plugin</em>.</p> <h2 id="the-skew-problem">The skew problem</h2> <p>Go plugin compilation creates an <a href="https://en.wikipedia.org/wiki/Executable_and_Linkable_Format">ELF</a> formatted <code>.so</code> file, which by definition has no information about the provenance of the object code.</p> <p>Skew between the compilation conditions (versions of package dependencies, <code>GOOS</code>, <code>GOARCH</code>) of the main program ELF and the plugin ELF will cause plugin load failure, with non-helpful error messages.</p> <p>Exec plugins also lack provenance, but won&rsquo;t fail due to compilation skew.</p> <p>In either case, the only sensible way to share a plugin is as some kind of <em>bundle</em> (a git repo URL, a git archive file, a tar file, etc.) containing source code, tests and associated data, unpackable under <code>$XDG_CONFIG_HOME/kustomize/plugin</code>.</p> <p>In the case of a Go plugin, an <em>end user</em> accepting a shared plugin <em>must compile both kustomize and the plugin</em>.</p> <p>This means a one-time run of</p> <pre><code># Or whatever is appropriate at time of reading GOPATH=${whatever} GO111MODULE=on go get sigs.k8s.io/kustomize/api </code></pre><p>and then a normal development cycle using</p> <pre><code>go build -buildmode plugin \ -o ${wherever}/${kind}.so ${wherever}/${kind}.go </code></pre><p>with paths and the release version tag (e.g. <code>v3.0.0</code>) adjusted as needed.</p> <p>For comparison, consider what one must do to write a <a href="https://www.tensorflow.org/guide/extend/op">tensorflow plugin</a>.</p> <h2 id="why-support-go-plugins">Why support Go plugins</h2> <h3 id="safety">Safety</h3> <p>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.</p> <p>Minor point: if the plugin reads files via the kustomize-provided file <code>Loader</code> interface, it will be constrained by kustomize file loading restrictions. Of course, nothing but a code audit prevents a Go plugin from importing the <code>io</code> package and doing whatever it wants.</p> <h3 id="debugging">Debugging</h3> <p>A Go plugin developer can debug the plugin <em>in situ</em>, setting breakpoints inside the plugin and elsewhere while running a plugin in feature tests.</p> <p>To get the best of both worlds (shareability and safety), a developer can write an <code>.go</code> program that functions as an <em>exec plugin</em>, but can be processed by <code>go generate</code> to emit a <em>Go plugin</em> (or vice versa).</p> <h3 id="unit-of-contribution">Unit of contribution</h3> <p>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).</p> <h3 id="ecosystems-grow-through-use">Ecosystems grow through use</h3> <p>Tooling could ease Go plugin <em>sharing</em>, but this requires some critical mass of Go plugin <em>authoring</em>, which in turn is hampered by confusion around sharing. <a href="https://github.com/golang/go/wiki/Modules">Go modules</a>, once they are more widely adopted, will solve the biggest plugin sharing difficulty: ambiguous plugin vs host dependencies.</p> Guides: Go plugin example https://kubernetes-sigs.github.io/kustomize/guides/plugins/gopluginguidedexample/ Mon, 01 Jan 0001 00:00:00 +0000 https://kubernetes-sigs.github.io/kustomize/guides/plugins/gopluginguidedexample/ <h1 id="go-plugin-guided-example-for-linux">Go Plugin Guided Example for Linux</h1> <p>This is a (no reading allowed!) 60 second copy/paste guided example.</p> <p>Full plugin docs <a href="README.md">here</a>. Be sure to read the <a href="goPluginCaveats.md">Go plugin caveats</a>.</p> <p>This demo uses a Go plugin, <code>SopsEncodedSecrets</code>, that lives in the <a href="https://github.com/monopole/sopsencodedsecrets">sopsencodedsecrets repository</a>. This is an inprocess <a href="https://golang.org/pkg/plugin">Go plugin</a>, not an sub-process exec plugin that happens to be written in Go (which is another option for Go authors).</p> <p>This is a guide to try it without damaging your current setup.</p> <h4 id="requirements">requirements</h4> <ul> <li>linux, git, curl, Go 1.13</li> </ul> <p>For encryption</p> <ul> <li>gpg</li> </ul> <p>Or</p> <ul> <li>Google cloud (gcloud) install</li> <li>a Google account with KMS permission</li> </ul> <h2 id="make-a-place-to-work">Make a place to work</h2> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#8f5902;font-style:italic"># Keeping these separate to avoid cluttering the DEMO dir.</span> <span style="color:#000">DEMO</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>mktemp -d<span style="color:#204a87;font-weight:bold">)</span> <span style="color:#000">tmpGoPath</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span>mktemp -d<span style="color:#204a87;font-weight:bold">)</span> </code></pre></div><h2 id="install-kustomize">Install kustomize</h2> <p>Need v3.0.0 for what follows, and you must <em>compile</em> it (not download the binary from the release page):</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">GOPATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$tmpGoPath</span> go install sigs.k8s.io/kustomize/kustomize </code></pre></div><h2 id="make-a-home-for-plugins">Make a home for plugins</h2> <p>A kustomize plugin is fully determined by its configuration file and source code.</p> <p>Kustomize plugin configuration files are formatted as kubernetes resource objects, meaning <code>apiVersion</code>, <code>kind</code> and <code>metadata</code> are <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields">required fields</a> in these config files.</p> <p>The kustomize program reads the config file (because the config file name appears in the <code>generators</code> or <code>transformers</code> field in the kustomization file), then locates the Go plugin&rsquo;s object code at the following location:</p> <blockquote> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">$XDG_CONFIG_HOME</span>/kustomize/plugin/<span style="color:#000">$apiVersion</span>/<span style="color:#000">$lKind</span>/<span style="color:#000">$kind</span>.so </code></pre></div></blockquote> <p>where <code>lKind</code> holds the lowercased kind. The plugin is then loaded and fed its config, and the plugin&rsquo;s output becomes part of the overall <code>kustomize build</code> process.</p> <p>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&rsquo;s not left to plugins to find their own config.</p> <p>This demo will house the plugin it uses at the ephemeral directory</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">PLUGIN_ROOT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$DEMO</span>/kustomize/plugin </code></pre></div><p>and ephemerally set <code>XDG_CONFIG_HOME</code> on a command line below.</p> <h3 id="what-apiversion-and-kind">What apiVersion and kind</h3> <p>At this stage in the development of kustomize plugins, plugin code doesn&rsquo;t know or care what <code>apiVersion</code> or <code>kind</code> appears in the config file sent to it.</p> <p>The plugin could check these fields, but it&rsquo;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.</p> <p>This demo uses a plugin called <em>SopsEncodedSecrets</em>, and it lives in the <a href="https://github.com/monopole/sopsencodedsecrets">SopsEncodedSecrets repository</a>.</p> <p>Somewhat arbitrarily, we&rsquo;ll chose to install this plugin with</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">apiVersion</span><span style="color:#ce5c00;font-weight:bold">=</span>mygenerators <span style="color:#000">kind</span><span style="color:#ce5c00;font-weight:bold">=</span>SopsEncodedSecrets </code></pre></div><h3 id="define-the-plugins-home-dir">Define the plugin&rsquo;s home dir</h3> <p>By convention, the ultimate home of the plugin code and supplemental data, tests, documentation, etc. is the lowercase form of its kind.</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">lKind</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span><span style="color:#204a87">echo</span> <span style="color:#000">$kind</span> <span style="color:#000;font-weight:bold">|</span> awk <span style="color:#4e9a06">&#39;{print tolower($0)}&#39;</span><span style="color:#204a87;font-weight:bold">)</span> </code></pre></div><h3 id="download-the-sopsencodedsecrets-plugin">Download the SopsEncodedSecrets plugin</h3> <p>In this case, the repo name matches the lowercase kind already, so we just clone the repo and get the proper directory name automatically:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">mkdir -p <span style="color:#000">$PLUGIN_ROOT</span>/<span style="color:#4e9a06">${</span><span style="color:#000">apiVersion</span><span style="color:#4e9a06">}</span> <span style="color:#204a87">cd</span> <span style="color:#000">$PLUGIN_ROOT</span>/<span style="color:#4e9a06">${</span><span style="color:#000">apiVersion</span><span style="color:#4e9a06">}</span> git clone git@github.com:monopole/sopsencodedsecrets.git </code></pre></div><p>Remember this directory:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">MY_PLUGIN_DIR</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$PLUGIN_ROOT</span>/<span style="color:#4e9a06">${</span><span style="color:#000">apiVersion</span><span style="color:#4e9a06">}</span>/<span style="color:#4e9a06">${</span><span style="color:#000">lKind</span><span style="color:#4e9a06">}</span> </code></pre></div><h3 id="try-the-plugins-own-test">Try the plugin&rsquo;s own test</h3> <p>Plugins may come with their own tests. This one does, and it hopefully passes:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#204a87">cd</span> <span style="color:#000">$MY_PLUGIN_DIR</span> go <span style="color:#204a87">test</span> SopsEncodedSecrets_test.go </code></pre></div><p>Build the object code for use by kustomize:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#204a87">cd</span> <span style="color:#000">$MY_PLUGIN_DIR</span> <span style="color:#000">GOPATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$tmpGoPath</span> go build -buildmode plugin -o <span style="color:#4e9a06">${</span><span style="color:#000">kind</span><span style="color:#4e9a06">}</span>.so <span style="color:#4e9a06">${</span><span style="color:#000">kind</span><span style="color:#4e9a06">}</span>.go </code></pre></div><p>This step may succeed, but kustomize might ultimately fail to load the plugin because of dependency <a href="https://kubernetes-sigs.github.io/kustomize/docs/plugins/README.md#caveats">skew</a>.</p> <p>On load failure</p> <ul> <li> <p>be sure to build the plugin with the same version of Go (<em>go1.13</em>) on the same <code>$GOOS</code> (<em>linux</em>) and <code>$GOARCH</code> (<em>amd64</em>) used to build the kustomize being <a href="#install-kustomize">used in this demo</a>.</p> </li> <li> <p>change the plugin&rsquo;s dependencies in its <code>go.mod</code> to match the versions used by kustomize (check kustomize&rsquo;s <code>go.mod</code> used in its tagged commit).</p> </li> </ul> <p>Lacking tools and metadata to allow this to be automated, there won&rsquo;t be a Go plugin ecosystem.</p> <p>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.</p> <h2 id="create-a-kustomization">Create a kustomization</h2> <p>Make a kustomization directory to hold all your config:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">MYAPP</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$DEMO</span>/myapp mkdir -p <span style="color:#000">$MYAPP</span> </code></pre></div><p>Make a config file for the SopsEncodedSecrets plugin.</p> <p>Its <code>apiVersion</code> and <code>kind</code> allow the plugin to be found:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">cat <span style="color:#4e9a06">&lt;&lt;EOF &gt;$MYAPP/secGenerator.yaml </span><span style="color:#4e9a06">apiVersion: ${apiVersion} </span><span style="color:#4e9a06">kind: ${kind} </span><span style="color:#4e9a06">metadata: </span><span style="color:#4e9a06"> name: mySecretGenerator </span><span style="color:#4e9a06">name: forbiddenValues </span><span style="color:#4e9a06">namespace: production </span><span style="color:#4e9a06">file: myEncryptedData.yaml </span><span style="color:#4e9a06">keys: </span><span style="color:#4e9a06">- ROCKET </span><span style="color:#4e9a06">- CAR </span><span style="color:#4e9a06">EOF</span> </code></pre></div><p>This plugin expects to find more data in <code>myEncryptedData.yaml</code>; we&rsquo;ll get to that shortly.</p> <p>Make a kustomization file referencing the plugin config:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">cat <span style="color:#4e9a06">&lt;&lt;EOF &gt;$MYAPP/kustomization.yaml </span><span style="color:#4e9a06">commonLabels: </span><span style="color:#4e9a06"> app: hello </span><span style="color:#4e9a06">generators: </span><span style="color:#4e9a06">- secGenerator.yaml </span><span style="color:#4e9a06">EOF</span> </code></pre></div><p>Now generate the real encrypted data.</p> <h3 id="assure-you-have-an-encryption-tool-installed">Assure you have an encryption tool installed</h3> <p>We&rsquo;re going to use <a href="https://github.com/mozilla/sops">sops</a> to encode a file. Choose either GPG or Google Cloud KMS as the secret provider to continue.</p> <h4 id="gpg">GPG</h4> <p>Try this:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">gpg --list-keys </code></pre></div><p>If it returns a list, presumably you&rsquo;ve already created keys. If not, try import test keys from sops for dev.</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">curl https://raw.githubusercontent.com/mozilla/sops/master/pgp/sops_functional_tests_key.asc <span style="color:#000;font-weight:bold">|</span> gpg --import <span style="color:#000">SOPS_PGP_FP</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#4e9a06">&#34;1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A&#34;</span> </code></pre></div><h4 id="google-cloude-kms">Google Cloude KMS</h4> <p>Try this:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">gcloud kms keys list --location global --keyring sops </code></pre></div><p>If it succeeds, presumably you&rsquo;ve already created keys and placed them in a keyring called sops. If not, do this:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">gcloud kms keyrings create sops --location global gcloud kms keys create sops-key --location global <span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> --keyring sops --purpose encryption </code></pre></div><p>Extract your keyLocation for use below:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">keyLocation</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#204a87;font-weight:bold">$(</span><span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> gcloud kms keys list --location global --keyring sops <span style="color:#000;font-weight:bold">|</span><span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> grep GOOGLE <span style="color:#000;font-weight:bold">|</span> cut -d <span style="color:#4e9a06">&#34; &#34;</span> -f1<span style="color:#204a87;font-weight:bold">)</span> <span style="color:#204a87">echo</span> <span style="color:#000">$keyLocation</span> </code></pre></div><h3 id="install-sops">Install <code>sops</code></h3> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">GOPATH</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$tmpGoPath</span> go install go.mozilla.org/sops/cmd/sops </code></pre></div><h3 id="create-data-encrypted-with-your-private-key">Create data encrypted with your private key</h3> <p>Create raw data to encrypt:</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">cat <span style="color:#4e9a06">&lt;&lt;EOF &gt;$MYAPP/myClearData.yaml </span><span style="color:#4e9a06">VEGETABLE: carrot </span><span style="color:#4e9a06">ROCKET: saturn-v </span><span style="color:#4e9a06">FRUIT: apple </span><span style="color:#4e9a06">CAR: dymaxion </span><span style="color:#4e9a06">EOF</span> </code></pre></div><p>Encrypt the data into file the plugin wants to read:</p> <p>With PGP</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">$tmpGoPath</span>/bin/sops --encrypt <span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> --pgp <span style="color:#000">$SOPS_PGP_FP</span> <span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> <span style="color:#000">$MYAPP</span>/myClearData.yaml &gt;<span style="color:#000">$MYAPP</span>/myEncryptedData.yaml </code></pre></div><p>Or GCP KMS</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">$tmpGoPath</span>/bin/sops --encrypt <span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> --gcp-kms <span style="color:#000">$keyLocation</span> <span style="color:#4e9a06">\ </span><span style="color:#4e9a06"></span> <span style="color:#000">$MYAPP</span>/myClearData.yaml &gt;<span style="color:#000">$MYAPP</span>/myEncryptedData.yaml </code></pre></div><p>Review the files</p> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">tree <span style="color:#000">$DEMO</span> </code></pre></div><p>This should look something like:</p> <blockquote> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="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 </code></pre></div></blockquote> <h2 id="build-your-app-using-the-plugin">Build your app, using the plugin</h2> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">XDG_CONFIG_HOME</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$DEMO</span> <span style="color:#000">$tmpGoPath</span>/bin/kustomize build --enable_alpha_plugins <span style="color:#000">$MYAPP</span> </code></pre></div><p>This should emit a kubernetes secret, with encrypted data for the names <code>ROCKET</code> and <code>CAR</code>.</p> <p>Above, if you had set</p> <blockquote> <div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#000">PLUGIN_ROOT</span><span style="color:#ce5c00;font-weight:bold">=</span><span style="color:#000">$HOME</span>/.config/kustomize/plugin </code></pre></div></blockquote> <p>there would be no need to use <code>XDG_CONFIG_HOME</code> in the <em>kustomize</em> command above.</p>