<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jose Armesto&#39;s Blog</title>
    <link>https://blog.armesto.net/tags/kind/index.xml</link>
    <description>Recent content on Jose Armesto&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>es-es</language>
    <copyright>Powered by [Hugo](//gohugo.io). Theme by [PPOffice](https://github.com/ppoffice).</copyright>
    <atom:link href="https://blog.armesto.net/tags/kind/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Continuous Integrating my Kubernetes controller using Kind</title>
      <link>https://blog.armesto.net/continuous-integrating-my-kubernetes-controller-using-kind/</link>
      <pubDate>Sat, 18 May 2019 23:47:55 +0000</pubDate>
      
      <guid>https://blog.armesto.net/continuous-integrating-my-kubernetes-controller-using-kind/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://stackoverflow.com/questions/47848258/kubernetes-controller-vs-kubernetes-operator/47857073#47857073&#34;&gt;Kubernetes controllers&lt;/a&gt; are processes that react to changes in the state of some objects saved on the Kubernetes API.
They do this by watching the Kubernetes API so that they get notified whenever the state changes.&lt;/p&gt;

&lt;p&gt;But how do we know if our Kubernetes controller is doing what&amp;rsquo;s supposed to do?
We can write &lt;a href=&#34;https://codeclimate.com/github/fiunchinho/iam-role-annotator&#34;&gt;unit tests&lt;/a&gt; that test our logic in isolation to get some confidence, but it would be great if we could add some &lt;a href=&#34;https://martinfowler.com/articles/practical-test-pyramid.html&#34;&gt;end to end tests&lt;/a&gt; using the real Kubernetes API.&lt;/p&gt;

&lt;p&gt;However, deploying Kubernetes is not an easy task. And even if we managed to accomplish it, it would take a long time to deploy a Kubernetes cluster so we could test every change to our code base.&lt;/p&gt;

&lt;p&gt;In this post, I&amp;rsquo;m going to explain how I used &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt; to test that &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;my controller&lt;/a&gt; is working as expected, using a real Kubernetes API.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h2 id=&#34;creating-the-cluster-using-kind&#34;&gt;Creating the cluster using kind&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;My Kubernetes controller&lt;/a&gt; is pretty simple. Whenever a Deployment object is created containing an specific annotation, it will add an annotation to the Pods belonging to that Deployment.
I created this controller because we needed a way for developers to specify that the application that they were deploying required AWS IAM credentials to use AWS services, but we didn&amp;rsquo;t want developers to have to know things like the AWS account id to use.
By using this controller, the Kubernetes cluster administrators configure which AWS Account to use on every Kubernetes namespace. And developers just indicate whether their application requires IAM authentication or not, by adding an annotation. Simple.&lt;/p&gt;

&lt;p&gt;Anyway, I wanted to create an end to end test that would do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Install the controller using the version that we are testing&lt;/li&gt;
&lt;li&gt;Create a Deployment that contains the annotation that will trigger the controller&lt;/li&gt;
&lt;li&gt;Test that the Pods of the Deployment contain the IAM annotation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the Kubernetes cluster I used &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt;, a recent project that let&amp;rsquo;s you deploy a Kubernetes cluster using only a Docker container. Obviously, it&amp;rsquo;s not meant to be used on production, but it&amp;rsquo;s perfect for testing purposes. To create a cluster with kind, you just need to&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ kind create cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that&amp;rsquo;s it! It&amp;rsquo;s that simple. We can see the cluster is running inside a single container&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                                  NAMES
c27f2c8fa39e        kindest/node:v1.14.1   &amp;quot;/usr/local/bin/entr…&amp;quot;   22 minutes ago      Up 22 minutes       63711/tcp, 127.0.0.1:63711-&amp;gt;6443/tcp   kind-control-plane
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you just need to make &lt;code&gt;kubectl&lt;/code&gt; send requests to that cluster, using the command&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ export KUBECONFIG=&amp;quot;$(kind get kubeconfig-path)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&amp;rsquo;m also creating a new Namespace where I&amp;rsquo;ll run all the things required for this test. This is not important during Continuous Integration (CI), where everything will be deleted when the CI job is finished.
But I do it this way because I can then easily run the same test locally on other Kubernetes clusters like Minikube, while not on CI.
When the test is done, I just remove the namespace to leave no traces of the test execution.&lt;/p&gt;

&lt;h2 id=&#34;installing-my-controller&#34;&gt;Installing my controller&lt;/h2&gt;

&lt;p&gt;Now I need to install the piece of code that I want to test: my controller. The controller repository contains a &lt;a href=&#34;https://helm.sh/&#34;&gt;Helm&lt;/a&gt; chart to make it easy to install it, so I&amp;rsquo;ll just use it. That way, I&amp;rsquo;m also testing whether or not the Helm chart is working as expected.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ helm upgrade --tiller-namespace ${NAMESPACE} --namespace &amp;quot;${NAMESPACE}&amp;quot; --wait --install &amp;quot;iam-role-annotator&amp;quot; &amp;quot;./charts/iam-role-annotator&amp;quot; --set image.tag=&amp;quot;${TRAVIS_COMMIT:-latest}&amp;quot; --set awsAccountId=&amp;quot;${AWS_ACCOUNT_ID}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The value for the &lt;code&gt;AWS_ACCOUNT_ID&lt;/code&gt; variable is important. It&amp;rsquo;s the same AWS Account Id that needs to be added as annotation on applications.
Notice that I passed the &lt;code&gt;--wait&lt;/code&gt; argument to Helm. This will block the command until my controller is running and ready. If it fails to start for whatever reason, the test would automatically fail.&lt;/p&gt;

&lt;h2 id=&#34;create-annotated-deploy-and-check-the-results&#34;&gt;Create annotated deploy and check the results&lt;/h2&gt;

&lt;p&gt;My controller is ready and waiting for applications that contain the right annotation.
Creating a simple hello world application containing that annotation is enough to trigger my controller. Let&amp;rsquo;s use a simple nginx deployment&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat &amp;lt;&amp;lt;EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  namespace: ${NAMESPACE}
  labels:
    app: nginx
  annotations:
    armesto.net/iam-role-annotator: &amp;quot;true&amp;quot;
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
      annotations:
        prometheus.io/scheme: http
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the important annotation for my controller: &lt;code&gt;armesto.net/iam-role-annotator: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now I just need to wait for my controller to react.&lt;/p&gt;

&lt;p&gt;Then I can test if the pods belonging to the hello world deployment contain the required annotation. For that I used &lt;code&gt;jq&lt;/code&gt;, a handly command line tool to inspect json data.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;POD_NAME=$(kubectl get pods --namespace ${NAMESPACE} --field-selector=status.phase=Running -l &amp;quot;app=nginx&amp;quot; -o jsonpath=&amp;quot;{.items[0].metadata.name}&amp;quot;)

if [[ $(kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.metadata.annotations&#39; | jq &#39;contains({&amp;quot;iam.amazonaws.com/role&amp;quot;})&#39;) == &#39;true&#39; ]]; then
  if [[ $(kubectl get pods --namespace ${NAMESPACE} ${POD_NAME} -o json | jq -r &#39;.metadata.annotations.&amp;quot;iam.amazonaws.com/role&amp;quot;&#39;) == &amp;quot;arn:aws:iam::${AWS_ACCOUNT_ID}:role/${DEPLOYMENT_NAME}&amp;quot; ]]; then
    echo &amp;quot;SUCCESS!&amp;quot;
    exit 0
  else
    echo &amp;quot;ERROR: the annotation contains the wrong value&amp;quot;
    kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.&#39;
    exit 1
  fi
else
  echo &amp;quot;ERROR: the POD does not contain the expected annotation&amp;quot;
  kubectl get pod --namespace ${NAMESPACE} ${POD_NAME} -o json | jq &#39;.&#39;
  exit 1
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Every time I push some changes &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator&#34;&gt;to this controller&lt;/a&gt;, the CI pipeline kicks in, and &lt;a href=&#34;https://github.com/fiunchinho/iam-role-annotator/blob/master/e2e_test.sh&#34;&gt;my end to end test&lt;/a&gt; gets executed creating a Kubernetes cluster thanks to &lt;a href=&#34;https://kind.sigs.k8s.io/&#34;&gt;kind&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I have much more confidence that my changes will work as expected when deploying the controller to our production clusters, and that makes a huge difference.&lt;/p&gt;</description>
    </item>
    
  </channel>
</rss>