<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steven</title>
    <description>The latest articles on DEV Community by Steven (@steg87).</description>
    <link>https://dev.to/steg87</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2442984%2Fa83db65e-57fd-4488-ab9c-d1f6ffe2a7c6.png</url>
      <title>DEV Community: Steven</title>
      <link>https://dev.to/steg87</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/steg87"/>
    <language>en</language>
    <item>
      <title>Adding Templating to a Kustomize Deployment</title>
      <dc:creator>Steven</dc:creator>
      <pubDate>Sun, 17 Nov 2024 16:54:44 +0000</pubDate>
      <link>https://dev.to/steg87/adding-templating-to-a-kustomize-deployment-38nc</link>
      <guid>https://dev.to/steg87/adding-templating-to-a-kustomize-deployment-38nc</guid>
      <description>&lt;p&gt;There are two main tools to help parameterise Kubernetes application deployments, Helm and Kustomize. While Helm focuses on allowing you to build or use others' applications based on parameterising manifests using templating, Kustomize avoids templating by allowing you to create overlays that can override a base configuration for specific deployments.&lt;/p&gt;

&lt;p&gt;While the two can be used independently, they are more powerful when used together. The approach I use the most is to use Kustomize to declare your own custom app deployments and use Helm to deploy ready made deployments of 3rd party services.&lt;/p&gt;

&lt;p&gt;However, I do run into a limitation of Kustomize frequently for larger, multi-app deployments, namely, repetition. &lt;/p&gt;

&lt;p&gt;Frequently we have parameters that we would like to declare at a global level and inject them somehow into our various apps within our deployment. Think the hostname for a platform, which is shared by all of our app ingresses. If the hostname changes, we need to find and replace it in all of our ingresses. This can be accomplished with other Kustomize functionality (replacements), which will be illustrated further down, but as we will see, this can only take us so far.&lt;/p&gt;

&lt;p&gt;Ultimately, the feature we are seeking is templating. Kustomize offers no templating option of its own, and the Kustomize maintainers have clearly stated that they do not plan to provide it. It is such a frequently requested feature that the Kustomize maintainers now require you to tick a box on feature requests stating that the feature you are requesting does not involve templating. This may be because Kustomize has declared itself as the "template free configuration tool" for Kubernetes, which is fine. But with so many requesting the feature it leaves us with the question, how can we solve this problem on our own?&lt;/p&gt;

&lt;p&gt;I would like to share the approach we have taken, as I think it would be of use. Firstly though, lets recap the built-in features that Kustomize offers to address duplication amongst manfiests. It may well be that your use case is already supported by Kustomize.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Example Repo
&lt;/h2&gt;

&lt;p&gt;I have created a repository to illustrate the problem and make the solution clearer. You can find it at &lt;a href="https://github.com/steg87/kustomize-templating" rel="noopener noreferrer"&gt;https://github.com/steg87/kustomize-templating&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is a very simple example, with two apps serving different (but very similar) html files using nginx containers.&lt;/p&gt;

&lt;p&gt;The different milestones in this blog post are tagged in git.&lt;/p&gt;

&lt;p&gt;There is a README.md included in the root of the repo to help you get set up with a demonstration deployment if you want to play about with it. The README.md instructions are updated in line with the tagged versions, so revisit it when you move on. In particular, the commands to build and deploy the platform are given in the makefile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The example starts with v0.1.0. The issue with this version is that we have our platform host define in two different ingresses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app1/ingress.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the /app1 prefix from path when passing to upstream service&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt; &lt;span class="c1"&gt;# Ingress class for default minikube ingress controller&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.minikube.com&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app1&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app2/ingress.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app2&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the /app2 prefix from path when passing to upstream service&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt; &lt;span class="c1"&gt;# Ingress class for default minikube ingress controller&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.minikube.com&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app2&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app2&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we need to update platform host, then we need to do so in both of the ingresses. While not a big deal in this deployment, if we have tens of apps it can be frustrating.&lt;/p&gt;

&lt;p&gt;In the first instance, we can resolve this with Kustomize Replacements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kustomize Replacements
&lt;/h2&gt;

&lt;p&gt;To allow for global parameters, we can use Kustomize Replacements. Replacements allow us to replace a target manifest field with a source value. Source values can be taken from any manifest generated as part of the Kustomize built, but often a ConfigMap is the best choice. In this ConfigMap we can declare our global variables at the root level (or wherever you choose, the point is that they are not local to any individual app).&lt;/p&gt;

&lt;p&gt;To follow along with the example repo, checkout v0.1.1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/globals.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;globals&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Tells Kustomize not to output this manifest as part of the build. It is&lt;/span&gt;
    &lt;span class="c1"&gt;# only used for local configuration.&lt;/span&gt;
    &lt;span class="na"&gt;config.kubernetes.io/local-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local.minikube.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, we have added &lt;code&gt;hostname: local.minikube.com&lt;/code&gt; as a global variable, which is now available for replacement into the apps.&lt;/p&gt;

&lt;p&gt;Before we proceed, let's remove the hardcoded hostnames from our ingresses. I like to declare values that should be replaced with &lt;code&gt;&amp;lt;&amp;lt;REPLACE&amp;gt;&amp;gt;&lt;/code&gt;. This can indicate to a developer that the value will be generated at build time. It's also handy to use a constant, known string to indicate replacements as Kustomize will not fail if you accidentally replace the wrong field. Instead it will overwrite that field and leave the indended one with its original value. I always test my builds locally by redirecting build output to a file (with &lt;code&gt;make build &amp;gt; k.yaml&lt;/code&gt;) and search for &lt;code&gt;&amp;lt;&amp;lt;REPLACE&amp;gt;&amp;gt;&lt;/code&gt; to check if any replacements have been misconfigured. Updating this value will also help convince you that replacements work, as there's not much point in replacing a field with the same value.&lt;/p&gt;

&lt;p&gt;Next, we need to map this replacement parameter to fields in our apps. We do this from the Kustomization file of our app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app1/kustomization.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kustomization&lt;/span&gt;

&lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;../config&lt;/span&gt; &lt;span class="c1"&gt;# We need to include the config map from the config directory&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;namespace.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deployment.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;service.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ingress.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# This generator just generates a config map from the local html file so we can&lt;/span&gt;
&lt;span class="c1"&gt;# mount it into the deployment to be served by nginx&lt;/span&gt;
&lt;span class="na"&gt;configMapGenerator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1-index&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;index.html=files/index.html&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;disableNameSuffixHash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Configure the replacements for the deployment&lt;/span&gt;
&lt;span class="na"&gt;replacements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;globals&lt;/span&gt;
      &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data.hostname&lt;/span&gt;
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
        &lt;span class="na"&gt;fieldPaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;spec.rules.0.host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly, note that we have to include the ./config directory as part of the app Kustomization resources. This gives Kustomize access to the global parameters.&lt;/p&gt;

&lt;p&gt;Next, we add a replacements section, mapping the source parameter (or globals configmap data field) to a target field (the ingress hostname).&lt;/p&gt;

&lt;p&gt;Note how paths are defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subfields are delimited by '.'&lt;/li&gt;
&lt;li&gt;Indices are input as subfields, zero indexed as you would expect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From &lt;code&gt;make build&lt;/code&gt; we can see the output is exactly the same as before.&lt;/p&gt;

&lt;p&gt;You can also replace only part of a field value, if you can configure the delimiters to allow that. I won't go into detail on that, but you can read more in the &lt;a href="https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/replacements/" rel="noopener noreferrer"&gt;replacement docs&lt;/a&gt;, see "Delimiter" and "Index" sections. But this is about as far as replacements can take us. We cannot replace anything that isn't a Kubernetes resource field. To see an illustration of this point, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templating
&lt;/h2&gt;

&lt;p&gt;We will move on to implementing templating in the worked example shortly. It is as simplified as possible, but demonstrates the point. First, to give more context, I will discuss a real world example. &lt;/p&gt;

&lt;h3&gt;
  
  
  Real World Problem
&lt;/h3&gt;

&lt;p&gt;The original reason I had to implement this was AWS ACK Controller Role resources. They require you to define the permission policies in string json form. Here is an AWS IAM role for Cert Manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;iam.services.k8s.aws/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Role&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cert-manager&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CertManager-dev&lt;/span&gt;
  &lt;span class="na"&gt;assumeRolePolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;{&lt;/span&gt;
      &lt;span class="s"&gt;"Version": "2012-10-17",&lt;/span&gt;
      &lt;span class="s"&gt;"Statement": [&lt;/span&gt;
          &lt;span class="s"&gt;{&lt;/span&gt;
              &lt;span class="s"&gt;"Effect": "Allow",&lt;/span&gt;
              &lt;span class="s"&gt;"Principal": {&lt;/span&gt;
                  &lt;span class="s"&gt;"Federated": "arn:aws:iam::123456789:oidc-provider/oidc.eks.eu-west-2.amazonaws.com/id/ABCDEGHIJKLMNOPQSTUVWXYZ"&lt;/span&gt;
              &lt;span class="s"&gt;},&lt;/span&gt;
              &lt;span class="s"&gt;"Action": "sts:AssumeRoleWithWebIdentity",&lt;/span&gt;
              &lt;span class="s"&gt;"Condition": {&lt;/span&gt;
                  &lt;span class="s"&gt;"StringEquals": {&lt;/span&gt;
                      &lt;span class="s"&gt;"oidc.eks.eu-west-2.amazonaws.com/id/ABCDEGHIJKLMNOPQSTUVWXYZ:sub": "system:serviceaccount:cert-manager:cert-manager"&lt;/span&gt;
                  &lt;span class="s"&gt;}&lt;/span&gt;
              &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;]&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;inlinePolicies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CertManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Version": "2012-10-17",&lt;/span&gt;
        &lt;span class="s"&gt;"Statement": [&lt;/span&gt;
            &lt;span class="s"&gt;{&lt;/span&gt;
              &lt;span class="s"&gt;"Effect": "Allow",&lt;/span&gt;
              &lt;span class="s"&gt;"Action": "route53:GetChange",&lt;/span&gt;
              &lt;span class="s"&gt;"Resource": "arn:aws:route53:::change/*"&lt;/span&gt;
            &lt;span class="s"&gt;},&lt;/span&gt;
            &lt;span class="s"&gt;{&lt;/span&gt;
              &lt;span class="s"&gt;"Effect": "Allow",&lt;/span&gt;
              &lt;span class="s"&gt;"Action": [&lt;/span&gt;
                &lt;span class="s"&gt;"route53:ChangeResourceRecordSets",&lt;/span&gt;
                &lt;span class="s"&gt;"route53:ListResourceRecordSets"&lt;/span&gt;
              &lt;span class="s"&gt;],&lt;/span&gt;
              &lt;span class="s"&gt;"Resource": [&lt;/span&gt;
                &lt;span class="s"&gt;"arn:aws:route53:::hostedzone/Z0123456789ABCDEFG"&lt;/span&gt;
              &lt;span class="s"&gt;]&lt;/span&gt;
            &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;]&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We needed to change the cluster OIDC provider in the trust policy between our deployments, because each cluster has its own. There is no way to do this with Kustomize alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worked Example
&lt;/h3&gt;

&lt;p&gt;Back to our example, which is tag v0.1.3 in the repo. Let's say we want to modify the header shown in each app based on the environment we deploy to. This not only demonstrates that we can modify arbitrary parameters in our deployments, but that it doesn't even have to be a Kubernetes resource.&lt;/p&gt;

&lt;p&gt;We will need to modify some of the text in the files/index.html file in each app.&lt;/p&gt;

&lt;p&gt;I was looking for a general Go templating style CLI that I could use as part of my build command. In &lt;a href="https://docs.gomplate.ca/" rel="noopener noreferrer"&gt;Gomplate&lt;/a&gt; I found just that. It is a very simple, yet very powerful tool giving us access to Go templating from the command line. It is very lightweight, has an &lt;a href="https://docs.gomplate.ca/installing/" rel="noopener noreferrer"&gt;easy install&lt;/a&gt; and minimal configuration required. That being said, there is some recommended configuration that I suggest, which I will cover.&lt;/p&gt;

&lt;p&gt;You can see the syntax that Gomplate uses in their &lt;a href="https://docs.gomplate.ca/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. We're only going to use simple variable substitution but much more is possible. I would advise not to get too carried away though, part of the reason I favour Kustomize over Helm is that it is far more readable.&lt;/p&gt;

&lt;p&gt;The default template syntax for variable substitution in Gomplate is &lt;code&gt;{{.config.myVar}}&lt;/code&gt;. You pass the variable values in on the command line using &lt;code&gt;gomplate -c config=config.yaml ...&lt;/code&gt;. Note that &lt;code&gt;-c config=config.yaml&lt;/code&gt; means your variables will be available under the &lt;code&gt;{{.config.myVar}}&lt;/code&gt; path. For clarity, this would also work &lt;code&gt;-c vars=config.yaml&lt;/code&gt; with &lt;code&gt;{{.vars.myVar}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can pass in templates (files containing template syntax) either by string (&lt;code&gt;-i&lt;/code&gt; flag), by file (&lt;code&gt;-f&lt;/code&gt; flag) or, as we will do, by stdin (kustomize build . | gomplate -c config=config.yaml). The makefile build command has been modified to reflect this. An additional command &lt;code&gt;make template&lt;/code&gt; has also been added, which is handy to see un-rendered templates when you encounter a templating error with a line number reference.&lt;/p&gt;

&lt;p&gt;Only one further configuration is required. The default delimiters of Gomplate templating are not great for our use case. If you try to assign a Kubernetes resource value to a template value, you will get an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app1/ingress.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the /app1 prefix from path when passing to upstream service&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt; &lt;span class="c1"&gt;# Ingress class for default minikube ingress controller&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;.config.hostname&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;  &lt;span class="c1"&gt;# this will error&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app1&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because a Kubernetes resource value starting with '{' will not be interpreted as a string and will fail validation. For this reason, I modified the left delimiter of Gomplate to be '${{', borrowing from bash syntax and in the knowledge that values stating with '$' will be interpreted as strings.&lt;/p&gt;

&lt;p&gt;One further modification I make, which is relevant when your Kustomize deployments use Helm charts as sources, is to move away from the '{{', '}}' delimiters, as Helm can erronously try to render these during Kustomize Helm chart inflation. Instead, I use '{[', ']}' (inner square brackets).&lt;/p&gt;

&lt;p&gt;We can define these configurations changes in a &lt;code&gt;.gomplate.yaml&lt;/code&gt; file in the root of the repo, which is where Gomplate will look for configuration by default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gomplate.yaml&lt;/span&gt;
&lt;span class="na"&gt;leftDelim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${["&lt;/span&gt;
&lt;span class="na"&gt;rightDelim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, with the configuration and makefile commands updated, we are ready to modify our app files/index.html files. We also need to template out the host in the ingress files, and remove the references to replacements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- app1/files/index.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;App 1&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;App 1 - ${[.config.env]}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app1/ingress.yaml&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove the /app1 prefix from path when passing to upstream service&lt;/span&gt;
    &lt;span class="na"&gt;nginx.ingress.kubernetes.io/rewrite-target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt; &lt;span class="c1"&gt;# Ingress class for default minikube ingress controller&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${[.config.hostname]}&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app1&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app1&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we render the output with &lt;code&gt;make build &amp;gt; k.yaml&lt;/code&gt; we can see that the files have been updated as we expect. We can deploy using &lt;code&gt;make deploy&lt;/code&gt; and see in our dev environment that the header at &lt;a href="http://local.minikube.com/app1" rel="noopener noreferrer"&gt;http://local.minikube.com/app1&lt;/a&gt; has been updated.&lt;/p&gt;

&lt;p&gt;This has been a simple example, but hopefully you can see how we can extend this by using multiple config.yaml files for different deployments. When combined with the app specific configuration options that Kustomize provides, we have the ability to parameterise anything we want in our deployments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To implemement deployment global parameters we can use Kustomize replacements to a point. If you require more complex templating features, you can implement Gomplate as part of your manifest build pipeline. The suggested implementation should not interfere with any existing Kustomize (or Helm, if you also use it) builds.&lt;/p&gt;

</description>
      <category>kustomize</category>
      <category>templating</category>
      <category>kubernetes</category>
    </item>
  </channel>
</rss>
