<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="http://willthames.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="http://willthames.github.io/" rel="alternate" type="text/html" /><updated>2020-05-16T18:12:49+10:00</updated><id>http://willthames.github.io/feed.xml</id><title type="html">Will Thames’ tech blog</title><subtitle>Lessons from working and playing with things like AWS, Ansible, and others</subtitle><entry><title type="html">Immutable Kubernetes configuration with Ansible</title><link href="http://willthames.github.io/2019/01/28/immutable-kubernetes-configuration-with-ansible.html" rel="alternate" type="text/html" title="Immutable Kubernetes configuration with Ansible" /><published>2019-01-28T20:00:00+10:00</published><updated>2019-01-28T20:00:00+10:00</updated><id>http://willthames.github.io/2019/01/28/immutable-kubernetes-configuration-with-ansible</id><content type="html" xml:base="http://willthames.github.io/2019/01/28/immutable-kubernetes-configuration-with-ansible.html">&lt;p&gt;This post touches on a key component of my &lt;a href=&quot;https://www.ansible.com/managing-kubernetes-is-easy-with-ansible&quot;&gt;Managing Kubernetes is Easy With
Ansible&lt;/a&gt;
talk that I gave at AnsibleFest 2018.
Since giving that talk, I’ve also solved some of the unforeseen consequences,
and go into further detail here.&lt;/p&gt;

&lt;p&gt;There are a number of problems associated with managing configuration (in the
form of configmaps and secrets) through the default mechanism.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Changes to configmaps or secrets are not picked up by the pods that rely on
them unless the related deployment is also updated&lt;/li&gt;
  &lt;li&gt;Rolling back a deployment to a previous version does not roll back the
associated configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can solve these problems through immutable configmaps and secrets that are
named based on their contents, such that if the contents change, their name
changes.&lt;/p&gt;

&lt;p&gt;The kubectl tool has part of a solution for that, in that you can pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--append-hash&lt;/code&gt;
when creating a configmap or secret, but only when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl create&lt;/code&gt;, it’s not
useful when applying resource definitions from files. However, this idea was
the inspiration to add an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;append_hash&lt;/code&gt; parameter to Ansible’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s&lt;/code&gt; module, and
this is part of the solution.&lt;/p&gt;

&lt;p&gt;Another part of the solution is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s_config_resource_name&lt;/code&gt; filter plugin, which
takes a configmap definition and returns the full name with the hash added.&lt;/p&gt;

&lt;p&gt;At this point we can define dicts of configmaps and secrets, and also refer to
the full resource name in deployments or similar resources.&lt;/p&gt;

&lt;p&gt;In inventory we might have something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
kube_resource_configmaps:
  env: &quot;{{ lookup('template', kube_resource_template_dir + 'env-configmap.yml') | from_yaml }}&quot;
kube_resource_secrets:
  env: &quot;{{ lookup('template', kube_resource_template_dir + 'env-secrets.yml') | from_yaml }}&quot;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;which then gets referenced in the deployment with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ kube_resource_name }}
  namespace: {{ kube_resource_namespace }}
spec:
  template:
    spec:
      containers:
        - envFrom:
            - configMapRef:
                name: {{ kube_resource_configmaps.env | k8s_config_resource_name }}
            - secretRef:
                name: {{ kube_resource_secrets.env | k8s_config_resource_name }}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Its best to create
the secrets and configmaps first, as the pods created by the deployment will depend
on them being present to be able to start - then you can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait&lt;/code&gt; functionality
that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s&lt;/code&gt; module gains with Ansible 2.8 to check the pods start correctly.&lt;/p&gt;

&lt;p&gt;At this point, we have met our original goals - changing a configmap or secret
will change its name, and so the deployment will change to point to the new name,
so new pods will be created using the new configuration. The replicaset to which
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl rollback deploy&lt;/code&gt; will roll back will contain the old configuration names,
and so rolling back a deployment will also roll back the configuration.&lt;/p&gt;

&lt;p&gt;However, my presentation ignores two issues (the first I was aware of but hadn’t yet
solved, the second my colleague highlighted as an obstacle to moving to the new
way of working)&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;there is no garbage collection of configmaps that were referenced by replicasets
but are no longer used&lt;/li&gt;
  &lt;li&gt;diffs no longer work - because the configmaps and secrets change name, it’s hard
to compare against the previous version of the configmap or secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution to both is simple to describe, and harder to implement with the current
tools, but possible.&lt;/p&gt;

&lt;p&gt;Kubernetes has the concept of resource owners: if a resource that owns another resource
is deleted, the owned resource is also deleted. So pods are owned by replicasets which
are in turn owned by deployments - deleting the deployment removes all replicasets
associated with the deployment, and all pods associated with those replicasets.&lt;/p&gt;

&lt;p&gt;We can use this to add owner references to configmaps and secrets - we find the replicaset
associated with the recent deployment, and set that resource as the owner in the
configmap/secret. This means that when replicasets are retired (Kubernetes keeps ten
replicasets by default, but this is configurable with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;spec.revisionHistoryLimit&lt;/code&gt;) the
associated configmaps and secrets are also retired.&lt;/p&gt;

&lt;p&gt;For diffs, first we lookup the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deployments.kubernetes.io/revision&lt;/code&gt; annotation of the
previous deployment and the current deployment. We then have to find all replicasets
(as there’s no way to search by annotation, unfortunately) and then select the
previous replicaset and current replicaset using the same annotation.&lt;/p&gt;

&lt;p&gt;Adding a new label, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube_resource_prefix&lt;/code&gt;, to our configmaps allows us to
iterate over our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube_resource_configmaps&lt;/code&gt; dict (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;label_selectors&lt;/code&gt; argument to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s_facts&lt;/code&gt; is useful here), each time looking for all configmaps
with the label of the current configmap, and then finding the configmap with the
owner reference set to the previous replicaset and the configmap owned by the current
replicaset. Exactly the same technique holds for secrets too. Once we’ve found
the before and after, we can display the differences using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debug&lt;/code&gt; module
(an explicit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diff&lt;/code&gt; module might be useful).&lt;/p&gt;

&lt;p&gt;I’ve updated the &lt;a href=&quot;https://github.com/willthames/ansible-role-kube-resource&quot;&gt;ansible-role-kube-resource&lt;/a&gt;
role with this new functionality so that it’s easier
to use, it does rely on all resources having &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube_resource_name&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube_resource_namespace&lt;/code&gt; correctly set. All of the owner reference based functionality
relies on replicasets updating when deployments change - so isn’t currently useful for
e.g. statefulsets.&lt;/p&gt;

&lt;p&gt;In other news on that role, I’ve now added a molecule test suite, inspired by
Jeff Geerling (@geerlingguy)’s talk at AnsibleFest and his super helpful
&lt;a href=&quot;https://www.jeffgeerling.com/blog/2018/testing-your-ansible-roles-molecule&quot;&gt;molecule blog post&lt;/a&gt;.
It relies on an existing kubernetes platform being set up and configured, but given
that Docker comes with Kubernetes (on some platforms at least), and that minikube exists,
that shouldn’t be insurmountable.&lt;/p&gt;

&lt;p&gt;There are quite a few features from Ansible 2.8 that are used in the role but
have been backported into the role - kubernetes resource validation, waiting for resources
to meet the desired state, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;append_hash&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s_config_resource_name&lt;/code&gt; for example.
Even if you choose not to use the role, you can make use of the same techniques through
setting and using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter_plugins&lt;/code&gt; configuration directives in ansible.cfg.&lt;/p&gt;</content><author><name></name></author><summary type="html">This post touches on a key component of my Managing Kubernetes is Easy With Ansible talk that I gave at AnsibleFest 2018. Since giving that talk, I’ve also solved some of the unforeseen consequences, and go into further detail here.</summary></entry><entry><title type="html">connection: local vs delegate_to: localhost</title><link href="http://willthames.github.io/2018/07/01/connection-local-vs-delegate_to-localhost.html" rel="alternate" type="text/html" title="connection: local vs delegate_to: localhost" /><published>2018-07-01T00:00:00+10:00</published><updated>2018-07-01T00:00:00+10:00</updated><id>http://willthames.github.io/2018/07/01/connection-local-vs-delegate_to-localhost</id><content type="html" xml:base="http://willthames.github.io/2018/07/01/connection-local-vs-delegate_to-localhost.html">&lt;p&gt;Performing tasks locally is a common operation when working with an API of some
kind—typical use cases are cloud services, network devices, cluster
management. There are three ways of achieving this in Ansible: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection:
local&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate_to: localhost&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;local_action&lt;/code&gt;. The last is rarely seen these
days and can be deemed equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate_to: localhost&lt;/code&gt; in terms of
advantages and disadvantages, but with the additional disadvantage of being
a very unusual style, adding a readability penalty.&lt;/p&gt;

&lt;p&gt;In a previous post I talked about the
&lt;a href=&quot;http://willthames.github.io/2017/10/31/making-the-most-of-inventory.html&quot;&gt;runner pattern&lt;/a&gt;
which allows better use of inventory for different scenarios even when the
controller is localhost. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection: local&lt;/code&gt; behaves very differently
if the host is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; or a ‘runner’ host, which is surprising.&lt;/p&gt;

&lt;p&gt;The main difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate_to&lt;/code&gt; is that connection can
be used at a play or task level, whereas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate_to&lt;/code&gt; operates at a task level
only. This means that if you have a playbook with fifty tasks, each will need
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;delegate_to&lt;/code&gt; set. Worse, if you’re using someone else’s role, you’ll have to
hope they’ve provided for this eventuality.&lt;/p&gt;

&lt;p&gt;The problem with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection: local&lt;/code&gt; for the runner pattern is that it assumes
that it’s an entirely new connection and will use the system python rather than
what ever python you prefer to use. Situations where this is a problem include
when using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;virtualenv&lt;/code&gt;s to install python libraries or on OS X where most rely
on python from brew. In this case, you might run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install library&lt;/code&gt;, run
Ansible and find that that library can’t be found because it’s looking in the
wrong place.&lt;/p&gt;

&lt;p&gt;To demonstrate this, I wrote a &lt;a href=&quot;https://github.com/ansible/ansible/pull/42083&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boto3_facts&lt;/code&gt; module&lt;/a&gt;,
which shows python location and version as well as boto3 and botocore versions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- hosts: localhost
  gather_facts: no

  tasks:
  - name: localhost without explicit connection
    boto3_facts:

- hosts: fakehost
  gather_facts: no

  tasks:
  - name: runner host using delegate_to
    boto3_facts:
    delegate_to: localhost

- hosts: fakehost
  gather_facts: no

  tasks:
  - name: runner host using local_action
    local_action:
      module: boto3_facts

- hosts: fakehost
  connection: local
  gather_facts: no

  tasks:
  - name: runner host using local connection
    boto3_facts:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ ansible-playbook boto3_facts.yml -v -i fakehost,
Using /Users/will/tmp/ansible/boto3_facts/ansible.cfg as config file

PLAY [localhost] *****************************************************************************************************************

TASK [localhost without explicit connection] *************************************************************************************
ok: [localhost] =&amp;gt; changed=false
  boto3_version: 1.7.42
  botocore_version: 1.10.42
  python: /usr/local/Cellar/python@2/2.7.14_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
  python_version: |-
    2.7.14 (default, Mar  9 2018, 23:57:12)
    [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]

PLAY [fakehost] ******************************************************************************************************************

TASK [runner host using delegate_to] *********************************************************************************************
ok: [fakehost -&amp;gt; localhost] =&amp;gt; changed=false
  boto3_version: 1.7.42
  botocore_version: 1.10.42
  python: /usr/local/Cellar/python@2/2.7.14_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
  python_version: |-
    2.7.14 (default, Mar  9 2018, 23:57:12)
    [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]

PLAY [fakehost] ******************************************************************************************************************

TASK [runner host using local_action] ********************************************************************************************
ok: [fakehost -&amp;gt; localhost] =&amp;gt; changed=false
  boto3_version: 1.7.42
  botocore_version: 1.10.42
  python: /usr/local/Cellar/python@2/2.7.14_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
  python_version: |-
    2.7.14 (default, Mar  9 2018, 23:57:12)
    [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]

PLAY [fakehost] ******************************************************************************************************************

TASK [runner host using local connection] ****************************************************************************************
ok: [fakehost] =&amp;gt; changed=false
  python: /usr/bin/python
  python_version: |-
    2.7.10 (default, Oct  6 2017, 22:29:07)
    [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]

PLAY RECAP ***********************************************************************************************************************
fakehost                   : ok=3    changed=0    unreachable=0    failed=0
localhost                  : ok=1    changed=0    unreachable=0    failed=0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The easiest way to fix this is to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_python_interpreter: &quot;{{ ansible_playbook_python }}&quot;&lt;/code&gt;.
My preferred approach is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;group_vars/all&lt;/code&gt; if all tasks run locally, or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;group_vars/runner&lt;/code&gt; if using the runner pattern—but, as with below, at playbook vars
level also works.&lt;/p&gt;

&lt;p&gt;In conclusion, I much prefer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection: local&lt;/code&gt; for the runner pattern now that
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_python_interpreter&lt;/code&gt; can be set dynamically.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- hosts: fakehost
  connection: local
  vars:
    ansible_python_interpreter: &quot;{{ ansible_playbook_python }}&quot;
  gather_facts: no

  tasks:
  - name: runner host using local connection and ansible_python_interpreter set
    boto3_facts:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PLAY [fakehost] ******************************************************************************************************************

TASK [runner host using local connection and ansible_python_interpreter set] *****************************************************
ok: [fakehost] =&amp;gt; changed=false
  boto3_version: 1.7.42
  botocore_version: 1.10.42
  python: /usr/local/Cellar/python@2/2.7.14_3/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
  python_version: |-
    2.7.14 (default, Mar  9 2018, 23:57:12)
    [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><summary type="html">Performing tasks locally is a common operation when working with an API of some kind—typical use cases are cloud services, network devices, cluster management. There are three ways of achieving this in Ansible: connection: local, delegate_to: localhost and local_action. The last is rarely seen these days and can be deemed equivalent to delegate_to: localhost in terms of advantages and disadvantages, but with the additional disadvantage of being a very unusual style, adding a readability penalty.</summary></entry><entry><title type="html">Managing Multiple AWS Consoles With Multi Account Containers</title><link href="http://willthames.github.io/2018/02/28/managing-multiple-aws-consoles-with-multi-account-containers.html" rel="alternate" type="text/html" title="Managing Multiple AWS Consoles With Multi Account Containers" /><published>2018-02-28T00:00:00+10:00</published><updated>2018-02-28T00:00:00+10:00</updated><id>http://willthames.github.io/2018/02/28/managing-multiple-aws-consoles-with-multi-account-containers</id><content type="html" xml:base="http://willthames.github.io/2018/02/28/managing-multiple-aws-consoles-with-multi-account-containers.html">&lt;p&gt;While there are ways of managing multiple AWS account consoles in a single browser
(such as assuming roles to access other accounts), various constraints might prevent that
(e.g. accounts owned by third parties to which access should be segregated).&lt;/p&gt;

&lt;p&gt;Most people in this situation have done the ‘browser juggle’, where you might have
Firefox open with another window in Safe mode, Chrome open with another window in
Incognito mode, and no doubt further browsers depending upon your OS.&lt;/p&gt;

&lt;p&gt;With Firefox’s &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/&quot;&gt;Multi Account Container add-on&lt;/a&gt;
this can be a thing of the past. This add on makes it easy to manage multiple AWS
accounts in a single Firefox window.&lt;/p&gt;

&lt;p&gt;First, set up a container per account by clicking the Multi Account Container button, usually
to the right end of the add ons bar, (or pressing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Ctrl&amp;gt;-.&lt;/code&gt;)
and then Edit Containers. Give each account a separate colour if possible (as the colour
appears as a line under the tab title, making it easier to distinguish which tab corresponds
to which account).&lt;/p&gt;

&lt;p&gt;Each time you need to use the console in a different account, long-click the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; new tab
button (also available via File &amp;gt; New Container Tab and, I’ve
&lt;a href=&quot;https://github.com/mozilla/multi-account-containers/issues/119#issuecomment-355050735&quot;&gt;just learned&lt;/a&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Ctrl&amp;gt;+.&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Tab&amp;gt;&lt;/code&gt; to select through the containers)&lt;/p&gt;

&lt;p&gt;You’ll still to need to log in to the appropriate account for the container in the normal way.
Even if you close all the container tabs, and later reopen the console in that same container,
it’ll remember your session—so you’ll be back in the console as long as your session hasn’t
expired.&lt;/p&gt;

&lt;p&gt;Note that you can also elect to always open a URL in a particular container (not so useful for multiple
AWS consoles, useful for segregating off sites whose business model depends on knowing what you’re doing,
yes, I mean Facebook—particularly if you then default all other sites to a new container, or even
just clear all cookies)&lt;/p&gt;</content><author><name></name></author><summary type="html">While there are ways of managing multiple AWS account consoles in a single browser (such as assuming roles to access other accounts), various constraints might prevent that (e.g. accounts owned by third parties to which access should be segregated).</summary></entry><entry><title type="html">Using updated modules, libraries and plugins with stable Ansible</title><link href="http://willthames.github.io/2017/12/12/using-updated-modules-with-stable-ansible.html" rel="alternate" type="text/html" title="Using updated modules, libraries and plugins with stable Ansible" /><published>2017-12-12T20:00:00+10:00</published><updated>2017-12-12T20:00:00+10:00</updated><id>http://willthames.github.io/2017/12/12/using-updated-modules-with-stable-ansible</id><content type="html" xml:base="http://willthames.github.io/2017/12/12/using-updated-modules-with-stable-ansible.html">&lt;div class=&quot;alert alert-info&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-info-sign&quot;&gt;&lt;/span&gt;
This page was updated 2020-05-16 to incorporate how to use collections to the
same effect
&lt;/div&gt;

&lt;div class=&quot;alert alert-info&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-info-sign&quot;&gt;&lt;/span&gt;
This page was updated on 2019-04-07 to improve `module_utils` information and
add plugin information&lt;/div&gt;

&lt;p&gt;There are many reasons to want to use newer modules than a chosen
stable Ansible core release:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Feature enhancements don’t get backported to stable branches&lt;/li&gt;
  &lt;li&gt;Non-security bug fixes only tend to get backported one version —
which means if say 2.N.0 hasn’t had all the core bugs ironed out yet,
you might not get the benefit of module bug fixes while you remain on
2.N-1.0&lt;/li&gt;
  &lt;li&gt;Some improvements only exist in PR form. Some improvements only exist
in branches made by combining multiple PRs.&lt;sup&gt;†&lt;/sup&gt; Some improvements are very
handy but so experimental they’re not even ready for a PR!&lt;sup&gt;†&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When that happens, thankfully you don’t have to run off your own megamerge
branch of ansible&lt;sup&gt;†&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Which ever of the two approaches below you take, I recommend keeping
a README.md file in the library directory. For modules, it looks a bit like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;|Module                     | PR                                            | Notes           |
|---------------------------|-----------------------------------------------|-----------------|
|cloudfront_distribution.py | https://github.com/ansible/ansible/pull/31284 | Unmerged        |
|ec2_placement_group.py     | https://github.com/ansible/ansible/pull/33139 | Available in 2.5|
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;but the same log is useful for plugins and collections.&lt;/p&gt;

&lt;p&gt;Keeping track of why I’m using non-core code allows me to remove it when the desired functionality
becomes available in newer Ansible or collections releases.&lt;/p&gt;

&lt;h2 id=&quot;the-collections-approach&quot;&gt;The collections approach&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/collections_using.html&quot;&gt;Using ansible collections&lt;/a&gt;
to incorporate updates from baseline can be a sensible approach when you need
several changes at once or just want to track latest or a branch.&lt;/p&gt;

&lt;p&gt;You can use ansible-galaxy to install collections. I always recommend pinning to
a specific version to avoid surprises. If you don’t want to use ansible-galaxy,
you can always just commit the results of installing
the collection (or just update a submodule) in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collections&lt;/code&gt; subdirectory
below your playbooks (other locations are available through configuring
&lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#collections-paths&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collections_paths&lt;/code&gt;&lt;/a&gt; -
take note of both the plurals, I wasted hours investigating why &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ANSIBLE_COLLECTION_PATHS&lt;/code&gt; wasn’t working).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir collections
git clone git@github.com:ansible-collections/community.kubernetes collections/community.kubernetes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will get you the latest merged commits from the official community.kubernetes
(as opposed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible-galaxy collection install -p collections community.kubernetes&lt;/code&gt; which
gets you the latest released version) - but you can also clone a fork and choose a
branch to get as yet unmerged commits (e.g. if you’re keen to include an unmerged pull
request)&lt;/p&gt;

&lt;h2 id=&quot;the-selective-approach&quot;&gt;The selective approach&lt;/h2&gt;

&lt;p&gt;Use this when you don’t necessarily want to update every module or plugin in a collection
which might introduce unexpected behaviours in modules you’re not looking to update&lt;/p&gt;

&lt;p&gt;My approach for this is to use the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-module-path&quot;&gt;default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;library&lt;/code&gt;
directory&lt;/a&gt;
feature —
create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;library&lt;/code&gt; directory in the top level of your playbooks repository,
and put any modules that you need but aren’t yet in the version of ansible
you’re using there.&lt;/p&gt;

&lt;p&gt;If you’re using modules that rely on updates to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt; shared libraries, you can  set
the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-module-utils-path&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt; config directive&lt;/a&gt;
in ansible.cfg (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./module_utils&lt;/code&gt; is an undocumented default) and copy the relevant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt; files into your codebase as well.
The layout of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt;
directory should reflect that of ansible. For example, if you need updates to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s&lt;/code&gt; module, you’ll probably
need to copy one or both of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s/common.py&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s/raw.py&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lib/ansible/module_utils&lt;/code&gt; to your
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils/k8s&lt;/code&gt; directory. You will also need an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; at the bottom level directory.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;module_utils/
└── k8s
    ├── __init__.py
    ├── common.py
    └── raw.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Similarly, you can optionally set &lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-filter-plugin-path&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter_plugins&lt;/code&gt;&lt;/a&gt;,
(which has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./filter_plugins&lt;/code&gt;as an undocumented default),
&lt;a href=&quot;https://docs.ansible.com/ansible/latest/reference_appendices/config.html#default-lookup-plugin-path&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lookup_plugins&lt;/code&gt;&lt;/a&gt;
(which has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./lookup_plugins&lt;/code&gt; as an undocumented default),
etc. to point to updated plugins — I tend to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins/lookup&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins/filter&lt;/code&gt; etc. to reflect Ansible’s codebase structure (but
I didn’t know about the defaults until doing some tests for the update to this page).&lt;/p&gt;

&lt;p&gt;One other point worth noting is that if you’re already using roles for your logic, you can put updates of modules, libraries
and plugins in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;library&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plugins&lt;/code&gt; directories at the top level of the role.
You’ll need a versioning strategy for your role that reflects Ansible versions so that you can remove published changes
later on (for example my &lt;a href=&quot;https://github.com/willthames/ansible-role-kube-resource&quot;&gt;Kubernetes role&lt;/a&gt;
has a v2.8 branch without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module_utils/k8s&lt;/code&gt; tree) with published &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v2.7-1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v2.8-1&lt;/code&gt; tags. The need for an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt; doesn’t
seem to be as apparent when using a role (i.e. my tests pass on my Kubernetes-role without needing an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__init__.py&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;†&lt;/sup&gt; — I’ve been there.&lt;/p&gt;</content><author><name></name></author><summary type="html">This page was updated 2020-05-16 to incorporate how to use collections to the same effect</summary></entry><entry><title type="html">Generating Inventory</title><link href="http://willthames.github.io/2017/11/01/generating-inventory.html" rel="alternate" type="text/html" title="Generating Inventory" /><published>2017-11-01T00:00:00+10:00</published><updated>2017-11-01T00:00:00+10:00</updated><id>http://willthames.github.io/2017/11/01/generating-inventory</id><content type="html" xml:base="http://willthames.github.io/2017/11/01/generating-inventory.html">&lt;p&gt;As mentioned in &lt;a href=&quot;/2017/10/31/making-the-most-of-inventory.html&quot;&gt;yesterday’s blogpost&lt;/a&gt;,
using a combination of environments, applications and operations can cause a cartesian
explosion in hosts and groups to manage.&lt;/p&gt;

&lt;p&gt;Even 10 applications in 3 environments over 2 operations can lead to sixty hosts, plus
likely as many groups.&lt;/p&gt;

&lt;p&gt;For example, we use a structure that looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[environment:children]
application-environment

[application-environment:children]
operation-application-environment

[operation-application:children]
operation-application-environment

[operation-application-environment]
operation-application-environment-runner

[runner]
operation-application-environment-runner

[application:children]
application-environment
operation-application

[operation:children]
operation-application
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And that’s just one host! Admittedly using groups that will be reused many times.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/op-app-env.png&quot; alt=&quot;op-app-env&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This problem has always been solvable using a script to generate inventory, but Ansible
2.4’s inventory plugin architecture, combined with the inspiration from the
&lt;a href=&quot;https://docs.ansible.com/ansible/devel/plugins/inventory/constructed.html&quot;&gt;constructed plugin&lt;/a&gt;
caused me to simplify my problem through creating a
&lt;a href=&quot;https://github.com/willthames/ansible/blob/generator_inventory_plugin/lib/ansible/plugins/inventory/generator.py&quot;&gt;generator plugin&lt;/a&gt;.
If the plugin proves popular I’ll likely raise a PR soon.&lt;/p&gt;

&lt;p&gt;The generator plugin is then installed by putting it into our ansible
playbooks repo under plugins/inventory/generator.py, and updating ansible.cfg to
include&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[defaults]
inventory_plugins = plugins/inventory

[inventory]
enable_plugins = generator,host_list,script,yaml,ini
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above inventory can be expressed with the inventory plugin using:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
# inventory.config file in YAML format
plugin: generator
strict: False
hosts:
    name: &quot;{{ operation }}-{{ application }}-{{ environment }}-runner&quot;
    parents:
      - name: &quot;{{ operation }}-{{ application }}-{{ environment }}&quot;
        parents:
          - name: &quot;{{ operation }}-{{ application }}&quot;
            parents:
              - name: &quot;{{ operation }}&quot;
              - name: &quot;{{ application }}&quot;
          - name: &quot;{{ application }}-{{ environment }}&quot;
            parents:
              - name: &quot;{{ application }}&quot;
              - name: &quot;{{ environment }}&quot;
      - name: runner
layers:
    operation:
        - build
        - launch
    environment:
        - dev
        - test
        - prod
    application:
        - web
        - api

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/launch-web-test-runner.png&quot; alt=&quot;launch-web-test-runner&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We are already using this to reduce 100+ line static inventory host files (our
biggest as-yet unreduced file is 500 lines!) to something very similar to the above.&lt;/p&gt;

&lt;p&gt;The major benefit is, as always, reducing repetition - expanding this to more environments
or applications is then a matter of adding a single element to the appropriate layer,
rather than adding the appropriate groups and hosts in 10 different places.&lt;/p&gt;

&lt;p&gt;ansible-inventory-grapher copes fine with the results (I put the effort in to
finally fixing it so that I could validate the result)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ansible-inventory-grapher -i inventory/generator.config all -a 'rankdir=LR;' -q | dot -Tpng | display png:-
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/all.png&quot; alt=&quot;all&quot; /&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">As mentioned in yesterday’s blogpost, using a combination of environments, applications and operations can cause a cartesian explosion in hosts and groups to manage.</summary></entry><entry><title type="html">Making The Most Of Inventory</title><link href="http://willthames.github.io/2017/10/31/making-the-most-of-inventory.html" rel="alternate" type="text/html" title="Making The Most Of Inventory" /><published>2017-10-31T00:00:00+10:00</published><updated>2017-10-31T00:00:00+10:00</updated><id>http://willthames.github.io/2017/10/31/making-the-most-of-inventory</id><content type="html" xml:base="http://willthames.github.io/2017/10/31/making-the-most-of-inventory.html">&lt;p&gt;When using Ansible to consume APIs such as cloud services, the
logic runs from the controller machine. As a result, people tend
to think that as this runs locally, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hosts: localhost&lt;/code&gt; is
the best option.&lt;/p&gt;

&lt;p&gt;In reality, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; as your host loses you a lot of power.
To avoid this, we can use a ‘runner’ pattern, where a runner
is a local target on which you can hang inventory.&lt;/p&gt;

&lt;p&gt;In our environment we tend to be either managing whole environments
(e.g. provisioning an entire network) and infrastructure components
within them (load balancers, database servies) or specific applications.&lt;/p&gt;

&lt;p&gt;For environments, we might have a hierarchy that looks a little like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[non_prod_account:children]
dev
test

[dev:children]
dev-runner

[test:children]
test-runner

[runner:children]
dev-runner
test-runner
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or, in picture form:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/test-runner.png&quot; alt=&quot;test-runner&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That way, when we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hosts: test-runner&lt;/code&gt; we can pick up all the
inventory associated with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test&lt;/code&gt; group - the CIDR prefix of networks,
the tag to use for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Environment&lt;/code&gt;, DNS zones, and many many other things
(in reality a lot of our variables come from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;all&lt;/code&gt; group but with
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env&lt;/code&gt; property used in populating variables - so our VPC name
is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{ env }}-{{ cidr_prefix }}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To avoid needing to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection: local&lt;/code&gt; to plays using runners,
create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runner&lt;/code&gt; group vars file (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inventory/group_vars/runners&lt;/code&gt;)
and add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_connection: local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With applications, and the right inventory structure, we can do things
like create all the loadbalancers with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
- hosts: application:&amp;amp;{{ env }}:&amp;amp;runner
  connection: local

  roles:
  - loadbalancer

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application&lt;/code&gt; group contains a (possibly empty) list of loadbalancers,
each item in the list containing the configuration used when calling
something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;elb_application_lb&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/application-web-test-runner.png&quot; alt=&quot;application-web-test-runner&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using the runner pattern allows us to avoid a whole bunch of variable files,
and structure inventory in a logical hierarchical fashion, making use of
inheritance to minimise repetition of data.&lt;/p&gt;

&lt;p&gt;One disadvantage of the runner pattern is that it creates a ridiculous
combinatorial explosion of hosts and groups in inventory files. In my
next post, I’ll show a very simple solution to that.&lt;/p&gt;

&lt;p&gt;Oh, and &lt;a href=&quot;https://github.com/willthames/ansible-inventory-grapher&quot;&gt;ansible-inventory-grapher&lt;/a&gt;
is currently working reasonably well against Ansible 2.4 at last (I need
to tidy some magic variables from the host before it’s totally ready for
release, but it’s on the ansible-2.4 branch if you’re keen)&lt;/p&gt;</content><author><name></name></author><summary type="html">When using Ansible to consume APIs such as cloud services, the logic runs from the controller machine. As a result, people tend to think that as this runs locally, using hosts: localhost is the best option.</summary></entry><entry><title type="html">So You Want To Test AWS Modules For Ansible</title><link href="http://willthames.github.io/2017/07/17/so-you-want-to-test-aws-modules-for-ansible.html" rel="alternate" type="text/html" title="So You Want To Test AWS Modules For Ansible" /><published>2017-07-17T00:00:00+10:00</published><updated>2017-07-17T00:00:00+10:00</updated><id>http://willthames.github.io/2017/07/17/so-you-want-to-test-aws-modules-for-ansible</id><content type="html" xml:base="http://willthames.github.io/2017/07/17/so-you-want-to-test-aws-modules-for-ansible.html">&lt;div class=&quot;alert alert-info&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-info-sign&quot;&gt;&lt;/span&gt;
This page was updated on 2018-02-28 to better document IAM policy changes,
the aliases file, YAML anchors for testing credentials&lt;/div&gt;

&lt;p&gt;You’re a (prospective) contributor to Ansible, and you have some
great improvements to make to an existing module or a brand new
module. As a conscientious developer, you know that having tests
will ensure that you don’t break existing behaviour, and that other
people’s future enhancements won’t break your desired behaviour.
The standard tests for AWS modules are integration tests as most
of them rely on creating some resources in AWS, updating them, and
then cleaning up afterwards.&lt;/p&gt;

&lt;p&gt;I’ll start this post from absolute first principles—I’ll use
a shiny new Fedora 26 vagrant VM.&lt;/p&gt;

&lt;h2 id=&quot;setting-up-ansible-for-development&quot;&gt;Setting up Ansible for development&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vagrant init fedora/26-cloud-base
vagrant up
vagrant ssh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The easiest way to ensure we have all the dependencies for Ansible
installed is then to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dnf&lt;/code&gt; to install ansible.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo dnf install ansible
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, clone the your fork of the source code for ansible:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh-keygen
eval `ssh-agent -s`
ssh-add
# Add the generated key to your github account
sudo dnf install git
git clone git@github.com:YOURUSER/ansible
cd ansible
git remote add upstream https://github.com/ansible/ansible
git pull upstream devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that if you’re doing this on a host with any other ssh keys,
it’s worth generating a github specific ssh key (using e.g.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-keygen -f ~/.ssh/github&lt;/code&gt;) and setting up
your .ssh/config appropriately:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Host github.com
  IdentityFile ~/.ssh/github
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible --version&lt;/code&gt; should give the released version of
Ansible (at the time of writing, this was 2.3.1.0). To use the development
version (not recommended for production use), do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;source hacking/env-setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible --version&lt;/code&gt; should now show the development version (currently
2.4.0)&lt;/p&gt;

&lt;h2 id=&quot;install-docker&quot;&gt;Install docker&lt;/h2&gt;

&lt;p&gt;Add docker and set it up so that you don’t have to be root to run it. There
are some security implications with this—being in the docker group is
&lt;a href=&quot;https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface&quot;&gt;effectively equivalent to root access&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo dnf install docker
sudo groupadd docker
sudo gpasswd -a $USER docker
sudo systemctl restart docker
newgrp docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;setting-up-aws-account&quot;&gt;Setting up AWS account&lt;/h2&gt;

&lt;p&gt;Install the relevant command line tools (you can do this in a virtualenv
if you prefer—it depends if you’d need to test with different versions
of boto3 etc). The boto library is currently still required for quite a
few modules, as well as the common code used to connect to AWS.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install --user botocore boto3 boto awscli
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ensure you have an IAM administrative user with API access. DO NOT
use this user for Ansible, or put its credentials anywhere near your
git repo! I use boto profiles for all of the AWS accounts that I use,
and don’t have a default profile so that I always have to choose.&lt;/p&gt;

&lt;p&gt;Note: DO NOT use your AWS root user—if you don’t have a suitable
user yet, create a new IAM user with
the AdministratorAccess managed policy (or similar) attached&lt;/p&gt;

&lt;p&gt;Ensure a &lt;a href=&quot;http://boto3.readthedocs.io/en/latest/guide/configuration.html&quot;&gt;profile for this IAM user&lt;/a&gt;
exists in ~/.aws/credentials and then run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export ADMIN_PROFILE=$your_profile_name
ansible-playbook hacking/aws_config/setup-iam.yml -e iam_group=ansible_test -e profile=$ADMIN_PROFILE -e region=us-east-2 -vv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You don’t actually have to set profile or region at all if you don’t need
them—region defaults to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;us-east-1&lt;/code&gt;, but you can only choose &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;us-east-2&lt;/code&gt;
as an alternative at this time.&lt;/p&gt;

&lt;p&gt;You’ll now need to go into AWS console, create an IAM user (called e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_test&lt;/code&gt;)
and make them a member of
the newly created group (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_test&lt;/code&gt; if you used that with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iam_group&lt;/code&gt; in
While you’re there, create an API credential for the test user.
This can all be automated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam create-user --user-name ansible_test --profile $ADMIN_PROFILE
aws iam add-user-to-group --user-name ansible_test --group_name ansible_test --profile $ADMIN_PROFILE
aws iam create-access-key --user-name ansible_test --profile $ADMIN_PROFILE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: This can be done through Ansible’s &lt;a href=&quot;docs.ansible.com/ansible/iam_module.html&quot;&gt;iam module&lt;/a&gt;
too. It’s just simpler to document the steps with the CLI as they fit on three lines
rather than one very long &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible&lt;/code&gt; adhoc command line or a larger ansible playbook.&lt;/p&gt;

&lt;p&gt;Using the information from the new credential, you can do:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cp test/integration/cloud-config-aws.yml.template test/integration/cloud-config-aws.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then update that file to include this new secret key and access key.
It’s also worth adding the following for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;region&lt;/code&gt; if you’re not using us-east-1&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
aws_region: us-east-2
ec2_region: &quot;{{ aws_region }}&quot;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: The credentials in ~/.aws/credentials and cloud-config-aws.yml should be
completely different. A good way to test this is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws sts get-caller-identity --profile=$ADMIN_PROFILE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
ansible -m shell -a 'AWS_SECRET_ACCESS_KEY={{ aws_secret_key }} AWS_ACCESS_KEY_ID={{ aws_access_key }} aws sts get-caller-identity' \
  -e @test/integration/cloud-config-aws.yml localhost

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first of these should return your administrator user. The second of these
should return your Ansible test user. If this isn’t the case, check the previous
steps (and let me know if I can improve the documentation!).&lt;/p&gt;

&lt;h2 id=&quot;ensuring-your-user-has-the-correct-iam-policies&quot;&gt;Ensuring your user has the correct IAM policies&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ansible-playbook hacking/aws_config/setup-iam.yml -e region=us-east-2 \
  -e profile=$ADMIN_PROFILE -e iam_group=ansible_test -vv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you need additional IAM policies for your module, you’ll need to add these
to a new or an existing policy file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hacking/aws_config/testing_policies&lt;/code&gt;.
Note that there is a maximum of 10 policies per group, so we’re now using consolidated
policies (e.g. compute, storage, network) etc. rather than a policy per service.&lt;/p&gt;

&lt;p&gt;Adding the necessary policies here is a good way of documenting the new policies
that you need, which will also help Ansible’s AWS account administrators add the
policies needed by the CI account.&lt;/p&gt;

&lt;h2 id=&quot;running-an-integration-test-suite&quot;&gt;Running an integration test suite&lt;/h2&gt;

&lt;p&gt;Check that you can run the integration tests&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ansible-test integration --docker -v ec2_group
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first run will be quite slow as you need to pull down all the required containers.
Future runs will be much quicker, particularly if you pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--docker-no-pull&lt;/code&gt; to
speed things up (it’s worth updating the containers fairly regularly, of course)&lt;/p&gt;

&lt;h2 id=&quot;writing-your-own-tests&quot;&gt;Writing your own tests&lt;/h2&gt;

&lt;p&gt;For your module, you will need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/integration/module_name/aliases&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/integration/module_name/tasks/main.yml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;alert alert-info&quot;&gt;&lt;span class=&quot;glyphicon glyphicon-info-sign&quot;&gt;&lt;/span&gt;
Previously, I said here that `meta/main.yml` was required, but it isn't&amp;mdash;most of
the AWS module test suites don't need to specify any dependencies at all.&lt;/div&gt;

&lt;h3 id=&quot;aliases&quot;&gt;aliases&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cloud/aws
posix/ci/cloud/groupX/aws
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud/aws&lt;/code&gt; in the aliases file, the cloud-config-aws.yml doesn’t get picked up.&lt;/p&gt;

&lt;p&gt;The second alias is used to parallelise the AWS integration tests across multiple test
runners. There are currently five groups (group1 through group5)—just choose one - we
can always rebalance if needed.&lt;/p&gt;

&lt;p&gt;If you want to reuse a test suite for multiple modules (e.g. the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws_waf_web_acl&lt;/code&gt; suite also tests &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws_waf_condition&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws_waf_rule&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aws_waf_facts&lt;/code&gt;), add those other dependent modules into aliases as well.&lt;/p&gt;

&lt;h3 id=&quot;tasksmainyml&quot;&gt;tasks/main.yml&lt;/h3&gt;

&lt;p&gt;This should contain all of the tasks needed for the tests. Most actions are
coupled with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert&lt;/code&gt; task that the test has had the desired effect.&lt;/p&gt;

&lt;p&gt;The main tasks should be in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;block&lt;/code&gt; with an accompanying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;always&lt;/code&gt; that
cleans up to avoid things being left behind by the tests. Clean up tasks
should always ignore errors.&lt;/p&gt;

&lt;p&gt;Before running any tasks, set up the account credentials in a reusable YAML
anchor for use in each AWS task.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
- block:
  - name: set up AWS credentials
    set_fact:
      aws_connection_info: &amp;amp;aws_connection_info
        aws_region: &quot;{{ aws_region }}&quot;
        aws_access_key: &quot;{{ aws_access_key }}&quot;
        aws_secret_key: &quot;{{ aws_secret_key }}&quot;
        security_token: &quot;{{ security_token }}&quot;
    no_log: yes

  - name: create resource
    aws_module:
      &amp;lt;&amp;lt;: *aws_connection_info
      state: present
      name: &quot;{{ resource_prefix }}-more-name&quot;
      ...
    register: aws_module_result

  - name: check that resource was created
    assert:
      that:
       &amp;amp;mdash;aws_module_result.changed
       &amp;amp;mdash;aws_module_result.name == &quot;{{ resource_prefix }}-more-name&quot;
    ...

- always:
  - name: remove resource
    aws_module:
      &amp;lt;&amp;lt;: *aws_connection_info
      state: absent
      name: &quot;{{ resource_prefix }}-more-name&quot;
      ...
    ignore_errors: yes

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Things worth checking&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Create a resource, assert that the returned properties are as expected&lt;/li&gt;
  &lt;li&gt;Run that task again, check that nothing changed&lt;/li&gt;
  &lt;li&gt;Update a resource, check that the returned properties are changed&lt;/li&gt;
  &lt;li&gt;Run that task again, check that nothing changed&lt;/li&gt;
  &lt;li&gt;If the task has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;purge_tags&lt;/code&gt; or similar, check that works as expected&lt;/li&gt;
  &lt;li&gt;Run the task again in check mode&lt;/li&gt;
  &lt;li&gt;Delete the resource&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h2&gt;

&lt;p&gt;While docker is quite useful for quickly running tests and cleaning up after
itself, occasionally you might need to get the debugger out to see why
your module isn’t doing what you expect.&lt;/p&gt;

&lt;p&gt;At this point, it’s likely easiest to create a quick test playbook&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- hosts: localhost
  vars_files:
  - test/integration/cloud-config-aws.yml

  roles:
  - test/integration/targets/aws_module
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And run this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ANSIBLE_KEEP_REMOTE_FILES=1 ansible-playbook test-playbook.yml -vvv&lt;/code&gt;.
You can then follow the &lt;a href=&quot;https://docs.ansible.com/ansible/dev_guide/developing_modules_best_practices.html#debugging-ansiblemodule-based-modules&quot;&gt;ansible debugging instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I tend to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;epdb&lt;/code&gt; to put in a breakpoint where the module is going wrong (or before the
module has gone wrong) and analyse from there.&lt;/p&gt;

&lt;p&gt;So my steps are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python /path/to/module explode&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd /path/to/debug_dir&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Edit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible_module_module_name.py&lt;/code&gt;, add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import epdb; epdb.st()&lt;/code&gt; at the relevant location&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python /path/to/module execute&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python -c 'import epdb; epdb.connect()'&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;tidy-up&quot;&gt;Tidy up&lt;/h2&gt;

&lt;p&gt;Detach the policies from your test IAM group—this will leave the test group and user in
place but with zero privileges.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export GROUP=$your_iam_group
for policy_arn in `aws iam list-attached-group-policies --group-name $GROUP --profile $ADMIN_PROFILE --query 'AttachedPolicies[].PolicyArn' --output text`
do aws iam detach-group-policy --group-name $GROUP --policy-arn $policy_arn --profile $ADMIN_PROFILE
done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Shut down docker and vagrant&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo systemctl stop docker
exit
vagrant halt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This post is designed to assist people with setting up the various steps needed to test
and debug AWS modules for Ansible. There are almost certainly errors, omissions, and
things that &lt;em&gt;I&lt;/em&gt; could learn to do better.&lt;/p&gt;

&lt;p&gt;If you have any suggestions for improvement, please raise an issue or PR
on https://github.com/willthames/willthames.github.io or just let me know on Twitter or email (links below). Thanks!&lt;/p&gt;

&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;/h2&gt;

&lt;p&gt;Thanks to Moritz Grimm for pointing out a couple of typos. These are now fixed.&lt;/p&gt;</content><author><name></name></author><summary type="html">This page was updated on 2018-02-28 to better document IAM policy changes, the aliases file, YAML anchors for testing credentials</summary></entry><entry><title type="html">An Introduction to Code Reviews</title><link href="http://willthames.github.io/2016/11/07/intro-to-code-reviews.html" rel="alternate" type="text/html" title="An Introduction to Code Reviews" /><published>2016-11-07T21:30:00+10:00</published><updated>2016-11-07T21:30:00+10:00</updated><id>http://willthames.github.io/2016/11/07/intro-to-code-reviews</id><content type="html" xml:base="http://willthames.github.io/2016/11/07/intro-to-code-reviews.html">&lt;p&gt;Most software development teams have long been doing code reviews, and
while it’s not uncommon amongst system administrators, 
it’s not universally practised.&lt;/p&gt;

&lt;h2 id=&quot;why-do-code-reviews&quot;&gt;Why do code reviews&lt;/h2&gt;

&lt;p&gt;Constructive code reviews are a means of ensuring the quality
of code is consistent across the team –
and typically all code gets raised to a much higher standard as a result.
Through code reviews, team members can obtain feedback from
their peers on their contributions before merge, as well as learn
from the feedback on other colleagues’ contributions. Code review
also promotes a shared understanding of the entire codebase amongst the team,
making it easier for everyone to contribute. Additionally it
provides visibility of changes to the codebase to everyone, which
helps to avoid errors, and reduce wasteful or overly complicated
code being added to the codebase.&lt;/p&gt;

&lt;h2 id=&quot;when-should-code-reviews-happen&quot;&gt;When should code reviews happen&lt;/h2&gt;

&lt;p&gt;Code reviews should happen before any commit gets merged into
a production codebase. Sometimes this principle needs to be
bypassed (e.g. an emergency fix for production when no reviewer
is available) but such changes should be still be reviewed
retrospectively.&lt;/p&gt;

&lt;p&gt;Ideally, code would undergo some kind of continuous integration
testing prior to review. The sophistication of such testing
can range from basic syntax checking up to full-scale test deployments.
If it passes this automated checking, then it’s worth spending
human effort on a review.&lt;/p&gt;

&lt;h2 id=&quot;where-do-code-reviews-happen&quot;&gt;Where do code reviews happen&lt;/h2&gt;

&lt;p&gt;Most social version control platforms (Github, Bitbucket, etc.)
have the ability to do
Pull Requests (or Merge Requests in Gitlab’s case). This is the
easiest (but not necessarily most robust) way to have them.&lt;/p&gt;

&lt;p&gt;Dedicated code review platforms such as Gerrit, Crucible may
also be used – it doesn’t really matter what tool you
use as long as you are able to comment at the line level, at
the general level, and provide some indicator of approval
(e.g. a +1 or shipit)&lt;/p&gt;

&lt;p&gt;Refer to the documentation for your version control platform,
and perhaps your internal documentation for your change
workflow, for how to submit reviews.&lt;/p&gt;

&lt;h2 id=&quot;for-the-reviewer-reviewing-the-code&quot;&gt;For the Reviewer: Reviewing the code&lt;/h2&gt;

&lt;p&gt;First of all, you have to understand the change you’re reviewing.
If you don’t understand the change, you can’t positively review
the change. A good commit message should contain the purpose of
the change, and if necessary, how that change is being achieved.
If, after reading the commit messages, you don’t understand what
is going on, you should ask for the commit message to be improved.&lt;/p&gt;

&lt;p&gt;When you understand the change, there are a few key levels of
code review:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;syntax – is the code well formatted, meeting indentation and
whitespace standards and free from parse errors.&lt;/li&gt;
  &lt;li&gt;style – is the code idiomatic for the language, are common
error-prone patterns for that language avoided, is the code
easily understood&lt;/li&gt;
  &lt;li&gt;functional – does the code do what it’s supposed to do&lt;/li&gt;
  &lt;li&gt;architecture – is the code required, are there any improvements
to be made through abstractions, reuse of other code. Does the
code tie in with the rest of the codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first two of these are excellent candidates for automated checks –
particularly as from a reviewer’s point of view, they’re really
tedious to review, and from a reviewee’s point of view, they can feel
like nitpicking. If the code has to meet such automated
checks before it even gets to review, then the human element
can be saved for the deep structural thought.
&lt;a href=&quot;/2016/06/28/announcing-ansible-review.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ansible-review&lt;/code&gt;&lt;/a&gt; is
an example of such a tool for Ansible; most languages and CM
frameworks have similar tools.&lt;/p&gt;

&lt;p&gt;Comment on specific lines of code if you can to say where the
code doesn’t meet standards or could be improved. General feedback
on the change as a whole can typically be provided as a comment
without referencing a specific line.&lt;/p&gt;

&lt;p&gt;Assume best intentions, and try and address the code rather than
the person writing the code. Criticism should never be personal.&lt;/p&gt;

&lt;p&gt;Code reviews should be objective where possible. There are always
subjective preferences in any code base, but such preferences should
be decided at a team level beforehand, and then be well documented –
by pointing to such documentation in the code
review, the feeling of subjectivity can be avoided. As you come
across undocumented preferences, determine that they are what
the team wish to use, and document them.&lt;/p&gt;

&lt;p&gt;If you are satisfied that there are no blocking issues with the
change, signify your approval in the appropriate way.&lt;/p&gt;

&lt;p&gt;I prefer to let the code contributor accept the change if possible,
in case there are any last minute issues that they notice. In some
tools or under some permission schemes, this may not be allowed,
and others may have to merge the result.&lt;/p&gt;

&lt;h2 id=&quot;for-the-reviewee-prepare-for-code-reviews&quot;&gt;For the Reviewee: Prepare for code reviews&lt;/h2&gt;

&lt;p&gt;For a contributor:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;ensure that your &lt;a href=&quot;http://chris.beams.io/posts/git-commit/&quot;&gt;commit messages explain
what you are trying to achieve and why&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;adhere to the standards of your code base.&lt;/li&gt;
  &lt;li&gt;assume best intentions from the reviewer.&lt;/li&gt;
  &lt;li&gt;realise that a code review is not a battle,
and try not to take criticism of your code personally.
However, if criticism is personal, then you should say so.&lt;/li&gt;
  &lt;li&gt;try and reduce conflict resulting from misunderstandings –
see if you can clear up such misunderstandings, either in the review, in the
commit messages or through talking it through with the reviewer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;If you don’t currently do code reviews, and you’re not using pull
requests for contributing code, and you don’t have documented
standards, all of the above might seem a little daunting.&lt;/p&gt;

&lt;p&gt;Not having standards is a bit of a chicken and egg situation –
without reviewing code, often preferences exist but aren’t
expressed anywhere (some of our preferences have been implicit
for years until a new contributor comes along and does something
off the wall, and we realise it needs to be explicit).&lt;/p&gt;

&lt;p&gt;One way to start might be to just ask a colleague to give you
feedback on your recent commits. This might help to start
discovering preferences, and then these can be documented.
From there, you’ll likely find that code review tools provide
a much easier way to provide feedback, because you can associate
your comments with a line of code very easily.&lt;/p&gt;

&lt;p&gt;In our global team of 20+ sysadmins, we actually use a code
review process for improving our standards
and best practices – all new standards must be accepted by at least
two colleagues, and all best practice suggestions must get at least
one +1. This is intended to ensure that no one feels that standards
are imposed upon them. In a small
co-located team a 2 minute chat might suffice instead!&lt;/p&gt;</content><author><name></name></author><summary type="html">Most software development teams have long been doing code reviews, and while it’s not uncommon amongst system administrators, it’s not universally practised.</summary></entry><entry><title type="html">Ansible Brisbane October 2016 talk</title><link href="http://willthames.github.io/2016/10/25/ansible-brisbane-ansible-review.html" rel="alternate" type="text/html" title="Ansible Brisbane October 2016 talk" /><published>2016-10-25T21:30:00+10:00</published><updated>2016-10-25T21:30:00+10:00</updated><id>http://willthames.github.io/2016/10/25/ansible-brisbane-ansible-review</id><content type="html" xml:base="http://willthames.github.io/2016/10/25/ansible-brisbane-ansible-review.html">&lt;p&gt;At Ansible Brisbane October 2016, I gave a talk on &lt;a href=&quot;/ansible-bris-oct-2016/&quot;&gt;Automating Ansible Code Reviews&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">At Ansible Brisbane October 2016, I gave a talk on Automating Ansible Code Reviews</summary></entry><entry><title type="html">DevOps Days Singapore Ansible Workshop</title><link href="http://willthames.github.io/2016/10/09/devops-singapore-workshop.html" rel="alternate" type="text/html" title="DevOps Days Singapore Ansible Workshop" /><published>2016-10-09T20:00:00+10:00</published><updated>2016-10-09T20:00:00+10:00</updated><id>http://willthames.github.io/2016/10/09/devops-singapore-workshop</id><content type="html" xml:base="http://willthames.github.io/2016/10/09/devops-singapore-workshop.html">&lt;p&gt;My three hour workshop on &lt;a href=&quot;/devops-singapore-2016/&quot;&gt;Ansible from Zero to Best Practices&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">My three hour workshop on Ansible from Zero to Best Practices</summary></entry></feed>