<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CodeProject on dwmkerr.com</title><link>https://dwmkerr.com/tags/codeproject/</link><description>Recent content in CodeProject on dwmkerr.com</description><generator>Hugo -- gohugo.io</generator><language>en-uk</language><managingEditor>Dave Kerr</managingEditor><copyright>Copright &amp;copy; Dave Kerr</copyright><lastBuildDate>Sat, 29 Apr 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://dwmkerr.com/tags/codeproject/index.xml" rel="self" type="application/rss+xml"/><item><title>Semantic Versioning, Conventional Commits and Release Pull Requests for .NET with Google's Release Please Project</title><link>https://dwmkerr.com/release-please-dotnet/</link><pubDate>Sat, 29 Apr 2023 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/release-please-dotnet/</guid><description>&lt;p&gt;&lt;a href="https://github.com/googleapis/release-please"&gt;Release Please&lt;/a&gt; is an excellent library from Google that simplifies the management of releases for projects. However, it is not immediately apparent from the Release Please documentation that you can very easily get this working for your .NET projects.&lt;/p&gt;
&lt;p&gt;In this article we&amp;rsquo;ll take a quick tour of how to configure release please for a .NET project and how it can save you time and effort.&lt;/p&gt;
&lt;h2 id="what-is-release-please"&gt;What is Release Please?&lt;/h2&gt;
&lt;p&gt;In a nutshell, Release Please is a tool that can be run as a CLI or a GitHub action, that looks over the commit history for a repository. Based on the commit history, the tool will attempt to identify whether there have been major, minor or patch level changes to the code, based on the &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt; spec and the usage of &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits&lt;/a&gt;. It will then open a pull request that updates the changelog for your project and sets a new version number, and allows you to then easily run custom release actions if the pull request is merged.&lt;/p&gt;
&lt;p&gt;This is easier to see with a few screenshots. I recently added Release Please to my &lt;a href="https://github.com/dwmkerr/SharpGL"&gt;SharpGL&lt;/a&gt; project. After making a change, I get a pull request like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/pull-request-summary.png" alt="Screenshot: Pull Request Summary"&gt;&lt;/p&gt;
&lt;p&gt;Going into the details of the pull request, what is particularly useful is that my changelog has been updated, as well as the version data in my &lt;code&gt;SharedAssemblyInfo.cs&lt;/code&gt; file:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/pull-request-details.png" alt="Screenshot: Pull Request Details"&gt;&lt;/p&gt;
&lt;h2 id="how-does-this-help"&gt;How does this help?&lt;/h2&gt;
&lt;p&gt;If you maintain a lot of open source projects, anything you can do to automate chores like this just brings rigour to the process and makes bringing in contributions far easier.&lt;/p&gt;
&lt;p&gt;A bug had been raised on the repo:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/sharpgl/issues/220"&gt;https://github.com/dwmkerr/sharpgl/issues/220&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This bug indicated that the published version on NuGet was mis-aligned with the version in the source code. This is obviously a real problem for consumers of the library as well as contributors.&lt;/p&gt;
&lt;p&gt;I use Release Please for most of my projects now, I particularly like having the release pull request to review, it makes it really easy to see what&amp;rsquo;ll be in the next published vesrion.&lt;/p&gt;
&lt;h2 id="how-do-you-configure-release-please-for-net"&gt;How do you configure Release Please for .NET?&lt;/h2&gt;
&lt;p&gt;It turns out that this is really easy! You can configure you manifest like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;release-type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;simple&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;package-name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sharpgl&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;extra-files&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;SharpGL/AssemblyInfo.cs&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or directly in your GitHub pipeline configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;main&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-20.04&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;google-github-actions/release-please-action@v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;token&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;release-type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;simple&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;package-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sharpgl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;extra-files&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; SharpGL/AssemblyInfo.cs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is not immediately apparent from the Release Please documentation that you can manage version numbers in a C# file, but the after looking at the &lt;a href="https://github.com/googleapis/release-please/blob/main/docs/customizing.md#updating-arbitrary-files"&gt;Customising Guide&lt;/a&gt;, it turns out there is a &amp;lsquo;generic&amp;rsquo; file updater that simply looks for version numbers, based on regexes, in any arbitrary files. You just need to use some comments to show where these version numbers are. In my case, the code looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.Reflection;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.Runtime.CompilerServices;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; System.Runtime.InteropServices;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[assembly: AssemblyProduct(&amp;#34;SharpGL&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[assembly: AssemblyCopyright(&amp;#34;Copyright © Dave Kerr 2023&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// x-release-please-start-version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[assembly: AssemblyVersion(&amp;#34;3.0.0.0&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[assembly: AssemblyFileVersion(&amp;#34;3.0.0.0&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// x-release-please-end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Adding these &lt;code&gt;x-release-please&lt;/code&gt; comments is enough for Release Please to be able to identify the lines that need to be updated.&lt;/p&gt;
&lt;h2 id="other-improvements"&gt;Other Improvements&lt;/h2&gt;
&lt;p&gt;There are two immediate improvements that you can make on top of these changes to make it even easier to handle releases.&lt;/p&gt;
&lt;h3 id="commit-linting"&gt;Commit Linting&lt;/h3&gt;
&lt;p&gt;The first improvement is to look at something like &lt;a href="https://github.com/conventional-changelog/commitlint"&gt;&lt;code&gt;commitlint&lt;/code&gt;&lt;/a&gt; to ensure that incoming pull requests have commits that meet the conventional commit spec. For Release Please to be able to properly infer the types of changes from the commit history, it is essential that &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;conventional commits&lt;/a&gt; are used.&lt;/p&gt;
&lt;p&gt;Remember - even with a hook to check commit messages, you&amp;rsquo;ll need need to confirm that the author of the changes has structured the commit message appropriately, such as correctly identifying breaking changes.&lt;/p&gt;
&lt;h3 id="automated-publishing"&gt;Automated Publishing&lt;/h3&gt;
&lt;p&gt;It is possible to update your build pipeline to actually publish your packages when the release pull request is merged. Here&amp;rsquo;s an example for a .NET project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# The &amp;#39;main&amp;#39; workflow is used to create the &amp;#39;Release Please&amp;#39; pull&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# request. This PR is opened when we have a new commit to main.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# See:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# https://github.com/googleapis/release-please&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;main&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;windows-2022&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Checkout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Note - this is where you put your build / test steps. See:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# https://github.com/dwmkerr/sharpgl/blob/main/.github/workflows/main.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# For an example.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Now that we know the build runs, create a release PR if needed.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;google-github-actions/release-please-action@v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;id&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;token&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;release-type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;simple&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;package-name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;sharpgl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;extra-files&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; source/SharpGL/SharedAssemblyInfo.cs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If we are building a release (i.e. we&amp;#39;ve merged from release-please) then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# we can deploy.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Publish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;dotnet nuget push ./source/SharpGL/artifacts/packages/*.nupkg --source &amp;#39;https://api.nuget.org/v3/index.json&amp;#39; --api-key ${{secrets.NUGET_API_KEY}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;shell&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bash&lt;/span&gt; &lt;span style="color:#75715e"&gt;# bash for globs in commmand above...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;if&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ steps.release.outputs.release_created }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d highly recommend this approach for .NET projects. Adopting semver and conventional commits should make managing contributions and your changelog easier. Release Please really is the icing on the cake - by having structured commits with strong semantics you can reduce overhead and effort and increase automation.&lt;/p&gt;
&lt;p&gt;I have not included much on enforcing conventional commits in the repository or this article at the moment, some of the best tooling for this uses the Node stack (Husky and commitlint specifically). The benefits of enforcing conventional commits may well be worth the extra effort of setting these tools up, it&amp;rsquo;s really up to you to decide.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>A simple Makefile 'help' command</title><link>https://dwmkerr.com/makefile-help-command/</link><pubDate>Sun, 25 Sep 2022 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/makefile-help-command/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to show you how to add a &lt;code&gt;make help&lt;/code&gt; command to your makefiles that quickly and easily shows simple documentation for your commands:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/demo.svg" alt="Screen recording of the &amp;lsquo;makefile help&amp;rsquo; command in action"&gt;&lt;/p&gt;
&lt;p&gt;To add the &lt;code&gt;help&lt;/code&gt; command to your makefile, add a recipe like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-make" data-lang="make"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; help
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;help&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Show help for each of the Makefile recipes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @grep -E &lt;span style="color:#e6db74"&gt;&amp;#39;^[a-zA-Z0-9 -]+:.*#&amp;#39;&lt;/span&gt; Makefile | sort | &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; read -r l; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; printf &lt;span style="color:#e6db74"&gt;&amp;#34;\033[1;32m&lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;(echo &lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;l | cut -f 1 -d&amp;#39;:&amp;#39;)\033[00m:&lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;(echo &lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;l | cut -f 2- -d&amp;#39;#&amp;#39;)\n&amp;#34;&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now just make sure that each of your recipes has a comment that follows the recipe name, which will be used as its documentation. For example, my &lt;a href="https://github.com/dwmkerr/dwmkerr.com"&gt;website repository&lt;/a&gt; has the following recipes in the makefile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-make" data-lang="make"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;default&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; help
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; help
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;help&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Show help for each of the Makefile recipes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @grep -E &lt;span style="color:#e6db74"&gt;&amp;#39;^[a-zA-Z0-9 -]+:.*#&amp;#39;&lt;/span&gt; Makefile | sort | &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; read -r l; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; printf &lt;span style="color:#e6db74"&gt;&amp;#34;\033[1;32m&lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;(echo &lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;l | cut -f 1 -d&amp;#39;:&amp;#39;)\033[00m:&lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;(echo &lt;/span&gt;$$&lt;span style="color:#e6db74"&gt;l | cut -f 2- -d&amp;#39;#&amp;#39;)\n&amp;#34;&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; setup
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;setup&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Setup tools required for local development.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; brew install hugo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hugo version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; git submodule update --init --recursive --remote
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; newpost
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;newpost&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Create a new post.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd dwmkerr.com; hugo new posts/my-first-post.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; serve
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;serve&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Serve the site locally for testing.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd dwmkerr.com; hugo server --baseURL &lt;span style="color:#e6db74"&gt;&amp;#34;http://localhost/&amp;#34;&lt;/span&gt; --buildDrafts -v --debug
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.PHONY&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; build &lt;span style="color:#75715e"&gt;# Build the site.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;build&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd dwmkerr.com; hugo --minify
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this setup, you can just enter &lt;code&gt;make&lt;/code&gt;, or &lt;code&gt;make help&lt;/code&gt;, to see the output below:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ make help
help: Show help for each of the Makefile recipes.
newpost: Create a new post.
serve: Serve the site locally for testing.
setup: Setup tools required for local development.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Simple! You can find the code at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/makefile-help"&gt;https://github.com/dwmkerr/makefile-help&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How it Works&lt;/h2&gt;
&lt;p&gt;This project was inspired by the project &lt;a href="https://github.com/FalcoSuessgott/golang-cli-template"&gt;&lt;code&gt;golang-cli-template&lt;/code&gt;&lt;/a&gt;, which I noticed had this cool feature of showing help for the makefile commands.&lt;/p&gt;
&lt;p&gt;I built my own version of the command, which is a little bit more verbose, but I think a little easier to read and parse. I&amp;rsquo;ve also included the original version, with a link to the source in the &lt;a href="https://github.com/dwmkerr/makefile-help"&gt;&lt;code&gt;makefile-help&lt;/code&gt;&lt;/a&gt; repo.&lt;/p&gt;
&lt;p&gt;Essentially, the code simply:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Searches for recipes - these are lines that start with text, have a colon and a hash symbol&lt;/li&gt;
&lt;li&gt;Goes through each recipe found, extracts the recipe name and documentation comment&lt;/li&gt;
&lt;li&gt;Write each of the recipe names and its documentation to the console&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="testing-scripts"&gt;Testing Scripts&lt;/h2&gt;
&lt;p&gt;I wanted to make sure that if I improve on the script over time, or add different versions, the code won&amp;rsquo;t break. There&amp;rsquo;s a test script, which is a simple shell script that runs the two help commands and assets the output is as expected.&lt;/p&gt;
&lt;p&gt;At the time of writing, the shell script looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set -e
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;recipes&lt;span style="color:#f92672"&gt;=(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;help&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;help-compact&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Some colour codes for formatting.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;green&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;\033[1;32m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;red&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;\033[1;31m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;reset&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;\033[00m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default to success for the result of tests.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;result&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; recipe in &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipes[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Create the path to the expected output file.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; expected_output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;./test-cases/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipe&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;-expected-output.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; ! -f &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;expected_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#e6db74"&gt;&amp;#34;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;red&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;FAIL&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;reset&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipe&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; failed, test file &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;expected_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; not found\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;elif&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;make &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipe&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cat &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;expected_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#e6db74"&gt;&amp;#34;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;green&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;PASS&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;reset&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipe&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; passed\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#e6db74"&gt;&amp;#34;[&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;red&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;FAIL&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;reset&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;] &amp;#39;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;recipe&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39; failed\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Return the exit code.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;exit &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;result&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One thing that is nice about the tests is that they are incorporated into a GitHub Action, which runs the tests using Ubuntu, MacOS and Windows, and tests on both Bash and the generic &lt;code&gt;sh&lt;/code&gt; shell.&lt;/p&gt;
&lt;p&gt;This uses the following features of GitHub actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources"&gt;Runner Images&lt;/a&gt; - predefined images are made available by GitHub for various operating systems&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell"&gt;Shell Specificity&lt;/a&gt; - GitHub actions allow you to specify the shell used for a step&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs"&gt;GitHub Action Matrix Strategies&lt;/a&gt; - A matrix of operating systems is specified, to avoid duplicating the pipeline steps for each supported operating system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This project provides a nice template or starting point if you want to build a simple shell script with some basic testing features.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;If you found this interesting, you might enjoy &lt;a href="https://effective-shell.com"&gt;Effective Shell&lt;/a&gt; - My free online book of shell techniques.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework</title><link>https://dwmkerr.com/building-least-privilege-permissions-aws/</link><pubDate>Fri, 23 Apr 2021 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/building-least-privilege-permissions-aws/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to give a brief overview of some techniques to build &amp;rsquo;least privilege&amp;rsquo; roles in AWS. This assumes a basic knowledge of AWS and Identity and Access Management. It uses the (at time of writing) &lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/04/iam-access-analyzer-easier-implement-least-privilege-permissions-generating-iam-policies-access-activity/"&gt;newly announced features in the AWS IAM Access Analyser&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be demoing the techniques using a project built on &lt;a href="https://www.serverless.com/"&gt;The Serverless Framework&lt;/a&gt; but you don&amp;rsquo;t need to know anything about how this framework works to follow the article - it is just used to demonstrate the concepts. You should be able to apply these techniques to almost any process which accesses or manages AWS resources.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get into it!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="updates-to-the-aws-iam-access-analyser"&gt;Updates to the AWS IAM Access Analyser&lt;/h2&gt;
&lt;p&gt;AWS recently announced some new features to the IAM Access Analyser, which are designed to help build &amp;rsquo;least privilege&amp;rsquo; policies for your AWS solutions. As I have been deploying a number of solutions based on &lt;a href="https://www.serverless.com/"&gt;The Serverless Application Framework&lt;/a&gt; I thought this would be a great time to try out these new features.&lt;/p&gt;
&lt;p&gt;The Serverless Application Framework is a useful framework if you want to rapidly create serverless applications. You can rapidly create lambda functions, deploy to AWS, test locally, debug and so on.&lt;/p&gt;
&lt;h2 id="our-use-case---a-serverless-framework-deployment-process"&gt;Our Use Case - A Serverless Framework Deployment Process&lt;/h2&gt;
&lt;p&gt;When you use the Serverless Framework, a common pattern for deployment is to let the framework itself deploy resources.&lt;/p&gt;
&lt;p&gt;A deploy command would look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;serverless deploy --stack uat
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Under the hood, this will do a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a CloudFormation template which defines an application stack&lt;/li&gt;
&lt;li&gt;Upload the template to an S3 bucket on a specified AWS account&lt;/li&gt;
&lt;li&gt;Deploy the stack&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now what is deployed is very dependent on what you decide to use in your application, but common resources would be things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CloudFormation Stacks&lt;/li&gt;
&lt;li&gt;Lambda Functions&lt;/li&gt;
&lt;li&gt;API Gateway resources&lt;/li&gt;
&lt;li&gt;DynamoDB tables&lt;/li&gt;
&lt;li&gt;SQS Queues&lt;/li&gt;
&lt;li&gt;&amp;hellip;and many more!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This raises some interesting questions - how should we secure this provisioning process?&lt;/p&gt;
&lt;h2 id="fundamental-principles---isolation-and-least-privilege"&gt;Fundamental Principles - Isolation and Least-Privilege&lt;/h2&gt;
&lt;p&gt;There are two fundamental principles&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; which make sense to consider when thinking about how to integrate the Serverless Framework into your stack:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Isolation of Resources&lt;/strong&gt;: Can we make sure that the stack resources are logically isolated from &lt;em&gt;other&lt;/em&gt; resources we are managing? This can reduce the impact of incidents - if the stack is compromised, it should only compromise specific resources, not all resources in your estate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Least Privilege&lt;/strong&gt;: Can we make sure that when the &lt;code&gt;serverless&lt;/code&gt; binary deploys resources, it has the least permissions required to do its work, again reducing the impact of a potential incident&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Isolation of resources can be handled in a number of ways - my preferred approach is to create separate AWS accounts for each application (and in fact, each environment, such as &amp;lsquo;dev&amp;rsquo;, &amp;rsquo;test&amp;rsquo; and so on). This is not something I will discuss in this article. What I would like to focus on is the second point - least privilege.&lt;/p&gt;
&lt;h2 id="least-privilege-in-aws"&gt;Least Privilege in AWS&lt;/h2&gt;
&lt;p&gt;We don&amp;rsquo;t quite know what the Serverless Framework does when it provisions resources. I don&amp;rsquo;t mean this in a bad way. We &lt;em&gt;could&lt;/em&gt; investigate and read in detail exactly what happens, but part of the benefit of the framework is that it takes care of this for you&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. In the early stages of a project this is probably a great time saver - in the later stages it represents a potential vulnerability.&lt;/p&gt;
&lt;p&gt;We understand that it creates a CloudFormation stack, but some of the details are not necessarily readily discoverable from the documentation.&lt;/p&gt;
&lt;p&gt;If we wanted to create a policy which represents the permissions which the Serverless we could try a few approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Give the process full access to an environment, with wide permissions to create any resources&lt;/li&gt;
&lt;li&gt;Give the process limited access to an environment, run the provisioning process, see the permissions issues which arise, then iteratively add more permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might think that the first instinct of a security team might be that a &amp;lsquo;full access&amp;rsquo; approach is fundamentally wrong. But great security experts balance risk, agility, cost all the time. They don&amp;rsquo;t want to stop experimentation - just make sure that it is done in a safe way.&lt;/p&gt;
&lt;p&gt;The first approach is perfectly fine in low sensitivity environments. If you want to move fast and try out the technology, you could lose valuable time trying to get the permissions just right. If you have a solid approach to &lt;em&gt;isolation&lt;/em&gt;, then you should be able to run your proof of concepts in an isolated sandbox environment, which has no access to sensitive resources.&lt;/p&gt;
&lt;p&gt;You can also add Billing Alerts, or even automate the &lt;em&gt;destruction&lt;/em&gt; of resources at a certain point, so that you automatically &amp;lsquo;clean up&amp;rsquo;. This is great security practice - provide teams with a way to experiment &lt;em&gt;safely&lt;/em&gt;. Give teams full access to their own self-serve sandbox environments, with automated guardrails to mitigate the risk of incidents&lt;sup id="fnref1:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="beyond-the-sandbox"&gt;Beyond the Sandbox&lt;/h2&gt;
&lt;p&gt;The &amp;lsquo;sandbox environment&amp;rsquo; approach &lt;em&gt;can&lt;/em&gt; be valid. But there will likely come a stage when this is no longer appropriate.&lt;/p&gt;
&lt;p&gt;When you want to deploy into an environment which has other resources, sensitive data, is accessible to the public, runs production workloads and so on. There will likely come a point where you cannot fully isolate your application - at this stage we should be looking at improving our security.&lt;/p&gt;
&lt;p&gt;Specifically, at this point we really should try to make sure that we limit the permissions of the process which runs the &lt;code&gt;serverless deploy&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Limiting the permissions has the benefit of reducing the &amp;lsquo;blast radius&amp;rsquo; of an attack. If the process is compromised it can do fewer things. It also has the benefit of &lt;em&gt;increasing transparency&lt;/em&gt; - we will explicitly document &lt;em&gt;what we expect the process to do&lt;/em&gt;. This is highly useful when performing security checks.&lt;/p&gt;
&lt;h2 id="the-challenges-of-limiting-permissions"&gt;The Challenges of Limiting Permissions&lt;/h2&gt;
&lt;p&gt;The process of working out the specific permissions required for a process can be challenging. It might involve looking through lots of documentation, trying to build a policy, seeing if the process works, adding permissions, changing permissions and so on.&lt;/p&gt;
&lt;p&gt;Just this month AWS released some updates to the IAM Access Manager, adding some features to help build &amp;rsquo;least permission&amp;rsquo; policies:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/about-aws/whats-new/2021/04/iam-access-analyzer-easier-implement-least-privilege-permissions-generating-iam-policies-access-activity/"&gt;https://aws.amazon.com/about-aws/whats-new/2021/04/iam-access-analyzer-easier-implement-least-privilege-permissions-generating-iam-policies-access-activity/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These features immediately caught my eye as I&amp;rsquo;m very interesting in security practices. We&amp;rsquo;re going to take a look at these features in detail for the rest of the article and see how we can use them to improve the security of a process like our &amp;lsquo;serverless deployment&amp;rsquo;.&lt;/p&gt;
&lt;h2 id="using-the-iam-access-analyser-to-build-fine-grained-policies"&gt;Using the IAM Access Analyser to Build Fine-Grained Policies&lt;/h2&gt;
&lt;p&gt;The process for generating fine grained policies with the IAM Access Analyser is quite simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ensure you are using CloudTrail to track access to resources&lt;/li&gt;
&lt;li&gt;Create an Access Analyser&lt;/li&gt;
&lt;li&gt;Run the process you want to create fine-grained permissions for, initially with wide permissions&lt;/li&gt;
&lt;li&gt;Use the Access Analyser to generate a policy based on the events in CloudTrail&lt;/li&gt;
&lt;li&gt;Refine the policy&lt;/li&gt;
&lt;li&gt;Document, document, document&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;m going to demonstrate this end-to-end with a &amp;lsquo;serverless framework deployment&amp;rsquo; process. Please keep an eye out for an article I&amp;rsquo;ll be writing soon on how to build a REST application with the Serverless Framework - for now don&amp;rsquo;t worry about the application itself too much, this could be any kind of deployment or operational process we&amp;rsquo;re running.&lt;/p&gt;
&lt;h3 id="step-1-enable-cloudtrail"&gt;Step 1: Enable CloudTrail&lt;/h3&gt;
&lt;p&gt;We need to enable CloudTrail so that we have a log of API calls which Access Advisor can analyse.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt;: if you trying these features out please be aware that they may fall outside of the &lt;a href="https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;amp;all-free-tier.sort-order=asc&amp;amp;awsf.Free%20Tier%20Types=*all&amp;amp;awsf.Free%20Tier%20Categories=*all"&gt;AWS Free Tier&lt;/a&gt; and so may incur a cost. Please be aware of this if you are testing these features.&lt;/p&gt;
&lt;p&gt;Open the CloudTrail portal and ensure that you have a trail setup which logs API calls. Once this is setup you should see something like this:&lt;/p&gt;
&lt;img src="./images/cloudtrail.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/cloudtrail.png" class="img-zoomable"&gt;
&lt;p&gt;You can use the code below as an example of how to create a trail:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws cloudtrail create-trail &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --name management-events &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --s3-bucket-name cloudtrail-account123-management-events-bucket &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --is-multi-region-trail
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that if you don&amp;rsquo;t use the &lt;code&gt;--is-multi-region-trail&lt;/code&gt; flag then the trail is created for the current region only.&lt;/p&gt;
&lt;p&gt;You can read more about this command on the &lt;a href="https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-and-update-a-trail-by-using-the-aws-cli-create-trail.html"&gt;Using create-trail&lt;/a&gt; documentation page.&lt;/p&gt;
&lt;h3 id="step-2-create-the-access-analyser"&gt;Step 2: Create the Access Analyser&lt;/h3&gt;
&lt;p&gt;You should be able to find the Access Analyser tool in the IAM page:&lt;/p&gt;
&lt;img src="./images/access-analyser.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/access-analyser.png" class="img-zoomable"&gt;
&lt;p&gt;You can now choose to create an analyser. Some things to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Analysers are region specific - if you have many regions, you need many analysers&lt;/li&gt;
&lt;li&gt;You can provide a name, not surprising, but it might be useful to be highly descriptive here&lt;/li&gt;
&lt;li&gt;You have an option to specify the &amp;lsquo;zone of trust&amp;rsquo; - this might be an account or an entire organisation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Choose &amp;lsquo;Create Analyser&amp;rsquo; to create the access analyser.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll shortly see the created access analyser and likely some &amp;lsquo;findings&amp;rsquo; as well:&lt;/p&gt;
&lt;img src="./images/access-analyser-created.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/access-analyser-created.png" class="img-zoomable"&gt;
&lt;p&gt;&amp;lsquo;Findings&amp;rsquo; are the descriptions of the policies that grant access to a resource to a principal which is &lt;em&gt;outside of your zone of trust&lt;/em&gt;. This is a quite complex topic, there are more details on the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html"&gt;Access Analyser findings&lt;/a&gt; documentation. But we are essentially seeing that my policies are set up in a which is exposing my resources to principals outside of the defined zone of trust - these findings are entirely correct, as I have set up &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html"&gt;Cross Account Access&lt;/a&gt; in this environment.&lt;/p&gt;
&lt;p&gt;Feel free to read more about findings - these findings can potentially be very useful for a security team to be aware. For now we&amp;rsquo;ll leave these findings alone and move onto the next step in creating fine grained policies, which is to run the process we want to secure.&lt;/p&gt;
&lt;h3 id="step-3-run-your-process"&gt;Step 3: Run your process&lt;/h3&gt;
&lt;p&gt;Now you should run the process you want to create fine grained permissions for. To save rework, try and make sure you run &lt;em&gt;all&lt;/em&gt; part of the process which will be needed. For example, my Serverless Framework policy should cover creation of the stack, updating of the stack as well as deleting.&lt;/p&gt;
&lt;p&gt;To exercise this, I just need to run the following commands from my local project:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# This command deploys a new stack...
serverless --stage dev deploy
# This command makes a change to a file, allowing us to update the stack...
echo &amp;#34;// testing a change to the stack...&amp;#34; &amp;gt;&amp;gt;&amp;gt; lambda_functions/my_function.js
serverless --stage dev deploy # this will update the stack
# This command deletes the stack, we then reset the changes to the file.
serverless --stage dev remove # this will destroy the stack
git checkout lambda_functions/my_function.js
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point you can run any process your want to secure. As CloudTrail is enable, API calls will be recorded.&lt;/p&gt;
&lt;h3 id="step-4-create-a-policy-based-on-access-advisor"&gt;Step 4: Create a Policy based on Access Advisor&lt;/h3&gt;
&lt;p&gt;Now we get to the interesting part. Open the Roles view in AWS, select the role which is used when running your process and choose &amp;lsquo;Generate Policy&amp;rsquo;. Here&amp;rsquo;s how this will look in the portal:&lt;/p&gt;
&lt;img src="./images/generate-policy.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/generate-policy.png" class="img-zoomable"&gt;
&lt;p&gt;Note that this screenshot shows &lt;em&gt;exactly why&lt;/em&gt; fine grained permissions are so important. I have run the Serverless Framework deployment from my local machine using the &amp;lsquo;Cross Account Administrator&amp;rsquo; role. This is an extremely high-privilege role which is used when I administer AWS accounts which are part of my organisation. This is &lt;em&gt;not&lt;/em&gt; an appropriate role for the Serverless Framework binary to assume outside of a sandbox or proof of concept context. However - remember at this stage we want to use a high-privilege role so that the process runs to completion, so that we can see the permissions needed and then create a more refined role.&lt;/p&gt;
&lt;p&gt;When you choose to generate a policy, you&amp;rsquo;ll have the option of specifying which trail to use and which region. You can also choose a date range for events to analyse. It will be a lot easier to build an appropriate policy if you make this window as short as possible - so try and run the entire process you want to create a policy for and then immediately generate the policy.&lt;/p&gt;
&lt;p&gt;It can take a few minutes to generate the policy. When it is complete, you&amp;rsquo;ll see the option to view the generated policy:&lt;/p&gt;
&lt;img src="./images/view-generated-policy.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/view-generated-policy.png" class="img-zoomable"&gt;
&lt;p&gt;Opening it up, you&amp;rsquo;ll see the services and actions used, as well as options to add more actions:&lt;/p&gt;
&lt;img src="./images/generated-policy.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/generated-policy.png" class="img-zoomable"&gt;
&lt;p&gt;When you move to the next screen you&amp;rsquo;ll get the option to customise the permissions:&lt;/p&gt;
&lt;img src="./images/customise-permissions.png" alt="Building Least Privilege Policies with the AWS Policy Advisor - and a Demo with the Serverless Application Framework ./images/customise-permissions.png" class="img-zoomable"&gt;
&lt;p&gt;At this stage I would suggest copying the policy, saving it to a local file and then moving onto the next step - refining the policy.&lt;/p&gt;
&lt;h3 id="step-5-refine-the-policy"&gt;Step 5: Refine the Policy&lt;/h3&gt;
&lt;p&gt;The generated policy will likely not be suitable for use yet. It might to too specific - limiting access to the specific resources which were used. You will also have services and actions listed for &lt;em&gt;any&lt;/em&gt; calls which have been made using the role - which might also include calls used for &lt;em&gt;other&lt;/em&gt; processes than just the one you tested earlier. For example, my policy has some permissions to list analyzers. That is &lt;em&gt;not&lt;/em&gt; needed by serverless - that has been included because I was using the same cross-account administrator role to create the analyzer earlier on, and this has been picked up.&lt;/p&gt;
&lt;p&gt;This also highlights the importance of using separate roles for separate processes - if you use a small number of roles to do a lot of different things, it can be very hard to understand why certain actions were taken. Again, for a quick test it might be OK to use the same role that you access the portal with, but it is good to get into the habit of quickly creating roles for specific purposes.&lt;/p&gt;
&lt;p&gt;This is where I would suggest going through the policy in detail and using it as a template for the &amp;lsquo;final&amp;rsquo; policy. This is what we&amp;rsquo;ll discus in the final step.&lt;/p&gt;
&lt;h3 id="step-6-document-document-document"&gt;Step 6: Document, Document, Document&lt;/h3&gt;
&lt;p&gt;The final policy we create should be clearly documented. It is really important to explain &lt;em&gt;why&lt;/em&gt; certain permissions are needed. If people cannot reason about why a policy is set up in a specific way, then it will be very hard for them to maintain it over time or decide whether the permissions are appropriate or not.&lt;/p&gt;
&lt;p&gt;Even more so than with &amp;rsquo;normal&amp;rsquo; code, code which relates to security has to be comprehensible by others (or yourself when you come back to it). If you cannot understand why a policy grants a certain permission, then when you review the policy you don&amp;rsquo;t know whether the remove the permissions or leave them in (this is an example of &lt;a href="https://github.com/dwmkerr/hacker-laws#chestertons-fence"&gt;Chesterton&amp;rsquo;s Fence&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Whether you document this policy by saving it in a file, adding comments and checking it into source control, or turning it into a re-usable Terraform module, or using Pulimi to define the policy, or some other solution, is not too important. What &lt;em&gt;is&lt;/em&gt; important is documenting the policy and making this documentation transparent to others - and ideally making sure that others can maintain it over time.&lt;/p&gt;
&lt;p&gt;As an example, this is how I might define the S3 permissions in a Terraform file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# This statement allows the creation and management of buckets, which are used
# by serverless for CloudFormation files. Because the bucket name is
# non-deterministic we have to allow the creation of _any_ bucket.
statement {
sid = &amp;#34;ServerlessFrameworkS3&amp;#34;
effect = &amp;#34;Allow&amp;#34;
actions = [
&amp;#34;s3:CreateBucket&amp;#34;,
&amp;#34;s3:DeleteBucket&amp;#34;,
&amp;#34;s3:DeleteBucketPolicy&amp;#34;,
&amp;#34;s3:GetBucketAcl&amp;#34;,
&amp;#34;s3:GetBucketPolicy&amp;#34;,
&amp;#34;s3:GetBucketPolicyStatus&amp;#34;,
&amp;#34;s3:GetBucketPublicAccessBlock&amp;#34;,
&amp;#34;s3:GetEncryptionConfiguration&amp;#34;,
&amp;#34;s3:GetObject&amp;#34;,
&amp;#34;s3:ListBucket&amp;#34;,
&amp;#34;s3:PutBucketPolicy&amp;#34;,
&amp;#34;s3:PutBucketPublicAccessBlock&amp;#34;,
&amp;#34;s3:PutBucketTagging&amp;#34;,
&amp;#34;s3:PutEncryptionConfiguration&amp;#34;,
&amp;#34;s3:PutObject&amp;#34;,
&amp;#34;s3:SetBucketEncryption&amp;#34;,
]
resources = [
&amp;#34;arn:aws:s3:::*&amp;#34;
]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;m being very explicit with my comments, giving the statement id a meaningful value and creating separate statements for &lt;em&gt;each service&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;How you structure your policies will depend on the tools you use and your own preferred processes but the principle will remain the same - document carefully!&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s It!&lt;/h2&gt;
&lt;p&gt;The IAM Access Advisor is a powerful feature and should be of interest to anyone managing sensitive environments in the cloud.&lt;/p&gt;
&lt;p&gt;It is not limited to creating fine grained permissions - it can also help identify &lt;em&gt;external access&lt;/em&gt; to resources. This means it can be used to identify when resources such as S3 buckets are accessed via processes such as Cross-Account access.&lt;/p&gt;
&lt;p&gt;There are a lot of exciting features here and it will be interesting to see how the Access Advisor works over time!&lt;/p&gt;
&lt;p&gt;As usual, please do share any comments, suggestions or observations!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Of course this is only a tiny part of the world of security best practices. To learn more I highly recommend &lt;a href="https://github.com/veeral-patel/how-to-secure-anything"&gt;Veeral Patel&amp;rsquo;s amazing &amp;lsquo;How to secure anything&amp;rsquo; project&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The exact permissions required are documented at &lt;a href="https://serverless-stack.com/chapters/customize-the-serverless-iam-policy.html"&gt;https://serverless-stack.com/chapters/customize-the-serverless-iam-policy.html&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Unit Testing the Windows Registry</title><link>https://dwmkerr.com/unit-testing-the-windows-registry/</link><pubDate>Sat, 05 Sep 2020 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/unit-testing-the-windows-registry/</guid><description>&lt;p&gt;I&amp;rsquo;ve been updating some of my .NET projects recently (read more about this in &lt;a href="https://dwmkerr.com/modernising-dotnet-projects/"&gt;Modernising .NET projects for .NET Core and beyond!&lt;/a&gt;). In one of these projects I have to work with the &lt;a href="https://en.wikipedia.org/wiki/Windows_Registry"&gt;Windows Registry&lt;/a&gt; - which can be quite painful, particularly if you want to make your code unit test friendly.&lt;/p&gt;
&lt;p&gt;In this article I&amp;rsquo;m going to introduce a simple approach to make testing the registry a little easier. If you are just interested in the code and not so much the story behind it, you can skip straight to the project at &lt;a href="https://github.com/dwmkerr/dotnet-windows-registry"&gt;github.com/dwmkerr/dotnet-windows-registry&lt;/a&gt;.&lt;/p&gt;
&lt;!-- vim-markdown-toc GFM --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-bother-testing"&gt;Why Bother Testing?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#why-bother-testing-the-registry"&gt;Why Bother Testing the Registry?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#talk-is-cheap-show-me-the-code"&gt;Talk is cheap, show me the code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-registry-is-not-easily-testable"&gt;The Registry is not easily testable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-testable-registry"&gt;The Testable Registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-forth-and-test"&gt;Go forth and test&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- vim-markdown-toc --&gt;
&lt;h1 id="why-bother-testing"&gt;Why Bother Testing?&lt;/h1&gt;
&lt;p&gt;There is a wealth of material available on the subject of testing. The value different of different types of tests has been discussed at length and is a constant source of debate. If you are interested in reading about testing in more detail, I recommend &lt;a href="https://martinfowler.com/testing/"&gt;Martin Fowler&amp;rsquo;s Software Testing Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to weigh in on the debate of the value of different tests. Instead, here are the specific issues I faced when working on my &lt;a href="https://github.com/dwmkerr/sharpshell"&gt;SharpShell&lt;/a&gt; project (which is where my registry testing project originated):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This is an open source project with a number of users, who would be inconvenienced if things broke from one release to another&lt;/li&gt;
&lt;li&gt;There are a number of scenarios in the project which involve extensive modification of the registry&lt;/li&gt;
&lt;li&gt;Even very small mistakes in the way the registry is accessed can break the code&lt;/li&gt;
&lt;li&gt;Manually testing these scenarios is &lt;em&gt;very&lt;/em&gt; time consuming&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip;and I have very limited time to work on this project&lt;/li&gt;
&lt;li&gt;I want to encourage others to contribute, but have confidence their changes will not cause unexpected failures&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In &lt;em&gt;this&lt;/em&gt; project, being able to test the changes my code is going to make to the registry has been valuable. Whether it is for your own projects will depend on your own circumstances.&lt;/p&gt;
&lt;h1 id="why-bother-testing-the-registry"&gt;Why Bother Testing the Registry?&lt;/h1&gt;
&lt;p&gt;The Registry is essentially a database. A problematic database. It has a complex schema, which has evolved over time. The schema for certain features (such as Windows Shell Extensions) has changed considerably over the years. It is often messy - many programs will write to it and programs can overwrite values.&lt;/p&gt;
&lt;p&gt;One thing I have discovered over my years maintaining the SharpShell project is that registry access is one of the most &lt;em&gt;brittle&lt;/em&gt; elements of the code. It is risky, it can have unexpected consequences.&lt;/p&gt;
&lt;p&gt;There are a few things which should cause anyone working with the registry to seriously consider testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What do you do if the keys you are accessing have been modified by other programs?&lt;/li&gt;
&lt;li&gt;What if your own programs have written incorrect data?&lt;/li&gt;
&lt;li&gt;Is your code going to run on different versions of Windows, which might use the registry in different ways?&lt;/li&gt;
&lt;li&gt;Registry access is &lt;em&gt;security sensitive&lt;/em&gt; - does your code run with the appropriate permissions to access what it needs to access?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The registry is a database, but it is not an ACID database, meaning you can quite easily end up writing data in an inconsistent format (for example, if your program crashes before it has written all of the data it needs to). It has very limited access control - there is no way to limit other privileged programs overwriting or corrupting your data.&lt;/p&gt;
&lt;p&gt;Hopefully covers some of the reasons it is worth testing the registry. Now lets see some code.&lt;/p&gt;
&lt;h1 id="talk-is-cheap-show-me-the-code"&gt;Talk is cheap, show me the code&lt;/h1&gt;
&lt;p&gt;Here&amp;rsquo;s an example of what I want to be able to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[Test]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Register_Server_Associations_Uses_Appropriate_Class_Id_For_Class_Of_Extension()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Pretty important test. Given we have a file extension in the registry, assert that we&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// register an extension with the appropriate ProgID.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Prime the registry with a progid for *.exe files.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _registry.AddStructure(RegistryView.Registry64, &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Join(Environment.NewLine,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34;HKEY_CLASSES_ROOT&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; .exe&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; (Default) = exefile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; Content Type = application/x-msdownload&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; exefile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; (Default) = Application&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Register a context menu with an *.exe association.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; clsid = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Guid(&lt;span style="color:#e6db74"&gt;&amp;#34;00000000-1111-2222-3333-444444444444&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; serverType = ServerType.ShellContextMenu;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; serverName = &lt;span style="color:#e6db74"&gt;&amp;#34;TestContextMenu&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; associations = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt;[] { &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; COMServerAssociationAttribute(AssociationType.ClassOfExtension, &lt;span style="color:#e6db74"&gt;&amp;#34;.exe&amp;#34;&lt;/span&gt;) };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; registrationType = RegistrationType.OS64Bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ServerRegistrationManager.RegisterServerAssociations(clsid, serverType, serverName, associations, registrationType);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Assert we have the new extention.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; print = _registry.Print(RegistryView.Registry64);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Assert.That(print, Is.EqualTo(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Join(Environment.NewLine,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34;HKEY_CLASSES_ROOT&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; .exe&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; (Default) = exefile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; Content Type = application/x-msdownload&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; exefile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; (Default) = Application&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; ShellEx&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; ContextMenuHandlers&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; TestContextMenu&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; (Default) = {00000000-1111-2222-3333-444444444444}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This test looks a little complex, but the details don&amp;rsquo;t matter. What matters is the &lt;em&gt;flow&lt;/em&gt;, which is just:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Given&lt;/strong&gt; a particular existing structure in the registry&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;strong&gt;when&lt;/strong&gt; I call a certain API&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;strong&gt;then&lt;/strong&gt; I expect a certain set of changes to have been made&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Should be easy right? Unfortunately, it&amp;rsquo;s not as easy as this.&lt;/p&gt;
&lt;h1 id="the-registry-is-not-easily-testable"&gt;The Registry is not easily testable&lt;/h1&gt;
&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.win32.registrykey"&gt;.NET Framework Registry classes&lt;/a&gt; are not written with testing in mind. This is not surprising - they are just wrappers around the &lt;a href="https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-functions"&gt;Win32 Registry APIs&lt;/a&gt;. These are APIs which have been around for a while, they have a very well-defined goal, which is to provide access to the registry. They were not written with unit testing in mind.&lt;/p&gt;
&lt;p&gt;There are in general two approaches which can be taken to testing &lt;a href="https://en.wikipedia.org/wiki/Side_effect_(computer_science)"&gt;&lt;em&gt;side effects&lt;/em&gt;&lt;/a&gt;. Side effects are changes to state &lt;em&gt;outside&lt;/em&gt; of your function or code&amp;rsquo;s state - such as the file system, databases and so on. These approaches are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test the System: We allow our tests to change the external system, making sure to prepare it in advance, read the changes, then clean up afterwards&lt;/li&gt;
&lt;li&gt;Mocks the System: We make sure our code doesn&amp;rsquo;t touch the external system when it is testing, we test a mock only and assert that the mocked code makes the expected changes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first approach is arguably better - you are &lt;em&gt;really&lt;/em&gt; asserting that the expected changes have been made. But it is also complex - you have to clean up after yourself, you run the risk of your tests actually changing (or even breaking your system) and you make it harder to have other developers easily run the tests. Some systems can mitigate this - for example, with some databases you could test in the context of a transaction which you never commit. But the registry offers no such capabilities.&lt;/p&gt;
&lt;p&gt;The second approach is more common and in general a little easier. It doesn&amp;rsquo;t cause side effects, but still allows us to at least ensure we are going to attempt to make the expected changes.&lt;/p&gt;
&lt;p&gt;To mock a service under test in .NET, we generally need to be calling functions on an &lt;em&gt;interface&lt;/em&gt;. There are some ways around this (fakes, modified assemblies, etc) but they are problematic. However, the .NET Registry classes are not exposed as interfaces. This is not a failure of the framework, arguably adding interfaces without a specific need is an anti-pattern. But it does make mocking the registry hard.&lt;/p&gt;
&lt;p&gt;The easiest way around this problem (at least in my opinion) is to wrap the registry access in an interface, then provide two implementations. One which uses the standard registry access methods, and one which mocks the changes to the registry in an isolated and testable fashion. In my SharpShell code this was the approach I took, and I have just extracted this code into its own library to help others who might want to use the same approach.&lt;/p&gt;
&lt;h1 id="the-testable-registry"&gt;The Testable Registry&lt;/h1&gt;
&lt;p&gt;The solution I&amp;rsquo;ve used is fairly simple. You can see the code at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/dotnet-windows-registry"&gt;github.com/dwmkerr/dotnet-windows-registry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Instead of making calls to &lt;code&gt;Regsitry&lt;/code&gt; or &lt;code&gt;RegistryKey&lt;/code&gt;, you make calls to &lt;code&gt;IRegsitry&lt;/code&gt; or &lt;code&gt;IRegsitryKey&lt;/code&gt;. Then use the appropriate implementation. There are examples in the project documentation, but here&amp;rsquo;s how it looks in a nutshell.&lt;/p&gt;
&lt;p&gt;First, make sure the code you have which access the registry does it via the &lt;code&gt;IRegistry&lt;/code&gt; interface:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Greeter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Greeter(IRegistry _registry)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _registry = registry;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Greet(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; name, &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; greeting)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; var key = registry.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; var subkey = key.OpenSubKey(&lt;span style="color:#e6db74"&gt;&amp;#34;Greetings&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subkey.SetValue(name, &lt;span style="color:#e6db74"&gt;$&amp;#34;{greeting}, {name}!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; IRegsitry _registry;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in your program, create your class and provide it with a &lt;code&gt;WindowsRegistry&lt;/code&gt; class:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; greeter = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Greeter(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; WindowsRegistry());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;greeter.Greet(&lt;span style="color:#e6db74"&gt;&amp;#34;Billy&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Howdy&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And you can test your code like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; registry = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; InMemoryRegistry();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; greeter = &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Greeter(registry);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; print = _registry.Print(RegistryView.Registry64);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Assert.That(print, Is.EqualTo(&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Join(Environment.NewLine,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34;HKEY_CURRENT_USER&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; Greetings&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;@&amp;#34; Billy = Howdy, Billy!&amp;#34;&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s the basics.&lt;/p&gt;
&lt;h1 id="go-forth-and-test"&gt;Go forth and test&lt;/h1&gt;
&lt;p&gt;There is a degree of inconvenience in having to use the interface rather than using the out-of-the-box implementation. This is a trade-off you will have to make to allow your code to be testable, and whether it is a worthwhile trade will depend on your project.&lt;/p&gt;
&lt;p&gt;The pattern of not relying on concrete implementations and instead providing interfaces to classes is known as &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt;. There are technologies which attempt to assist with this pattern, known as Inversion of Control Containers - whether they make life easier to simply move complexity around (see &lt;a href="https://github.com/dwmkerr/hacker-laws#the-law-of-conservation-of-complexity-teslers-law"&gt;The Law of Conservation of Complexity&lt;/a&gt;). But if you are &lt;em&gt;already using&lt;/em&gt; an IoC container then adopting this library and pattern will be trivial.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it - the code has been internal to the SharpShell project for years and I have only just extracted it into its own library. I&amp;rsquo;ll be using it in my &lt;a href="https://github.com/dwmkerr/dotnet-com-admin"&gt;ComAdmin&lt;/a&gt; project (which is also being extracted from SharpShell). Given that it is new it might change a bit, and I&amp;rsquo;d love any feedback:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/dotnet-windows-registry"&gt;https://github.com/dwmkerr/dotnet-windows-registry&lt;/a&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Modernising .NET projects for .NET Core and beyond!</title><link>https://dwmkerr.com/modernising-dotnet-projects/</link><pubDate>Wed, 01 Jul 2020 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/modernising-dotnet-projects/</guid><description>&lt;p&gt;The world of .NET is going through a transformation. The .NET Framework is reaching end of life, &lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/"&gt;.NET Core&lt;/a&gt; is an increasingly feature rich and robust platform to develop solutions which target Linux, MacOS, embedded devices, containers and more. There&amp;rsquo;s also the .NET Standard.&lt;/p&gt;
&lt;p&gt;But what does this mean for .NET &lt;em&gt;Framework&lt;/em&gt; projects? In this article I&amp;rsquo;ll describe how to modernise your .NET Framework projects for .NET Core, the .NET Standard and .NET 5, which is planned to be released this year. I&amp;rsquo;ll also explain the high level differences between the platforms and what the consequences of upgrading are for consumers, developers and maintainers.&lt;/p&gt;
&lt;!-- vim-markdown-toc GFM --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#the-net-framework-net-core-and-the-future"&gt;The .NET Framework, .NET Core and the Future&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-challenge-modernisation-and-compatibility"&gt;The Challenge: Modernisation and Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-modernisation-process---introducing-our-two-villains"&gt;The Modernisation Process - Introducing our two Villains&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#step-1---understand-the-domain"&gt;Step 1 - Understand the Domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-2---understand-the-goal---multi-platform-builds"&gt;Step 2 - Understand the Goal - Multi-Platform Builds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-3---migrate-projects-leaf-wise"&gt;Step 3 - Migrate Projects &amp;ldquo;Leaf-wise&amp;rdquo;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-4---refactor-rinse-repeat"&gt;Step 4 - Refactor, Rinse, Repeat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-5---update-your-builds"&gt;Step 5 - Update Your Builds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-6---simplify"&gt;Step 6 - Simplify!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-7---test-test-test"&gt;Step 7 - Test, Test, Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#step-8---document-compatibility"&gt;Step 8 - Document Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-key-learnings"&gt;The Key Learnings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- vim-markdown-toc --&gt;
&lt;h1 id="the-net-framework-net-core-and-the-future"&gt;The .NET Framework, .NET Core and the Future&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s a lot which has been written on this topic, but it can still be a little overwhelming to understand just how all of these things fit together.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a simple visual I&amp;rsquo;ve created to try and put things into context:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/dotnet-timeline.png" alt="Diagram: .NET Timeline"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m only going to cover the bare essentials - but there are links to further reading on each topic if you want to go deeper. This article is mainly going to be focused on the practicality and consequence of migration and re-targeting.&lt;/p&gt;
&lt;p&gt;First, the &lt;strong&gt;.NET Framework&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The .NET Framework was created in 2002 as set of unified tools and standards to allow developers on the Microsoft Platform to more quickly build solutions, provide interoperability between languages and more. &lt;a href="https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet-framework"&gt;Read more about the .NET Framework&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The .NET Framework rapidly gained popularity, partly due to the convenience of developing in C# rather than Basic or C/C++. C# provided a more developer friendly language than C or C++ for many use cases, and was heavily inspired by Java. &lt;a href="https://docs.microsoft.com/en-us/dotnet/csharp/"&gt;Read more about C#&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;With the increase in popularity, the .NET Framework started to have more frequent releases and became a standard part of the Windows operating system, installed out of the box rather than on-demand if needed.&lt;/li&gt;
&lt;li&gt;However - the .NET Framework only functioned on Microsoft Windows, which greatly limited its potential uses cases, even as more and more engineers used it for Web, Client Applications and mobile.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Enter &lt;strong&gt;.NET Core&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft signaled a &lt;em&gt;radical&lt;/em&gt; switch in their strategy with the appointment of &lt;a href="https://en.wikipedia.org/wiki/Satya_Nadella"&gt;Satya Nadella&lt;/a&gt;, becoming increasingly focused on open source, and more importantly, deciding that the Microsoft development toolchain should not &lt;em&gt;force&lt;/em&gt; users to use Windows as their execution environment&lt;/li&gt;
&lt;li&gt;.NET Core was developed as a lightweight version of the .NET Framework, which could run on multiple platforms - including Linux and MacOS. &lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/"&gt;Read more about .NET Core&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In a short period of time .NET Core became more and more feature rich, providing a lot of capabilities for web developers and front-end application developers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The challenges of &lt;strong&gt;divergence&lt;/strong&gt; and the &lt;strong&gt;.NET Standard&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;As .NET Core became more feature rich, the API became closer to the .NET Framework - but they are still fundamentally different runtimes. A binary compiled for the .NET Core does not run on the .NET Framework and vice-versa.&lt;/li&gt;
&lt;li&gt;To deal with this issue, Microsoft developed the &lt;strong&gt;.NET Standard&lt;/strong&gt; - a specification of a set of APIs. If a runtime offered these APIs, then solutions built on &lt;em&gt;any runtime which meets the standard&lt;/em&gt; could run on any compliant platform.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What does this mean? Basically, the table below shows the consequences of this. If you build on .NET Core 2.0 (for example), you can also run on the .NET Framework 4.6.1. Mono 5.4, Unity 2018.1 and more, because all of these runtimes implement the &lt;em&gt;.NET Standard 2.0&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Of course, some features are always going to be very platform specific, so the standard started out small but has grown over time.&lt;/p&gt;
&lt;p&gt;Moving to &lt;strong&gt;convergence&lt;/strong&gt; and &lt;strong&gt;.NET&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Given that the later versions of the .NET Framework and .NET Core actually follow the same standard, the platforms are actually starting to become more and more similar.&lt;/li&gt;
&lt;li&gt;They are becoming &lt;em&gt;so&lt;/em&gt; similar that it no longer makes sense to maintain them separately. The next major version of &lt;em&gt;both&lt;/em&gt; platforms is &lt;strong&gt;.NET 5&lt;/strong&gt;. This is a new runtime which is the next version of .NET Core &lt;em&gt;and&lt;/em&gt; the .NET Framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that the .NET Framework and .NET Core are going to converge into a single platform, which will be wonderful for developers and simplify a complex landscape.&lt;/p&gt;
&lt;p&gt;But what does this mean if you have .NET Framework projects? How do we modernise, and do we have to make trade-offs around compatibility?&lt;/p&gt;
&lt;h1 id="the-challenge-modernisation-and-compatibility"&gt;The Challenge: Modernisation and Compatibility&lt;/h1&gt;
&lt;p&gt;I have a number of projects which target the .NET Framework. On &lt;em&gt;all&lt;/em&gt; of these projects I have had multiple requests to migrate to the .NET Core, but I have had to hold off on this work until I could really understand in detail a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What would this mean for &lt;em&gt;consumers&lt;/em&gt; of the libraries? Would they have to change the platform they use? Could this break things for them?&lt;/li&gt;
&lt;li&gt;What would this mean for &lt;em&gt;developers&lt;/em&gt; on the platform? Would they need to change their development environment? Would this cause problems?&lt;/li&gt;
&lt;li&gt;What would this mean for &lt;em&gt;maintainers&lt;/em&gt; of the libraries? Would this greatly increase build and deployment complexity?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Finally I have found the time to be able to start to address these issues in detail - hopefully the learnings will be useful to anyone who is maintaining a .NET codebase and thinking about the future.&lt;/p&gt;
&lt;h1 id="the-modernisation-process---introducing-our-two-villains"&gt;The Modernisation Process - Introducing our two Villains&lt;/h1&gt;
&lt;p&gt;There are two key projects I wanted to modernise. They are both reasonably well used, complex, and have some potentially serious complexities for multi-platform builds.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/sharpgl"&gt;&lt;strong&gt;SharpGL&lt;/strong&gt;&lt;/a&gt; is a library that allows developers to use &lt;a href="https://www.opengl.org/"&gt;OpenGL&lt;/a&gt; in .NET applications. The big challenge? OpenGL is cross platform, but SharpGL &lt;em&gt;specifically&lt;/em&gt; provides an interface to the &lt;em&gt;Windows&lt;/em&gt; version of OpenGL. Can this possibly be made more future-proof? Could it ever target other platforms?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/sharpshell"&gt;&lt;strong&gt;SharpShell&lt;/strong&gt;&lt;/a&gt; is a library that allows developers to build &amp;lsquo;Shell Extensions&amp;rsquo; for Windows. Shell extensions are customisations to the Windows user interface, so would not be portable across platforms, but I still want to ensure that the project is future proof.&lt;/p&gt;
&lt;p&gt;What would be the experience with these two projects? I have other .NET projects, but they are far less popular and much more simple, my instinct is that if I can work through the process with &lt;em&gt;these&lt;/em&gt; projects, the others should be more straightforward.&lt;/p&gt;
&lt;p&gt;These are the steps I&amp;rsquo;ve followed to modernise. I&amp;rsquo;ll finish the article with a summary of the key learnings.&lt;/p&gt;
&lt;h2 id="step-1---understand-the-domain"&gt;Step 1 - Understand the Domain&lt;/h2&gt;
&lt;p&gt;I cannot stress this enough. In all meaningful technology work, &lt;em&gt;understand the domain&lt;/em&gt; you are dealing with. A quick Google on how to migrate, or following the formal migration guide was not enough for me. I knew I had to actually understand, at a reasonably detailed level, the differences in the runtime, the trade-offs, the process, the complexity.&lt;/p&gt;
&lt;p&gt;This article is the result of that work - sometimes writing about a topic is the best way to force yourself to learn it.&lt;/p&gt;
&lt;p&gt;Making changes rapidly and waiting to see what the consequences are can often work for small projects, internal tools and so on, but for a library which is relied upon by others is not good for the community. The last thing I wanted to do was make changes which had unintended consequences for users. So making sure that I learnt about this space, how things work under the hood, and what the expected changes in the future are was critical.&lt;/p&gt;
&lt;p&gt;Hopefully for others the process of understanding the domain will be a little easier with this article to cover the high level topics. During my actual process of writing and migrating, I went a lot deeper than this article goes.&lt;/p&gt;
&lt;p&gt;The key document to follow to actually &lt;em&gt;execute&lt;/em&gt; the migration is the excellent official &lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/porting/"&gt;.NET Framework to .NET Core Porting Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="step-2---understand-the-goal---multi-platform-builds"&gt;Step 2 - Understand the Goal - Multi-Platform Builds&lt;/h2&gt;
&lt;p&gt;Given the understanding of the domain, it made it much easier to understand what the required steps would be. Essentially, all that would be needed would be to target a version of the .NET Framework which adheres to a recent version of the .NET Standard. Once this was done, in theory the project could be built for the .NET Framework &lt;em&gt;and&lt;/em&gt; for .NET Core, and also be ready for the upcoming .NET 5 release.&lt;/p&gt;
&lt;p&gt;Multi-platform builds are supported in Visual Studio 2019. These builds allow us to have a single codebase, but build libraries for multiple platforms (i.e. the .NET Framework and .NET Core). The resulting binaries can be packed as a single package, and when consumers install the package, the appropriate library is installed.&lt;/p&gt;
&lt;p&gt;This introduces the first of the significant consequences - modernising your project means you must migrate it to Visual Studio 2019.&lt;/p&gt;
&lt;p&gt;In the past, this might have been more of an issue, licenses for Visual Studio were expensive, and many organisations were locked onto specific versions for compatibility issues (or because they were slow to upgrade). This seems to be the case less often nowadays, but is still an important consideration.&lt;/p&gt;
&lt;p&gt;My projects were using Visual Studio 2017. This is how the project properties looked:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/sharpgl-target-framework-2017.png" alt="Screenshot: SharpGL Target Framework Properties for Visual Studio 2017"&gt;&lt;/p&gt;
&lt;p&gt;Unsurprisingly the .NET Standard isn&amp;rsquo;t mentioned. Time to upgrade to 2019. While I installed it I could reminisce about the excitement of buying Visual C++ .NET Learning Edition:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/visual-cpp-dotnet-learning-edition.jpg" alt="Photo: Visual C++ .NET 2003 Learning Edition"&gt;&lt;/p&gt;
&lt;p&gt;And try and remember what is was like to be a 15 years old. I wonder if that box set is still kicking around somewhere, I want to see it again. So much has changed. But long install processes for Visual Studio haven&amp;rsquo;t, at least they kept that:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/install-visual-studio-2019.png" alt="Screenshot: Visual Studio 2019 Installer"&gt;&lt;/p&gt;
&lt;p&gt;When installing, remember to enable the .NET Core features.&lt;/p&gt;
&lt;h2 id="step-3---migrate-projects-leaf-wise"&gt;Step 3 - Migrate Projects &amp;ldquo;Leaf-wise&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;As per the &lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/porting/"&gt;Porting Guide&lt;/a&gt;, we need to migrate each of the projects which make up the solution, starting with the &amp;rsquo;leaves&amp;rsquo; (projects which don&amp;rsquo;t depend on other projects) and then moving up the tree to the &amp;lsquo;root&amp;rsquo; projects (projects which are depended on by others).&lt;/p&gt;
&lt;p&gt;Visually, for a solution like SharpGL, that would mean the projects will need to be converted in the following order:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/sharpgl-project-structure.png" alt="Diagram: SharpGL Project Dependency Graph"&gt;&lt;/p&gt;
&lt;p&gt;I was expecting each project to have quite different experiences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SharpGL.Serialization&lt;/code&gt; is just a set of classes which load data from files. In theory, this library should become completely portable.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SharpGL.WPF&lt;/code&gt; and &lt;code&gt;SharpGL.WinForms&lt;/code&gt; are &lt;em&gt;specifically&lt;/em&gt; for Windows front-end technologies. I expected these to be able to be ported, but don&amp;rsquo;t expect them to work on other platforms (in the future there might be &lt;code&gt;SharpGL.OSx&lt;/code&gt;, or &lt;code&gt;SharpGL.Gnome&lt;/code&gt;, who knows)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SharpGL.SceneGraph&lt;/code&gt; is a set of classes which represent 3D scenes - things like lights, cameras, materials and so on. I expect &lt;em&gt;some&lt;/em&gt; of this to &amp;lsquo;just work&amp;rsquo;, but things like image loading to perhaps need some tweaking.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SharpGL&lt;/code&gt; is just a wrapper around the Windows &lt;code&gt;opengl32.dll&lt;/code&gt; library. I can&amp;rsquo;t imagine this &lt;em&gt;working&lt;/em&gt; anywhere but Windows, but how would the project structure porting go and would it build?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The details on &lt;em&gt;how&lt;/em&gt; to migrate a project are in the &lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/porting/"&gt;Porting Guide&lt;/a&gt;, but the general approach will be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Attempt to convert to the latest project format with the &lt;code&gt;try-convert&lt;/code&gt; tool&lt;/li&gt;
&lt;li&gt;Re-target the project to the .NET Framework 4.7.2 (the first version which supports the .NET standard)&lt;/li&gt;
&lt;li&gt;Repeat for projects which this project depends on, walking the tree of projects to the root&lt;/li&gt;
&lt;li&gt;Run the Portability Analysis tool to see if there are APIs which are not available on certain platforms&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is how you project might look after migration, having run the &lt;code&gt;try-convert&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/migrate-project.png" alt="Screenshot: Ported Visual Studio Project"&gt;&lt;/p&gt;
&lt;p&gt;Now we just need to edit the project files and change the line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;4.7.2&lt;span style="color:#f92672"&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp2.0;netcoreapp3.0;netcoreapp3.1;net40;net45;net472&lt;span style="color:#f92672"&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The targets you will choose will depend on the APIs you want to use. There is an Portability Analysis extension available which can build a portability report, here&amp;rsquo;s what one looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/portability-report-summary.png" alt="Screenshot: Portability Report Summary"&gt;&lt;/p&gt;
&lt;p&gt;This will also show the &lt;em&gt;specific&lt;/em&gt; APIs which are not compatible with specific targets:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/portability-report.png" alt="Screenshot: Portability Report Details"&gt;&lt;/p&gt;
&lt;p&gt;Now it&amp;rsquo;s time to move to the next step.&lt;/p&gt;
&lt;h2 id="step-4---refactor-rinse-repeat"&gt;Step 4 - Refactor, Rinse, Repeat&lt;/h2&gt;
&lt;p&gt;This is the tricky part. You&amp;rsquo;ll now need to work out whether you want to &lt;em&gt;remove&lt;/em&gt; API calls which are not portable, try and use alternatives, or conditionally compile the code for different platforms.&lt;/p&gt;
&lt;p&gt;If you are using non-portable APIs you may need to use conditional blocks to execute different code depending on the framework used. The &lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks"&gt;Target frameworks in SDK-style projects&lt;/a&gt; guide shows how to do this.&lt;/p&gt;
&lt;p&gt;You may also have to manually edit the project file to ensure that certain dependencies are &lt;em&gt;only&lt;/em&gt; used for certain targets. You solution file and dependencies may end up looking something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/conditional-dependencies.png" alt="Screenshot: Conditional Dependencies"&gt;&lt;/p&gt;
&lt;p&gt;Once you have reloaded the project you&amp;rsquo;ll see your dependencies can now be specified on a per-framework basis, and a build generates assemblies for each of the targets:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/generated-assemblies.png" alt="Screenshot: Generated Assemblies"&gt;&lt;/p&gt;
&lt;p&gt;This process might be simple, or complex, depending on the nuances of your project. For me it was fairly iterative - starting by targeting only &lt;code&gt;net40&lt;/code&gt; (the original target framework which I&amp;rsquo;d used), then adding more and more targets.&lt;/p&gt;
&lt;p&gt;Some targets will simply not be possible - for example .NET Core only supports WinForms and WPF from .NET Core 3.0 onwards; you won&amp;rsquo;t be able to build a WinForms or WPF assembly which targets a lower version, the framework doesn&amp;rsquo;t support it.&lt;/p&gt;
&lt;h2 id="step-5---update-your-builds"&gt;Step 5 - Update Your Builds&lt;/h2&gt;
&lt;p&gt;At this stage, having fixed compatibility issues, you should have code which builds in Visual Studio.&lt;/p&gt;
&lt;p&gt;Now I would recommend porting all of your build code to use the &lt;code&gt;dotnet&lt;/code&gt; build system. This is going to maximise the portability and future-proof your project, you&amp;rsquo;ll be able to run the builds on multiple platforms and are using the preferred standard tool (&lt;code&gt;msbuild&lt;/code&gt; will essentially become legacy).&lt;/p&gt;
&lt;p&gt;The way I like to structure things personally is have a set of scripts which you can run to build, test and package the code locally. You can then call these scripts from you CI tool of choice to automate things, but still keep the logic in your own code, rather than hidden away in a build system.&lt;/p&gt;
&lt;p&gt;For example, in my SharpGL project I have the following scripts:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;config.ps1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensure your machine can run builds by installing necessary components such as &lt;code&gt;nunit&lt;/code&gt;. Should only need to be run once.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build.ps1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build all solutions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;test.ps1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run all tests, including those in samples.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;coverage.ps1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a coverage report. Reports are written to &lt;code&gt;./artifacts/coverage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pack.ps1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create all of the SharpGL NuGet packages, which are copied to &lt;code&gt;./artifacts/packages&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I updated my scripts to use the &lt;code&gt;dotnet&lt;/code&gt; tool. For example, the &amp;lsquo;build&amp;rsquo; script looks something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ps" data-lang="ps"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;#&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Run&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;build,&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;hiding&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;documentation&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;warnings&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pinvoke&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;code.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$buildCommand&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;=&amp;#34;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;msbuild&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$PSScriptRoot\SharpGL.sln&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;-noWarn:CS1591&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;-noWarn:CS1573&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;-t:Rebuild&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;-p:Configuration=Release&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Write-Host&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;Running:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;&amp;#34;$buildCommand&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Invoke-Expression&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$buildCommand&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the &amp;lsquo;pack&amp;rsquo; script looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ps" data-lang="ps"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pack&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-restore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-build&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;$PSScriptRoot&lt;/span&gt;/Core/SharpGL/SharpGL.csproj&amp;#34; &lt;span style="color:#a6e22e"&gt;-c:Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pack&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-restore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-build&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;$PSScriptRoot&lt;/span&gt;/Core/SharpGL.SceneGraph/SharpGL.SceneGraph.csproj&amp;#34; &lt;span style="color:#a6e22e"&gt;-c:Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pack&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-restore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-build&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;$PSScriptRoot&lt;/span&gt;/Core/SharpGL.Serialization/SharpGL.Serialization.csproj&amp;#34; &lt;span style="color:#a6e22e"&gt;-c:Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pack&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-restore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-build&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;$PSScriptRoot&lt;/span&gt;/Core/SharpGL.WinForms/SharpGL.WinForms.csproj&amp;#34; &lt;span style="color:#a6e22e"&gt;-c:Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dotnet&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pack&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-restore&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;--no-build&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;&amp;#34;$PSScriptRoot&lt;/span&gt;/Core/SharpGL.WPF/SharpGL.WPF.csproj&amp;#34; &lt;span style="color:#a6e22e"&gt;-c:Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The actual scripts are a little more complex. But the key thing here is that I can run &lt;em&gt;any&lt;/em&gt; part of the CI/CD process locally (to test, debug and so on) or on a CI/CD platform.&lt;/p&gt;
&lt;p&gt;You will most likely have to &lt;em&gt;conditionally&lt;/em&gt; reference certain components. The dependency for &lt;code&gt;net40&lt;/code&gt; might be different to that for &lt;code&gt;netcoreapp3.0&lt;/code&gt;. You&amp;rsquo;ll see that in many of my project files there is now code like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Design&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net40&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Design&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net45&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Design&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net472&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Windows.Forms&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net40&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Windows.Forms&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net45&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Reference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Windows.Forms&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net472&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Microsoft.CSharp&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.7.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;netcoreapp3.0&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Microsoft.CSharp&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.7.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;netcoreapp3.1&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Microsoft.CSharp&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.7.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net45&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Microsoft.CSharp&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.7.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net472&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Data.DataSetExtensions&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.5.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;netcoreapp3.0&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Data.DataSetExtensions&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.5.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;netcoreapp3.1&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Data.DataSetExtensions&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.5.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net45&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Include=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;System.Data.DataSetExtensions&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Version=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;4.5.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;$(TargetFramework)&amp;#39; == &amp;#39;net472&amp;#39;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In my case quite a bit of trial and error was needed to find the appropriate references for each platform.&lt;/p&gt;
&lt;h2 id="step-6---simplify"&gt;Step 6 - Simplify!&lt;/h2&gt;
&lt;p&gt;One benefit I have found during this process is that you can &lt;em&gt;simplify&lt;/em&gt; your projects. You no longer need any kind of &amp;lsquo;automated NuGet restore&amp;rsquo; functionality. This means you can remove code like this from your project files:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;Import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Project=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;$(SolutionDir)\.nuget\NuGet.targets&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Exists(&amp;#39;$(SolutionDir)\.nuget\NuGet.targets&amp;#39;)&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;Target&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Name=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;EnsureNuGetPackageBuildImports&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BeforeTargets=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;PrepareForBuild&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;ErrorText&amp;gt;&lt;/span&gt;This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.&lt;span style="color:#f92672"&gt;&amp;lt;/ErrorText&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Error&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;!Exists(&amp;#39;..\packages\NUnit.3.11.0\build\NUnit.props&amp;#39;)&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Text=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;$([System.String]::Format(&amp;#39;$(ErrorText)&amp;#39;, &amp;#39;..\packages\NUnit.3.11.0\build\NUnit.props&amp;#39;))&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Error&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Condition=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;!Exists(&amp;#39;..\packages\NUnit3TestAdapter.3.10.0\build\net35\NUnit3TestAdapter.props&amp;#39;)&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Text=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;$([System.String]::Format(&amp;#39;$(ErrorText)&amp;#39;, &amp;#39;..\packages\NUnit3TestAdapter.3.10.0\build\net35\NUnit3TestAdapter.props&amp;#39;))&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also remove your &lt;code&gt;project.json&lt;/code&gt; as all of the data is now in the &lt;code&gt;csproj&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Another nice update is that you no longer need to maintain an &lt;code&gt;AssemblyInfo.cs&lt;/code&gt; file; you can keep all of your assembly metadata in the &lt;code&gt;csproj&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Finally, you can almost certainly remove any &lt;code&gt;nuspec&lt;/code&gt; files - all NuGet packaging data can also be embedded in the &lt;code&gt;csproj&lt;/code&gt; file. For example, here&amp;rsquo;s what my SharpShell project metadata looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;Project&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Sdk=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Microsoft.NET.Sdk.WindowsDesktop&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;netcoreapp2.0;netcoreapp3.0;netcoreapp3.1;net40;net45;net472&lt;span style="color:#f92672"&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Library&lt;span style="color:#f92672"&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- The following properies are used to manage how the project is packaged. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageId&amp;gt;&lt;/span&gt;SharpShell&lt;span style="color:#f92672"&gt;&amp;lt;/PackageId&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Copyright&amp;gt;&lt;/span&gt;Copyright (c) Dave Kerr 2020&lt;span style="color:#f92672"&gt;&amp;lt;/Copyright&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageProjectUrl&amp;gt;&lt;/span&gt;https://github.com/dwmkerr/sharpshell&lt;span style="color:#f92672"&gt;&amp;lt;/PackageProjectUrl&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;RepositoryUrl&amp;gt;&lt;/span&gt;https://github.com/dwmkerr/sharpshell&lt;span style="color:#f92672"&gt;&amp;lt;/RepositoryUrl&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;3.1.1.0&lt;span style="color:#f92672"&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;Dave Kerr&lt;span style="color:#f92672"&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Company&amp;gt;&lt;/span&gt;Dave Kerr&lt;span style="color:#f92672"&gt;&amp;lt;/Company&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;PackageTags&amp;gt;&lt;/span&gt;Shell;SharpShell;COM;Context Menu;Icon Handler&lt;span style="color:#f92672"&gt;&amp;lt;/PackageTags&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;SharpShell is a framework that lets you build Windows Shell Extensions using .NET Core or the .NET Framework.&lt;span style="color:#f92672"&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- ...snip... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This helps to keep a lot of the project dependency and property data in one place and is probably more convenient for many users.&lt;/p&gt;
&lt;p&gt;You can see the &lt;a href="https://github.com/dwmkerr/sharpgl/pull/177"&gt;Pull Request&lt;/a&gt; for SharpGL to see how the project files were updated in this case. You can also see the &lt;a href="https://github.com/dwmkerr/sharpshell/pull/331"&gt;SharpShell Pull Request&lt;/a&gt;. The SharpShell version is still work in progress at the time of writing.&lt;/p&gt;
&lt;h2 id="step-7---test-test-test"&gt;Step 7 - Test, Test, Test&lt;/h2&gt;
&lt;p&gt;Now for the fun part. You are going to &lt;em&gt;really&lt;/em&gt; have to test the new packages on each platform. Sadly, this kind of migration is not something which will have issues exposed via unit tests, you&amp;rsquo;ll need to create test projects which import your packages, ideally for each platform, and make sure they work. There could be runtime errors, particularly if you have made mistakes with the references.&lt;/p&gt;
&lt;p&gt;Many issues will be caught at compile time - some will not.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a screenshot of me having fun trying out the .NET Framework 4 package for WinForms, and the .NET Core 3.1 package for WPF:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/test-packages.png" alt="Screenshot: Testing SharpGL"&gt;&lt;/p&gt;
&lt;p&gt;How you test your packages will be very dependent on what you are building. If it is highly platform specific then you will likely have to do lots of testing. If it is fairly self-contained code then you might be able to get away with some basic smoke testing.&lt;/p&gt;
&lt;h2 id="step-8---document-compatibility"&gt;Step 8 - Document Compatibility&lt;/h2&gt;
&lt;p&gt;If you are supporting multiple platforms and frameworks, it&amp;rsquo;s going to be a lot of help to consumers of your code if you can be very clear about &lt;em&gt;what is supported&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This may be more complex than you think. Your library may run fine as part of a .NET Core Console Application on Windows - but does it work on MacOS? What about Linux?&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a screenshot I would never have imagined when I started the SharpGL project - a terminal application running on MacOS which is using the &lt;code&gt;SharpGL.Serialization&lt;/code&gt; library to load geometry from a file:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/sharpgl-on-mac.png" alt="Screenshot: Loading Geometry in SharpGL on MacOS"&gt;&lt;/p&gt;
&lt;p&gt;Now of course for something like SharpGL to run on a Mac or Linux, a lot more work would be needed. SharpGL is at its core nothing more than a wrapper around &lt;code&gt;opengl32.dll&lt;/code&gt; on Windows, on other platforms there are no DLLs, but OpenGL &lt;em&gt;is&lt;/em&gt; still available. So support is possible, but not ready yet. So at this stage, docmenting what you know works &lt;em&gt;as well as what doesn&amp;rsquo;t&lt;/em&gt; will be really helpful.&lt;/p&gt;
&lt;p&gt;You might also want to preserve your &amp;lsquo;pre-migration&amp;rsquo; code in a separate branch, in case you have users who for some reason have issues migrating and need to use an older version. For SharpGL, I updated the project page to indicate compatibility, what has been tested and so on:&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/readme-compatability.png" alt="Screenshot: SharpGL README showing compatibility information"&gt;&lt;/p&gt;
&lt;h1 id="the-key-learnings"&gt;The Key Learnings&lt;/h1&gt;
&lt;p&gt;Here are the key learnings which stood out for me as I worked on migration of these projects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer Experience&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you are careful, you don&amp;rsquo;t have to break anything for consumers - with multi-targeting you can &lt;em&gt;still&lt;/em&gt; target older frameworks.&lt;/li&gt;
&lt;li&gt;You can potentially greatly increase the compatibility of your projects by offering support for .NET Core.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Developer Experience&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need to upgrade to Visual Studio 2019&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip;however, you can use Visual Studio for Mac or even the command-line to build across many platforms.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Maintainer Experience&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You will have a much larger set of potential consumers, but you will likely find bugs which are framework or platform specific.&lt;/li&gt;
&lt;li&gt;You will likely need to work on migrating your project files and use the latest &lt;code&gt;dotnet&lt;/code&gt; tooling.&lt;/li&gt;
&lt;li&gt;You should be careful to document known compatibility issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, the process was less painful than I expected. Now that this work is done I can focus on more exciting things, such as potentially getting projects like SharpGL working on Linux or MacOS, which is much more exciting.&lt;/p&gt;
&lt;p&gt;As always, questions, comments, suggestions, rants, anything are welcome!&lt;/p&gt;
&lt;p&gt;The pull request which migrates the SharpGL project and SharpShell projects are below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dwmkerr/sharpgl/pull/177/"&gt;github.com/dwmkerr/sharpgl/pull/177/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dwmkerr/sharpshell/pull/331"&gt;github.com/dwmkerr/sharpshell/pull/331&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Useful References&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/"&gt;Microsoft Docs: .NET Core Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-gb/dotnet/core/porting/"&gt;Microsoft Docs: Overview of porting from .NET Framework to .NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks"&gt;Microsoft Docs: Target frameworks in SDK-style projects&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><category>CodeProject</category></item><item><title>Observations, tips and tricks for the CKA certification</title><link>https://dwmkerr.com/tips-for-cka/</link><pubDate>Thu, 04 Jun 2020 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/tips-for-cka/</guid><description>&lt;p&gt;In this article I&amp;rsquo;ll share some observations, tips and tricks for the &lt;a href="https://www.linuxfoundation.org/"&gt;Linux Foundation&amp;rsquo;s&lt;/a&gt; &amp;ldquo;&lt;a href="https://training.linuxfoundation.org/certification/certified-kubernetes-administrator-cka/"&gt;Certified Kubernetes Administrator&lt;/a&gt; certification and exam.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been operating Kubernetes in multiple environments for a few years now. I thought this would be an easy certification to get, but I was surprised by how hard it was!&lt;/p&gt;
&lt;p&gt;I took this exam without doing any formal training, I mostly focused on the areas of the curriculum which I knew I was a little weak at. The task-based structure for the exam I thought was really excellent. It took me two attempts to pass, and I learnt a few things along the way.&lt;/p&gt;
&lt;p&gt;Here I&amp;rsquo;ll share some thoughts on the certification which hopefully will be useful if you are considering taking it!&lt;/p&gt;
&lt;!-- vim-markdown-toc GFM --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#tip-do-the-right-certification"&gt;Tip: Do the right Certification!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-understand-the-format"&gt;Tip: Understand the Format!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-know-your-vim"&gt;Tip: Know your Vim&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#you-need-to-know-the-architecture-of-kubernetes"&gt;You need to know the architecture of Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-you-need-to-know-linux-sysadmin"&gt;Tip: You Need to know Linux Sysadmin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-dry-run-is-your-friend"&gt;Tip: &amp;ldquo;Dry Run&amp;rdquo; is your friend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-know-how-to-troubleshoot-networking"&gt;Tip: Know how to troubleshoot networking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip-nail-the-easy-questions-quickly"&gt;Tip: Nail the easy questions quickly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#thats-it"&gt;That&amp;rsquo;s it!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- vim-markdown-toc --&gt;
&lt;h2 id="tip-do-the-right-certification"&gt;Tip: Do the right Certification!&lt;/h2&gt;
&lt;p&gt;The CKA exam tests &lt;em&gt;administration&lt;/em&gt; and &lt;em&gt;operation&lt;/em&gt; skills and techniques for Kubernetes. If you have set up and administered clusters before, this will likely not be too challenging. But if you&amp;rsquo;ve never set up a cluster by hand, troubleshot weird issues, fixed clusters and so on, then this is likely going to be very hard.&lt;/p&gt;
&lt;p&gt;There is a certification which is much more geared towards developers who use Kubernetes, but don&amp;rsquo;t necessarily administer it - that&amp;rsquo;s the &lt;a href="https://www.cncf.io/certification/ckad/"&gt;CKAD&lt;/a&gt; exam and might be the one to take if you are not too familiar with system administration.&lt;/p&gt;
&lt;h2 id="tip-understand-the-format"&gt;Tip: Understand the Format!&lt;/h2&gt;
&lt;p&gt;This is not a multiple choice question exam. It&amp;rsquo;s a task based exam, meaning you have about 22 or so specific tasks to complete, in a web browser which has a terminal connected to a cluster.&lt;/p&gt;
&lt;p&gt;It is open-book - meaning that you can use the &lt;a href="https://kubernetes.io/docs/home/"&gt;Kubernetes Documentation&lt;/a&gt; during the exam. It&amp;rsquo;s not a memory test of specific flags for commands or whatever, it will really require you to work with a running cluster. This means you&amp;rsquo;ll have to be pretty familiar with &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;kubeadm&lt;/code&gt; and also Linux in general!&lt;/p&gt;
&lt;h2 id="tip-know-your-vim"&gt;Tip: Know your Vim&lt;/h2&gt;
&lt;p&gt;In the two exams I took, &lt;code&gt;nano&lt;/code&gt; was available. But if you are using &lt;code&gt;nano&lt;/code&gt; to work with files you may struggle for time.&lt;/p&gt;
&lt;p&gt;I spent a &lt;em&gt;lot&lt;/em&gt; of time in &lt;code&gt;vim&lt;/code&gt; in the exam. &lt;code&gt;vim&lt;/code&gt; is my main text editor for day to day work, so I&amp;rsquo;m fairly familiar with it. Knowing how to quickly copy a file (lets say for example a file which represents a deployment) and quickly manipulate the text in it will be crucial. Make sure you are going to be using a text editor which you can be efficient in!&lt;/p&gt;
&lt;p&gt;You won&amp;rsquo;t be using a graphical text editor to work with files, so being competent in a terminal editor like &lt;code&gt;vim&lt;/code&gt; or &lt;code&gt;emacs&lt;/code&gt; could make a big difference. Of course you could install your favourite text editor, but you won&amp;rsquo;t be able to use a graphical editor like VS Code.&lt;/p&gt;
&lt;p&gt;Also, as in most Linux distributions, &lt;code&gt;screen&lt;/code&gt; is available out of the box, and &lt;code&gt;tmux&lt;/code&gt; can also be installed. If you are familiar with either of these terminal mutliplexers it could save you a tonne of time, for example being able to run &lt;code&gt;watch -n 5 -d kubectl get pods&lt;/code&gt; in one pane while applying resources in another.&lt;/p&gt;
&lt;h2 id="you-need-to-know-the-architecture-of-kubernetes"&gt;You need to know the architecture of Kubernetes&lt;/h2&gt;
&lt;p&gt;This exam will require you to deal with trivial tasks such as running a deployment or creating a volume. But the questions which focus on that tend to only count for one or two percent of the overall grade each. Questions which deal with troubleshooting actual Kubernetes issues could count for six or seven percent each.&lt;/p&gt;
&lt;p&gt;This means you &lt;em&gt;need&lt;/em&gt; to know how Kubernetes is architecture. The &lt;code&gt;kubelet&lt;/code&gt; which runs on nodes, the API server, the &lt;code&gt;etcd&lt;/code&gt; store, all of these things you &lt;em&gt;have&lt;/em&gt; to understand how they work and how they fit together.&lt;/p&gt;
&lt;p&gt;The online documentation covers the architecture in detail, here&amp;rsquo;s the best place to start:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/overview/components/"&gt;https://kubernetes.io/docs/concepts/overview/components/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/docs/concepts/overview/components/"&gt;&lt;img src="./images/k8s-architecture.png" alt="Kubernetes Architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You will need to know how the control plane works, how nodes communicate, how transport of messages works and is secured if you are going to have a chance at dealing with the harder questions.&lt;/p&gt;
&lt;h2 id="tip-you-need-to-know-linux-sysadmin"&gt;Tip: You Need to know Linux Sysadmin&lt;/h2&gt;
&lt;p&gt;If you are not familiar with &lt;code&gt;systemctl&lt;/code&gt;, &lt;code&gt;journalctl&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;systemd&lt;/code&gt; units and how the core Kubernetes components are configured, you&amp;rsquo;ll really struggle.&lt;/p&gt;
&lt;p&gt;Look over the &lt;a href="https://github.com/cncf/curriculum"&gt;CNCF curriculum&lt;/a&gt; - expect to not just have to know how to deal with &amp;lsquo;happy path&amp;rsquo; situations, but also broken clusters, incorrect configuration and so on.&lt;/p&gt;
&lt;h2 id="tip-dry-run-is-your-friend"&gt;Tip: &amp;ldquo;Dry Run&amp;rdquo; is your friend&lt;/h2&gt;
&lt;p&gt;One thing which helped me a lot in my second attempt at the exam was the &lt;code&gt;--dry-run&lt;/code&gt; flag. Before you create resources or change anything, run the operation with the &lt;code&gt;--dry-run&lt;/code&gt; flag and see whether the output is what you would expect.&lt;/p&gt;
&lt;p&gt;This is a quick and easy way to see the changes to the cluster which you are going to apply - and troubleshoot them - before making any actual changes.&lt;/p&gt;
&lt;h2 id="tip-know-how-to-troubleshoot-networking"&gt;Tip: Know how to troubleshoot networking&lt;/h2&gt;
&lt;p&gt;Networking in Kubernetes is complex. You must be able to troubleshoot networking issues in the cluster to be able to deal with the more complex tasks.&lt;/p&gt;
&lt;p&gt;This means that you should know how to be able to run typical networking tools like &lt;code&gt;dig&lt;/code&gt;, &lt;code&gt;nslookup&lt;/code&gt;, &lt;code&gt;telnet&lt;/code&gt; etc, in the cluster itself.&lt;/p&gt;
&lt;p&gt;If you are not familiar with these tools you might need to take an online course in Kubernetes or Linux Networking Administration before considering this certification. The &lt;a href="https://training.linuxfoundation.org/certification/linux-foundation-certified-sysadmin-lfcs/"&gt;Linux Certified Systems Administrator&lt;/a&gt; training would be a good place to start.&lt;/p&gt;
&lt;p&gt;If you have taken the &lt;a href="https://success.docker.com/certification"&gt;Docker Certified Associate&lt;/a&gt; exam then some of this should be familiar. If you are not very familiar with how Docker itself works, you&amp;rsquo;ll likely struggle with Kubernetes.&lt;/p&gt;
&lt;h2 id="tip-nail-the-easy-questions-quickly"&gt;Tip: Nail the easy questions quickly&lt;/h2&gt;
&lt;p&gt;There are a lot of tasks which only count for one or two percent each; these ones you should be able to complete in a few minutes. You&amp;rsquo;ll need all the time in the exam to work on the really hard questions which deal with diagnosing and fixing cluster issues.&lt;/p&gt;
&lt;p&gt;Know your core Kubernetes concepts; if you have done the CKAD exam you should be good, if not, check the curriculum and make sure you can quickly complete all of the trivial tasks without wasting too much time.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s it!&lt;/h2&gt;
&lt;p&gt;Hopefully this was helpful! Good luck if you are taking the exam and hopefully you&amp;rsquo;ll find it a challenging but rewarding experience. I&amp;rsquo;ve taken many exams over the years but this was one of the most challenging, but also one of the most enjoyable, I really felt like it was testing practical techniques rather than your ability to just remember random commands and flags.&lt;/p&gt;
&lt;p&gt;As always, if you have any comments or questions, please just add them in the section below!&lt;/p&gt;
&lt;p&gt;&lt;img src="./images/cka-cert.png" alt="CKA Certification"&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Supercharge your Java Projects with Conventional Commits, Semantic Versioning and Semantic Releases</title><link>https://dwmkerr.com/conventional-commits-and-semantic-versioning-for-java/</link><pubDate>Sun, 17 May 2020 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/conventional-commits-and-semantic-versioning-for-java/</guid><description>&lt;p&gt;In this article we&amp;rsquo;ll look at a few simple techniques which can really supercharge your Java project and make them much easier to work with!&lt;/p&gt;
&lt;!-- vim-markdown-toc GFM --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#semantic-versioning"&gt;Semantic Versioning&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#why-does-this-matter"&gt;Why Does This Matter?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-semantic-versioning-specification"&gt;The Semantic Versioning Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-semantic-versions"&gt;Using Semantic Versions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-challenge-of-semantic-versions"&gt;The Challenge of Semantic Versions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#conventional-commits"&gt;Conventional Commits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#time-for-magic"&gt;Time for Magic&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#enforcing-conventional-commits-with-git-hooks"&gt;Enforcing Conventional Commits with Git Hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#how-the-hook-works"&gt;How the Hook Works&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#creating-the-initial-release"&gt;Creating the Initial Release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#go-forth-and-devops"&gt;Go Forth And DevOps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#the-gradle-version"&gt;The Gradle Version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#thats-it"&gt;That&amp;rsquo;s It&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- vim-markdown-toc --&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; If you know the concepts, then just jump straight to my fork of &lt;code&gt;standard-version&lt;/code&gt; at &lt;a href="https://github.com/dwmkerr/standard-version"&gt;&lt;code&gt;github.com/dwmkerr/standard-version&lt;/code&gt;&lt;/a&gt;. It adds support for Java projects. I am currently trying to get it into the mainline, so if you like this, please comment on the &lt;a href="https://github.com/conventional-changelog/standard-version/pull/591"&gt;Pull Request&lt;/a&gt; here. &lt;strong&gt;tl;dr end!&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="semantic-versioning"&gt;Semantic Versioning&lt;/h2&gt;
&lt;p&gt;First, let&amp;rsquo;s talk about the idea of a &lt;em&gt;Semantic Version&lt;/em&gt;. A semantic version is nothing more than a versioning scheme you will probably be familiar with, where versions look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1.2.3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only thing special about a &lt;em&gt;Semantic Version&lt;/em&gt; is that we give a very specific meaning to each part of the version. In short:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; is the &lt;em&gt;major&lt;/em&gt; part of the version&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt; is the &lt;em&gt;minor&lt;/em&gt; part of the version&lt;/li&gt;
&lt;li&gt;&lt;code&gt;3&lt;/code&gt; is the &lt;em&gt;patch&lt;/em&gt; part of the version&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we give &lt;em&gt;semantics&lt;/em&gt; (meaning and context) to these parts:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Major&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;major&lt;/em&gt; version number change means something big has changed, and the API of the software is different to the earlier version. Essentially, this is a potentially &lt;em&gt;breaking&lt;/em&gt; change, so you should only use this new version after carefully reading about the changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Minor&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;minor&lt;/em&gt; version number change means that something has been added or changed, which affects the functionality of the code, but in a &lt;em&gt;non breaking&lt;/em&gt; way. An example would be the addition of a new API. That won&amp;rsquo;t affect existing users, so they can generally safely upgrade minor versions without too much risk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Patch&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;patch&lt;/em&gt; version number change means something really inconsequential to the user of the code has changed. It might be new documentation, better logging, but it is generally not a &lt;em&gt;functional&lt;/em&gt; change.&lt;/p&gt;
&lt;h3 id="why-does-this-matter"&gt;Why Does This Matter?&lt;/h3&gt;
&lt;p&gt;If we have Semantic Versions, we can be a lot more sure about &lt;em&gt;when it is safe to upgrade&lt;/em&gt;. If we see a &lt;em&gt;major&lt;/em&gt; version change, we know we need to be careful. &lt;em&gt;Minor&lt;/em&gt; changes might need attention, and &lt;em&gt;patches&lt;/em&gt; are almost always going to be safe.&lt;/p&gt;
&lt;p&gt;Managing dependencies and keeping them up to date is hard in software development, and one of the reasons people are wary of updating dependencies is that &lt;em&gt;they don&amp;rsquo;t know if they upgrade will break their code&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Semantic Versioning tries to bring a little order to this chaotic world.&lt;/p&gt;
&lt;h3 id="the-semantic-versioning-specification"&gt;The Semantic Versioning Specification&lt;/h3&gt;
&lt;p&gt;There is a detailed specification for semantic versioning, which also covers more sophisticated cases, you can find it here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://semver.org/"&gt;https://semver.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d suggest this is recommend reading for &lt;em&gt;any&lt;/em&gt; software engineer!&lt;/p&gt;
&lt;h3 id="using-semantic-versions"&gt;Using Semantic Versions&lt;/h3&gt;
&lt;p&gt;Now the easiest way to start with semantic versioning is to simply adhere to the spec! For example, if you make a change which could break something for users, bump the &lt;em&gt;major&lt;/em&gt; part of the version.&lt;/p&gt;
&lt;p&gt;But, things aren&amp;rsquo;t all that easy&amp;hellip;&lt;/p&gt;
&lt;h3 id="the-challenge-of-semantic-versions"&gt;The Challenge of Semantic Versions&lt;/h3&gt;
&lt;p&gt;The challenge is this. Imagine you are cutting a new release of your code and many people have contributed. Some bug fixes, some patches, some documentation. How do you look through all of those changes and decide how to appropriately change the version number?&lt;/p&gt;
&lt;p&gt;To solve this problem, say hello to &lt;em&gt;Conventional Commits&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="conventional-commits"&gt;Conventional Commits&lt;/h2&gt;
&lt;p&gt;If you have a commit history like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Updated the users API
Bugfix
trying the build again, got it working
Bugfix: [JIRA-21] fixed that issue
you can now get user&amp;#39;s friends with this change
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then it is &lt;em&gt;very&lt;/em&gt; hard to reason about what is going on. What about if the commit history looked like this?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;feat(users): [#12] fetching users returns their avatar url
fix(users): [#45] display names with emojis return correctly
build(cicd): update the expired deploy key
fix(docs): [#22] fix broken links to the javadocs
feat(users): [#49] users api optionally returns friends, non-breaking
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&amp;rsquo;s much easier to see what each change means, at least at a high level.&lt;/p&gt;
&lt;p&gt;By having some kind of standard for commit messages, we can do a lot. We can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Classify changes by type (such as a feature or fix)&lt;/li&gt;
&lt;li&gt;Include a clear description of the change&lt;/li&gt;
&lt;li&gt;Use a convention to indicate a breaking change&lt;/li&gt;
&lt;li&gt;Link to a ticketing system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just like semantic versioning, conventional commits have a specification too:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;https://www.conventionalcommits.org/en/v1.0.0/&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="time-for-magic"&gt;Time for Magic&lt;/h2&gt;
&lt;p&gt;Now if we have conventional commits, and want to use semantic versions, we can actually skip the whole process of looking over a commit history to create a new semantic version - we can automate it.&lt;/p&gt;
&lt;p&gt;We can even automate the process of creating a &amp;lsquo;changelog&amp;rsquo;, a list of each change which comes in each version. There&amp;rsquo;s an &lt;em&gt;excellent&lt;/em&gt; library which does this, called &lt;code&gt;standard-version&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/conventional-changelog/standard-version"&gt;https://github.com/conventional-changelog/standard-version&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s maintained by the same group behind conventional commits. The only problem? It only works for JavaScript projects (unless you are willing to write custom code which can be complex).&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ve updated the library to support Maven projects and Gradle projects, so you can use it for Java now as well!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see it in action. Here&amp;rsquo;s a very simple Java library built with Maven:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/java-maven-standard-version-sample"&gt;https://github.com/dwmkerr/java-maven-standard-version-sample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This library has no changelog, no tags, no version data at all except for &lt;code&gt;0.1.0&lt;/code&gt; in the &lt;code&gt;pom.xml&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Now if I was to clone the library, make a change and make a commit, which &lt;em&gt;didn&amp;rsquo;t&lt;/em&gt; follow the conventional commit spec, we&amp;rsquo;ll just see the usual success message:&lt;/p&gt;
&lt;img src="./images/bad-commit-message.png" alt="Bad commit message" width="800px" /&gt;
&lt;p&gt;This is a problem; we want to &lt;em&gt;enforce&lt;/em&gt; conventional commits.&lt;/p&gt;
&lt;h3 id="enforcing-conventional-commits-with-git-hooks"&gt;Enforcing Conventional Commits with Git Hooks&lt;/h3&gt;
&lt;p&gt;Git has a powerful &amp;lsquo;hooks&amp;rsquo; facility, which let you run logic at key points in operations. This is a &lt;em&gt;massive&lt;/em&gt; topic on its own, so we&amp;rsquo;re not going to go into lots of details, but if you are interested you can read about them here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks"&gt;https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now the issue with Git Hooks is that they are &lt;em&gt;per user&lt;/em&gt; - if I add a hook to my &lt;code&gt;.git&lt;/code&gt; folder, no one else will get it. We want the same hooks for &lt;em&gt;all&lt;/em&gt; users.&lt;/p&gt;
&lt;p&gt;There are a few ways to get around this. You can set up server side hooks (which could reject a push if it has an invalid commit message), but this isn&amp;rsquo;t easy to do (and with some providers, like GitHub for public projects, not even available as an option). Also, we want fast feedback, so if I make a bad commit message, it fails straight away and I can fix it.&lt;/p&gt;
&lt;p&gt;The way I suggest getting around this is this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.githooks&lt;/code&gt; folder in your repo&lt;/li&gt;
&lt;li&gt;Instruct people to configure the git repo to look for hooks there&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That way there are no global changes, only project specific ones. We still need to make sure the developer sets up the hooks though! You&amp;rsquo;ll notice in my sample project&amp;rsquo;s &lt;a href="https://github.com/dwmkerr/java-maven-standard-version-sample#developer-guide"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt; file the first thing I do is instruct people to setup the hooks:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git config core.hooksPath .githooks
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s see how the same operation would look if we&amp;rsquo;d setup the hook first:&lt;/p&gt;
&lt;img src="./images/git-hook-bad-message.png" alt="Bad commit message with hook" width="800px" /&gt;
&lt;p&gt;Our hook has fired off and told us we&amp;rsquo;ve not used a conventional commit message - it even let&amp;rsquo;s us know where to go to find out more.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s try a message which should work, as it meets the standard:&lt;/p&gt;
&lt;img src="./images/git-hook-good-message.png" alt="Good commit message with hook" width="800px" /&gt;
&lt;p&gt;Awesome! We&amp;rsquo;ve been informed that our message meets the standard (useful to actually remind us that this is being checked!) and the commit has succeeded!&lt;/p&gt;
&lt;p&gt;Remember; we only need to setup the hooks once - it&amp;rsquo;s a one time activity.&lt;/p&gt;
&lt;h3 id="how-the-hook-works"&gt;How the Hook Works&lt;/h3&gt;
&lt;p&gt;Hooks are just shell scripts. You can write them in Ruby, Python, whatever. I have written this one in pure Bash because it&amp;rsquo;s really just checking a regex, which Bash is more than capable of. Also, I can&amp;rsquo;t be sure the developer will have Ruby or another tool on their machine.&lt;/p&gt;
&lt;p&gt;The hook is as simple as this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Create a regex for a conventional commit.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convetional_commit_regex&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z \-]+\))?!?: .+&lt;/span&gt;$&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get the commit message (the parameter we&amp;#39;re given is just the path to the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# temporary file which holds the message).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;commit_message&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cat &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check the message, if we match, all good baby.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$commit_message&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt;~ $convetional_commit_regex &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo -e &lt;span style="color:#e6db74"&gt;&amp;#34;\e[32mCommit message meets Conventional Commit standards...\e[0m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Uh-oh, this is not a conventional commit, show an example and link to the spec.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo -e &lt;span style="color:#e6db74"&gt;&amp;#34;\e[31mThe commit message does not meet the Conventional Commit standard\e[0m&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;An example of a valid message is: &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34; feat(login): add the &amp;#39;remember me&amp;#39; button&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;More details at: https://www.conventionalcommits.org/en/v1.0.0/#summary&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The only really tricky bit is the regex, and the weird &lt;code&gt;\e[32&lt;/code&gt; type characters which are used to set the colours. You might find it easier to write your hooks in a proper programming language - and for anything more complex I&amp;rsquo;d suggest that makes far more sense! But if a bit of Bash will do the trick, there&amp;rsquo;s nothing wrong with that.&lt;/p&gt;
&lt;p&gt;As a side-note, if you are into Bash and the shell, check out my online &lt;a href="https://effective-shell.com"&gt;Effective Shell&lt;/a&gt; book.
git config core.hooksPath .githooks&lt;/p&gt;
&lt;h2 id="creating-the-initial-release"&gt;Creating the Initial Release&lt;/h2&gt;
&lt;p&gt;Now the chances are, if you are interested in this technique, you&amp;rsquo;ve probably got an existing project you want to use it on. It probably doesn&amp;rsquo;t have a changelog or conventional commits. That&amp;rsquo;s OK, just start from now.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how we&amp;rsquo;d start using the &lt;code&gt;standard-version&lt;/code&gt; library to manage our versions. I&amp;rsquo;ve added a new API to the &lt;a href="https://github.com/dwmkerr/java-maven-standard-version-sample/tree/release"&gt;&lt;code&gt;release&lt;/code&gt;&lt;/a&gt; branch (to keep &lt;code&gt;master&lt;/code&gt; clean for people reading the sample) and committed it.&lt;/p&gt;
&lt;p&gt;Now lets actually create our changelog:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx @dwmkerr/standard-version --first-release --packageFiles pom.xml --bumpFiles pom.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img src="./images/first-release.png" alt="First release" width="800px" /&gt;
&lt;p&gt;Now it&amp;rsquo;s a pain I know, but you need &lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt; installed for this to work. The &lt;code&gt;standard-version&lt;/code&gt; library is built on node, that&amp;rsquo;s what is used to do all of the logic around writing a changelog and working out what the version bump should be. You also have to use my fork &lt;code&gt;@dwmkerr/standard-version&lt;/code&gt; rather than the main version, because at the time of writing my pull request which adds support for &lt;code&gt;pom.xml&lt;/code&gt; files is not yet merged.&lt;/p&gt;
&lt;p&gt;What has happened here is that the &lt;code&gt;standard-version&lt;/code&gt; tool has &lt;em&gt;not&lt;/em&gt; changed the version number. We told it this is the &lt;code&gt;first-release&lt;/code&gt;, meaning we haven&amp;rsquo;t published yet, so there&amp;rsquo;s no need to create a new number. What is &lt;em&gt;has&lt;/em&gt; done is given us a changelog and told use how to push the tags and code. If we push, we can now see the changelog:&lt;/p&gt;
&lt;img src="./images/changelog-v1.png" alt="Changelog v1" width="800px" /&gt;
&lt;p&gt;See how we get a changelog showing the changes, the version and the date? We even have links to the commits for each key change!&lt;/p&gt;
&lt;p&gt;If we&amp;rsquo;d linked the message to GitHub Issue numbers it&amp;rsquo;d automatically have links to the issues too!&lt;/p&gt;
&lt;p&gt;Now in this code I deliberately made a mistake - the test for the &lt;code&gt;Goodbye&lt;/code&gt; api is a copy and paste of the &lt;code&gt;Hello&lt;/code&gt; test! And the &lt;code&gt;Goodbye&lt;/code&gt; api has a spelling mistake. Let&amp;rsquo;s fix this and cut a new release.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve made the change on the &lt;code&gt;release&lt;/code&gt; branch, now I&amp;rsquo;ll run &lt;code&gt;standard-version&lt;/code&gt; again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx @dwmkerr/standard-version --packageFiles pom.xml --bumpFiles pom.xml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;img src="./images/second-release.png" alt="Second release" width="800px" /&gt;
&lt;p&gt;Note that there was no need for the &lt;code&gt;--first-release&lt;/code&gt; flag.&lt;/p&gt;
&lt;p&gt;Now this time, a new version has been generated. This was a &lt;code&gt;fix&lt;/code&gt; commit, so it has made it a &lt;em&gt;minor&lt;/em&gt; version bump. If we needed to make it a breaking change, we can use a message with an exclamation after the type, such as &lt;code&gt;fix(goodbye)!: fix the typo&lt;/code&gt;. Check the &lt;code&gt;standard-version&lt;/code&gt; docs for more about this.&lt;/p&gt;
&lt;p&gt;Finally, let&amp;rsquo;s look at our new changelog:&lt;/p&gt;
&lt;img src="./images/second-changelog.png" alt="Second changelog" width="800px" /&gt;
&lt;p&gt;We have even more info now - we have a link to the tag. This is &lt;em&gt;incredibly&lt;/em&gt; useful for managing releases.&lt;/p&gt;
&lt;p&gt;The icing on the cake? Let&amp;rsquo;s look at the &lt;code&gt;pom.xml&lt;/code&gt;:&lt;/p&gt;
&lt;img src="./images/updated-pom.png" alt="Updated pom.xml" width="800px" /&gt;
&lt;p&gt;Note that &lt;em&gt;the version has been updated&lt;/em&gt;. &lt;code&gt;standard-release&lt;/code&gt; is keeping our Git Tags and our Java Library Version numbers &lt;em&gt;automatically in sync&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve started doing this and seen it in action for a while, you&amp;rsquo;ll wonder how you lived without it!&lt;/p&gt;
&lt;h2 id="go-forth-and-devops"&gt;Go Forth And DevOps&lt;/h2&gt;
&lt;p&gt;This is just the beginning! Think of all the cool things we can do with this in place, here&amp;rsquo;s just a few:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update our build pipeline so that when we merge into &lt;code&gt;master&lt;/code&gt; we automatically run &lt;code&gt;standard-version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Update our build pipeline so that when a new version tag is added, we automatically publish the library&lt;/li&gt;
&lt;li&gt;Send out a slack notification with the changelog when a new version is committed&lt;/li&gt;
&lt;li&gt;Share the changelog with our consumers as our libraries are updated&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these basic building blocks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Conventional Commits&lt;/li&gt;
&lt;li&gt;Semantic Versioning&lt;/li&gt;
&lt;li&gt;Enforcing of Commit Standards&lt;/li&gt;
&lt;li&gt;Usage of the &lt;code&gt;standard-release&lt;/code&gt; tool&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have created a very powerful way to manage what is actually a highly complex process. We&amp;rsquo;ve introduced almost no additional complexity, just a few guidelines for developers.&lt;/p&gt;
&lt;h2 id="the-gradle-version"&gt;The Gradle Version&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s basically the same technique for Gradle, you just tell &lt;code&gt;standard-version&lt;/code&gt; to hit your &lt;code&gt;build.gradle&lt;/code&gt; file;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx @dwmkerr/standard-version --packageFiles build.gradle --bumpFiles build.gradle
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s an accompanying sample project at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/java-gradle-standard-version-sample"&gt;github.com/dwmkerr/java-gradle-standard-version-sample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the same as the Maven version in that the &lt;code&gt;master&lt;/code&gt; branch has no &lt;code&gt;standard-version&lt;/code&gt; code or changelogs, just open the &lt;a href="https://github.com/dwmkerr/java-gradle-standard-version-sample/tree/release"&gt;&lt;code&gt;release&lt;/code&gt;&lt;/a&gt; branch to see what it looks like after we&amp;rsquo;ve applied the same techniques as we did to the Maven version.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s It&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a whole world of libraries for this. &lt;a href="https://github.com/commitizen/cz-cli"&gt;&lt;code&gt;commitizen&lt;/code&gt;&lt;/a&gt; which helps you write conventional commit messages for example. But I found very little for Java. If you find this useful, please do chip in on the pull request here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/conventional-changelog/standard-version/pull/591"&gt;https://github.com/conventional-changelog/standard-version/pull/591&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As it would be great to add it to the mainline. I&amp;rsquo;m also just finishing off the update which adds support for Gradle.&lt;/p&gt;
&lt;p&gt;As always, questions, comments, suggestions, rants, anything are welcome!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Effective Shell for Beginners</title><link>https://dwmkerr.com/effective-shell-for-beginners/</link><pubDate>Tue, 21 Jan 2020 00:00:00 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-for-beginners/</guid><description>&lt;p&gt;I have rebuilt my &amp;ldquo;Effective Shell&amp;rdquo; series as an online book - it&amp;rsquo;s available now on:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://effective-shell.com"&gt;https://effective-shell.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The whole site is built from a GitHub repo at &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;github.com/dwmkerr/effective-shell&lt;/a&gt;. It is open for contributions, changes, issues and suggestions. I&amp;rsquo;ve also added a comment section to each page to get input.&lt;/p&gt;
&lt;p&gt;To keep the material as accessible as possible, I have added a new section for beginners, to help anyone who has not used a shell before. It goes over who the book is useful for, what the shell is, and how to set up your computer to work through the material:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://effective-shell.com"&gt;&lt;img src="images/effective-shell-screenshot.png" alt="Effective Shell: Screenshot" width="1024px" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All comments and suggestions are welcome!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Part 7: The Subtleties of Shell Commands</title><link>https://dwmkerr.com/effective-shell-7-shell-commands/</link><pubDate>Tue, 25 Jun 2019 07:25:23 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-7-shell-commands/</guid><description>&lt;p&gt;In this chapter, we&amp;rsquo;ll take a look at the various different types of shell commands that exist and how this can affect your work.&lt;/p&gt;
&lt;p&gt;By the end of this chapter, you might even be able to make sense of the horrifying and perfectly syntactically valid code below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;which &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;where &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;what &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;whence &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;whereis who&lt;span style="color:#66d9ef"&gt;))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-are-commands"&gt;What Are Commands?&lt;/h2&gt;
&lt;p&gt;This is &lt;em&gt;really&lt;/em&gt; important to understand! A &lt;em&gt;command&lt;/em&gt; in a shell is something you execute. It might take parameters. Generally it&amp;rsquo;ll have a form like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;command param1 param2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve already seen many commands during this series:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls &lt;span style="color:#75715e"&gt;# Show the contents of the current directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd ~ &lt;span style="color:#75715e"&gt;# Move to the user&amp;#39;s home&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat file.txt &lt;span style="color:#75715e"&gt;# Output the contents of &amp;#39;file.txt&amp;#39; to stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But to be an effective shell user, you must understand that not all commands are created equal. The differences between the types of commands will affect how you use them.&lt;/p&gt;
&lt;p&gt;There are four types of commands in most shells:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Executables&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Built-Ins&amp;rdquo; (which we&amp;rsquo;ll just call &lt;em&gt;builtins&lt;/em&gt; from now on)&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Aliases&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s quickly dig in and see a bit more.&lt;/p&gt;
&lt;h2 id="executables---programs"&gt;Executables - Programs&lt;/h2&gt;
&lt;p&gt;Executables are just files with the &amp;rsquo;executable&amp;rsquo; bit set&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. If I execute the &lt;code&gt;cat&lt;/code&gt; command, the shell will search for an executable named &lt;code&gt;cat&lt;/code&gt; in my &lt;code&gt;$PATH&lt;/code&gt;. If it finds it, it will run the program.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ cat file.txt
This is a simple text file
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What is &lt;code&gt;$PATH&lt;/code&gt;? &lt;code&gt;$PATH&lt;/code&gt; is the standard environment variable used to define &lt;em&gt;where&lt;/em&gt; the shell should search for programs. If we temporarily &lt;em&gt;empty&lt;/em&gt; this variable, the shell won&amp;rsquo;t find the command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; cat file.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bash: cat: No such file or directory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Normally your &lt;code&gt;$PATH&lt;/code&gt; variable will include the standard locations for Linux programs - folders such as &lt;code&gt;/bin&lt;/code&gt;, &lt;code&gt;/sbin&lt;/code&gt;, &lt;code&gt;/usr/bin&lt;/code&gt; and so on&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;If you were to print the variable, you&amp;rsquo;d see a bunch of paths (they are separated by colons; I&amp;rsquo;ve put them on separate lines for readability):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/usr/local/bin
/usr/bin
/bin
/usr/sbin
/sbin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The shell will start with the &lt;em&gt;earlier&lt;/em&gt; locations and move to the later ones. This allows &lt;em&gt;local&lt;/em&gt; flavours of tools to be installed for users, which will take precedence over &lt;em&gt;general&lt;/em&gt; versions of tools.&lt;/p&gt;
&lt;p&gt;There will likely be other locations too - you might see Java folders, package manager folders and so on.&lt;/p&gt;
&lt;h2 id="executables---scripts"&gt;Executables - Scripts&lt;/h2&gt;
&lt;p&gt;Imagine we create a text file called &lt;code&gt;dog&lt;/code&gt; in the local folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;🐶 woof 🐶&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we make the file &lt;em&gt;executable&lt;/em&gt;, by running &lt;code&gt;chmod +x dog&lt;/code&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;, then we can run this just like any other program - as long as we tell the shell to look for programs in the current directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;.&amp;#34;&lt;/span&gt; dog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;🐶 woof 🐶
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More common would be to run the program by giving a path:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ./dog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;🐶 woof 🐶
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or just move it to a standard location that the shell already checks for programs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ mv dog /usr/local/bin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ dog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;🐶 woof 🐶
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The point is that executables don&amp;rsquo;t &lt;em&gt;have&lt;/em&gt; to be compiled program code. If a file starts with &lt;code&gt;#!&lt;/code&gt; (the &amp;lsquo;shebang&amp;rsquo;), then the system will try to run the contents of the file with the program specified in the shebang.&lt;/p&gt;
&lt;p&gt;We will look at shebangs in greater detail in a later chapter.&lt;/p&gt;
&lt;h2 id="builtins"&gt;Builtins&lt;/h2&gt;
&lt;p&gt;OK, so we&amp;rsquo;ve seen executables. What about a command like this?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;local V&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; echo $V
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will not find the &lt;code&gt;local&lt;/code&gt; executable anywhere on your system. It is a &lt;em&gt;builtin&lt;/em&gt; - a special command built directly into the shell program.&lt;/p&gt;
&lt;p&gt;Builtins are often highly specific to your shell. They might be used for programming (&lt;code&gt;local&lt;/code&gt; for example is used to declare a locally scoped variable), or they might be for very shell-specific features.&lt;/p&gt;
&lt;p&gt;This is where we need to take note. As soon as you are running a builtin, you are potentially using a feature that is specific to &lt;em&gt;your&lt;/em&gt; shell, rather than a program that is shared across the system and can be run by &lt;em&gt;any&lt;/em&gt; shell.&lt;/p&gt;
&lt;p&gt;Trying to programmatically execute &lt;code&gt;local&lt;/code&gt; as a process will fail - there is no executable with that name; it is purely a shell construct.&lt;/p&gt;
&lt;p&gt;So how do we know if a command is a builtin? The preferred method is to use the &lt;code&gt;type&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ type local
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;local is a shell builtin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;type&lt;/code&gt; command (which is &lt;em&gt;itself&lt;/em&gt; a builtin!) can tell you the exact type of shell command.&lt;/p&gt;
&lt;p&gt;Interestingly, you might be using more builtins than you think. &lt;code&gt;echo&lt;/code&gt; is a program, but most of the time you are not executing it when you are in a shell:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ type -a echo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo is a shell builtin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo is /bin/echo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By using the &lt;code&gt;-a&lt;/code&gt; flag on &lt;code&gt;type&lt;/code&gt; to show &lt;em&gt;all&lt;/em&gt; commands that match the name, we see that &lt;code&gt;echo&lt;/code&gt; is actually both a builtin &lt;em&gt;and&lt;/em&gt; a program.&lt;/p&gt;
&lt;p&gt;Many simple programs have builtin versions. The shell can execute them much faster.&lt;/p&gt;
&lt;p&gt;Some commands are a builtin so that they can function in a sensible manner. The &lt;code&gt;cd&lt;/code&gt; command changes the current directory - if we executed it as a process, it would change only the directory for the &lt;code&gt;cd&lt;/code&gt; process itself, not the shell, making it much less useful.&lt;/p&gt;
&lt;p&gt;Builtins will vary from shell to shell, but many shells are &amp;lsquo;Bash-like&amp;rsquo; - meaning they will have a set very similar to the Bash builtins, which you can see here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html"&gt;https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As should be familiar from &lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;, you can get help for builtins:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ man source &lt;span style="color:#75715e"&gt;# source is a builtin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BUILTIN&lt;span style="color:#f92672"&gt;(&lt;/span&gt;1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; BSD General Commands Manual BUILTIN&lt;span style="color:#f92672"&gt;(&lt;/span&gt;1&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NAME
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builtin, !, %, &lt;span style="color:#75715e"&gt;# ...snip...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SYNOPSIS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builtin &lt;span style="color:#f92672"&gt;[&lt;/span&gt;-options&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;args ...&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, the manual will &lt;em&gt;not&lt;/em&gt; show information on specific builtins, which is a pain. Your shell &lt;em&gt;might&lt;/em&gt; have an option to show more details - for example, in Bash you can use &lt;code&gt;help&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ help source
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source: source filename &lt;span style="color:#f92672"&gt;[&lt;/span&gt;arguments&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Read and execute commands from FILENAME and &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;. The pathnames
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; in $PATH are used to find the directory containing FILENAME. If any
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ARGUMENTS are supplied, they become the positional parameters when
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FILENAME is executed.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But remember: &lt;code&gt;help&lt;/code&gt; is a builtin; you might not find it in all shells (you won&amp;rsquo;t find it in &lt;code&gt;zsh&lt;/code&gt;, for example). This highlights again the challenges of builtins.&lt;/p&gt;
&lt;h2 id="functions"&gt;Functions&lt;/h2&gt;
&lt;p&gt;You can define your own shell functions. We will see a lot more of this later, but let&amp;rsquo;s show a quick example for now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ restart-shell &lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt; exec -l &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$SHELL&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This snippet creates a function that restarts the shell (quite useful if you are messing with shell configuration files or think you might have irreversibly goofed up your current session).&lt;/p&gt;
&lt;p&gt;We can execute this function just like any command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ restart-shell
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And running &lt;code&gt;type&lt;/code&gt; will show us that this is a function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ type restart-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;restart-shell is a &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;restart-shell &lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exec -l $SHELL
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Functions are one of the most powerful shell constructs we will see; they are extremely useful for building sophisticated logic. We&amp;rsquo;re going to see them in a lot more detail later, but for now it is enough to know that they exist, and can run logic, and are run as commands.&lt;/p&gt;
&lt;h2 id="aliases"&gt;Aliases&lt;/h2&gt;
&lt;p&gt;An alias is just a shortcut. Type in a certain set of characters, and the shell will replace them with the value defined in the alias.&lt;/p&gt;
&lt;p&gt;Some common commands are actually already aliases - for example, in my &lt;code&gt;zsh&lt;/code&gt; shell, the &lt;code&gt;ls&lt;/code&gt; command is an alias:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% type -a ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is an alias &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ls -G
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is /bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I make sure that when I use the &lt;code&gt;ls&lt;/code&gt; command, the shell always expands it to &lt;code&gt;ls -G&lt;/code&gt;, which colours the output.&lt;/p&gt;
&lt;p&gt;We can quickly define aliases to save on keystrokes. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ alias k&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;kubectl&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From this point on, I can use the &lt;code&gt;k&lt;/code&gt; alias as shorthand for the &lt;code&gt;kubectl&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Aliases are far less sophisticated than functions. Think of them as keystroke savers and nothing more, and you won&amp;rsquo;t go far wrong. Aliases are not portable across shells and have certain behaviours which can make them problematic to work with, there will be an entire chapter dedicated to alisases coming up in the series.&lt;/p&gt;
&lt;h2 id="so-what"&gt;So What?&lt;/h2&gt;
&lt;p&gt;So we now hopefully have a greater understanding of the variety of shell commands. Not all commands are executables, not all of the commands we &lt;em&gt;think&lt;/em&gt; are executables necessarily are, and some commands might be more sophisticated.&lt;/p&gt;
&lt;p&gt;As a shell user, the key things to remember are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Executables are &amp;lsquo;safe&amp;rsquo; - they are programs your system can use; your shell just calls out to them.&lt;/li&gt;
&lt;li&gt;Builtins are &lt;em&gt;very&lt;/em&gt; shell-specific and usually control the shell itself&lt;/li&gt;
&lt;li&gt;Functions are powerful ways to write logic but will normally be shell-specific.&lt;/li&gt;
&lt;li&gt;Aliases are conveniences for human operators, but only in the context of an interactive shell.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To find out how a command is implemented, just use the &lt;code&gt;type -a&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ type -a cat
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat is /bin/cat
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="more-than-you-need-to-know"&gt;More than You Need to Know&lt;/h2&gt;
&lt;p&gt;OK, for the masochistic few, you might be wondering about all of the other commands and utilities you may have seen that can tell you about programs and commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;what&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;whatis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;which&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;whence&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;where&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;whereis&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;command&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;em&gt;lot&lt;/em&gt; of these are legacy and should be avoided, but for completeness sake, we&amp;rsquo;ll go through them.&lt;/p&gt;
&lt;h3 id="what"&gt;&lt;code&gt;what&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;what&lt;/code&gt; reads out special metadata embedded in a program, generally used to identify the version of source code it was built from:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ what /bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Copyright &lt;span style="color:#f92672"&gt;(&lt;/span&gt;c&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 1989, 1993, &lt;span style="color:#ae81ff"&gt;1994&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PROGRAM:ls PROJECT:file_cmds-272.220.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There should be almost no circumstance in which you need to use it in your day-to-day work, but you might come across it if you &lt;em&gt;meant&lt;/em&gt; to type &lt;code&gt;whatis&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="whatis"&gt;&lt;code&gt;whatis&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;whatis&lt;/code&gt; searches a local help database for text. This can be useful in tracking down manual pages:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ whatis bash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bash&lt;span style="color:#f92672"&gt;(&lt;/span&gt;1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; - GNU Bourne-Again SHell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bashbug&lt;span style="color:#f92672"&gt;(&lt;/span&gt;1&lt;span style="color:#f92672"&gt;)&lt;/span&gt; - report a bug in bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I can&amp;rsquo;t imagine it will be a regularly used tool by most users.&lt;/p&gt;
&lt;h3 id="which"&gt;&lt;code&gt;which&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;which&lt;/code&gt; will search your &lt;code&gt;$PATH&lt;/code&gt; to see whether an executable can be found. With the &lt;code&gt;-a&lt;/code&gt; flag, it will show all results.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ which -a vi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/local/bin/vi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/bin/vi
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;which&lt;/code&gt; originated in &lt;code&gt;csh&lt;/code&gt;. It remains on many systems for compatibility but in general should be avoided due to potentially odd behaviour&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h3 id="whence"&gt;&lt;code&gt;whence&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;whence&lt;/code&gt; was added to the Korn shell. You are unlikely to use it unless you are on systems using &lt;code&gt;ksh&lt;/code&gt;. &lt;code&gt;zsh&lt;/code&gt; also has this command, but it should be avoided and considered non-standard.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% whence brew
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/usr/local/bin/brew
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="where"&gt;&lt;code&gt;where&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This is a shell builtin that can provide information on commands, similar to &lt;code&gt;type&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% where ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls: aliased to ls -G
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, &lt;code&gt;type&lt;/code&gt; should be preferred, as it is more standard.&lt;/p&gt;
&lt;h3 id="whereis"&gt;&lt;code&gt;whereis&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;whereis&lt;/code&gt; is available on some systems and generally operates the same as &lt;code&gt;which&lt;/code&gt;, searching paths for an executable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% whereis ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, &lt;code&gt;type&lt;/code&gt; should be preferred for compatability.&lt;/p&gt;
&lt;h3 id="command"&gt;&lt;code&gt;command&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;command&lt;/code&gt; is defined in the POSIX standard, so should be expected to be present on most modern systems. Without arguments, it simply executes a command. With the &lt;code&gt;-v&lt;/code&gt; argument, you get a fairly machine-readable or processable response; with the &lt;code&gt;-V&lt;/code&gt; argument, you get a more human readable response:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% command -v ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias ls&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;ls -G&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% command -V ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is an alias &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ls -G
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;command&lt;/code&gt; can be useful in scripts, as we will see in later chapters.&lt;/p&gt;
&lt;h3 id="type"&gt;&lt;code&gt;type&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt; is part of the Unix standard and will be present in most modern systems. As we&amp;rsquo;ve already seen, it will identify the type of command as well as the location for an executable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% type -a ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is an alias &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; ls -G
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is /bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command can also be used to only search for paths:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% type -p ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls is /bin/ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In summary, avoid anything that starts with &amp;lsquo;&lt;code&gt;w&lt;/code&gt;&amp;rsquo;! These are legacy commands, generally needed only when working on older Unix machines. &lt;code&gt;type&lt;/code&gt; or &lt;code&gt;command&lt;/code&gt; should be used instead.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;We will cover permissions and modes in later chapters.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Why these names and locations? It&amp;rsquo;s a long story. The best place to start if you are intersted is the &lt;a href="https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard"&gt;Filesystem Hierarchy Standard&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;code&gt;chmod&lt;/code&gt; changes the mode of a file; &lt;code&gt;+x&lt;/code&gt; means &amp;lsquo;add the executable bit&amp;rsquo;. This tells the operating system the file can be executed.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then"&gt;Stack Exchange: Why not use “which”? What to use then?&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Part 6: Everything You Don't Need To Know About Job Control</title><link>https://dwmkerr.com/effective-shell-6-job-control/</link><pubDate>Mon, 10 Jun 2019 08:26:33 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-6-job-control/</guid><description>&lt;p&gt;&lt;em&gt;Job control&lt;/em&gt; is a feature of most shells, which is generally not particularly intuitive to work with. However, knowing the basics can help prevent you from getting yourself into a tangle, and can from time to time make certain tasks a little easier.&lt;/p&gt;
&lt;p&gt;In this chapter, we&amp;rsquo;ll look at the main features of job control, why it can be a problematic, and some alternatives.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-is-job-control"&gt;What Is Job Control?&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with an example. I am building a simple web page. It has one &lt;code&gt;index.html&lt;/code&gt; file, one &lt;code&gt;styles.css&lt;/code&gt; file, and one &lt;code&gt;code.js&lt;/code&gt; file. The &lt;code&gt;index.html&lt;/code&gt; file looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;My New Project&amp;lt;/&lt;span style="color:#f92672"&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;link&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rel&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text/css&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;href&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;styles.css&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;code.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- Snip... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Opening the file in a browser doesn&amp;rsquo;t quite work, as it won&amp;rsquo;t load the code or the styles. We need a web server to serve styles and code.&lt;/p&gt;
&lt;p&gt;A super-useful one-liner to run a web server on any machine with Python installed is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In fact, this is so useful that I normally &lt;em&gt;alias&lt;/em&gt; this command, so that I can just type &lt;code&gt;serve&lt;/code&gt;. We&amp;rsquo;ll see aliases in a later chapter.&lt;/p&gt;
&lt;p&gt;For now, if we run this command (you can get &lt;a href="https://github.com/dwmkerr/effective-shell/tree/master/6-job-control/sample"&gt;the three sample files here&lt;/a&gt; if you want to try this yourself), then we can open the webpage in a browser, with the styles and code loaded:&lt;/p&gt;
&lt;img src="images/website-screenshot.png" alt="Screenshot: Website" width="600" /&gt;
&lt;p&gt;We can also see that the server has served the HTML, JavaScript, and CSS files:&lt;/p&gt;
&lt;img src="images/server-screenshot.png" alt="Screenshot: Server" width="600" /&gt;
&lt;p&gt;All well and good so far.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say we want to now continue using our shell, maybe to edit the website with a terminal editor like Vim or Emacs, or we want to zip up the site, or just run any shell command&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We have a problem. The &lt;code&gt;python&lt;/code&gt; process is still running - it&amp;rsquo;s serving the website. Our shell is essentially useless, until we stop the server. See what happens when I try to edit a file:&lt;/p&gt;
&lt;img src="images/blocked-shell.gif" alt="Demo: Blocked Shell" width="600" /&gt;
&lt;p&gt;In the example above, I try to run &lt;code&gt;vi&lt;/code&gt;, but nothing is happening. Standard input is not being read by the server and not being interpreted by the shell.&lt;/p&gt;
&lt;p&gt;I have to kill the server by hitting &lt;code&gt;Ctrl+C&lt;/code&gt; (which sends a &lt;code&gt;SIGINT&lt;/code&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; - we&amp;rsquo;ll see more about signals later), clear my screen to get rid of all of the error messages, then start again.&lt;/p&gt;
&lt;p&gt;This is obviously not optimal. Let&amp;rsquo;s look at some solutions.&lt;/p&gt;
&lt;h2 id="solution-1-start-the-server-in-the-background"&gt;Solution 1: Start the Server in the Background&lt;/h2&gt;
&lt;p&gt;In most shells, you can run a command and instruct the shell to run it in the &lt;em&gt;background&lt;/em&gt;. To do this, you end the line with an ampersand. Here&amp;rsquo;s how the example would look in this case:&lt;/p&gt;
&lt;img src="images/start-in-background.gif" alt="Demo: Starting a Background Job" width="600" /&gt;
&lt;p&gt;By ending the command with an &lt;code&gt;&amp;amp;&lt;/code&gt; ampersand symbol, we instruct the shell to run the command as a &lt;em&gt;background job&lt;/em&gt;. This means that our shell is still functional. The shell has also notified us that this command is running as a background job with a specific &lt;em&gt;job number&lt;/em&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt; &amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;19372&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In slightly obtuse language, the shell has informed us that it has started a job in the background, with job number &lt;code&gt;1&lt;/code&gt; and that this job is currently handling the process with ID &lt;code&gt;19372&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The ampersand solution is a fairly common pattern used in day-to-day work.&lt;/p&gt;
&lt;h2 id="solution-2-move-the-server-to-the-background"&gt;Solution 2: Move the Server to the Background&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say you forgot to start the command in the background. Most likely in this case you&amp;rsquo;d kill the server with &lt;code&gt;Ctrl+C&lt;/code&gt; and then start it again with the &lt;code&gt;&amp;amp;&lt;/code&gt; option. However, what if this was a large file download or a task you didn&amp;rsquo;t want to abort?&lt;/p&gt;
&lt;p&gt;In the example below, we&amp;rsquo;ll move the job to the background:&lt;/p&gt;
&lt;img src="images/move-to-background.gif" alt="Demo: Moving a Job to the Background" width="600" /&gt;
&lt;p&gt;The process is currently in the foreground, so my shell is inactive. Hitting &lt;code&gt;Ctrl+Z&lt;/code&gt; sends a &amp;lsquo;suspend&amp;rsquo; signal to the process&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;, pausing it and moving it to the background.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dissect this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Serving HTTP on 0.0.0.0 port &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;127.0.0.1 - - &lt;span style="color:#f92672"&gt;[&lt;/span&gt;03/Jun/2019 13:38:45&lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;GET / HTTP/1.1&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; -
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;^Z
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + &lt;span style="color:#ae81ff"&gt;21268&lt;/span&gt; suspended python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The shell echos as I type, so we see &lt;code&gt;^Z&lt;/code&gt; (i.e., the &lt;code&gt;Ctrl+Z&lt;/code&gt; chord I entered). The shell responds by moving the process into a background job and suspending it.&lt;/p&gt;
&lt;p&gt;The key here is that it is &lt;em&gt;suspended&lt;/em&gt;. The process is paused. So the web server is no longer serving. If you are following with the sample, reload your browser. The webpage fails to load, as the server process is not able to respond to requests.&lt;/p&gt;
&lt;p&gt;To &lt;em&gt;continue&lt;/em&gt; the job, in the background, we use the &lt;code&gt;bg&lt;/code&gt; (&amp;lsquo;background&amp;rsquo;) command, with a &lt;em&gt;job identifier&lt;/em&gt; (which always starts with a &lt;code&gt;%&lt;/code&gt; symbol - we&amp;rsquo;ll see why soon) to tell the shell to continue the job:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% bg %1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + &lt;span style="color:#ae81ff"&gt;21268&lt;/span&gt; continued python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The shell lets us know the job is being continued, and if we load the webpage again, the content is shown as expected.&lt;/p&gt;
&lt;p&gt;As a final check, we run the &lt;code&gt;jobs&lt;/code&gt; command to see what jobs the shell is running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% jobs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + running python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And there you have it - our server is running as a background job. This is exactly what we would see if we run &lt;code&gt;jobs&lt;/code&gt; after starting the server with an &lt;code&gt;&amp;amp;&lt;/code&gt; at the end. In fact, using an &lt;code&gt;&amp;amp;&lt;/code&gt; is perhaps an easier way to remember how to continue a suspended job:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% %1 &amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + &lt;span style="color:#ae81ff"&gt;21268&lt;/span&gt; continued python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the same way ending a command with &lt;code&gt;&amp;amp;&lt;/code&gt; runs it in the background, ending a job identifier with &lt;code&gt;&amp;amp;&lt;/code&gt; &lt;em&gt;continues&lt;/em&gt; it in the background.&lt;/p&gt;
&lt;p&gt;There is at least one more way to move a job to the background&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;, but I have not yet found it useful in any scenarios, and it is overly complex to explain. See the footnote for details if you are interested.&lt;/p&gt;
&lt;h2 id="moving-background-jobs-to-the-foreground"&gt;Moving Background Jobs to the Foreground&lt;/h2&gt;
&lt;p&gt;If you have a job in the background, you can bring it back to the foreground with the &lt;code&gt;fg&lt;/code&gt; (&amp;lsquo;foreground&amp;rsquo;) command. Let&amp;rsquo;s show the jobs, with the &lt;code&gt;jobs&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% jobs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + running python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here I have a background job running a server. Any one of the following commands will bring it back to the foreground:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fg %1 &lt;span style="color:#75715e"&gt;# Explicitly bring Job 1 into the foreground&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;%1 &lt;span style="color:#75715e"&gt;# ...or in shorthand, just enter the job id...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fg &lt;span style="color:#75715e"&gt;# ...if not given an id, fg and bg assume the most recent job.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the job is in the foreground, and you can interact with the process again however you like.&lt;/p&gt;
&lt;h2 id="cleaning-up-jobs"&gt;Cleaning Up Jobs&lt;/h2&gt;
&lt;p&gt;You might realise you cannot continue what you are doing because an old job is &lt;em&gt;still running&lt;/em&gt;. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;img src="images/kill-job.gif" alt="Demo: Cleaning Up Jobs" width="600" /&gt;
&lt;p&gt;I tried to run my web server, but there was still one running as a background job. The server failed to start because the port is in use.&lt;/p&gt;
&lt;p&gt;To clean it up, I run the &lt;code&gt;jobs&lt;/code&gt; command to list the jobs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% jobs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + suspended python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s my old web server. Note that even though it is suspended, it&amp;rsquo;ll still be blocking the port it is serving on&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;. The process is paused, but it is still holding onto all of the resources it is using.&lt;/p&gt;
&lt;p&gt;Now that I know the job identifier (&lt;code&gt;%1&lt;/code&gt; in this case), I can kill the job:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% kill %1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt; + &lt;span style="color:#ae81ff"&gt;22843&lt;/span&gt; terminated python -m SimpleHTTPServer &lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;This is why job identifiers start with a percentage sign!&lt;/em&gt; The &lt;code&gt;kill&lt;/code&gt; command I have used is not a special job control command (like &lt;code&gt;bg&lt;/code&gt; or &lt;code&gt;fg&lt;/code&gt;). It is the normal &lt;code&gt;kill&lt;/code&gt; command, which terminates a process. But shells that support job control can normally use a job identifier in place of a &lt;em&gt;process identifier&lt;/em&gt;. So rather than working out what the process identifier is that I need to kill, I can just use the job identifier&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="why-you-shouldnt-use-jobs"&gt;Why You Shouldn&amp;rsquo;t Use Jobs&lt;/h2&gt;
&lt;p&gt;Avoid jobs. They are not intuitive to interface with, and they suffer from some serious problems.&lt;/p&gt;
&lt;p&gt;The most obvious one is that all jobs write to the same output, meaning you can quickly get garbled output like this:&lt;/p&gt;
&lt;img src="images/output.png" alt="Screenshot: Garbled Output" width="600" /&gt;
&lt;p&gt;This is what happens when I run a job, which just outputs text every second. It&amp;rsquo;s in the background, but it&amp;rsquo;s printing all over my commands. Even running the &lt;code&gt;jobs&lt;/code&gt; command to try and find the job to stop it is difficult.&lt;/p&gt;
&lt;p&gt;Input is even more complex. If a job is &lt;em&gt;running&lt;/em&gt; in the background, but requires input, it will be &lt;em&gt;silently suspended&lt;/em&gt;. This can cause confusion.&lt;/p&gt;
&lt;p&gt;Jobs &lt;em&gt;can&lt;/em&gt; be used in scripts but must be done so with caution and could easily confuse a consumer of the script if they leave background jobs hanging around, which cannot be easily cleaned up&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Handling errors and exit codes for jobs can be problematic, causing confusion, poor error handling, or overly complex code.&lt;/p&gt;
&lt;h2 id="how-to-escape-jobs"&gt;How to Escape Jobs&lt;/h2&gt;
&lt;p&gt;If there are two things to take away, it would be this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have started running a command in the foreground, and you don&amp;rsquo;t want to stop it and would rather move it to the background, hit &lt;code&gt;Ctrl+Z&lt;/code&gt;. Then Google &amp;ldquo;job control&amp;rdquo;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you think there is a job running in the background, and it is messing with your screen, type &lt;code&gt;fg&lt;/code&gt; to bring it to the front and kill it with &lt;code&gt;Ctrl+C&lt;/code&gt;. Repeat as needed!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In either case, if you need to do something more subtle, you can return to this reference. But the first command should allow you to get your shell back while you work out how to continue the job, and the second should kill a background job that is messing with your screen.&lt;/p&gt;
&lt;h2 id="alternatives-to-jobs"&gt;Alternatives to Jobs&lt;/h2&gt;
&lt;p&gt;If you are using any kind of modern terminal such as iTerm, Terminal or the GNOME Terminal, just open a new tab or split! Much easier.&lt;/p&gt;
&lt;p&gt;The benefit to this is that each tab gets its own standard input and output, so there&amp;rsquo;s no risk of overwriting. And of course you can hide/reveal/rearrange the tabs however you like.&lt;/p&gt;
&lt;p&gt;The traditional alternative to a job for an operator who simply wants more than one thing going on at once would be a &lt;em&gt;terminal multiplexer&lt;/em&gt;, such as &lt;code&gt;screen&lt;/code&gt; or &lt;code&gt;tmux&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/terminal-multiplexer.gif" alt="terminal-multiplexer"&gt;&lt;/p&gt;
&lt;p&gt;Multiplexers work in a very similar way to a modern graphical terminal - they manage many shell instances. The benefits to a modern terminal, such as iTerm, is that you have a very intuitive GUI and lots of features.&lt;/p&gt;
&lt;p&gt;The benefits to a multiplexer are that you can run them over SSH sessions to manage complex operations on remote machines and that they run a client-server model, meaning many people can work with many multiplexed processes (and they can persist beyond sessions).&lt;/p&gt;
&lt;p&gt;My personal preference is both - I use a modern terminal &lt;em&gt;and&lt;/em&gt; run everything inside it in &lt;code&gt;tmux&lt;/code&gt;. We&amp;rsquo;ll look at both of these options in later chapters.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference&lt;/h2&gt;
&lt;p&gt;You might find that jobs are useful, or you might find that they are not. Either way, here&amp;rsquo;s a quick reference of some common commands:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command &amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the command as a background job.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;Ctrl+Z&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move the current process into a background job, suspended.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all jobs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fg %1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move background job number 1 into the foreground.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bg %1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continue background job number 1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kill %1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terminate job number 1.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wait %1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block until job number 1 exits.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you want to find out more about the gory details of jobs, the best place to start is the &lt;a href="https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Job-Control"&gt;Bash Manual - Job Control Section&lt;/a&gt;, or the &amp;lsquo;Job Control&amp;rsquo; section of your preferred shell&amp;rsquo;s manual.&lt;/p&gt;
&lt;p&gt;I hope you found this useful, and, as always, please leave comments, questions or suggestions below!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If you are not a heavy shell user, this might seem unlikely. But if you do a lot of work in shells, such as sysadmin, devops, or do your coding from a terminal, this happens all the time!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Signals like &lt;code&gt;SIGINT&lt;/code&gt;, &lt;code&gt;SIGKILL&lt;/code&gt;, &lt;code&gt;SIGTERM&lt;/code&gt; and so on will be covered in a later chapter.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Technically, &lt;code&gt;SIGTSTP&lt;/code&gt; - which is &amp;lsquo;TTY stop&amp;rsquo;. If you have always wondered about the &amp;lsquo;TTY&amp;rsquo; acroynm, check the previous chatper, &lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Interlude: Understanding the Shell&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;The alternative method is to use &lt;code&gt;Ctrl+Y&lt;/code&gt;, which will send a &lt;em&gt;delayed interrupt&lt;/em&gt;, which will continue to run the process until it tries to read from &lt;code&gt;stdin&lt;/code&gt;. At this point, the job is suspended and the control given to the shell. The operator can then use &lt;code&gt;bg&lt;/code&gt; or &lt;code&gt;kill&lt;/code&gt; or &lt;code&gt;fg&lt;/code&gt; to either move to the background, stop the process, or keep in the foreground as preferred. See: &lt;a href="https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Job-Control"&gt;https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Job-Control&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Another super-useful snippet: &lt;code&gt;lsof -i -P -n | grep 8000&lt;/code&gt; to find any process that has a given port open. Another one for the aliases chapter!&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;There are times this is needed. If a job runs &lt;em&gt;many processes&lt;/em&gt; - for example, by running a pipeline - the process identifier will change as the command moves from one stage of the pipeline to the next. The job identifier will remain constant. Remember, a job is a shell &lt;em&gt;command&lt;/em&gt;, so could run many processes.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;To see how bad this can be, create a script that starts jobs, then run it. Then run the &lt;code&gt;jobs&lt;/code&gt; command to see what is running. The output might surprise you!&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Interlude: Understanding the Shell</title><link>https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/</link><pubDate>Tue, 21 May 2019 09:22:05 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/</guid><description>&lt;p&gt;This is the first &amp;lsquo;interlude&amp;rsquo; in my &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;Effective Shell&lt;/a&gt; series. These interludes give some background, history or more flavour to some of the topics.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This one &lt;em&gt;should&lt;/em&gt; be high-level enough for even non-technical readers to enjoy (or at least understand!). I&amp;rsquo;ve tried to make sure any term that might be unfamiliar is described in a footnote&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. For the more technical reader, it provides an important grounding on some of the key concepts relating to shells and how they work.&lt;/p&gt;
&lt;h2 id="introduction-for-the-non-technical-reader"&gt;Introduction for the Non-Technical Reader&lt;/h2&gt;
&lt;p&gt;It might come as a surprise that &lt;em&gt;many&lt;/em&gt; technical computer users (programmers, data scientists, systems administrators etc) spend a lot of time using an interface which looks like it&amp;rsquo;s from the sixties:&lt;/p&gt;
&lt;img src="images/screenshot-shell.png" alt="Diagram: The Shell" width="600px" /&gt;
&lt;p&gt;If you work with technologists, you might have seen them using an interface like this. This kind of simple, text-based interface is called a &lt;em&gt;shell&lt;/em&gt;, and it has been a common way to interface with computers ever since the first screens and keyboards were created.&lt;/p&gt;
&lt;p&gt;Given how much computing has advanced, why would people use such an interface? Just look at how much the Windows operating-system has changed over the last three decades:&lt;/p&gt;
&lt;img src="images/screenshot-windows-evolution.png" alt="Image: The Evolution of Windows" width="600px" /&gt;
&lt;p&gt;&lt;em&gt;(By Source (WP:NFCC#4), Fair use, &lt;a href="https://en.wikipedia.org/w/index.php?curid=58853841"&gt;https://en.wikipedia.org/w/index.php?curid=58853841&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Why would people choose to use such an archaic interface as a shell?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typing is &lt;em&gt;fast&lt;/em&gt;: A skilled shell user can manipulate a system at dazzling speeds just using a keyboard. Typing commands is generally &lt;em&gt;much&lt;/em&gt; faster than exploring through user interfaces with a mouse&lt;/li&gt;
&lt;li&gt;Shells are &lt;em&gt;programmable&lt;/em&gt;: Users will often being programming as they work in a shell, creating scripts to automate time-consuming or repetetive processes&lt;/li&gt;
&lt;li&gt;Shells are &lt;em&gt;portable&lt;/em&gt;: A shell can be used to interface to almost any type of computer, from a mainframe to a Raspberry Pi, in a very similar way.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not all technical users will use a shell regularly, but there are many who will spend the bulk of their time in such an interface. It is such a crucial skill to be able to operate one effectively that I have been writing this series primarily to show ways to be more efficient with this kind of interface.&lt;/p&gt;
&lt;h2 id="introduction-for-the-technical-reader"&gt;Introduction for the Technical Reader&lt;/h2&gt;
&lt;p&gt;You may be familar with the shell, but it can be useful to understand some of the surrounding concepts in detail. How does a shell differ from a terminal? What is a &lt;em&gt;tty&lt;/em&gt;? How do shells really work? Hopefully as you read this article you&amp;rsquo;ll discovery something that you didn&amp;rsquo;t know about shells.&lt;/p&gt;
&lt;h2 id="lets-get-started"&gt;Let&amp;rsquo;s Get Started!&lt;/h2&gt;
&lt;p&gt;To understand what shells, terminals, command-prompts and so on are and how they relate, we need to start with the basics: how a modern computer works!&lt;/p&gt;
&lt;h2 id="a-computer-in-a-nutshell"&gt;A Computer in a Nutshell&lt;/h2&gt;
&lt;p&gt;The diagram below shows a simplified view of a typical computer:&lt;/p&gt;
&lt;img src="images/diagram1-operating-system.png" alt="Diagram: Operating System" width="600px" /&gt;
&lt;p&gt;Already there&amp;rsquo;s a lot going on.&lt;/p&gt;
&lt;p&gt;Your computer is going to have a CPU&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; and memory&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;, and almost certainly a network adapter&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; and display adapter&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;. Most computers will have at least one hard disk. For home PCs, there&amp;rsquo;ll also likely be a bunch of peripherals, such as a mouse, keyboard, printers, flash drives, webcams and so on.&lt;/p&gt;
&lt;h3 id="the-operating-system"&gt;The Operating System&lt;/h3&gt;
&lt;p&gt;The operating system is the piece of software installed on a computer that can interface with the &lt;em&gt;hardware&lt;/em&gt;. Without hardware, such as a CPU, memory, a network adapter, a graphics card, disk drives and so on, there&amp;rsquo;s not much that you can do with the computer. The operating system is the primary interface to this hardware. No normal programs will talk to hardware directly - the operating system abstracts this hardware away and provides a &lt;em&gt;software&lt;/em&gt; interface to it.&lt;/p&gt;
&lt;p&gt;The abstraction the operating system provides is essential. Developers don&amp;rsquo;t need to know the specifics of how to work with individual devices from different vendors; the operating system provides a standardised interface to all of this. It also handles various tasks such as making sure the system starts up properly.&lt;/p&gt;
&lt;p&gt;The operating system is generally broken down into two parts - the &lt;em&gt;kernel&lt;/em&gt; and &lt;em&gt;user space&lt;/em&gt;:&lt;/p&gt;
&lt;img src="images/diagram2-the-kernel-and-user-space.png" alt="Diagram: The Kernel and User Space" width="600px" /&gt;
&lt;p&gt;Let&amp;rsquo;s look at these in more detail.&lt;/p&gt;
&lt;h3 id="the-kernel"&gt;The Kernel&lt;/h3&gt;
&lt;p&gt;This is the part of the operating system that is responsible for the most sensitive tasks: interfacing with physical devices, managing the resources that are available for users and programs, starting up the various systems that are needed, and so on.&lt;/p&gt;
&lt;p&gt;Software running in the kernel has direct access to resources, so is &lt;em&gt;extremely&lt;/em&gt; sensitive. The kernel will balance resources between the programs in user space, which we&amp;rsquo;ll look at shortly. If you&amp;rsquo;ve ever had to install &amp;lsquo;drivers&amp;rsquo;, these are examples of pieces of software that will run in the kernel - they&amp;rsquo;ll have direct access to a physical device you&amp;rsquo;ve installed, and expose it to the rest of the software on the computer.&lt;/p&gt;
&lt;p&gt;Why &amp;lsquo;kernel&amp;rsquo;? The kernel is the soft, edible part of a nut or seed, which is surrounded by a shell. Below you can see a walnut - the kernel is the soft bit in the middle, and the shell surrounds and protects it. This is a useful metaphor that is used for parts of a computer.&lt;/p&gt;
&lt;img src="images/image-walnut.jpg" alt="Image: Photo of a walnut, showing the kernel and the shell" width="200px" /&gt;
&lt;p&gt;&lt;em&gt;(By Kkchaudhary11 - Own work, CC BY-SA 4.0, &lt;a href="https://commons.wikimedia.org/w/index.php?curid=49069244"&gt;https://commons.wikimedia.org/w/index.php?curid=49069244&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The operating system kernel really is the &lt;em&gt;core&lt;/em&gt; of the operating system. It&amp;rsquo;s such a sensitive area of the operating system that we actually want to avoid running software in it if possible&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;. And that is where &lt;em&gt;user space&lt;/em&gt; comes in.&lt;/p&gt;
&lt;h3 id="user-space"&gt;User Space&lt;/h3&gt;
&lt;p&gt;The vast majority of programs run in &amp;lsquo;user space&amp;rsquo; (also commonly called &amp;lsquo;user land&amp;rsquo;).&lt;/p&gt;
&lt;p&gt;When a program starts, the kernel will allocate it a private segment of memory and provide &lt;em&gt;limited&lt;/em&gt; access to resources. The program is given access to a library of functions by the operating system, which it can use to access resources such as files, devices and so on. Programs in user space are essentially in sandboxes, where there is a limit to how much damage they can do.&lt;/p&gt;
&lt;p&gt;For example, a program running in user space can use the standard &lt;a href="http://man7.org/linux/man-pages/man3/fopen.3.html"&gt;&lt;code&gt;fopen&lt;/code&gt;&lt;/a&gt; function, which is provided on almost every operating system as part of the &lt;a href="https://www.gnu.org/software/libc/"&gt;C Standard Library&lt;/a&gt;. This allows a program to attempt to open a file. The operating system will make a decision on whether the program is &lt;em&gt;allowed&lt;/em&gt; to open the file (based on things such as permissions, where the file is and so on) and then, if it is OK with the call, will give the program access to the file. Under the hood, this &amp;lsquo;user space&amp;rsquo; call translates to a system call in the kernel.&lt;/p&gt;
&lt;p&gt;Now that the key components have been introduced, we can look at the &lt;em&gt;shell&lt;/em&gt;. The name should come as no surprise, as it is a &lt;em&gt;wrapper&lt;/em&gt; or outer layer to the operating system (which itself contains the sensitive nugget of the kernel).&lt;/p&gt;
&lt;h3 id="the-shell"&gt;The Shell&lt;/h3&gt;
&lt;p&gt;So what is the shell? The shell is just a general name for any &lt;em&gt;user space&lt;/em&gt; program that allows access to resources in the system, via some kind of interface.&lt;/p&gt;
&lt;p&gt;Shells come in many different flavours but are generally provided to aid a human operator in accessing the system. This could be interactively, by typing at a terminal, or via scripts, which are files that contain a sequence of commands.&lt;/p&gt;
&lt;p&gt;For example, to see all of the files in a folder, the human operator &lt;em&gt;could&lt;/em&gt; write a program in a language such as C, making system calls to do what they want. But for day-to-day tasks, this would be repetitive. A shell will normally offer us a quick way to do that exact task, without having to manually write a program to do it.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example, where a shell is being used to show the &amp;lsquo;png&amp;rsquo; images in the folder I am working in&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;img src="images/screenshot1-example-shell.png" alt="Screenshot: Browsing Contents of the File System the the Bourne Again Shell" width="600px" /&gt;
&lt;p&gt;So a shell is a user-space program to interface with the computer. But there a few more moving parts than just a shell we are seeing in the image above. There are different types of shells, there are terminal programs, and there are the programs or commands that the shell calls (in the example above, &lt;code&gt;tree&lt;/code&gt; is a program). Let&amp;rsquo;s pick these apart.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a diagram that more accurately shows what is going on:&lt;/p&gt;
&lt;img src="images/diagram3-terminal-and-shell.png" alt="Diagram: The Terminal &amp; The Shell" width="600px" /&gt;
&lt;p&gt;We&amp;rsquo;ve introduced a few new things here. There&amp;rsquo;s a &lt;em&gt;user&lt;/em&gt;, who is interfacing with a &lt;em&gt;terminal&lt;/em&gt;, which is running a &lt;em&gt;shell&lt;/em&gt;, which is showing a &lt;em&gt;command prompt&lt;/em&gt;. The user has written a command that is calling a program (in this case, the &lt;code&gt;tree&lt;/code&gt; program).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dissect this bit by bit.&lt;/p&gt;
&lt;h3 id="the-terminal"&gt;The Terminal&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re not &lt;em&gt;directly&lt;/em&gt; interacting with the &amp;lsquo;shell&amp;rsquo; in this diagram. We&amp;rsquo;re actually using a &lt;em&gt;terminal&lt;/em&gt;. When a user wants to work with a shell interactively, using a keyboard to provide input and a display to see the output on the screen, the user uses a &lt;em&gt;terminal&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A terminal is just a program that reads input from the keyboard, passes that input to another program (normally a shell), and displays the results on the screen. A shell program on its own does not do this - it requires a terminal as an interface.&lt;/p&gt;
&lt;p&gt;Why the word &lt;em&gt;terminal&lt;/em&gt;? This makes sense when you look at how people interfaced with computers historically. Input to a computer might be through punch cards, and output would often be via a printer. The &lt;em&gt;Teletype Termimal&lt;/em&gt;&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt; became a common way for users to interface with computers.&lt;/p&gt;
&lt;img src="images/image-asr-33.jpg" alt="Photo: ASR-33 TTY" width="600px" /&gt;
&lt;p&gt;&lt;em&gt;(Photograph by Rama, Wikimedia Commons, Cc-by-sa-2.0-fr, CC BY-SA 2.0 fr, &lt;a href="https://commons.wikimedia.org/w/index.php?curid=17821795"&gt;https://commons.wikimedia.org/w/index.php?curid=17821795&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At this time, computers were very large, complex, and expensive machines. It was common to have &lt;em&gt;many&lt;/em&gt; terminals connected to a single large machine (or &amp;lsquo;mainframe&amp;rsquo;), or a few terminals that people would share. But the terminal itself was just a human interface to the operating system. A more modern terminal would be something like an IBM 3486:&lt;/p&gt;
&lt;img src="images/image-ibm3486.jpg" alt="Photo: IBM 3486" width="600px" /&gt;
&lt;p&gt;&lt;em&gt;(By ClickRick - Own work, CC BY-SA 3.0, &lt;a href="https://commons.wikimedia.org/w/index.php?curid=6693700"&gt;https://commons.wikimedia.org/w/index.php?curid=6693700&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is a very small computer in its own right but still basically just a dumb screen and keyboard connected by a cable to a larger mainframe computer in another location.&lt;/p&gt;
&lt;p&gt;This mechanism is still very much the case today. When I want to work with a computer in a data centre, I don&amp;rsquo;t go and find the machine, plug in a keyboard and a display and directly interface to it. I run a &lt;em&gt;terminal program&lt;/em&gt; on my computer to provide access to the remote machine. My terminal program allows me to use my keyboard and display to work with a remote machine - all via a &lt;em&gt;secure shell&lt;/em&gt; - which is a secured-shell connection over a network.&lt;/p&gt;
&lt;p&gt;So terminals in many ways are quite simple - they are interfaces. But because they are quite simple programs, we can&amp;rsquo;t do much with them. So normally, the first thing that a terminal program will do is run a &lt;em&gt;shell&lt;/em&gt; program - a program that we can use to operate the computer.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s nothing special about terminals - anyone can write a program to operate as a terminal, which is why you will see many different terminals around. Examples are the standard &amp;rsquo;terminal&amp;rsquo; app for MacOS X, the &lt;a href="https://wiki.gnome.org/Apps/Terminal/VTE"&gt;gnome-terminal&lt;/a&gt; for Linux, and &lt;a href="https://www.iterm2.com/"&gt;iTerm2&lt;/a&gt; and &lt;a href="https://hyper.is/"&gt;Hyper&lt;/a&gt;. There&amp;rsquo;s a bunch of screenshots of different setups at the end of the article.&lt;/p&gt;
&lt;h2 id="back-to-the-shell"&gt;Back to the Shell&lt;/h2&gt;
&lt;p&gt;Now that we&amp;rsquo;ve described the terminal, we can go back and look at the shell in detail.&lt;/p&gt;
&lt;p&gt;The shell is the program that is going to take input from somewhere and run a series of commands. When the shell is running in a terminal, it is normally taking input interactively from the user. As the user types in commands, the terminal feeds the input to the shell and presents the output of the shell on the screen.&lt;/p&gt;
&lt;p&gt;A shell program can also take input from files; these files will then generally be &amp;lsquo;shell scripts&amp;rsquo;. This might be used to run automated operations, such as cleaning up certain folders when a computer starts.&lt;/p&gt;
&lt;p&gt;Shells can write output to files or other locations, and so on. You can run a shell program outside of a terminal - you just won&amp;rsquo;t be able to interface with it using a keyboard or display. And in fact, lots of operations happen in this way: automated scripts, startup tasks, installers and so on.&lt;/p&gt;
&lt;p&gt;So what else does a shell do? Most of the features are related to helping human operators work with the system more efficiently.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quickly enter commands, see the history of commands and quickly restructure commands (see &lt;a href="http://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Effective Shell - Navigating the Command Line&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Navigate through the file system, moving from folder to folder (see &lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Effective Shell - Move Around!&lt;/a&gt;), which makes it easier for an operator to navigate the file system.&lt;/li&gt;
&lt;li&gt;Chain the output of commands together - for example, taking the output of one basic program, such as the &lt;code&gt;tree&lt;/code&gt; program we saw, and writing it to a file (see &lt;a href="https://github.com/dwmkerr/effective-shell#coming-soon"&gt;Effective Shell - Understanding Pipelines&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Offer a programming language, allowing the operator to perform more complicated tasks (see &lt;a href="https://github.com/dwmkerr/effective-shell#coming-soon"&gt;Effective Shell - Basic Shell Scripting&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And a lot more! In fact, that&amp;rsquo;s what the whole &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;Effective Shell&lt;/a&gt; series is about - how to get the most from these powerful programs, particularly for those who use them regularly.&lt;/p&gt;
&lt;h3 id="the-command-prompt-or-command-line"&gt;The Command Prompt or Command Line&lt;/h3&gt;
&lt;p&gt;The last part of the diagram, which we haven&amp;rsquo;t covered yet, is the &lt;em&gt;command prompt&lt;/em&gt;.&lt;/p&gt;
&lt;img src="images/diagram4-command-prompt-1.png" alt="Diagram: Command Prompt" width="300px" /&gt;
&lt;p&gt;When a &lt;em&gt;shell&lt;/em&gt; is running in &lt;em&gt;terminal&lt;/em&gt;, it knows that a human operator will be interfacing with it. So to make sure that the operator has some kind of visual hint that &lt;em&gt;they have to enter commands&lt;/em&gt;, the shell will output some kind of prompt.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve included a set of screenshots at the end of the article, just after this section, and you can see how some different command prompts look.&lt;/p&gt;
&lt;p&gt;Note that shells don&amp;rsquo;t have to use command prompts - if you use a shell program to execute a script, there will be no command prompt. Shells only show a prompt when they know they are being used interactively. Many programs which allow a user to operate interactively will show a command prompt.&lt;/p&gt;
&lt;p&gt;Shell command prompts can be customised, so they will often look different from machine to machine (for more details, see &lt;a href="https://github.com/dwmkerr/effective-shell#coming-soon"&gt;Effective Shell - Customising the Command Line&lt;/a&gt;). Below is an example that shows a &lt;em&gt;lot&lt;/em&gt; of technical information. This is from the highly popular &lt;a href="https://ohmyz.sh/"&gt;oh-my-zsh&lt;/a&gt; framework for the &amp;lsquo;Z Shell&amp;rsquo; shell, which is very popular among developers:&lt;/p&gt;
&lt;img src="images/image-ohmyzsh.jpg" alt="Image: Customised oh-my-zsh" width="600px" /&gt;
&lt;p&gt;*(Source: &lt;a href="https://ohmyz.sh/"&gt;https://ohmyz.sh/&lt;/a&gt;)&lt;/p&gt;
&lt;h3 id="shell-commands-and-different-shells"&gt;Shell Commands and Different Shells&lt;/h3&gt;
&lt;p&gt;A lot of the &amp;lsquo;commands&amp;rsquo; in a shell, such as &lt;code&gt;cat&lt;/code&gt; (which shows the contents of a file), are actually just simple programs, which will interface with the kernel. No matter what shell you use, these commands will behave the same way, because really all you are doing is calling another progam.&lt;/p&gt;
&lt;p&gt;Some commands, such as &lt;code&gt;cd&lt;/code&gt; (change directory), are built into the shell. Some commands are functions that have been defined, or aliases to other commands (for more details on commands, see &lt;a href="https://github.com/dwmkerr/effective-shell#coming-soon"&gt;Effective Shell - Commands&lt;/a&gt;). Commands will often differ between shells.&lt;/p&gt;
&lt;p&gt;Not all shells are created equal - anyone can write a shell program, maybe creating a simple interface to the computer or a highly complex one with many features. In fact, a later article in this series will look at the geneology of the most common shells.&lt;/p&gt;
&lt;p&gt;On most Unix-like systems, the default shell is a program called &lt;code&gt;bash&lt;/code&gt;, which stands for &amp;quot; Bourne Again Shell&amp;quot; (the name and history around it will be discussed at length in the later article). But there are many other shells: the C Shell, the Korn Shell, Z Shell and Fish, just to name just a few.&lt;/p&gt;
&lt;p&gt;Users and administators can configure what shell they like to use. When a terminal opens, it will immediately start the user&amp;rsquo;s preferred shell program. It is possible to change this. Different users will have different preferences, given that shells offer varying features. This can cause complexity when working with systems, as we cannot always expect every user to have the same shell, or even for the same shell to be set up consistently, as they can be extensively customised.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s review the earlier diagram again:&lt;/p&gt;
&lt;img src="images/diagram3-terminal-and-shell-1.png" alt="Diagram: The Terminal &amp; The Shell" width="600px" /&gt;
&lt;p&gt;We can see the real internals of what is going on in this &amp;ldquo;Terminal -&amp;gt; Shell -&amp;gt; Program&amp;rdquo; chain in the diagram above quite easily.&lt;/p&gt;
&lt;p&gt;Try the command &lt;code&gt;pstree -psa $$&lt;/code&gt; in a shell&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;img src="images/image-psforest.png" alt="Image: Process Tree" width="600px" /&gt;
&lt;p&gt;The first &lt;code&gt;systemd&lt;/code&gt; process is the primary process for the OS - it is process number &lt;code&gt;1&lt;/code&gt;, which initialises everything else. The second &lt;code&gt;systemd&lt;/code&gt; process is the process that is running the interface for my user. We can ignore these for now; they are internals to how the operating system boots and starts processes.&lt;/p&gt;
&lt;p&gt;What is interesting is that we can see a &lt;em&gt;terminal&lt;/em&gt; (the gnome terminal), which has started my preferred &lt;em&gt;shell&lt;/em&gt; (which is &lt;code&gt;zsh&lt;/code&gt;), which is running a &lt;em&gt;command&lt;/em&gt; (the program &lt;code&gt;pstree&lt;/code&gt;). Here we can see the exact chain as shown in the diagram earlier.&lt;/p&gt;
&lt;h3 id="thats-a-wrap"&gt;That&amp;rsquo;s a Wrap!&lt;/h3&gt;
&lt;p&gt;These are the key technologies and concepts that surround a shell.&lt;/p&gt;
&lt;p&gt;If you are interested in more technical details of working with shells, then my &lt;a href="https://github.com/effective-shell"&gt;Effective Shell&lt;/a&gt; series goes into these topics in depth. The goal of this series is to help teach techniques that making working with shells more efficient.&lt;/p&gt;
&lt;p&gt;To close the article, below are some examples of different terminals, shells, command prompts and so on.&lt;/p&gt;
&lt;h4 id="example-iterm-2--tmux--zsh"&gt;Example: iTerm 2 / tmux / zsh&lt;/h4&gt;
&lt;img src="images/example-iterm-zsh.png" alt="Example: iTerm 2, tmux, zsh" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A MacOS operating system&lt;/li&gt;
&lt;li&gt;iTerm2 as the terminal program&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tmux&lt;/code&gt; running as a &amp;rsquo;terminal multiplexer&amp;rsquo; (see &lt;a href="https://github.com/dwmkerr/effective-shell#coming-soon"&gt;Effective Shell: Terminal Multiplexers&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zsh&lt;/code&gt; (Z Shell) as the shell program, using &amp;lsquo;oh my zsh&amp;rsquo;, which is easily recognised by the &lt;code&gt;%&lt;/code&gt; sign in the command prompt.&lt;/li&gt;
&lt;li&gt;A customised command line, which shows the user and folder on one line, with only the &lt;code&gt;%&lt;/code&gt; symbol below, to leave lots of space for the input commands&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="example-bash"&gt;Example: Bash&lt;/h4&gt;
&lt;img src="images/example-bash.png" alt="Example: Bash" width="600px" /&gt;
&lt;img src="images/example-bash-root.png" alt="Example: Bash Elevated" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Linux operating system (Ubuntu 14)&lt;/li&gt;
&lt;li&gt;The gnome terminal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bash&lt;/code&gt; as the shell&lt;/li&gt;
&lt;li&gt;In the second screenshot, the user has &amp;lsquo;root privileges&amp;rsquo;, and to indicate this, &lt;code&gt;bash&lt;/code&gt; helpfully changes the default command prompt from a dollar sign to a hash sign&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="example-windows-explorer"&gt;Example: Windows Explorer&lt;/h4&gt;
&lt;img src="images/example-explorer.png" alt="Example: Windows Explorer" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Windows 10 operating system&lt;/li&gt;
&lt;li&gt;No terminal&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;explorer.exe&lt;/code&gt; program showing us a &lt;em&gt;graphical&lt;/em&gt; shell&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This looks different from previous examples. The program, which shows the familiar Windows interface, &lt;code&gt;explorer.exe&lt;/code&gt;, is in fact a shell as well, offering interactive access to the operating system and computer resources. The bulk of the Windows APIs to interact with this interface are in the &lt;a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb773177(v=vs.85).aspx"&gt;Shell Library&lt;/a&gt;. I also maintain a popular library for building extensions to the graphical Windows shell - &lt;a href="https://github.com/dwmkerr/sharpshell"&gt;sharpshell&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="example-windows-command-prompt"&gt;Example: Windows Command Prompt&lt;/h4&gt;
&lt;img src="images/example-cmd.png" alt="Example: Command Prompt" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Windows 10 operating system&lt;/li&gt;
&lt;li&gt;The command prompt terminal and shell&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Windows, the terminal and shell are combined into a single &lt;code&gt;cmd.exe&lt;/code&gt; program. There&amp;rsquo;s an excellent article on the internals - &lt;a href="https://devblogs.microsoft.com/commandline/windows-command-line-inside-the-windows-console/"&gt;Microsoft DevBlogs: Windows Command-Line: Inside the Windows Console&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="example-windows-powershell"&gt;Example: Windows PowerShell&lt;/h4&gt;
&lt;img src="images/example-powershell.png" alt="Example: Windows Powershell" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Windows 10 operating system&lt;/li&gt;
&lt;li&gt;The PowerShell terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PowerShell is an improvement on the &amp;lsquo;command prompt&amp;rsquo; program that was originally used in Windows, offering much more functionality for scripting and other modern shell features.&lt;/p&gt;
&lt;h4 id="example-windows-subsystem-for-linux-wsl"&gt;Example: Windows Subsystem for Linux (WSL)&lt;/h4&gt;
&lt;img src="images/example-wsl.png" alt="Example: WSL" width="600px" /&gt;
&lt;p&gt;In this example, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Windows 10 operating system&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Bash.exe&lt;/code&gt; program&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This screenshot, from &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/faq"&gt;MSDN: Frequently Asked Questions about Windows Subsystem for Linux&lt;/a&gt; shows Bash running in Windows. This is a relatively new feature at the time of writing, allowing Windows users to use a Linux interface to the PC. This is a feature that may become increasingly valuable, as in general it is challenging to write shell code that can run on Windows and Unix-like systems.&lt;/p&gt;
&lt;h2 id="share-and-discuss"&gt;Share and Discuss&lt;/h2&gt;
&lt;p&gt;If you enjoyed this article, please do share it! Feel free to include suggestions, improvements or corrections in the comments below.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Useful References&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A simple Linux kernel module, showing how basic kernel programming works in Linux: &lt;a href="https://github.com/dwmkerr/linux-kernel-module"&gt;github.com/dwmkerr/linux-kernel-module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.com/How-Linux-Works-2nd-Superuser/dp/1593275676"&gt;How Linux Works - Brian Ward&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://unix.stackexchange.com/questions/4126/what-is-the-exact-difference-between-a-terminal-a-shell-a-tty-and-a-con/4132"&gt;StackExchange: What is the exact difference between a &amp;rsquo;terminal&amp;rsquo;, a &amp;lsquo;shell&amp;rsquo;, a &amp;rsquo;tty&amp;rsquo;, and a console?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devblogs.microsoft.com/commandline/windows-command-line-inside-the-windows-console/"&gt;Microsoft: Inside the Windows Console&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I&amp;rsquo;d be fascinated to know if this is at all interesting to less technically inclined people, so please do go ahead and let me know in the comments!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;CPU: central processing unit. This is the chip in the computer that does most of the work (which after many layers of abstraction eventually becomes arithmetic and sending simple instructions to other places).&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Memory is the &amp;lsquo;working space&amp;rsquo; where the state of your system is stored. If you are writing a document, the text lives in memory, until you save it, when it then gets written to a hard drive. Memory is &lt;em&gt;ephemeral&lt;/em&gt; - everything is gone when you turn off the power to it.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;This is the part of your computer that knows how to do things like connect to a WiFi network, or has a network socket you might plug a network cable into.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;This is the part of your computer you plug the screen into.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;This is because a mistake in &lt;em&gt;Kernel Mode&lt;/em&gt; programs can have disasterous effects. It could access any files, no matter who they belong do, control the hardware, install more software - almost anything. Errors in this code can cause terrible issues (like the infamous Windows &amp;lsquo;blue screen of death&amp;rsquo;), and malicious code in the kernel essentially has full access to not only all your data but also your webcam, network adapter and so on.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;As an aside, if you are curious about the visual style of my setup or customisations that have been made, everything in my setup is available online on my &amp;lsquo;dotfiles&amp;rsquo; repo - &lt;a href="https://github.com/dwmkerr/dotfiles"&gt;github.com/dwmkerr/dotfiles&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;And that&amp;rsquo;s where the &amp;lsquo;TTY&amp;rsquo; acronym you will see sometimes comes from. Enter the &lt;code&gt;ps&lt;/code&gt; command, and you&amp;rsquo;ll actually see the TTY interface each process is attached to. This is a topic that will come up later in the series.&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;&lt;code&gt;$$&lt;/code&gt; is a Bash &lt;a href="https://www.tldp.org/LDP/abs/html/internalvariables.html#PROCCID"&gt;internal variable&lt;/a&gt;. These will also be covered in a later article in the series.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;Feel free to see my &lt;a href="https://github.com/dwmkerr/dotfiles"&gt;dotfiles&lt;/a&gt; to configure a similar setup for yourself.&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Effective Shell 4: Move Around!</title><link>https://dwmkerr.com/effective-shell-4-moving-around/</link><pubDate>Mon, 11 Mar 2019 09:02:00 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-4-moving-around/</guid><description>&lt;p&gt;This is the fourth part of my &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;Effective Shell&lt;/a&gt; series, a set of practical examples of ways to be more efficient with everyday tasks in the shell or at the command line.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article we&amp;rsquo;ll look at the key elements of navigation in the shell.&lt;/p&gt;
&lt;h2 id="getting-comfortable-moving-around"&gt;Getting Comfortable Moving Around&lt;/h2&gt;
&lt;p&gt;You might already spend a lot of time in the shell, running various command line programs or using tooling for development projects or operational tasks. But you might also still switch back to a more visual paradigm for working with files, directories and resources.&lt;/p&gt;
&lt;p&gt;Being able to perform everyday file and folder manipulation tasks directly from the shell can really speed up your workflow. Let&amp;rsquo;s look at some common tasks and see how we can work with them in the shell. Along the way we&amp;rsquo;ll also introduce some of the most frequently used tools and commands to work with the filesystem.&lt;/p&gt;
&lt;h2 id="where-am-i"&gt;Where Am I?&lt;/h2&gt;
&lt;p&gt;The first command to become familiar with is &lt;code&gt;pwd&lt;/code&gt; (&amp;lsquo;print working directory&amp;rsquo;). This command will echo the current absolute path. You can also use the &lt;code&gt;$PWD&lt;/code&gt; environment variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $PWD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Depending on your shell, or your command-line setup (which we will discuss in a later chapter), you might also see your working directly on the command-line.&lt;/p&gt;
&lt;h2 id="changing-directory"&gt;Changing Directory&lt;/h2&gt;
&lt;p&gt;Most likely one of the most familiar commands out there, the &lt;code&gt;cd&lt;/code&gt; or &lt;code&gt;chdir&lt;/code&gt; function changes the current directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/users/dave
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd -
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd ~
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/users/dave
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we can see that running &lt;code&gt;cd&lt;/code&gt; with no parameters moves to the users &amp;lsquo;home&amp;rsquo; directory. This directory is always available in the &lt;code&gt;$HOME&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;cd -&lt;/code&gt; will switch &lt;em&gt;back&lt;/em&gt; to the previous directory — this is very useful if you want to quickly jump somewhere and then back again.&lt;/p&gt;
&lt;p&gt;You can use &lt;code&gt;~&lt;/code&gt; as an alias for the home directory, allowing you to quickly move to personal folders, with commands such as &lt;code&gt;cd ~/Downloads&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Most commonly, you will specify a path when changing directory. This can be a fully qualified path, or it can be a relative path:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd /dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd ~/repos
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd ./github
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can use the special link &lt;code&gt;..&lt;/code&gt;, which is a folder that points to the &lt;em&gt;parent&lt;/em&gt; directory to move &amp;lsquo;upwards&amp;rsquo;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd ../../
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="exploring-a-directory"&gt;Exploring a Directory&lt;/h2&gt;
&lt;p&gt;Once we are in a directory, we will often want to see the contents. The &lt;code&gt;ls&lt;/code&gt; (&amp;ldquo;list directory contents&amp;rdquo;) command is useful here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1-navigating-the-command-line LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2-clipboard-gymnastics README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3-getting-help sed.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4-moving-around
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, the &lt;code&gt;ls&lt;/code&gt; command will list the files and directories. We can show more information with the &lt;code&gt;-l&lt;/code&gt; (&amp;ldquo;long format&amp;rdquo;) flag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls -l
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;192&lt;/span&gt; Mar &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 16:01 1-navigating-the-command-line
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;160&lt;/span&gt; Oct &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; 2-clipboard-gymnastics
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;128&lt;/span&gt; Dec &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; 3-getting-help
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;96&lt;/span&gt; Mar &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 15:39 4-moving-around
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;1066&lt;/span&gt; Jun &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;561&lt;/span&gt; Mar &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 15:30 README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff &lt;span style="color:#ae81ff"&gt;15707&lt;/span&gt; Mar &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 16:01 sed.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can see the permissions, the link count (which is rarely particularly useful and varies from platform to platform), the owner, the group, the size and the modification date (as well as the name).&lt;/p&gt;
&lt;p&gt;We can make the sizes more human readable, and sort by size with a few more flags &lt;code&gt;-h&lt;/code&gt; (&amp;ldquo;human readable&amp;rdquo;) and &lt;code&gt;-s&lt;/code&gt; (&amp;ldquo;sort by size&amp;rdquo;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls -lhS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff 15K Mar &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 16:01 sed.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff 1.0K Jun &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; LICENSE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; dave staff 561B Mar &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 15:30 README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; dave staff 192B Mar &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; 16:01 1-navigating-the-command-line
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; dave staff 160B Oct &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; 2-clipboard-gymnastics
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; dave staff 128B Dec &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2017&lt;/span&gt; 3-getting-help
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; dave staff 96B Mar &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; 15:39 4-moving-around
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are &lt;em&gt;lot&lt;/em&gt; of options for &lt;code&gt;ls&lt;/code&gt;. Check the chapter &lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Getting Help&lt;/a&gt; for some tips on how to get more information on a command!&lt;/p&gt;
&lt;h2 id="managing-the-directory-stack"&gt;Managing the Directory Stack&lt;/h2&gt;
&lt;p&gt;You might find that you want to move to a number of directories, then return to where you started. This can be particularly useful when scripting. You can use the &lt;code&gt;pushd&lt;/code&gt; (&amp;ldquo;push onto directory stack&amp;rdquo;) and &lt;code&gt;popd&lt;/code&gt; (&amp;ldquo;pop from directory stack&amp;rdquo;) commands to add or remove directories from the stack:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ pwd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/Users/dave/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# OK - I&amp;#39;m writing my article at the moment, but want to check my downloads, and come back shortly...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Move to the downloads folder...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws-nuke-v2.8.0-darwin-amd64
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# OK cool - the tool I was downloading has arrived, let&amp;#39;s use it...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd aws-nuke-v2.8.0-darwin-amd64
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./aws-nuke
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Now I want to go back to my article...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ popd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/Downloads ~/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/Downloads
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ popd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/repos/github/dwmkerr/effective-shell
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, using &lt;code&gt;cd -&lt;/code&gt; would not be sufficient — that would just switch us from the &lt;code&gt;aws-nuke&lt;/code&gt; folder to &lt;code&gt;Downloads&lt;/code&gt; and back again. But by using the &lt;em&gt;directory stack&lt;/em&gt; we can save where we are, move, and then &amp;lsquo;pop&amp;rsquo; our way back to where we started.&lt;/p&gt;
&lt;h2 id="auto-completion"&gt;Auto-Completion&lt;/h2&gt;
&lt;p&gt;Pressing &lt;code&gt;tab&lt;/code&gt; when using commands like &lt;code&gt;cd&lt;/code&gt; will generally show an auto-completion menu:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd ~/repos/ &lt;span style="color:#75715e"&gt;# press &amp;#39;tab&amp;#39; now...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;github/ gitlab/ local/ scratch/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pressing tab again will cycle through options, and shift-tab will cycle backwards. Enter will select an option, escape (or Ctrl-C) will cancel.&lt;/p&gt;
&lt;p&gt;Some shells, such as &lt;code&gt;zsh&lt;/code&gt;, support even more advanced auto-completion. For example, we can auto-complete to fill in partially specified directory names:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% cd ~/r/g/d/e &lt;span style="color:#75715e"&gt;# press tab now...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;% cd ~/repos/github/dwmkerr/effective-
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;effective-container-engineering/ effective-shell/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Auto-completion is generally &lt;em&gt;very&lt;/em&gt; shell specific. We&amp;rsquo;ll look more into the different shells that are available in later chapters. But in general, if you are uncertain, pressing tab will often show a sensible set of options.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s It!&lt;/h2&gt;
&lt;p&gt;This is a small chapter, but an important one. Later on, as we start to do more file and system manipulation from the shell, moving and copying files and so on, we will build on these concepts. But it is critical to first know the basics of how to move around the filesystem with the shell.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Dynamic and Configurable Availability Zones in Terraform</title><link>https://dwmkerr.com/dynamic-and-configurable-availability-zones-in-terraform/</link><pubDate>Tue, 11 Dec 2018 21:24:34 +0000</pubDate><guid>https://dwmkerr.com/dynamic-and-configurable-availability-zones-in-terraform/</guid><description>&lt;p&gt;When building Terraform modules, it is a common requirement to want to allow the client to be able to choose which region resources are created in, and which availability zones are used.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve seen a few ways of doing this, none of which felt entirely satisfactory. After a bit of experimentation I&amp;rsquo;ve come up with a solution which I think really works nicely. This solution avoids having to know in advance how many availability zones we&amp;rsquo;ll support.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/screenshot-1.jpg" alt="screenshot"&gt;&lt;/p&gt;
&lt;p&gt;To demonstrate, I&amp;rsquo;ve set up a module which deploys a cluster of web servers. My goal is to be able to configure the region, VPC CIDR block, subnets and subnet CIDR blocks as below:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;module &amp;#34;cluster&amp;#34; {
source = &amp;#34;github.com/dwmkerr/terraform-aws-vpc&amp;#34;
# Note how we can specify any number of availability zones here...
region = &amp;#34;ap-northeast-2&amp;#34;
vpc_cidr = &amp;#34;10.0.0.0/16&amp;#34;
subnets = {
ap-northeast-2a = &amp;#34;10.0.1.0/24&amp;#34;
ap-northeast-2b = &amp;#34;10.0.2.0/24&amp;#34;
ap-northeast-2c = &amp;#34;10.0.3.0/24&amp;#34;
}
# This just defines the number of web servers to deploy, and uses
# adds my public key so I can SSH into the servers...
web_server_count = &amp;#34;3&amp;#34;
public_key_path = &amp;#34;~/.ssh/id_rsa.pub&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The example module is at &lt;a href="https://github.com/dwmkerr/terraform-aws-vpc"&gt;github.com/dwmkerr/terraform-aws-vpc&lt;/a&gt;. Let&amp;rsquo;s take a look at some of the key elements.&lt;/p&gt;
&lt;h2 id="the-variables"&gt;The Variables&lt;/h2&gt;
&lt;p&gt;We define the required variables very explicitly, with descriptions and a variable type to avoid confusion:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;variable &amp;#34;region&amp;#34; {
description = &amp;#34;The region to deploy the VPC in, e.g: us-east-1.&amp;#34;
type = &amp;#34;string&amp;#34;
}
variable &amp;#34;vpc_cidr&amp;#34; {
description = &amp;#34;The CIDR block for the VPC, e.g: 10.0.0.0/16&amp;#34;
type = &amp;#34;string&amp;#34;
}
variable &amp;#34;subnets&amp;#34; {
description = &amp;#34;A map of availability zones to CIDR blocks, which will be set up as subnets.&amp;#34;
type = &amp;#34;map&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="the-vpc"&gt;The VPC&lt;/h2&gt;
&lt;p&gt;Now that we have defined the variables, we can set up the VPC:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Define the VPC.
resource &amp;#34;aws_vpc&amp;#34; &amp;#34;cluster&amp;#34; {
cidr_block = &amp;#34;${var.vpc_cidr}&amp;#34;
enable_dns_hostnames = true
}
// An Internet Gateway for the VPC.
resource &amp;#34;aws_internet_gateway&amp;#34; &amp;#34;cluster_gateway&amp;#34; {
vpc_id = &amp;#34;${aws_vpc.cluster.id}&amp;#34;
}
// Create one public subnet per key in the subnet map.
resource &amp;#34;aws_subnet&amp;#34; &amp;#34;public-subnet&amp;#34; {
count = &amp;#34;${length(var.subnets)}&amp;#34;
vpc_id = &amp;#34;${aws_vpc.cluster.id}&amp;#34;
cidr_block = &amp;#34;${element(values(var.subnets), count.index)}&amp;#34;
map_public_ip_on_launch = true
depends_on = [&amp;#34;aws_internet_gateway.cluster_gateway&amp;#34;]
availability_zone = &amp;#34;${element(keys(var.subnets), count.index)}&amp;#34;
}
// Create a route table allowing all addresses access to the IGW.
resource &amp;#34;aws_route_table&amp;#34; &amp;#34;public&amp;#34; {
vpc_id = &amp;#34;${aws_vpc.cluster.id}&amp;#34;
route {
cidr_block = &amp;#34;0.0.0.0/0&amp;#34;
gateway_id = &amp;#34;${aws_internet_gateway.cluster_gateway.id}&amp;#34;
}
}
// Now associate the route table with the public subnet - giving
// all public subnet instances access to the internet.
resource &amp;#34;aws_route_table_association&amp;#34; &amp;#34;public-subnet&amp;#34; {
count = &amp;#34;${length(var.subnets)}&amp;#34;
subnet_id = &amp;#34;${element(aws_subnet.public-subnet.*.id, count.index)}&amp;#34;
route_table_id = &amp;#34;${aws_route_table.public.id}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are a few things of interest here. First, we can easily build a variable number of subnets by using the &lt;code&gt;count&lt;/code&gt; field on the &lt;code&gt;aws_subnet&lt;/code&gt; resource:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_subnet&amp;#34; &amp;#34;public-subnet&amp;#34; {
count = &amp;#34;${length(var.subnets)}&amp;#34;
availability_zone = &amp;#34;${element(keys(var.subnets), count.index)}&amp;#34;
cidr_block = &amp;#34;${element(values(var.subnets), count.index)}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By using the &lt;a href="https://www.terraform.io/docs/configuration/interpolation.html"&gt;Terraform Interpolation Syntax&lt;/a&gt;, and in particular the &lt;code&gt;count&lt;/code&gt;, &lt;code&gt;keys&lt;/code&gt;, &lt;code&gt;values&lt;/code&gt; and &lt;code&gt;element&lt;/code&gt; functions, we can grab the subnet name and CIDR block from the variables.&lt;/p&gt;
&lt;h2 id="the-web-server-cluster"&gt;The Web Server Cluster&lt;/h2&gt;
&lt;p&gt;A cluster of web servers behind a load balancer are created by the module, to demonstrate that it works. There is little of interest in the script except for how the subnets are referenced:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_autoscaling_group&amp;#34; &amp;#34;cluster_node&amp;#34; {
name = &amp;#34;cluster_node&amp;#34;
vpc_zone_identifier = [&amp;#34;${aws_subnet.public-subnet.*.id}&amp;#34;]
launch_configuration = &amp;#34;${aws_launch_configuration.cluster_node.name}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that we can specify the entire list of subnet ids by using the &lt;code&gt;*&lt;/code&gt; symbol in the resource path - &lt;code&gt;[&amp;quot;${aws_subnet.public-subnet.*.id}&amp;quot;]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s It!&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s really all there is to it. I quite like this approach. I think it makes it very clear what is going on with the infrastructure, and is fairly manageable.&lt;/p&gt;
&lt;p&gt;One question which may be raised is why I am not using the &lt;a href="https://www.terraform.io/docs/configuration/interpolation.html#cidrsubnet-iprange-newbits-netnum-"&gt;&lt;code&gt;cidrsubnet&lt;/code&gt;&lt;/a&gt; function to automatically calculate the CIDR blocks for the subnets. The reason is purely one of preference - I prefer to explicitly specify the CIDR blocks and use various patterns to set conventions. For example, if I see an IP address such as &lt;code&gt;10.0.3.121&lt;/code&gt; then it is in the third AZ of my public subnet, or &lt;code&gt;10.2.2.11&lt;/code&gt; is in the second AZ of my locked down data zone.&lt;/p&gt;
&lt;p&gt;You can see a sample Terraform module which uses this pattern at: &lt;a href="https://github.com/dwmkerr/terraform-aws-vpc-example"&gt;github.com/dwmkerr/terraform-aws-vpc-example&lt;/a&gt;. This module also has a basic build pipeline and is published on the &lt;a href="https://registry.terraform.io/modules/dwmkerr/vpc-example"&gt;Terraform Registry&lt;/a&gt;. I&amp;rsquo;ll also be updating my &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;AWS Openshift&lt;/a&gt; module to use this pattern.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>A portable and magic-free way to open Pull Requests from the Command Line</title><link>https://dwmkerr.com/a-portable-and-magic-free-way-to-open-pull-requests-from-the-command-line/</link><pubDate>Wed, 10 Oct 2018 09:17:26 +0000</pubDate><guid>https://dwmkerr.com/a-portable-and-magic-free-way-to-open-pull-requests-from-the-command-line/</guid><description>&lt;p&gt;This little bash snippet will let you open a GitHub or GitLab pull request from the command line on most Unix-like systems (OSX, Ubuntu, etc), without using any magic libraries, ZSH tricks or other dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; download the &lt;a href="https://gist.github.com/dwmkerr/bae3fdca2d7208ec5d0008911d79b47d"&gt;&lt;code&gt;gpr.sh&lt;/code&gt;&lt;/a&gt; gist.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/gpr.png" alt="gpr"&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it looks in action OSX:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/gpr.gif" alt="gpr"&gt;&lt;/p&gt;
&lt;p&gt;And Ubuntu:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/gpr-ubuntu.gif" alt="gpr-ubuntu"&gt;&lt;/p&gt;
&lt;p&gt;The script is available as the &lt;a href="https://gist.github.com/dwmkerr/bae3fdca2d7208ec5d0008911d79b47d"&gt;&lt;code&gt;gpr.sh&lt;/code&gt;&lt;/a&gt; gist. You can also find it in my &lt;a href="https://github.com/dwmkerr/dotfiles"&gt;dotfiles&lt;/a&gt;, in the &lt;a href="https://github.com/dwmkerr/dotfiles/blob/master/profile/git.sh"&gt;git.sh&lt;/a&gt; file.&lt;/p&gt;
&lt;h2 id="the-script"&gt;The Script&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the script in its entirety:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Colour constants for nicer output.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GREEN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;\033[0;32m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RESET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;\033[0m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Push the current branch to origin, set upstream, open the PR page if possible.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpr&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Get the current branch name, or use &amp;#39;HEAD&amp;#39; if we cannot get it.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git symbolic-ref -q HEAD&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch##refs/heads/&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch&lt;span style="color:#66d9ef"&gt;:-&lt;/span&gt;HEAD&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Pushing take a little while, so let the user know we&amp;#39;re working.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Opening pull request for &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;GREEN&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;branch&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;RESET&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Push to origin, grabbing the output but then echoing it back.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; push_output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;git push origin -u &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;push_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If there&amp;#39;s anything which starts with http, it&amp;#39;s a good guess it&amp;#39;ll be a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# link to GitHub/GitLab/Whatever. So open it.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; link&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;push_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; | grep -o &lt;span style="color:#e6db74"&gt;&amp;#39;http.*&amp;#39;&lt;/span&gt; | sed -e &lt;span style="color:#e6db74"&gt;&amp;#39;s/[[:space:]]*$//&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Opening: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;GREEN&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;RESET&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; python -mwebbrowser &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="how-it-works"&gt;How It Works&lt;/h2&gt;
&lt;p&gt;Blow-by-blow, let&amp;rsquo;s take a look.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Colour constants for nicer output.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;GREEN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;\033[0;32m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RESET&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;\033[0m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make colouring console output easier, we create strings with the escape code required to set the &amp;lsquo;green&amp;rsquo; colour, and reset the text colour.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpr&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Get the current branch name, or use &amp;#39;HEAD&amp;#39; if we cannot get it.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git symbolic-ref -q HEAD&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch##refs/heads/&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; branch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch&lt;span style="color:#66d9ef"&gt;:-&lt;/span&gt;HEAD&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we define the &lt;code&gt;gpr&lt;/code&gt; (Git Pull Request) function. We&amp;rsquo;ll need to push the current branch, so we need to get the current branch name. There&amp;rsquo;s plenty of discussion on how this works on &lt;a href="https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git"&gt;Stack Overflow: How to get the current branch name in Git&lt;/a&gt;. Essentially we just get the symbolic name for the head of our current branch, which will be something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;refs/heads/my-new-branch
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We then use &lt;a href="https://www.tldp.org/LDP/abs/html/string-manipulation.html"&gt;Bash substring removal&lt;/a&gt; to rip out the &lt;code&gt;ref/heads/&lt;/code&gt; part. If we have no branch (for example, we are detached) we just use &lt;code&gt;HEAD&lt;/code&gt; a the branch name.&lt;/p&gt;
&lt;p&gt;Next we have this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Pushing take a little while, so let the user know we&amp;#39;re working.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Opening pull request for &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;GREEN&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;branch&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;RESET&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Push to origin, grabbing the output but then echoing it back.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; push_output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;git push origin -u &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;branch&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;push_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve previously defined some strings which include the escape codes to colour terminal output. Now we just show the user the branch we&amp;rsquo;re going to push, push it and then store all of the output in the &lt;code&gt;push_output&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; idiom is a common one. This simply makes sure we put all &lt;code&gt;stderr&lt;/code&gt; output (which is always file descriptor 2) into &lt;code&gt;stdout&lt;/code&gt; (which is always file descriptor 1). This means whether the program writes output to &lt;code&gt;stdout&lt;/code&gt; or &lt;code&gt;stderr&lt;/code&gt;, we capture it. There&amp;rsquo;s a nice write-up on this in the blog post &amp;lsquo;&lt;a href="https://www.brianstorti.com/understanding-shell-script-idiom-redirect/"&gt;Understanding Shell Script&amp;rsquo;s idiom: 2&amp;gt;&amp;amp;1
&lt;/a&gt;&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;The output from Git push will be dependent on the Git server being used. For GitHub it&amp;rsquo;ll look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;remote:
remote: Create a pull request for &amp;#39;feat/doc-cleanup&amp;#39; on GitHub by visiting:
remote: https://github.com/dwmkerr/dotfiles/pull/new/feat/doc-cleanup
remote:
To github.com:dwmkerr/dotfiles
* [new branch] feat/doc-cleanup -&amp;gt; feat/doc-cleanup
Branch feat/doc-cleanup set up to track remote branch feat/doc-cleanup from origin.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now all we want to do is see if there is any text which starts with &lt;code&gt;http&lt;/code&gt; and if there is, then open it. Here&amp;rsquo;s how we do that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If there&amp;#39;s anything which starts with http, it&amp;#39;s a good guess it&amp;#39;ll be a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# link to GitHub/GitLab/Whatever. So open it.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; link&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;push_output&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; | grep -o &lt;span style="color:#e6db74"&gt;&amp;#39;http.*&amp;#39;&lt;/span&gt; | sed -e &lt;span style="color:#e6db74"&gt;&amp;#39;s/[[:space:]]*$//&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Opening: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;GREEN&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}${&lt;/span&gt;RESET&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; python -mwebbrowser &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;link&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This uses &lt;code&gt;grep&lt;/code&gt; to rip out everything from &lt;code&gt;http&lt;/code&gt; onwards, and the &lt;code&gt;sed&lt;/code&gt; to remove any trailing whitespace. If we have found a link, we use &lt;code&gt;python&lt;/code&gt; to open it (which is a fairly safe cross-platform solution).&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! When you have a branch ready which you want to push and create a pull request from, just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gpr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the branch will be pushed to &lt;code&gt;origin&lt;/code&gt;, and if there is a Pull Request webpage, it&amp;rsquo;ll be opened.&lt;/p&gt;
&lt;h2 id="prior-art"&gt;Prior Art&lt;/h2&gt;
&lt;p&gt;My colleague Tobias recently shared a nice trick we worked out to open a GitLab merge request - which also now works for GitHub:&lt;/p&gt;
&lt;blockquote class="twitter-tweet" data-lang="en"&gt;&lt;p lang="en" dir="ltr"&gt;git push and directly open PR in Chrome - works for &lt;a href="https://twitter.com/github?ref_src=twsrc%5Etfw"&gt;@github&lt;/a&gt; &amp;amp; &lt;a href="https://twitter.com/gitlab?ref_src=twsrc%5Etfw"&gt;@gitlab&lt;/a&gt; 🚀&lt;br&gt;&lt;br&gt;Here is how to set it up 👉 &lt;a href="https://t.co/YfNTmdwTFt"&gt;https://t.co/YfNTmdwTFt&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/github?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#github&lt;/a&gt; &lt;a href="https://twitter.com/hashtag/gitlab?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#gitlab&lt;/a&gt; &lt;a href="https://t.co/ISE9kVZmw1"&gt;pic.twitter.com/ISE9kVZmw1&lt;/a&gt;&lt;/p&gt;&amp;mdash; Tobias Büschel (@TobiasBueschel) &lt;a href="https://twitter.com/TobiasBueschel/status/1042452158430502915?ref_src=twsrc%5Etfw"&gt;September 19, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;p&gt;I wanted to be able to use the same trick in Ubuntu and other Linux distros, but realised it relied on &lt;a href="https://github.com/robbyrussell/oh-my-zsh"&gt;oh-my-zsh&lt;/a&gt; and assumed OSX with Chrome as the browser, so tweaked it to the above. Thanks Tobi!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Manipulating Istio and other Custom Kubernetes Resources in Golang</title><link>https://dwmkerr.com/manipulating-istio-and-other-custom-kubernetes-resources-in-golang/</link><pubDate>Mon, 08 Oct 2018 21:34:02 +0000</pubDate><guid>https://dwmkerr.com/manipulating-istio-and-other-custom-kubernetes-resources-in-golang/</guid><description>&lt;p&gt;In this article I&amp;rsquo;ll demonstrate how to use Golang to manipulate Kubernetes Custom Resources, with Istio as an example. No knowledge of Istio is needed, I&amp;rsquo;ll just use it to demonstrate the concepts!&lt;/p&gt;
&lt;p&gt;&lt;img src="images/code-2.jpg" alt="code"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://istio.io"&gt;Istio&lt;/a&gt; is a highly popular Service Mesh platform which allows engineers to quickly add telemetry, advanced traffic management and more to their service-based applications.&lt;/p&gt;
&lt;p&gt;One interesting element of how Istio works is that when deployed into a Kubernetes cluster, many key configuration objects are handled as &lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/"&gt;Custom Resources&lt;/a&gt;. Custom Resources are a very powerful Kubernetes feature, which allow you to create your own &amp;lsquo;first class&amp;rsquo; resources (just like Pods, ReplicaSets, Deployments or whatever) and then interface with them using &lt;code&gt;kubectl&lt;/code&gt; or the Kubernetes APIs.&lt;/p&gt;
&lt;p&gt;In this article I&amp;rsquo;ll show you how to interface with these Custom Resources using the Golang Kubernetes client.&lt;/p&gt;
&lt;h2 id="crds-a-quick-overview"&gt;CRDs: A Quick Overview&lt;/h2&gt;
&lt;p&gt;When you set up Istio for your cluster, one common thing you will likely do is specify how you will route traffic. This can be quite sophisticated, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/TrafficManagementOverview.svg" alt="TrafficManagementOverview"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://istio.io/docs/concepts/traffic-management/"&gt;Figure 1: Istio Traffic Management Examples, from istio.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One way for a system like this to be configured would be to have a ConfigMap which contains the definition of how services are routed.&lt;/p&gt;
&lt;p&gt;However, Istio actually registers new types of resources (Custom Resource Definitions) which represent things like Gateways or Services. We can create/update/delete/manipulate them just like any other Kubernetes object.&lt;/p&gt;
&lt;p&gt;For example, I could create a virtual service for the example above with something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; EOF | kubectl create -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;apiVersion: networking.istio.io/v1alpha3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;kind: VirtualService
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;metadata:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; name: service2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;spec:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; hosts:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - &amp;#34;*&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; gateways:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - demo1-gateway
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; http:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - route:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - destination:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; host: service2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; subset: v1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; weight: 95
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; - destination:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; host: service2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; subset: v2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; weight: 5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, the important thing is not the specific content of this resource, more the fact that I can treat my Istio resources just like I would any other Kubernetes object:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ kubectl get virtualservices.networking.istio.io
NAME AGE
service2 93s
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ kubectl delete virtualservices.networking.istio.io/service2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can use &lt;code&gt;edit&lt;/code&gt;, &lt;code&gt;describe&lt;/code&gt;, register lifecycle events, watch for changes, and so on.&lt;/p&gt;
&lt;h2 id="working-with-crds-in-golang"&gt;Working with CRDs in Golang&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/kubernetes/client-go"&gt;Golang Kubernetes Client&lt;/a&gt; allows you to create strongly defined types which you can then use to interface with CRDs. An example is in the Red Hat blog post &lt;a href="https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/"&gt;Kubernetes Deep Dive: Code Generation for Custom Resources&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is an excellent approach, but can feel pretty heavy if you want to quickly access some data, and don&amp;rsquo;t want to have to generate a lot of code.&lt;/p&gt;
&lt;p&gt;There is an alternative, which is to use the &lt;a href="https://github.com/kubernetes/client-go/blob/master/dynamic/interface.go"&gt;&lt;code&gt;DynamicClient&lt;/code&gt;&lt;/a&gt;. The &lt;em&gt;preferred&lt;/em&gt; approach seems to be the first, which involves code generation, so little documentation exists for the second approach. However, it is actually very simple.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example of how you can list all Istio &lt;code&gt;VirtualService&lt;/code&gt; resources, without having to generate any code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;metav1&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/apimachinery/pkg/apis/meta/v1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/client-go/dynamic&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a Dynamic Client to interface with CRDs.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;dynamicClient&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dynamic&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewForConfig&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a GVR which represents an Istio Virtual Service.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;virtualServiceGVR&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;schema&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GroupVersionResource&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Group&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;networking.istio.io&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;v1alpha3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Resource&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;virtualservices&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// List all of the Virtual Services.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;virtualServices&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dynamicClient&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Resource&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;virtualServiceGVR&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;Namespace&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;List&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;metav1&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ListOptions&lt;/span&gt;{})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;virtualService&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;virtualServices&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Items&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;VirtualService: %s\n&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;virtualService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GetName&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This snippet omits setup and error-handling for clarity, the full example is in the &lt;a href="https://gist.github.com/dwmkerr/09ac0fd98595460456e17d5ef0c77667"&gt;k8s-list-virtualservices.go&lt;/a&gt; gist.&lt;/p&gt;
&lt;h2 id="patching-crds-in-golang"&gt;Patching CRDs in Golang&lt;/h2&gt;
&lt;p&gt;You may have noticed that the &lt;code&gt;.Resource().Namespace().List()&lt;/code&gt; code looks very similar to the structure for making API calls when using the Kubernetes &lt;code&gt;Clientset&lt;/code&gt;. In fact, it is essentially the same. Looking at &lt;a href="https://github.com/kubernetes/client-go/blob/master/dynamic/interface.go"&gt;the interface&lt;/a&gt;, you can see you have all of the operations you&amp;rsquo;d expect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Create&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Delete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Get&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And so on. This is nice because you can use the same trick in my article &amp;lsquo;&lt;a href="https://www.dwmkerr.com/patching-kubernetes-resources-in-golang/"&gt;Patching Kubernetes Resources in Golang&lt;/a&gt;&amp;rsquo; to manipulate these entities, without ever having to create a structure to represent it.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s another abbreviated example, this time showing how we can adjust the weight of the routing from the services to 50%/50%:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;metav1&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/apimachinery/pkg/apis/meta/v1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/client-go/dynamic&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a GVR which represents an Istio Virtual Service.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;virtualServiceGVR&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;schema&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;GroupVersionResource&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Group&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;networking.istio.io&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Version&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;v1alpha3&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Resource&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;virtualservices&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Weight the two routes - 50/50.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; make([]&lt;span style="color:#a6e22e"&gt;PatchUInt32Value&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Op&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Path&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;/spec/http/0/route/0/weight&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Op&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Path&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;/spec/http/0/route/1/weight&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;patchBytes&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Marshal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;patchPayload&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Apply the patch to the &amp;#39;service2&amp;#39; service.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dynamicClient&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Resource&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;virtualServiceGVR&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;Namespace&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;Patch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;service2&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;types&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;JSONPatchType&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;patchBytes&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the full example in the gist &lt;a href="https://gist.github.com/dwmkerr/7332888e092156ce8ce4ea551b0c321f"&gt;k8s-patch-virtualservice.go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After running the sample, you can use the Kubernetes CLI to verify the changes:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ kubectl get virtualservices.networking.istio.io/service2 -o yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
clusterName: &amp;#34;&amp;#34;
creationTimestamp: 2018-10-08T09:53:16Z
generation: 0
name: service2
namespace: default
resourceVersion: &amp;#34;487435&amp;#34;
selfLink: /apis/networking.istio.io/v1alpha3/namespaces/default/virtualservices/service2
uid: fac5930c-cadf-11e8-90a2-42010a94005b
spec:
gateways:
- demo1-gateway
hosts:
- &amp;#39;*&amp;#39;
http:
- route:
- destination:
host: service2
subset: v1
weight: 50
- destination:
host: service2
subset: v2
weight: 50
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="keep-it-simple"&gt;Keep It Simple!&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s it! This trick made something I was working on a &lt;em&gt;lot&lt;/em&gt; easier, but it took a little bit of experimentation to get right. I hope you find the approach useful. Please share any thoughts/questions in the comments.&lt;/p&gt;
&lt;h2 id="further-reading"&gt;Further Reading&lt;/h2&gt;
&lt;p&gt;The following articles were using in working out this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/"&gt;Red Hat: Deep Dive: Code Generation for Custom Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/"&gt;Kubernetes Docs: Custom Resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><category>CodeProject</category></item><item><title>Procedural Smiles - Animating SVG with pure JavaScript</title><link>https://dwmkerr.com/procedural-smiles-animating-svg-with-pure-javascript/</link><pubDate>Sun, 29 Jul 2018 23:36:46 +0000</pubDate><guid>https://dwmkerr.com/procedural-smiles-animating-svg-with-pure-javascript/</guid><description>&lt;p&gt;I recently needed to be able to generate a simple face image, with the face being able to scale from happy to sad.&lt;/p&gt;
&lt;p&gt;(&lt;em&gt;Why&lt;/em&gt; I needed to do this is a long story!)&lt;/p&gt;
&lt;p&gt;This gave me the opportunity to have a play with SVG, which is something I&amp;rsquo;ve not done in a while and always wished I could spend more time with. You can see the result below, move the slider to see the smile animate:&lt;/p&gt;
&lt;p data-height="265" data-theme-id="0" data-slug-hash="ejejeX" data-default-tab="result" data-user="dwmkerr" data-pen-title="SVG Smile" class="codepen"&gt;See the Pen &lt;a href="https://codepen.io/dwmkerr/pen/ejejeX/"&gt;SVG Smile&lt;/a&gt; by Dave Kerr (&lt;a href="https://codepen.io/dwmkerr"&gt;@dwmkerr&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;
&lt;script async src="https://static.codepen.io/assets/embed/ei.js"&gt;&lt;/script&gt;
&lt;p&gt;Source: &lt;a href="https://github.com/dwmkerr/svg-smile"&gt;github.com/dwmkerr/svg-smile/&lt;/a&gt;
CodePen: &lt;a href="https://codepen.io/dwmkerr/pen/ejejeX"&gt;codepen.io/dwmkerr/pen/ejejeX&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="how-it-works---geometry"&gt;How it works - geometry&lt;/h3&gt;
&lt;p&gt;This is quite a simple effect to achieve, the trick is just to work out how the geometry of the smile will work:&lt;/p&gt;
&lt;img src="images/points.jpg" alt="Smile Geometry" /&gt;
&lt;p&gt;The black points are the start and end point of the smile, the red points are the control points for the &lt;a href="%5E1"&gt;bezier curve&lt;/a&gt;. This means that we can scale from a smile to a frown by just interpolating the position of the anchor and control points from the two extremes shown above.&lt;/p&gt;
&lt;p&gt;The face itself (without styling) just looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;svg&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;viewbox&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0 0 120 120&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;g&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;transform&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;translate(60 60)&amp;#39;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- First the main circle for the face. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;circle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cx&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cy&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;50&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- Then the left eye... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;circle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cx&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-20&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cy&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-10&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- Then the right... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;circle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cx&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;20&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cy&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-10&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;5&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;!-- The smile bezier curve. --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;g&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;transform&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;translate(0, 25)&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;d&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;M-20,-10 C-20,10 20,10 20,-10&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;g&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;g&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;svg&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The trick here is really just to use whatever coordinate system works for you. I start by defining a viewbox that gives me some space, translate the origin and then put the main circle of the face slap bang in the middle at &lt;code&gt;(0, 0)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The code to interpolate the smile control points is easier again if we shift the origin of the smile as well. This technique works well for SVGs (or any computer graphics), manipulate and transform to get the coordinate system to work for you and make it easier to reason about what is going on.&lt;/p&gt;
&lt;h3 id="how-it-works---animation"&gt;How it works - animation&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve not animated SVG before. When looking into doing this, the vast majority of tips, blogs, articles and so on were suggesting to use a libary (common suggestions were &lt;a href="https://maxwellito.github.io/vivus/"&gt;vivus&lt;/a&gt;, &lt;a href="http://snapsvg.io/"&gt;snap.svg&lt;/a&gt; and &lt;a href="http://svgjs.com/"&gt;svg.js&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve got no doubt that when you know what you are doing with SVG, using a library is a huge accelerator and saves on boilerplate. But if you don&amp;rsquo;t know what a library is doing, what it is wrapping, or the problems it is solving for you, you are likely missing out some fundamentals.&lt;/p&gt;
&lt;p&gt;Using a library is great if you know &lt;em&gt;what the problem is you are solving&lt;/em&gt;. But if you don&amp;rsquo;t, you end up never really learning. I had no idea whether this would be challenging to do with the pure SVG APIs and definitely wanted to work by hand.&lt;/p&gt;
&lt;p&gt;After some experimentation, I was able to write the markup which would move the smile to a frown:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;g&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;transform&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;translate(0, 25)&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;path&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;smilepath&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;d&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;M-20,-10 C-20,10 20,10 20,-10&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#f92672"&gt;animate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;attributeName&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;d&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;attributeType&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;XML&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;M-20,10 C-20,-10 20,-10 20,10&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dur&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;3s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repeatCount&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;indefinite&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fill&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;freeze&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#f92672"&gt;path&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;g&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The geometry we&amp;rsquo;ve already seen, all we&amp;rsquo;ve done here is swap the position of each anchor and its associated control point. The trick is just making sure that we get the attributes of the &lt;code&gt;animate&lt;/code&gt; element right.&lt;/p&gt;
&lt;p&gt;Once this is done, the final step is just to make it all programmatic. The code to generate the geometry of the path, based on a scale from 0 (sad) to 1 (happy) is online, but the interesting thing is how to run the animation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// note that &amp;#39;scale&amp;#39; is 0-&amp;gt;1 (sad-&amp;gt;happy)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;points&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;writeSmilePoints&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;smilePoints&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scale&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;svg&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;getElementById&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;svg&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;smilePath&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;getElementById&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;smilepath&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;createElementNS&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;svg&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;namespaceURI&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;animate&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;attributeName&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;d&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;attributeType&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;XML&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;to&amp;#39;&lt;/span&gt;,&lt;span style="color:#a6e22e"&gt;points&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;dur&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;0.3s&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;repeatCount&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setAttribute&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;fill&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;freeze&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;smilePath&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;appendChild&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;animate&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;beginElement&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s not much to it. The bulk of the code is just setting up the attributes for the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate"&gt;&lt;code&gt;animate&lt;/code&gt;&lt;/a&gt; tag. Then we add it to the path as a child and call &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SVGAnimationElement"&gt;&lt;code&gt;beginElement&lt;/code&gt;&lt;/a&gt; to start the animation.&lt;/p&gt;
&lt;p&gt;The face is coloured in a similar way. Interpolating between a happy Simpsons yellow and angry red in JavaScript, then setting an &lt;code&gt;animate&lt;/code&gt; element to target the &lt;code&gt;fill&lt;/code&gt; of the appropriate circle.&lt;/p&gt;
&lt;h3 id="wrapping-up"&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;Playing with graphics is fun! This is only the most basic scratching of the surface of what SVG can do. The JavaScript to animate is trivial (although I can appreciate that browser inconsistencies and so on mean a libary is probably useful at some point).&lt;/p&gt;
&lt;p&gt;The code is available on GitHub at &lt;a href="https://github.com/dwmkerr/svg-smile"&gt;github.com/dwmkerr/svg-smile&lt;/a&gt; or on CodePen:&lt;/p&gt;
&lt;p data-height="265" data-theme-id="0" data-slug-hash="ejejeX" data-default-tab="js,result" data-user="dwmkerr" data-pen-title="SVG Smile" class="codepen"&gt;See the Pen &lt;a href="https://codepen.io/dwmkerr/pen/ejejeX/"&gt;SVG Smile&lt;/a&gt; by Dave Kerr (&lt;a href="https://codepen.io/dwmkerr"&gt;@dwmkerr&lt;/a&gt;) on &lt;a href="https://codepen.io"&gt;CodePen&lt;/a&gt;.&lt;/p&gt;
&lt;script async src="https://static.codepen.io/assets/embed/ei.js"&gt;&lt;/script&gt;</description><category>CodeProject</category></item><item><title>Patching Kubernetes Resources in Golang</title><link>https://dwmkerr.com/patching-kubernetes-resources-in-golang/</link><pubDate>Tue, 24 Jul 2018 06:33:17 +0000</pubDate><guid>https://dwmkerr.com/patching-kubernetes-resources-in-golang/</guid><description>&lt;p&gt;Recently I needed to be able to quickly adjust the number of replicas in a Kubernetes Replication Controller. The original solution I&amp;rsquo;d seen pulled down the spec, modified it, then updated it. There&amp;rsquo;s a better way!&lt;/p&gt;
&lt;p&gt;&lt;img src="images/patch-1.jpg" alt="Kuberentes Patch API"&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a &lt;a href="https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/"&gt;patch API for Kubernetes resources&lt;/a&gt;. Patching resources is faster and easier than pulling them and updating the spec wholesale. However, the documentation is a little limited.&lt;/p&gt;
&lt;p&gt;After some trial and error I got it working, here&amp;rsquo;s the solution. I thought it might be helpful to share for others!&lt;/p&gt;
&lt;h3 id="the-solution"&gt;The Solution&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ll start with the solution. If this is all you need, you are good to go. The details of how this works are presented afterwards. In this example I&amp;rsquo;ll update the number of replicas in the &lt;code&gt;my-rc&lt;/code&gt; controller:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;encoding/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;types&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/apimachinery/pkg/types&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/client-go/kubernetes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/client-go/plugin/pkg/client/auth&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;k8s.io/client-go/tools/clientcmd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Leave blank for the default context in your kube config.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;context&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Name of the replication controller to scale, and the desired number of replicas.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;replicationControllerName&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;my-rc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt; = uint32(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// patchStringValue specifies a patch operation for a string.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;patchStringValue&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Op&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;op&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Path&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;path&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;value&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// patchStringValue specifies a patch operation for a uint32.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;patchUInt32Value&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Op&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;op&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Path&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;path&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint32&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`json:&amp;#34;value&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scaleReplicationController&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientSet&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Clientset&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;replicasetName&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;scale&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint32&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; []&lt;span style="color:#a6e22e"&gt;patchUInt32Value&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Op&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Path&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/spec/replicas&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;scale&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;payloadBytes&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;json&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Marshal&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;clientSet&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;CoreV1&lt;/span&gt;().
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ReplicationControllers&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;default&amp;#34;&lt;/span&gt;).
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Patch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;replicasetName&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;types&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;JSONPatchType&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;payloadBytes&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get the local kube config.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Connecting to Kubernetes Context %v\n&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;context&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;clientcmd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewNonInteractiveDeferredLoadingClientConfig&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientcmd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewDefaultClientConfigLoadingRules&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;clientcmd&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ConfigOverrides&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;CurrentContext&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;context&lt;/span&gt;}).&lt;span style="color:#a6e22e"&gt;ClientConfig&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Creates the clientset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;clientset&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewForConfig&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Scale our replication controller.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Scaling replication controller %v to %v\n&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;replicationControllerName&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;scaleReplicationController&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;clientset&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;replicationControllerName&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;replicas&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; panic(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code is also available in the &lt;a href="https://gist.github.com/dwmkerr/447692c8bba28929ef914239781c4e59"&gt;k8s-patch.go&lt;/a&gt; gist.&lt;/p&gt;
&lt;h3 id="the-mechanism"&gt;The Mechanism&lt;/h3&gt;
&lt;p&gt;The Kubernetes Patch API supports a few different methods for modifying resources. It is important to be aware that there is not a universally accepted &amp;lsquo;standard&amp;rsquo; approach to representing a &lt;em&gt;change&lt;/em&gt; to a resource in a REST API.&lt;/p&gt;
&lt;p&gt;There are three strategies you can use to patch:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;merge&lt;/code&gt;: follows the &lt;a href="https://tools.ietf.org/html/rfc7386"&gt;JSON Merge Patch Spec (RFC 7386)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stragetic&lt;/code&gt;: A strategic merge, which addresses some limitations of the merge patch (noted in &lt;a href="%5Bdocs/devel/api-conventions.md#patch-operations%5D(https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/devel/api-conventions.md#patch-operations)"&gt;this doc&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;json&lt;/code&gt;: follows the &lt;a href="https://tools.ietf.org/html/rfc6902"&gt;JSON Patch Spec (RFC 6902)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are documented in detail at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/devel/api-conventions.md#patch-operations"&gt;docs/devel/api-conventions.md#patch-operations&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The mechanism I&amp;rsquo;ve used here is &lt;code&gt;json&lt;/code&gt;, which I think is the clearest to the reader. To use this strategy we need to build a payload describing what we are changing. This might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;op&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/spec/replicas&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;value&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;op&lt;/code&gt; field can be &lt;code&gt;remove&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt; etc etc (all the details are in the &lt;a href="https://tools.ietf.org/html/rfc6902"&gt;RFC 6902)&lt;/a&gt;, or the slightly more readable &lt;a href="jsonpatch.com"&gt;jsonpatch.com&lt;/a&gt;). This allows the operation to be very &lt;em&gt;explicit&lt;/em&gt; to the reader, which is helpful. We create a struct which represents an operation on a string or integer (or whatever data type we need), serialize it and pass to the API.&lt;/p&gt;
&lt;p&gt;Under the hood, the Golang client will simply translate this into an HTTP call which will look like something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;PATCH /api/v1/namespaces/default/replicationcontrollers/app-server-blue HTTP/1.1
Host: 127.0.0.1
Content-Type: application/json-patch+json
Content-Length: 70
[{
&amp;#34;op&amp;#34;: &amp;#34;replace&amp;#34;,
&amp;#34;path&amp;#34;: &amp;#34;/spec/replicas&amp;#34;,
&amp;#34;value&amp;#34;: 4
}]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This corresponds to the documentation on the &lt;a href="https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/devel/api-conventions.md#patch-operations"&gt;Patch Operations&lt;/a&gt;. Note that the patch operation type is specified in the &lt;code&gt;Content-Type&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;Hopefully this&amp;rsquo;ll help you if you need to patch resources, are struggling with the docs and are a Go noob like me! Any tips on how to make the code cleaner or more idomatic would be welcome.&lt;/p&gt;
&lt;p&gt;Thanks to the following articles and issues which helped me unpick this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/43415728/kubernetes-go-client-patch-example"&gt;Stack Overflow: Kubernetes Go Client Patch Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/"&gt;Kubernetes Docs: Update API Objects in Place Using kubectl patch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kubernetes/kubernetes/blob/release-1.1/docs/devel/api-conventions.md#patch-operations"&gt;Kubernetes Docs: Patch Operations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><category>CodeProject</category></item><item><title>mongo-monitor - a simple CLI to monitor your MongoDB cluster</title><link>https://dwmkerr.com/mongo-monitor-cli/</link><pubDate>Wed, 16 May 2018 20:09:53 +0000</pubDate><guid>https://dwmkerr.com/mongo-monitor-cli/</guid><description>&lt;p&gt;The &lt;code&gt;mongo-monitor&lt;/code&gt; CLI is a lean and simple tool to check the status of a MongoDB server or cluster. The code is on GitHub:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/mongo-monitor"&gt;github.com/dwmkerr/mongo-monitor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it looks in action:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/overview.gif" alt="Screenshot: Using the mongo-monitor CLI to monitor a sharded cluster"&gt;&lt;/p&gt;
&lt;p&gt;In this animation I am monitoring a simple sharded cluster, and running some example maintenance operations, adding a node to a replicaset, stepping down a primary and shutting down a replicaset node.&lt;/p&gt;
&lt;p&gt;A simple CLI which shows the status in real-time can be very useful to keep open when performing admin, letting you see how your changes affect the cluster as you work on it.&lt;/p&gt;
&lt;h2 id="installing-the-cli"&gt;Installing the CLI&lt;/h2&gt;
&lt;p&gt;The CLI is installed with &lt;code&gt;npm&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -g mongo-monitor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="connecting-to-a-cluster"&gt;Connecting to a Cluster&lt;/h2&gt;
&lt;p&gt;Connect to a cluster by providing a connection string. The tool uses &lt;a href="https://github.com/dwmkerr/mongo-connection-string"&gt;&lt;code&gt;mongo-connection-string&lt;/code&gt;&lt;/a&gt; to parse the connection string, so you can be flexible with the input:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Connect to a local instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mongo-monitor localhost:27107
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Connect to a remote replicaset, authenticated&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mongo-monitor admin:P@sswrd@mdbnode1,mdbnode2,mdbnode3?replicaSet&lt;span style="color:#f92672"&gt;=&lt;/span&gt;rs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once a connection is established, the tool will periodically check the status of the cluster. If the cluster is sharded, it will also inspect each individual replicaset.&lt;/p&gt;
&lt;h2 id="replicaset-status"&gt;Replicaset Status&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the kind of output you might get from a replicaset:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/replicaset.jpg" alt="Screenshot: Replicaset Status"&gt;&lt;/p&gt;
&lt;p&gt;The name of the replicaset is shown, along with each member. The status of each member is also shown, updating automatically every second.&lt;/p&gt;
&lt;p&gt;This is convenient when administering replicasets, stepping down a master, adding or removing nodes and so on.&lt;/p&gt;
&lt;h2 id="sharded-cluster-status"&gt;Sharded Cluster Status&lt;/h2&gt;
&lt;p&gt;When connecting to a sharded cluster, you will get output like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/sharded-cluster.jpg" alt="Screenshot: Sharded Cluster Status"&gt;&lt;/p&gt;
&lt;p&gt;Each shard is shown, along with the details of the replicaset which make it up.&lt;/p&gt;
&lt;p&gt;Keeping a view like this open is useful when administering sharded clusters, adding or removing shards, desharding, updating the replicasets which make up shards and so on.&lt;/p&gt;
&lt;h2 id="get-involved"&gt;Get Involved!&lt;/h2&gt;
&lt;p&gt;If you like the tool, check out the code and feel free to make pull requests with additions! There are a few &lt;a href="https://github.com/dwmkerr/mongo-monitor/issues"&gt;issues&lt;/a&gt; on the project already, and there are all sorts of features I&amp;rsquo;d love to add but haven&amp;rsquo;t had the time, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Being able to see the lag for replicaset members, to see if secondaries are falling behind&lt;/li&gt;
&lt;li&gt;Being able to perform replicaset operations directly from the tool&lt;/li&gt;
&lt;li&gt;Showing the priorities of nodes if they are not the default&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All ideas are welcome, let me know in the comments or repo, and share the tool if you find it useful!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>The Death of Microservice Madness in 2018</title><link>https://dwmkerr.com/the-death-of-microservice-madness-in-2018/</link><pubDate>Fri, 12 Jan 2018 10:52:25 +0000</pubDate><guid>https://dwmkerr.com/the-death-of-microservice-madness-in-2018/</guid><description>&lt;p&gt;&lt;a href="https://www.campusmvp.es/recursos/post/la-muerte-de-la-locura-de-los-microservicios-en-2018.aspx"&gt;En Español&lt;/a&gt; | &lt;a href="https://www.reddit.com/r/programming/comments/7pxriw/the_death_of_microservice_madness_in_2018/"&gt;Reddit Thread&lt;/a&gt; | &lt;a href="https://news.ycombinator.com/item?id=16200007"&gt;Hacker News Thread&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Microservices became a very popular topic over the last couple of years&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. &amp;lsquo;Microservice madness&amp;rsquo; goes something like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Netflix are great at devops.
Netflix do microservices.
Therefore: If I do microservices, I am great at devops.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are many cases where great efforts have been made to adopt microservice patterns without necessarily understanding how the costs and benefits will apply to the specifics of the problem at hand.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to describe in detail what microservices are, why the pattern is so appealing, and also some of the key challenges that they present.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll finish with a set of simple questions might be valuable to ask yourself when you are considering whether microservices are the right pattern &lt;em&gt;for you&lt;/em&gt;. The questions are at the end of the article.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/letterbox.png" alt="Letterbox sample of diagram"&gt;&lt;/p&gt;
&lt;h2 id="what-are-microservices-and-why-are-they-so-popular"&gt;What are microservices, and why are they so popular?&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with the basics. Here is how a hypothetical video sharing platform might be implemented, first in the form of a monolith (single large unit) and then in the form of microservices:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/video-platform-monolith-microservices.png" alt="Diagram: Comparison of a Video Sharing Platform, Monolith vs Microservice"&gt;&lt;/p&gt;
&lt;p&gt;The difference between the two systems is that the first is a single large unit; a monolith. The second is a set of small, specific services. Each service has a specific role.&lt;/p&gt;
&lt;p&gt;When the diagram is drawn &lt;em&gt;at this level of detail&lt;/em&gt;, it is easy to see the appeal. There are a whole host of potential benefits:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Independent Development&lt;/strong&gt;: Small, independent components can be built by small, independent teams. A group can work on a change to the &amp;lsquo;Upload&amp;rsquo; service without interfering with the &amp;lsquo;Transcode&amp;rsquo; service, or even knowing about it. The amount of time to learn about a component is greatly reduced, and it is easier to develop new features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Independent Deployment&lt;/strong&gt;: Each individual component can be deployed independently. This allows new features to be released with greater velocity and less risk. Fixes or features for the &amp;lsquo;Streaming&amp;rsquo; component can be deployed without requiring other components to be deployed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Independent Scalability&lt;/strong&gt;: Each component can be scaled independently of each other. During busy periods when new shows are released, the &amp;lsquo;Download&amp;rsquo; component can be scaled up to handle the increased load, without having to scale up every component, which makes elastic scaling more feasible and reduces costs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: Components fulfil a small, specific function. This means that they can more easily be adapted for use in other systems, services or products. The &amp;lsquo;Transcode&amp;rsquo; component could be used by other business units, or even turned into a new business, perhaps offering transcoding services for other groups.&lt;/p&gt;
&lt;p&gt;At this level of detail, the benefits of a microservice model over a monolithic model seem obvious. So if that&amp;rsquo;s the case - why is this pattern only recently in vogue? Where has it been all my life?&lt;/p&gt;
&lt;h2 id="if-this-is-so-great-why-hasnt-it-been-done-before"&gt;If this is so great, why hasn&amp;rsquo;t it been done before?&lt;/h2&gt;
&lt;p&gt;There are two answers to this question. One is that &lt;em&gt;it has&lt;/em&gt; - to the best of our technical capabilities, and the other is that more recent technical advances have allowed us to take it to a new level.&lt;/p&gt;
&lt;p&gt;When I started writing the answer to this question, it turned into a &lt;em&gt;long&lt;/em&gt; description, so I&amp;rsquo;m actually going to separate it into another article and publish it a little later&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. At this stage, I will skip the journey from single program to many programs, ignore ESBs and Service Orientated Architecture, component design and bounded contexts, and so on.&lt;/p&gt;
&lt;p&gt;Those who are interested can read more about the journey separately. Instead I&amp;rsquo;ll say that in many ways we&amp;rsquo;ve been doing this for a while, but with the recent explosion in popularity of container technology (Docker in particular) and in orchestration technology (such as Kubernetes, Mesos, Consul and so on) this pattern has become much more viable to implement from a technical standpoint.&lt;/p&gt;
&lt;p&gt;So if we take it as a given that we &lt;em&gt;can&lt;/em&gt; implement a microservice arrangement, we need to think carefully about the &lt;em&gt;should&lt;/em&gt;. We&amp;rsquo;ve seen the high-level theoretical benefits, but what about the challenges?&lt;/p&gt;
&lt;h2 id="whats-the-problem-with-microservices"&gt;What&amp;rsquo;s the problem with microservices?&lt;/h2&gt;
&lt;p&gt;If microservices are so great, what&amp;rsquo;s the big deal? Here are some of the biggest issues I&amp;rsquo;ve seen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increased complexity for developers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Things &lt;em&gt;can&lt;/em&gt; get a lot harder for developers. In the case where a developer wants to work on a &lt;em&gt;journey&lt;/em&gt;, or feature which might span many services, that developer has to run them all on their machine, or connect to them. This is often more complex than simply running a single program.&lt;/p&gt;
&lt;p&gt;This challenge can be partially mitigated with tooling&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;, but as the number of services which makes up a system increases, the more challenges developers will face when running the system as a whole.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increased complexity for operators&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For teams who don&amp;rsquo;t develop services, but maintain them, there is an explosion in potential complexity. Instead of perhaps managing a few running services, they are managing dozens, hundreds or thousands of running services. There are more services, more communication paths, and more areas of potential failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Increased complexity for devops&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Reading the two points above, it may grate that operations and development are treated separately, especially given the popularity of devops as a practice (which I am a big proponent of). Doesn&amp;rsquo;t devops mitigate this?&lt;/p&gt;
&lt;p&gt;The challenge is that many organisations still run with separated development and operations teams - and a organisation that does is much more likely to struggle with adoption of microservices.&lt;/p&gt;
&lt;p&gt;For organisations which have adopted devops, it&amp;rsquo;s still hard. Being both a developer and an operator is already tough (but critical to build good software), but having to also understand the nuances of container orchestration systems, particularly systems which are evolving at a rapid pace, is very hard. Which brings me onto the next point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It requires serious expertise&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When done by experts, the results can be wonderful. But imagine an organisation where perhaps things are not running smoothly with a single monolithic system. What possible reason would there be that things would be any better by increasing the number of systems, which increases the operational complexity?&lt;/p&gt;
&lt;p&gt;Yes, with effective automation, monitoring, orchestration and so on, this is all possible. But the challenge is rarely the technology - the challenge is finding people who can use it effectively. These skillsets are currently in very high demand, and may be difficult to find.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real world systems often have poorly defined boundaries&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In all of the examples we used to describe the benefits of microservices, we spoke about &lt;em&gt;independent&lt;/em&gt; components. However in many cases components are simply not independent. On paper, certain domains may look bounded, but as you get into the muddy details, you may find that they are more challenging to model than you anticipated.&lt;/p&gt;
&lt;p&gt;This is where things can get &lt;em&gt;extremely&lt;/em&gt; complex. If your boundaries are actually not well defined, then what happens is that even though &lt;em&gt;theoretically&lt;/em&gt; services can be deployed in isolation, you find that due to the inter-dependencies between services, you have to deploy &lt;em&gt;sets&lt;/em&gt; of services as a group.&lt;/p&gt;
&lt;p&gt;This then means that you need to manage coherent versions of services which are proven and tested when working together, you don&amp;rsquo;t actually have an independently deployable system, because to deploy a new feature, you need to carefully orchestrate the simultaneous deployment of many services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The complexities of state are often ignored&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the previous example, I mentioned that a feature deployment may require the simultaneous rollout of many versions of many services in tandem. It is tempting to assume that sensible deployment techniques will mitigate this, for example blue/green deployments (which most service orchestration platforms handle with little effort), or multiple versions of a service being run in parallel, with consuming channels deciding which version to use.&lt;/p&gt;
&lt;p&gt;These techniques mitigate a large number of the challenges &lt;em&gt;if the services are stateless&lt;/em&gt;. But stateless services are quite frankly, easy to deal with. In fact, if you have stateless services, then I&amp;rsquo;d be inclined to consider skipping microservices altogether and consider using a serverless model.&lt;/p&gt;
&lt;p&gt;In reality, many services require state. An example from our video sharing platform might be the subscription service. A new version of the subscriptions service may store data in the subscriptions database in a different shape. If you are running both services in parallel, you are running the system with two schemas at once. If you do a blue green deployment, and other services depend on data in the new shape, then they must be updated &lt;em&gt;at the same time&lt;/em&gt;, and if the subscription service deployment fails and rolls back, they might need to roll back too, with cascading consequences.&lt;/p&gt;
&lt;p&gt;Again, it might be tempting to think that with NoSQL databases these issues of schema go away, but they don&amp;rsquo;t. Databases which don&amp;rsquo;t enforce schema do not lead to schemaless systems - they just mean that schema tends to be managed at the application level, rather than the database level. The fundamental challenge of understanding the shape of your data, and how it evolves, cannot be eliminated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The complexitities of communication are often ignored&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As you build a large network of services which depend on each other, the liklihood is that there will be a lot of inter-service communication. This leads to a few challenges. Firstly, there are a lot more points at which things can fail. We must expect that network calls will fail, which means when one service calls another, it should expect to have to retry a number of times at the least. Now when a service has to potentially call many services, we end up in a complicated situation.&lt;/p&gt;
&lt;p&gt;Imagine a user uploads a video in the video sharing service. We might need to run the upload service, pass data to the transcode service, update subscriptions, update recommendations and so on. All of these calls require a degree of orchestration, if things fail we need to retry.&lt;/p&gt;
&lt;p&gt;This retry logic can get hard to manage. Trying to do things synchronously often ends up being untenable, there are too many points of failure. In this case, a more reliable solution is to use asynchronous patterns to handle communication. The challenge here is that asynchronous patterns inherently make a system stateful. As mentioned in the previous point, stateful systems and systems with distributed state are very hard to handle.&lt;/p&gt;
&lt;p&gt;When a microservice system uses message queues for intra-service communication, you essentially have a large database (the message queue or broker) glueing the services together. Again, although it might not seem like a challenge at first, schema will come back to bite you. A service at version X might write a message with a certain format, services which depend on this message will also need to be updated when the sending service changes the details of the message it sends.&lt;/p&gt;
&lt;p&gt;It is possible to have services which can handle messages in many different formats, but this is hard to manage. Now when deploying new versions of services, you will have times where two different versions of a service may be trying to process messages from the same queue, perhaps even messages sent by different versions of a sending service. This can lead to complicated edge cases. To avoid these edge cases, it may be easier to only allow certain versions of messages to exist, meaning that you need to deploy a set of versions of a set of services as a coherent whole, ensuring messages of older versions are drained appropriately first.&lt;/p&gt;
&lt;p&gt;This highlights again that the idea of independent deployments may not hold as expected when you get into the details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Versioning can be hard&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To mitigate the challenges mentioned previously, versioning needs to be very carefully managed. Again, there can be a tendency to assume that following a standard such as semver[4] will solve the problem. It doesn&amp;rsquo;t. Semver is a sensible convention to use, but you will still have to track the versions of services and APIs which can work together.&lt;/p&gt;
&lt;p&gt;This can get very challenging very quickly, and may get to the point where you don&amp;rsquo;t know which versions of services will actually work properly together.&lt;/p&gt;
&lt;p&gt;Managing dependencies in software systems is notoriously hard, whether it is node modules, Java modules, C libraries or whatever. The challenges of &lt;em&gt;conflicts between independent components&lt;/em&gt; when consumed by a single entity are very hard to deal with.&lt;/p&gt;
&lt;p&gt;These challenges are hard to deal with when the dependencies are static, and can be patched, updated, edited and so on, but if the dependencies are themselves &lt;em&gt;live services&lt;/em&gt;, then you may not be able to just update them - you may have to run many versions (with the challenges already described) or bring down the system until it is fixed holistically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Distributed Transactions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In situations where you need transaction integrity across an operation, microservices can be very painful. Distributed state is hard to deal with, many small units which can fail make orchestrating transactions very hard.&lt;/p&gt;
&lt;p&gt;It may be tempting to attempt to avoid the problem by making operations idempotent, offering retry mechanisms and so on, and in many cases this might work. But you may have scenarios where you simply need a transaction to fail or succeed, and never be in an intermediate state. The effort involved in working around this or implementing it in a microservice model may be very high.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microservices can be monoliths in disguise&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes, individual services and components &lt;em&gt;may&lt;/em&gt; be deployed in isolation, however in most cases you are going to have to be running some kind of orchestration platform, such as Kubernetes. If you are using a managed service, such as Google&amp;rsquo;s GKE&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; or Amazon&amp;rsquo;s EKS&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;, then a large amount of the complexity of managing the cluster is handled for you.&lt;/p&gt;
&lt;p&gt;However, if you are managing the cluster yourself, you are managing a large, complicated, mission critical system. Although the individual services may have all of the benefits described earlier, you need to very carefully manage your cluster. Deployments of this system can be hard, updates can be hard, failover can be hard and so on.&lt;/p&gt;
&lt;p&gt;In many cases the overall benefits are still there, but it is important not to trivialise or underestimate the additional complexity of managing another big, complex system. Managed services may help, but in many cases these services are nascent (Amazon EKS was only announced at the end of 2017 for example).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Networking Nightmares&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A more traditional model of services running on known hosts, with known addresses, has a fairly simple networking setup.&lt;/p&gt;
&lt;p&gt;However, when using microservices, generally there will be many services distributed across many nodes, which typically means there&amp;rsquo;s going to be a &lt;em&gt;much&lt;/em&gt; more complicated networking arrangement. There will be load balancing between services, DNS may be more heavily used, virtual networking layers, etc etc, to attempt to &amp;lsquo;hide&amp;rsquo; the complexity of this networking.&lt;/p&gt;
&lt;p&gt;However, as per &lt;a href="https://github.com/dwmkerr/hacker-laws/#the-law-of-conservation-of-complexity-teslers-law"&gt;Tesler&amp;rsquo;s Law&lt;/a&gt; (or the Law of Conservation of Compexlity), this networking complexity is inherent - when you are finding real, runtime issues in larger scale clusters, it can often be at a very low networking level. These sorts of issues can be &lt;em&gt;very&lt;/em&gt; hard to diagnose. I have started tracking some examples at the end of the article, but I think that &lt;a href="https://medium.com/@tinder.engineering/tinders-move-to-kubernetes-cda2a6372f44"&gt;Tinder&amp;rsquo;s Migration to Kuberenetes&lt;/a&gt; shows this challenge very well.&lt;/p&gt;
&lt;p&gt;Overall - the transition is still likely to be for the best, but doesn&amp;rsquo;t come without some serious challenges at the networking level, which will require some serious expertise to deal with!&lt;/p&gt;
&lt;h2 id="the-death-of-microservice-madness"&gt;The Death of Microservice Madness!&lt;/h2&gt;
&lt;p&gt;Avoid the madness by making careful and considered decisions. To help out on this I&amp;rsquo;ve noted a few questions you might want to ask yourself, and what the answers might indicate:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/questions.png" alt="Diagram: Questions to ask yourself when considering microservices"&gt;&lt;/p&gt;
&lt;p&gt;You can download a PDF copy here: &lt;a href="https://github.com/dwmkerr/blog/blob/master/articles/2018/microservice-madness/images/microservice-questions.pdf"&gt;microservice-questions.pdf&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="final-thoughts-dont-confuse-microservices-with-architecture"&gt;Final Thoughts: Don&amp;rsquo;t Confuse Microservices with Architecture&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve deliberately avoided the &amp;lsquo;a&amp;rsquo; word in this article. But my friend &lt;a href="http://twitter.com/zoltanarvai"&gt;Zoltan&lt;/a&gt; made a very good point when proofing this article (which he has contributed to).&lt;/p&gt;
&lt;p&gt;There is no microservice architecture. Microservices are just another pattern or implementation of components, nothing more, nothing less. Whether they are present in a system or not does not mean that the architecture of the system is solved.&lt;/p&gt;
&lt;p&gt;Microservices relate in many ways more to the technical processes around packaging and operations rather than the intrinsic design of the system. Appropriate boundaries for components continues to be one of the most important challenges in engineering systems.&lt;/p&gt;
&lt;p&gt;Regardless of the size of your services, whether they are in Docker containers or not, you will always need to think carefully about how to put a system together. There are no right answers, and there are a &lt;em&gt;lot&lt;/em&gt; of options.&lt;/p&gt;
&lt;p&gt;I hope you found this article interesting! As always, please do comment below if you have any questions or thoughts. You can also follow some lively discussions on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/programming/comments/7pxriw/the_death_of_microservice_madness_in_2018/"&gt;Reddit - The Death of Microservice Madness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://news.ycombinator.com/item?id=16200007"&gt;Hacker News - The Death of Microservice Madness&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="appendix-further-reading"&gt;Appendix: Further Reading&lt;/h2&gt;
&lt;p&gt;The following links might be of interest:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/BoundedContext.html"&gt;Martin Fowler - Bounded Context&lt;/a&gt; - Martin&amp;rsquo;s articles are great, I&amp;rsquo;d thoroughly recommend this.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/articles/microservices.html"&gt;Martin Fowler - Microservices&lt;/a&gt; - An often recommended introduction to the pattern.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://r2m.se/microservices-good-or-bad/"&gt;Microservices - Good or Bad?&lt;/a&gt; - Björn Frantzén&amp;rsquo;s thoughts on microservices, after reading this article.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.christianposta.com/microservices/when-not-to-do-microservices/"&gt;When Not To Do Microservices&lt;/a&gt; - Excellent post on the topic from Christian Posta&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.iheavy.com/2017/03/13/30-questions-to-ask-a-serverless-fanboy/"&gt;Sean Hull - 30 questions to ask a serverless fanboy&lt;/a&gt; - Interesting thoughts on the challenges of serverless, from a serverless fan!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/NVb7aljfKYo?t=6657"&gt;Dave Kerr - Monoliths to Microservices - Practical tips for CI/CD and DevOps in the Microservice world&lt;/a&gt; - A recent conference presentation I did on devops with microservices.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yermakov.net/microservices-without-fundamentals/"&gt;Alexander Yermakov - Microservices without fundamentals&lt;/a&gt; - A response to this article, with Alex&amp;rsquo;s thoughts and counterpoints to the points raised here (see also &lt;a href="https://yermakov.net/microservices-as-a-self-sufficient-concept/"&gt;Microservices as a self sufficient concept&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please do share anything else you think makes great reading or watching on the topic!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="thanks"&gt;Thanks&lt;/h2&gt;
&lt;p&gt;Thanks José from &lt;a href="https://www.campusmvp.es"&gt;campusmvp.es&lt;/a&gt; for having the article translated in Spanish - &lt;a href="https://www.campusmvp.es/recursos/post/la-muerte-de-la-locura-de-los-microservicios-en-2018.aspx"&gt;La muerte de la locura de los microservicios en 2018&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id="case-studies"&gt;Case Studies&lt;/h2&gt;
&lt;p&gt;Some interesting examples of experiences I am collecting of larger organisations who have made large scale transitions to microservices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tinder.engineering/tinders-move-to-kubernetes-cda2a6372f44"&gt;Tinder&amp;rsquo;s Move to Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://trends.google.com/trends/explore?date=today%205-y&amp;amp;q=microservice"&gt;https://trends.google.com/trends/explore?date=today%205-y&amp;amp;q=microservice&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;If you don&amp;rsquo;t want to miss the article, you can subscribe to the &lt;a href="http://www.dwmkerr.com/rss/"&gt;RSS Feed&lt;/a&gt;, or follow me on &lt;a href="https://www.linkedin.com/in/dwmkerr/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/dwmkerr"&gt;Twitter&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Docker Compose is a good solution, &lt;a href="https://github.com/apparatus/fuge"&gt;Fuge&lt;/a&gt; is very clever, and there is also the option of running orchestration locally as is the case with something like MiniKube.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Google Kubernetes Engine, a managed service from Google Cloud Platform for Kubernetes: &lt;a href="https://cloud.google.com/kubernetes-engine/"&gt;https://cloud.google.com/kubernetes-engine/&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Amazon Elastic Container Services for Kubernetes, a managed service from Amazon Web Services for Kubernetes: &lt;a href="https://aws.amazon.com/eks/"&gt;https://aws.amazon.com/eks/&lt;/a&gt;&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Part 3: Getting Help</title><link>https://dwmkerr.com/effective-shell-part-3-getting-hepl/</link><pubDate>Tue, 19 Dec 2017 09:05:18 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-part-3-getting-hepl/</guid><description>&lt;p&gt;This is the third part of my &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;Effective Shell&lt;/a&gt; series - practical examples of ways to be more efficient with everyday tasks in a shell.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article I&amp;rsquo;ll show you how to quickly get help when working with tools in the shell, without disrupting your flow!&lt;/p&gt;
&lt;h2 id="getting-help-is-important"&gt;Getting Help is Important!&lt;/h2&gt;
&lt;p&gt;If you are trying to be more effective when using the shell, it is crucial to know how to quickly look things up.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;ll be many circumstances where you&amp;rsquo;ll need to open a browser to search for help, but there&amp;rsquo;s also a wealth of information only a few keystrokes away. Looking up parameters, checking how to run commads, C library docs or useful information like ASCII charts are available directly in the system.&lt;/p&gt;
&lt;p&gt;Before we look at the standard way of accessing documentation on unix-like systems, which is the &lt;code&gt;man&lt;/code&gt; command, I&amp;rsquo;m going to introduce &lt;a href="https://github.com/tldr-pages/tldr"&gt;&lt;code&gt;tldr&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Nine times out of ten I get the help I need in a few seconds with &lt;code&gt;tldr&lt;/code&gt;, so if you take only one thing away from the article, take the first section. Then if you want to learn more about the system manuals, read on!&lt;/p&gt;
&lt;h2 id="tldr"&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say I need to find and replace some text in a file. I know I can do this with the &lt;code&gt;sed&lt;/code&gt; command, but have forgotten the syntax.&lt;/p&gt;
&lt;p&gt;All I need to do is run &lt;code&gt;tldr sed&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/tldr-sed.png" alt="tldr sed screenshot"&gt;&lt;/p&gt;
&lt;p&gt;The first example is exactly what I&amp;rsquo;m looking for. Now for any more detail than a few basic examples, I&amp;rsquo;m going to have to go to the manual, but it&amp;rsquo;s overkill for the basics. Here&amp;rsquo;s what &lt;code&gt;man sed&lt;/code&gt; shows me:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/man-sed.png" alt="sed manpage"&gt;&lt;/p&gt;
&lt;p&gt;And this is just page one of six! There&amp;rsquo;s a &lt;em&gt;lot&lt;/em&gt; of detail, which is great sometimes, but for a quick lookup, &lt;code&gt;tldr&lt;/code&gt; is perfect.&lt;/p&gt;
&lt;p&gt;You can install the &lt;a href="https://github.com/tldr-pages/tldr"&gt;&lt;code&gt;tldr&lt;/code&gt;&lt;/a&gt; tool with &lt;code&gt;npm install -g tldr&lt;/code&gt;. It&amp;rsquo;s open source and community maintained.&lt;/p&gt;
&lt;p&gt;Now a lot of the time, you are still going to need more help or more detail. For the rest of the article, we&amp;rsquo;ll dive a bit deeper into &lt;code&gt;man&lt;/code&gt;, the system manual pages.&lt;/p&gt;
&lt;h2 id="understanding-man"&gt;Understanding &amp;lsquo;man&amp;rsquo;&lt;/h2&gt;
&lt;p&gt;Most tools you encounter in the shell have manual pages available. Many people will be familiar with the &lt;code&gt;man&lt;/code&gt; command to get help on a tool, but let&amp;rsquo;s take a look in a bit more detail, there&amp;rsquo;s actually a lot more available than just the documentation for common commands.&lt;/p&gt;
&lt;h3 id="getting-help-on-a-command"&gt;Getting help on a command&lt;/h3&gt;
&lt;p&gt;The most basic way to get help on a command is with &lt;code&gt;man&lt;/code&gt;. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ man cp
CP(1) BSD General Commands Manual CP(1)
NAME
cp -- copy files
SYNOPSIS
cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file target_file
cp [-R [-H | -L | -P]] [-fi | -n] [-apvX] source_file ...
target_directory
DESCRIPTION
In the first synopsis form, the cp utility copies the contents of the
source_file to the target_file. In the second synopsis form, the con-
tents of each named source_file is copied to the destination
target_directory. The names of the files themselves are not changed. If
cp detects an attempt to copy a file to itself, the copy will fail.
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;man&lt;/code&gt; command opens the manual for the given tool. These manuals should contain all command line options and details of how to use the tool.&lt;/p&gt;
&lt;p&gt;You can scroll up and down through the content with the arrow keys, this is because the information is presented in the shell &lt;em&gt;pager&lt;/em&gt;, which is a tool for looking through content which might not easily fit on a screen.&lt;/p&gt;
&lt;h3 id="using-the-pager"&gt;Using the pager&lt;/h3&gt;
&lt;p&gt;The first thing you might notice is that you can move through the manual pages with the arrow keys.&lt;/p&gt;
&lt;p&gt;Manpages are just text files, and &lt;code&gt;man&lt;/code&gt; opens them in a pager tool, which is what is providing the keyboard interface to look through the file.&lt;/p&gt;
&lt;p&gt;On most systems, the pager will be the &lt;code&gt;less&lt;/code&gt; program. There are lots of commands you can use to navigate through files with &lt;code&gt;less&lt;/code&gt;, but the bare essentials are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;d&lt;/code&gt; - Scroll down half a page&lt;/li&gt;
&lt;li&gt;&lt;code&gt;u&lt;/code&gt; - Scroll up half a page&lt;/li&gt;
&lt;li&gt;&lt;code&gt;j&lt;/code&gt; / &lt;code&gt;k&lt;/code&gt; - Scroll down or up a line. You can also use the arrow keys for this&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; - Quit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&amp;lt;search&amp;gt;&lt;/code&gt; - Search for text&lt;/li&gt;
&lt;li&gt;&lt;code&gt;n&lt;/code&gt; - When searching, find the next occurrence&lt;/li&gt;
&lt;li&gt;&lt;code&gt;N&lt;/code&gt; - When searching, find the previous occurrence&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are &lt;em&gt;many&lt;/em&gt; other commands, but the set above is normally what I find myself using the most.&lt;/p&gt;
&lt;p&gt;If you are interested, you can actually see what your pager is with the command below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $PAGER
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;less
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;$PAGER&lt;/code&gt; environment variable is used to tell the shell what program to use for paging. More details are found with &lt;code&gt;man man&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can put any text content into your pager - try this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ls -al /usr/bin | less
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This lists the contents of the &lt;code&gt;/usr/bin&lt;/code&gt; folder, piping the output to &lt;code&gt;less&lt;/code&gt; so we can easily scroll through it.&lt;/p&gt;
&lt;p&gt;There are alternative pagers available (on many Unix-y systems you&amp;rsquo;ll have &lt;code&gt;less&lt;/code&gt;, &lt;code&gt;more&lt;/code&gt; and &lt;code&gt;most&lt;/code&gt;) but in general you&amp;rsquo;ll normally get what you need with &lt;code&gt;less&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="whats-with-the-numbers"&gt;What&amp;rsquo;s with the numbers?&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll often see tools referred to in manpages with numbers after them. Take a look at &lt;code&gt;man less&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/numbers.png" alt="Screenshot of numbers"&gt;&lt;/p&gt;
&lt;p&gt;The number is the manual &lt;strong&gt;Section Number&lt;/strong&gt;. The different sections of the manual are documented be found on most unix-like systems in &lt;code&gt;man&lt;/code&gt;&amp;rsquo;s documentation, which you can check by running &lt;code&gt;man man&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. Here&amp;rsquo;s what you&amp;rsquo;d get on Ubuntu 16:&lt;/p&gt;
&lt;p&gt;| 1 | Executable programs or shell commands |
| 2 | System calls (functions provided by the kernel) |
| 3 | Library calls (functions within program libraries) |
| 4 | Special files (usually found in /dev) |
| 5 | File formats and conventions eg /etc/passwd |
| 6 | Games |
| 7 | Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) |
| 8 | System administration commands (usually only for root) |
| 9 | Kernel routines [Non standard] |&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll go through the setions in detail shorltly.&lt;/p&gt;
&lt;p&gt;You can specifically choose &lt;em&gt;which&lt;/em&gt; section of the manual you are looking in by using:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;man &amp;lt;section&amp;gt; &amp;lt;search&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also get more information about the sections themselves by opening up the &lt;code&gt;intro&lt;/code&gt; page. For example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ man 1 intro
INTRO(1) BSD General Commands Manual INTRO(1)
NAME
intro -- introduction to general commands (tools and utilities)
DESCRIPTION
Section one of the manual contains most of the commands which comprise...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why would you do this, and why would you care? A few examples from each section show how this can be quite useful to know about.&lt;/p&gt;
&lt;h4 id="section-1-programs-and-shell-commands"&gt;Section 1: Programs and Shell Commands&lt;/h4&gt;
&lt;p&gt;These are programs, probably what you are going to be looking up most regularly! For example, &lt;code&gt;man 1 time&lt;/code&gt; shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;TIME(1) BSD General Commands Manual TIME(1)
NAME
time -- time command execution
SYNOPSIS
time [-lp] utility
DESCRIPTION
The time utility executes and times utility. After the utility finishes, time writes the total time
elapsed, the time consumed by system overhead, and the time used to execute utility to the standard
error stream. Times are reported in seconds.
...
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="section-2-system-calls"&gt;Section 2: System Calls&lt;/h4&gt;
&lt;p&gt;You&amp;rsquo;ll probably not use this section unless you are doing systems programming&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. This section contains info on the avaiable Linux Kernel system calls. For example, running &lt;code&gt;man 2 chown&lt;/code&gt; gives:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;CHOWN(2) BSD System Calls Manual CHOWN(2)
NAME
chown, fchown, lchown, fchownat -- change owner and group of a file
SYNOPSIS
#include &amp;lt;unistd.h&amp;gt;
int
chown(const char *path, uid_t owner, gid_t group);
...
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="section-3-library-calls"&gt;Section 3: Library Calls&lt;/h4&gt;
&lt;p&gt;These are the manpages for the C standard library functions. For example, &lt;code&gt;man 3 time&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;TIME(3) BSD Library Functions Manual TIME(3)
NAME
time -- get time of day
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include &amp;lt;time.h&amp;gt;
time_t
time(time_t *tloc);
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we can see why the sections are important to know about.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;man time&lt;/code&gt; would &lt;em&gt;not&lt;/em&gt; open the page above, because &lt;code&gt;man&lt;/code&gt; searches the library in ascending section order, meaning that it actually finds &lt;code&gt;time(1)&lt;/code&gt; and shows the pages for the &lt;code&gt;time&lt;/code&gt; program, not the &lt;code&gt;time&lt;/code&gt; C library call.&lt;/p&gt;
&lt;p&gt;Because of the potential ambiguity of names if no section number is included, in lots of Linux documentation you&amp;rsquo;ll see the man section number written next to library calls, system calls, programs and so on (things will refer to &lt;code&gt;sed(1)&lt;/code&gt; or &lt;code&gt;time(3)&lt;/code&gt; for example.&lt;/p&gt;
&lt;h4 id="section-4-devices"&gt;Section 4: Devices&lt;/h4&gt;
&lt;p&gt;This section deals with the special devices which live in the &lt;code&gt;/dev/*&lt;/code&gt; folder. For example, running &lt;code&gt;man 4 random&lt;/code&gt; shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;RANDOM(4) BSD Kernel Interfaces Manual RANDOM(4)
NAME
random , urandom -- random data source devices.
SYNOPSIS
pseudo-device random
DESCRIPTION
The random device produces uniformly distributed random byte values of
potentially high quality.
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, we see that section numbers can be important. If you just run &lt;code&gt;man random&lt;/code&gt;, you&amp;rsquo;ll see:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;RANDOM(3) BSD Library Functions Manual RANDOM(3)
NAME
initstate, random, setstate, srandom, srandomdev -- better random num-
ber generator; routines for changing generators
LIBRARY
Standard C Library (libc, -lc)
SYNOPSIS
#include &amp;lt;stdlib.h&amp;gt;
char *
initstate(unsigned seed, char *state, size_t size);
long
random(void);
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which is the manpage for &lt;code&gt;random(3)&lt;/code&gt;, which is C library function, not the &lt;code&gt;/dev/random&lt;/code&gt; file!&lt;/p&gt;
&lt;h4 id="section-5-file-formats"&gt;Section 5: File Formats&lt;/h4&gt;
&lt;p&gt;This section details special files in the system. For example, &lt;code&gt;man 5 crontab&lt;/code&gt; shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;CRONTAB(5) BSD File Formats Manual CRONTAB(5)
NAME
crontab -- tables for driving cron
DESCRIPTION
A crontab file contains instructions to the cron(8) daemon of the gen-
eral form: ``run this command at this time on this date&amp;#39;&amp;#39;. Each user
has their own crontab, and commands in any given crontab will be exe-
cuted as the user who owns the crontab. Uucp and News will usually
have their own crontabs, eliminating the need for explicitly running
su(1) as part of a cron command.
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which describes the crontab file used to define scheduled tasks. Again, this is different to &lt;code&gt;man crontab&lt;/code&gt; which would document &lt;code&gt;crontab(1)&lt;/code&gt;. Similarly, &lt;code&gt;man 5 passwd&lt;/code&gt; is going to show something quite different to &lt;code&gt;man passwd&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id="section-6-games"&gt;Section 6: Games&lt;/h4&gt;
&lt;p&gt;Nothing says it better than &lt;code&gt;man 6 intro&lt;/code&gt; itself (this&amp;rsquo;ll not work on a Mac sadly, but try it on another Linux system):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;...
DESCRIPTION
Section 6 of the manual describes all the games and funny little programs available on the system.
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are probably a few silly programs available on your system, here you&amp;rsquo;ll find their manuals. For example, &lt;code&gt;man 6 banner&lt;/code&gt; on a Mac shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;BANNER(6) BSD Games Manual BANNER(6)
NAME
banner -- print large banner on printer
SYNOPSIS
banner [-d] [-t] [-w width] message ...
DESCRIPTION
Banner prints a large, high quality banner on the standard output. If
the message is omitted, it prompts for and reads one line of its stan-
dard input.
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This section is going to be highly dependent on your OS!&lt;/p&gt;
&lt;h4 id="section-7-miscellaneous"&gt;Section 7: Miscellaneous&lt;/h4&gt;
&lt;p&gt;This is where you&amp;rsquo;ll find additional assorted documentation. For example, &lt;code&gt;man 7 ascii&lt;/code&gt; shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ASCII(7) BSD Miscellaneous Information Manual ASCII(7)
NAME
ascii -- octal, hexadecimal and decimal ASCII character sets
DESCRIPTION
The octal set:
000 nul 001 soh 002 stx 003 etx 004 eot 005 enq 006 ack 007 bel
...
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="section-8-system-commands"&gt;Section 8: System Commands&lt;/h4&gt;
&lt;p&gt;We&amp;rsquo;ve actually already seen one of these commands mentioned, in the manpage for &lt;code&gt;crontab(5)&lt;/code&gt; it mentions &lt;code&gt;cron(8)&lt;/code&gt;. Let&amp;rsquo;s see, with &lt;code&gt;man 8 cron&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;CRON(8) BSD System Manager&amp;#39;s Manual CRON(8)
NAME
cron -- daemon to execute scheduled commands (Vixie Cron)
SYNOPSIS
cron [-s] [-o] [-x debugflag[,...]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These are commands which sysadmins would normally run. You might open section eight unexpectedly, for example &lt;code&gt;man chmod&lt;/code&gt; will open &lt;code&gt;chmod(1)&lt;/code&gt;, but &lt;code&gt;man chown&lt;/code&gt; will open &lt;code&gt;chown(8)&lt;/code&gt;, as it is a system command.&lt;/p&gt;
&lt;p&gt;Some distributions might vary for Section Nine. On my Mac it contains information about the kernel interfaces, a C style guide and some more.&lt;/p&gt;
&lt;h4 id="getting-the-index-of-manual-section"&gt;Getting the Index of Manual Section&lt;/h4&gt;
&lt;p&gt;Manpages are just files on the filesystem, so you can get the index of a section just by looking in the appropriate folder.&lt;/p&gt;
&lt;p&gt;For example, to index the available system calls, try &lt;code&gt;ls /usr/share/man/man2&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;EV_SET.2
FD_CLR.2
FD_COPY.2
FD_ISSET.2
FD_SET.2
FD_ZERO.2
_exit.2
accept.2
access.2
acct.2
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is quick and easy way to see what sort of entries you have on your system. If you want to work out where an entry lives, use the &lt;code&gt;-w&lt;/code&gt; flag:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ man -w printf
/usr/share/man/man1/printf.1
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="searching-the-manual"&gt;Searching the Manual&lt;/h3&gt;
&lt;p&gt;You can search the manpage titles and summaries with &lt;code&gt;man -k&lt;/code&gt;. For example, &lt;code&gt;man -k cpu&lt;/code&gt; shows:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cpuwalk.d(1m) - Measure which CPUs a process runs on. Uses DTrace
dispqlen.d(1m) - dispatcher queue length by CPU. Uses DTrace
gasm(n), grammar::me::cpu::gasm(n) - ME assembler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can find more advanced options for searching by using your newfound &lt;code&gt;man&lt;/code&gt; skills on &lt;code&gt;man&lt;/code&gt; itself.&lt;/p&gt;
&lt;h2 id="thats-enough"&gt;That&amp;rsquo;s Enough!&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d recommend &lt;code&gt;tldr&lt;/code&gt; as a first-call for checking to see how to use a command.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;man&lt;/code&gt; is a powerful tool to dive deeper into how programs and components of the system work. Like many tools which have been around for a long time, there&amp;rsquo;s a lot you can do with &lt;code&gt;man&lt;/code&gt;. Much of it you&amp;rsquo;ll likely never need, so I&amp;rsquo;ve tried to keep this article to the basics.&lt;/p&gt;
&lt;p&gt;Understanding manpage sections is useful - you&amp;rsquo;ll see them referenced again and again in documentation on the system and online.&lt;/p&gt;
&lt;p&gt;I hope this helps you save some time when you are working! Please let me know in the comments if you have any questions or thoughts.&lt;/p&gt;
&lt;p&gt;You can also check out the &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;rest of the effective shell series&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="appendix-dash"&gt;Appendix: Dash&lt;/h2&gt;
&lt;p&gt;As a final note, if you find yourself using &lt;code&gt;man&lt;/code&gt; a lot because you work offline (I fly a lot so find it very helpful when on a plane with no WiFi), you should also look at &lt;em&gt;Dash&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Dash is simply an offline documentation aggregator. It can download online manuals for many, many different programming languages, frameworks, technologies and so on. I actually have a &lt;code&gt;vim&lt;/code&gt; keyboard command to open the word under the cursor in dash, with the documentation automatically set based on the type of the file.&lt;/p&gt;
&lt;p&gt;This is super-useful if you are offline at lot and need more sophisticated offline documentation. You can find out more about it at &lt;a href="https://kapeli.com/dash"&gt;https://kapeli.com/dash&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Weirdly satisfying to run.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Which it is always fun to try if you get the chance, and a great way to learn more about the fundamentals of the operating system.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Dash is a paid product. Full disclosure - I don&amp;rsquo;t get any money from them or anyone else to write about anything, all content is 100% based on my experiences. I don&amp;rsquo;t run ads on my site either.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Integrating OpenShift and Splunk for Docker Container Logging</title><link>https://dwmkerr.com/integrating-openshift-and-splunk-for-logging/</link><pubDate>Sun, 29 Oct 2017 07:15:04 +0000</pubDate><guid>https://dwmkerr.com/integrating-openshift-and-splunk-for-logging/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to show you how to set up OpenShift to integrate with Splunk for logging in a Docker container orchestration environment.&lt;/p&gt;
&lt;p&gt;These techniques could easily be adapted for a standard Kubernetes installation as well!&lt;/p&gt;
&lt;p&gt;&lt;img src="images/counter-service-splunk.png" alt="Screenshot: Counter service splunk"&gt;&lt;/p&gt;
&lt;p&gt;The techniques used in this article are based on the &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging"&gt;Kubernetes Logging Cluster Administration Guide&lt;/a&gt;. I also found Jason Poon&amp;rsquo;s article &lt;a href="http://jasonpoon.ca/2017/04/03/kubernetes-logging-with-splunk/"&gt;Kubernetes Logging with Splunk&lt;/a&gt; very helpful.&lt;/p&gt;
&lt;p&gt;First, clone the &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;Terraform AWS OpenShift&lt;/a&gt; repo:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git clone git@github.com:dwmkerr/terraform-aws-openshift
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This repo can be used to create a vanilla OpenShift cluster. I&amp;rsquo;m adding &amp;lsquo;recipes&amp;rsquo; to the project, which will allow you to mix in more features (but still keep the main codebase clean). For now, let&amp;rsquo;s merge in the &amp;lsquo;splunk&amp;rsquo; recipe:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd terraform-aws-openshift
git pull origin recipes/splunk
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pulling this recipe in adds the extra config and scripts required to set up Splunk&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;ve got the code, we can get started!&lt;/p&gt;
&lt;h2 id="create-the-infrastructure"&gt;Create the Infrastructure&lt;/h2&gt;
&lt;p&gt;To create the cluster, you&amp;rsquo;ll need to install the &lt;a href="https://aws.amazon.com/cli/"&gt;AWS CLI&lt;/a&gt; and log in, and install &lt;a href="https://www.terraform.io/downloads.html"&gt;Terraform&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before you continue, &lt;font color="red"&gt;&lt;strong&gt;be aware&lt;/strong&gt;&lt;/font&gt;: the machines on AWS we&amp;rsquo;ll create are going to run to about $250 per month:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/aws-cost.png" alt="AWS Cost Calculator"&gt;&lt;/p&gt;
&lt;p&gt;Once you are logged in with the AWS CLI just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make infrastructure
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll be asked to specify a region:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/region.png" alt="Specify Region"&gt;&lt;/p&gt;
&lt;p&gt;Any &lt;a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions"&gt;AWS region&lt;/a&gt; will work fine, use &lt;code&gt;us-east-1&lt;/code&gt; if you are not sure.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;ll take about 5 minutes for Terraform to build the required infrastructure, which looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/splunk-architecture.png" alt="AWS Infrastructure"&gt;&lt;/p&gt;
&lt;p&gt;Once it&amp;rsquo;s done you&amp;rsquo;ll see a message like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/apply-complete.png" alt="Apply Complete"&gt;&lt;/p&gt;
&lt;p&gt;The infrastructure is ready! A few of the most useful parameters are shown as output variables. If you log into AWS you&amp;rsquo;ll see our new instances, as well as the VPC, network settings etc etc:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/aws.png" alt="AWS"&gt;&lt;/p&gt;
&lt;h2 id="installing-openshift"&gt;Installing OpenShift&lt;/h2&gt;
&lt;p&gt;Installing OpenShift is easy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make openshift
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will take quite some time to run (sometimes up to 30 minutes). Once it is complete you&amp;rsquo;ll see a message like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/openshift-complete.png" alt="OpenShift Installation Complete"&gt;&lt;/p&gt;
&lt;p&gt;You can now open the OpenShift console. Use the public address of the master node (which you can get with &lt;code&gt;$(terraform output master-url)&lt;/code&gt;), or just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make browse-openshift
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The default username and password is &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;123&lt;/code&gt;. You&amp;rsquo;ll see we have a clean installation and are ready to create our first project:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/welcome-to-openshift.png" alt="Welcome to OpenShift"&gt;&lt;/p&gt;
&lt;p&gt;Close the console for now.&lt;/p&gt;
&lt;h2 id="installing-splunk"&gt;Installing Splunk&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ve probably figured out the pattern by now&amp;hellip;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make splunk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once this command is complete, you can open the Splunk console with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make browse-splunk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again the username and password is &lt;code&gt;admin&lt;/code&gt; and &lt;code&gt;123&lt;/code&gt;. You can change the password on login, or leave it:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/splunk-home.png" alt="Splunk Login"&gt;&lt;/p&gt;
&lt;p&gt;You can close the Splunk console now, we&amp;rsquo;ll come back to it shortly.&lt;/p&gt;
&lt;h2 id="demoing-splunk-and-openshift"&gt;Demoing Splunk and OpenShift&lt;/h2&gt;
&lt;p&gt;To see Splunk and OpenShift in action, it helps to have some kind of processing going on in the cluster. You can create a very basic sample project which will spin up two nodes which just write a counter every second as a way to get something running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make sample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create a simple &amp;lsquo;counter&amp;rsquo; service:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/counter-service.png" alt="Screenshot: The counter service"&gt;&lt;/p&gt;
&lt;p&gt;We can see the logs in OpenShift:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/counter-service-logs.png" alt="Screenshot: The counter service logs"&gt;&lt;/p&gt;
&lt;p&gt;Almost immediately you&amp;rsquo;ll be able to see the data in Splunk:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/counter-service-splunk-data-summary.png" alt="Screenshot: The Splunk data explorer"&gt;&lt;/p&gt;
&lt;p&gt;And because of the way the log files are named, we can even rip out the namespace, pod, container and id:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/counter-service-splunk.png" alt="Screenshot: Counter service splunk"&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! You have OpenShift running, Splunk set up and automatically forwarding of all container logs. Enjoy!&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve tried to keep the setup as simple as possible. Here&amp;rsquo;s how it works.&lt;/p&gt;
&lt;h3 id="how-log-files-are-written"&gt;How Log Files Are Written&lt;/h3&gt;
&lt;p&gt;The Docker Engine has a &lt;a href="https://docs.docker.com/engine/admin/logging/overview/"&gt;log driver&lt;/a&gt; which determines how container logs are handled&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;. It defaults to the &lt;code&gt;json-file&lt;/code&gt; driver, which means that logs are written as a json file to:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/var/lib/docker/containers/{container-id}/{container-id}-json.log
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or visually:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/logging-docker-1.png" alt="Diagram: How Docker writes log files"&gt;&lt;/p&gt;
&lt;p&gt;Normally we wouldn&amp;rsquo;t touch this file, in theory it is supposed to be used internally&lt;sup id="fnref1:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and we would use &lt;code&gt;docker logs &amp;lt;container-id&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In theory, all we need to do is use a &lt;a href="http://docs.splunk.com/Documentation/Forwarder/7.0.0/Forwarder/Abouttheuniversalforwarder"&gt;Splunk Forwarder&lt;/a&gt; to send this file to our indexer. The only problem is that we only get the container ID from the file name, finding the right container ID for your container can be a pain. However, we are running on Kubernetes, which means the picture is a little different&amp;hellip;&lt;/p&gt;
&lt;h3 id="how-log-files-are-written---on-kubernetes"&gt;How Log Files Are Written - on Kubernetes&lt;/h3&gt;
&lt;p&gt;When running on Kubernetes, things are little different. On machines with &lt;code&gt;systemd&lt;/code&gt;, the log driver for the docker engine is set to &lt;code&gt;journald&lt;/code&gt; (see &lt;a href="https://kubernetes.io/docs/concepts/cluster-administration/logging/"&gt;Kubernetes - Logging Architecture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It &lt;em&gt;is&lt;/em&gt; possible to forward &lt;code&gt;journald&lt;/code&gt; to Splunk, but only by streaming it to a file and then forwarding the file. Given that we need to use a file as an intermediate, it seems easier just to change the driver back to &lt;code&gt;json-file&lt;/code&gt; and forward that.&lt;/p&gt;
&lt;p&gt;So first, we configure the docker engine to use &lt;code&gt;json-file&lt;/code&gt; (see &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift/blob/recipes/splunk/scripts/postinstall-master.sh"&gt;this file&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sed -i &lt;span style="color:#e6db74"&gt;&amp;#39;/OPTIONS=.*/c\OPTIONS=&amp;#34;--selinux-enabled --insecure-registry 172.30.0.0/16 --log-driver=json-file --log-opt max-size=1M --log-opt max-file=3&amp;#34;&amp;#39;&lt;/span&gt; /etc/sysconfig/docker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we just change the options to default to the &lt;code&gt;json-file&lt;/code&gt; driver, with a max file size of 1MB (and maximum of three files, so we don&amp;rsquo;t chew all the space on the host).&lt;/p&gt;
&lt;p&gt;Now the cool thing about Kubernetes is that it creates symlinks to the log files, which have much more descriptive names:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/logging-k8s.png" alt="Symlink diagram"&gt;&lt;/p&gt;
&lt;p&gt;We still have the original container log, in the same location. But we also have a pod container log (which is a symlink to the container log) and another container log, which is a symlink to the pod container log.&lt;/p&gt;
&lt;p&gt;This means we can read the container log, and extract some really useful information from the file name. The container log file name has the following format:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/var/log/containers/{container-id}/{container-id}-json.log
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="how-log-files-are-read"&gt;How Log Files Are Read&lt;/h3&gt;
&lt;p&gt;Now that we are writing the log files to a well defined location, reading them is straightforward. The diagram below shows how we use a splunk-forwarder to complete the picture:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/how-logs-are-read.png" alt="Diagram: How logs are read"&gt;&lt;/p&gt;
&lt;p&gt;First, we create a DaemonSet, which ensures we run a specific pod on every node.&lt;/p&gt;
&lt;p&gt;The DaemonSet runs with a new account which has the &amp;lsquo;any id&amp;rsquo; privilege, allowing it to run as root. We then mount the log folders into the container (which are owned by root, which is why our container needs these extra permissions to read the files).&lt;/p&gt;
&lt;p&gt;The pod contains a splunk-forwarder container, which is configured to monitor the &lt;code&gt;/var/log/containers&lt;/code&gt; folder. It also monitors the docker socket, allowing us to see docker events. The forwarder is also configured with the IP address of the Splunk Indexer.&lt;/p&gt;
&lt;h2 id="footnotes"&gt;Footnotes&lt;/h2&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;As a reference, you can also see the recipe pull request to see what changes from a &amp;lsquo;vanilla&amp;rsquo; installation to add Splunk: &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift/pull/16"&gt;Splunk Recipe Pull Request&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It is useful to check the documentation on logging drivers for Docker. See &lt;a href="https://docs.docker.com/engine/admin/logging/overview/#supported-logging-drivers"&gt;Configure Logging Drivers&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/extend/plugins_logging/"&gt;Docker Log Driver Plugins&lt;/a&gt;. It is possible to create custom log drivers. However, at the time of writing only the journald and json-file log drivers will work with the integrated logging view in OpenShift.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Part 2: Become a Clipboard Gymnast</title><link>https://dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/</link><pubDate>Tue, 10 Oct 2017 09:57:54 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/</guid><description>&lt;p&gt;This is the second part of my &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;Effective Shell&lt;/a&gt; series, which contains practical tips for using the shell to help with every day tasks and be more efficient:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article I&amp;rsquo;ll show you how you can use the shell as an efficient tool to compliment how you use the clipboard.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note for Linux Users: In this article I&amp;rsquo;ll use the &lt;code&gt;pbcopy&lt;/code&gt; and &lt;code&gt;pbpaste&lt;/code&gt; commands to access the clipboard, which are available on a Mac only. To get access to the same commands on other platforms, check &lt;a href="#appendixclipboardaccessonlinux"&gt;Appendix: Clipboard Access on Linux&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="use-the-shell-on-the-clipboard"&gt;Use the Shell on the Clipboard&lt;/h2&gt;
&lt;p&gt;You can easily use shell commands on the contents of your clipboard. Just use &lt;code&gt;pbpaste&lt;/code&gt; to output the clipboard, run the output through some commands, then use &lt;code&gt;pbcopy&lt;/code&gt; to copy the result.&lt;/p&gt;
&lt;p&gt;Try copying the following text:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Kirk Van Houten
Timothy Lovejoy
Artie Ziff
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then in the shell, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pbpaste
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You should see the contents of the clipboard. Now we&amp;rsquo;ll look at some ways that shell access to the clipboard can help with common tasks.&lt;/p&gt;
&lt;h2 id="removing-formatting"&gt;Removing Formatting&lt;/h2&gt;
&lt;p&gt;Don&amp;rsquo;t you hate it when you have to copy formatted text and don&amp;rsquo;t have an easy way to paste it as &lt;em&gt;unformatted&lt;/em&gt; text? Here&amp;rsquo;s an example, I want to copy this Wikipedia page on &amp;lsquo;bash&amp;rsquo;, and paste it into a Word document:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/strip-formatting-before.png" alt="Copying and pasting with formatting"&gt;&lt;/p&gt;
&lt;p&gt;Many programs have a shortcut to paste the contents of the clipboard (such as &amp;lsquo;command + shift + v&amp;rsquo;) but if you are like me you might find yourself pasting &lt;em&gt;into&lt;/em&gt; a plain text editor just to copy &lt;em&gt;out&lt;/em&gt; the plain text.&lt;/p&gt;
&lt;p&gt;If you just run the command &lt;code&gt;pbpaste | pbcopy&lt;/code&gt;, you can easily strip the formatting:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/strip-formatting-after-2.png" alt="Stripping formatting from the clipboard"&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re just piping out the clipboard (which ends up as plain text, cause we&amp;rsquo;re in a terminal!) and then piping that plain text &lt;em&gt;back into the clipboard&lt;/em&gt;, replacing the formatted text which was there before.&lt;/p&gt;
&lt;p&gt;This little trick can be very useful. But we can use the same pattern to quickly manipulate the contents of the clipboard in more sophisticated ways.&lt;/p&gt;
&lt;h2 id="manipulating-text"&gt;Manipulating Text&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say someone has emailed me a list of people I need to invite to an event:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/email_list_excel.png" alt="Email List"&gt;&lt;/p&gt;
&lt;p&gt;The problem is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The list is in Excel and is formatted&lt;/li&gt;
&lt;li&gt;The list has duplicates&lt;/li&gt;
&lt;li&gt;I need to turn each name into an email address like &amp;lsquo;&lt;a href="mailto:Artie_Ziff@simpsons.com"&gt;Artie_Ziff@simpsons.com&lt;/a&gt;&amp;rsquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And I want to email everyone quickly.&lt;/p&gt;
&lt;p&gt;We can quickly handle this task without leaving the shell.&lt;/p&gt;
&lt;p&gt;Copy the raw text below if you want to try out the same commands and follow along:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Artie Ziff
Kirk Van Houten
Timothy Lovejoy
Artie Ziff
Nick Riviera
Seymore Skinner
Hank Scorpio
Timothy Lovejoy
John Frink
Cletus Spuckler
Ruth Powers
Artie Ziff
Agnes Skinner
Helen Lovejoy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First, we copy the text to the clipboard.&lt;/p&gt;
&lt;p&gt;Now we can paste and sort:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pbpaste | sort
Agnes Skinner
Artie Ziff
Artie Ziff
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy
Timothy Lovejoy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then remove the duplicates:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pbpaste | sort | uniq
Agnes Skinner
Artie Ziff
Cletus Spuckler
Hank Scorpio
Helen Lovejoy
John Frink
Kirk Van Houten
Nick Riviera
Ruth Powers
Seymore Skinner
Timothy Lovejoy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Replace the underscore with an ampersand:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pbpaste | sort | uniq | tr &amp;#34; &amp;#34; &amp;#34;_&amp;#34;
Agnes_Skinner
Artie_Ziff
Cletus_Spuckler
Hank_Scorpio
Helen_Lovejoy
John_Frink
Kirk_Van_Houten
Nick_Riviera
Ruth_Powers
Seymore_Skinner
Timothy_Lovejoy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then add the final part of the email address:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pbpaste | sort | uniq | tr &amp;#34; &amp;#34; &amp;#34;_&amp;#34; | sed &amp;#39;s/$/@simpsons.com/&amp;#39;
Agnes_Skinner@simpsons.com
Artie_Ziff@simpsons.com
Cletus_Spuckler@simpsons.com
Hank_Scorpio@simpsons.com
Helen_Lovejoy@simpsons.com
John_Frink@simpsons.com
Kirk_Van_Houten@simpsons.com
Nick_Riviera@simpsons.com
Ruth_Powers@simpsons.com
Seymore_Skinner@simpsons.com
Timothy_Lovejoy@simpsons.com
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This looks perfect! We can now put the transformed text back onto the clipboard:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ pbpaste | sort | uniq | tr &amp;#39; &amp;#39; &amp;#39;_&amp;#39; | sed &amp;#39;s/$/@simpsons.com&amp;#39; | pbcopy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All in all we have the following pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;pbpaste&lt;/code&gt; - output the clipboard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sort&lt;/code&gt; - order the output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uniq&lt;/code&gt; - deduplicate the rows&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tr ' ' '_'&lt;/code&gt; - replace spaces with underscores&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sed /$/@simpsons.com&lt;/code&gt; - add the email domain to the end of the row&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Building this in one go is hard, let&amp;rsquo;s look at little more at the pipeline.&lt;/p&gt;
&lt;h2 id="thinking-in-pipelines"&gt;Thinking in Pipelines&lt;/h2&gt;
&lt;p&gt;Some of these commands might be unfamiliar, some might not make sense, and you might be thinking &amp;lsquo;how would I remember that&amp;rsquo;. Actually, there are many ways to solve the problem above, this is the one I came up with by &lt;em&gt;iteratively&lt;/em&gt; changing my input text.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what I mean - you&amp;rsquo;ll see that I actually build a pipeline like this step-by-step:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/pipeline.gif" alt="Animation of the process of building a pipeline"&gt;&lt;/p&gt;
&lt;p&gt;You can see in the screenshots that I start simple, and step by step add the stages we need.&lt;/p&gt;
&lt;p&gt;(P.S - if you are wondering how I am jumping backwards and forwards a word at a time, check the last chapter &amp;lsquo;&lt;a href="www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Navigating the Command Line&lt;/a&gt;&amp;rsquo;).&lt;/p&gt;
&lt;p&gt;What we&amp;rsquo;re doing here is only possible because these simple commands all follow &amp;rsquo;the Unix Philosophy&amp;rsquo;. They do one thing well, and each command expects it&amp;rsquo;s input to become the input of &lt;em&gt;another&lt;/em&gt; command later on. Specifically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The commands are primitive and simple - &lt;code&gt;sort&lt;/code&gt; is sorting a list, &lt;code&gt;uniq&lt;/code&gt; is making elements unique.&lt;/li&gt;
&lt;li&gt;The commands don&amp;rsquo;t produce unnecessary output - &lt;code&gt;sort&lt;/code&gt; doesn&amp;rsquo;t add a header such as &lt;code&gt;Sorted Items&lt;/code&gt;, which is great because otherwise it would clutter our pipeline.&lt;/li&gt;
&lt;li&gt;We are chaining commands together, the output of one becomes the input of another.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We don&amp;rsquo;t need a command such as &amp;lsquo;Take a muddy list, sort and clean it, then turn pairs of words into an email address&amp;rsquo; - with a few simple &amp;lsquo;workhorse&amp;rsquo; commands we can easily build this functionality ourselves.&lt;/p&gt;
&lt;p&gt;These workhorse commands will be introduced and detailed as we go through the series. We&amp;rsquo;ll also spend a lot more time looking at pipelines.&lt;/p&gt;
&lt;p&gt;I hope this was useful! Please comment if you have any questions or tips. To see further articles as they come out, follow the repo at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/effective-shell"&gt;github.com/dwmkerr/effective-shell&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or just follow &lt;a href="https://twitter.com/dwmkerr"&gt;@dwmkerr&lt;/a&gt; on Twitter.&lt;/p&gt;
&lt;h1 id="appendix---clipboard-access-on-linux"&gt;Appendix - Clipboard Access on Linux&lt;/h1&gt;
&lt;p&gt;If you are using Linux, there is no &lt;code&gt;pbcopy&lt;/code&gt; and &lt;code&gt;pbpaste&lt;/code&gt; commands. You can use the &lt;a href="https://linux.die.net/man/1/xclip"&gt;&lt;code&gt;xclip&lt;/code&gt;&lt;/a&gt; tool to create equivalent commands.&lt;/p&gt;
&lt;p&gt;First, install &lt;code&gt;xclip&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt-get install -y xclip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then add the following to your &lt;code&gt;.bashrc&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Create mac style aliases for clipboard access.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias pbcopy&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;xclip -selection c&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias pbpaste&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;xclip -selection c -o&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Obviously you can use any alias you like! The article assumes that &lt;code&gt;pbcopy&lt;/code&gt; and &lt;code&gt;pbpaste&lt;/code&gt; have been used.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Effective Shell Part 1: Navigating the Command Line</title><link>https://dwmkerr.com/effective-shell-part-1-navigating-the-command-line/</link><pubDate>Sun, 11 Jun 2017 23:05:40 +0000</pubDate><guid>https://dwmkerr.com/effective-shell-part-1-navigating-the-command-line/</guid><description>&lt;p&gt;This is the &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;first part of a series&lt;/a&gt; I am writing which contains practical tips for using the shell more effectively.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-1-navigating-the-command-line/"&gt;Part 1: Navigating the Command Line&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-2-become-a-clipboard-gymnast/"&gt;Part 2: Become a Clipboard Gymnast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dwmkerr.com/effective-shell-part-3-getting-hepl/"&gt;Part 3: Getting Help&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-4-moving-around/"&gt;Part 4: Moving Around&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-part-5-understanding-the-shell/"&gt;Part 5: Interlude - Understanding the Shell&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-6-job-control/"&gt;Part 6: Everything You Don&amp;rsquo;t Need to Know About Job Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dwmkerr.com/effective-shell-7-shell-commands/"&gt;Part 7: The Subtleties of Shell Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can&amp;rsquo;t think of a better place to start than &lt;em&gt;navigating the command line&lt;/em&gt;. As you start to do more and more in the shell, text in the command line can quickly become hard to handle. In this article I&amp;rsquo;ll show some simple tricks for working with the command line more effectively.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick reference diagram, the rest of the article goes into the details!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/effective-shell"&gt;&lt;img src="images/command-line-3.png" alt="command line"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This article, examples and diagrams are available at &lt;a href="https://github.com/dwmkerr/effective-shell"&gt;github.com/dwmkerr/effective-shell&lt;/a&gt;.&lt;/p&gt;
&lt;!-- TOC depthFrom:2 depthTo:3 withLinks:1 updateOnSave:1 orderedList:0 --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#basicnavigation"&gt;Basic Navigation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#searching"&gt;Searching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#editinginplace"&gt;Editing In-Place&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#clearthescreen"&gt;Clear the Screen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#protipallthekeys"&gt;Pro Tip: All The Keys!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#protiptransposing"&gt;Pro Tip: Transposing!&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#closingthoughts"&gt;Closing Thoughts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- /TOC --&gt;
&lt;h2 id="basic-navigation"&gt;Basic Navigation&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s assume we have a very simple command we are writing, which is going to write a quote to a text file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;The trouble with writing fiction is that it has to make sense,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;whereas real life doesn&amp;#39;t. -- Iain M. Banks&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; quote.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Navigating around long lines of text is a slow process if you are only relying on the arrow keys, so take the time to learn the following shortcuts:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Go to beginning / end&lt;/td&gt;
&lt;td&gt;&lt;p&gt;&lt;code&gt;Ctrl + a&lt;/code&gt;, &lt;code&gt;Ctrl + e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/begin-end.gif" target="_blank"&gt;&lt;img src="images/begin-end.gif" alt="begin / end" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go backwards / forwards one word&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Alt + b&lt;/code&gt; / &lt;code&gt;Alt + f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/forward-backwards.gif" target="_blank"&gt;&lt;img src="images/forward-backwards.gif" alt="backward / forward" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete a word / undo&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl + w&lt;/code&gt; / &lt;code&gt;Ctrl + -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/delete-undo.gif" target="_blank"&gt;&lt;img src="images/delete-undo.gif" alt="delete / undo" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete next word&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Alt + d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/delete-next-word.gif" target="_blank"&gt;&lt;img src="images/delete-next-word.gif" alt="delete next word" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete all the way to the beginning[^1]&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl + u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/delete-to-beginning.gif" target="_blank"&gt;&lt;img src="images/delete-to-beginning.gif" alt="delete to beginning" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete all the way to the end&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl + k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/delete-to-end.gif" target="_blank"&gt;&lt;img src="images/delete-to-end.gif" alt="delete to end" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Note that if you are on a Mac, you might need to tweak your console to allow the &amp;lsquo;Alt&amp;rsquo; key to work.&lt;/p&gt;
&lt;p&gt;For iTerm2, go to settings (Command + ,) &amp;gt; Profiles Tab &amp;gt; select the profile you are using &amp;gt; Keys tab. There, you should see Left Option key and Right Option Key with three radio buttons. Select &amp;ldquo;Esc+&amp;rdquo; for the Left Option Key.&lt;/p&gt;
&lt;p&gt;For Terminal, go to Profiles Tab &amp;gt; Keyboard Tab &amp;gt; check &amp;ldquo;Use Option as Meta key&amp;rdquo; at the bottom of the screen.&lt;/p&gt;
&lt;h2 id="searching"&gt;Searching&lt;/h2&gt;
&lt;p&gt;Once you have the basic navigation commands down, the next essential is searching. Let&amp;rsquo;s assume we&amp;rsquo;ve run the following three commands:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ command1 param1 param2 param3
$ command2 param4 param5 param6
$ command3 param7 param8 param9
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can search backwards or forwards with &lt;code&gt;Ctrl + r&lt;/code&gt; and &lt;code&gt;Ctrl + s&lt;/code&gt;. This will search in the current command and then iteratively through previous commands:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/search-backwards-and-forwards.gif" alt="search backwards and forwards"&gt;&lt;/p&gt;
&lt;p&gt;This is useful for searching in the current command, but can be also used to quickly search backwards and forwards through the command history:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/search-commands-backwards-and-forwards-1.gif" alt="search commands backwards and forwards"&gt;&lt;/p&gt;
&lt;p&gt;As you type, your command history is searched, the most recent commands coming first. Use the arrow keys to edit the command, press enter to execute it, or &lt;code&gt;Ctrl + g&lt;/code&gt; to cancel the search.&lt;/p&gt;
&lt;p&gt;Here are the same commands applied to the original example:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search backwards / forwards&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl + r&lt;/code&gt; / Ctrl + s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/search-history-next.gif" target="_blank"&gt;&lt;img src="images/search-history-next.gif" alt="find next occurrence" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run the command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/search-history-execute.gif" target="_blank"&gt;&lt;img src="images/search-history-execute.gif" alt="execute" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit the command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Right Arrow&lt;/code&gt; / &lt;code&gt;Right Arrow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/search-history-edit.gif" target="_blank"&gt;&lt;img src="images/search-history-edit.gif" alt="edit command" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop searching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl + g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="images/search-history-cancel.gif" target="_blank"&gt;&lt;img src="images/search-history-cancel.gif" alt="cancel search" style="max-width:100%;"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id="editing-in-place"&gt;Editing In-Place&lt;/h2&gt;
&lt;p&gt;These tips and tricks are helpful, but if you are working with a really long or complex command, you might find it useful just to jump into your favourite editor.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;Ctrl + x , Ctrl + e&lt;/code&gt; to edit-in place:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/edit-in-place.gif" alt="edit in place"&gt;&lt;/p&gt;
&lt;p&gt;In a later article I&amp;rsquo;ll talk a little more about how to configure the default editor.&lt;/p&gt;
&lt;h2 id="clear-the-screen"&gt;Clear the Screen&lt;/h2&gt;
&lt;p&gt;Probably the shortcut I use the most is &lt;code&gt;Ctrl + l&lt;/code&gt;, which clears the screen without trashing your current command. Here&amp;rsquo;s how it looks:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/clear-screen-2.gif" alt="clear screen"&gt;&lt;/p&gt;
&lt;h2 id="pro-tip-all-the-keys"&gt;Pro Tip: All The Keys!&lt;/h2&gt;
&lt;p&gt;You can use the &lt;code&gt;bindkey&lt;/code&gt; command to see a list of all keyboard shortcuts:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ bindkey
&amp;#34;^@&amp;#34; set-mark-command
&amp;#34;^A&amp;#34; beginning-of-line
&amp;#34;^B&amp;#34; backward-char
&amp;#34;^D&amp;#34; delete-char-or-list
&amp;#34;^E&amp;#34; end-of-line
&amp;#34;^F&amp;#34; forward-char
&amp;#34;^G&amp;#34; send-break
&amp;#34;^H&amp;#34; backward-delete-char
&amp;#34;^I&amp;#34; expand-or-complete
&amp;#34;^J&amp;#34; accept-line
&amp;#34;^K&amp;#34; kill-line
&amp;#34;^L&amp;#34; clear-screen
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is an extremely useful command to use if you forget the specific keyboard shortcuts, or just want to see the shortcuts which are available.&lt;/p&gt;
&lt;h2 id="pro-tip-transposing"&gt;Pro Tip: Transposing!&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve mastered all of the commands here and feel like adding something else to your repertoire, try this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/transpose-word.gif" alt="transpose-word"&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Alt + t&lt;/code&gt; shortcut will transpose the last two words. Use &lt;code&gt;Ctrl + t&lt;/code&gt; to transpose the last two letters:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/transpose-letters.gif" alt="transpose-letters"&gt;&lt;/p&gt;
&lt;p&gt;These were new to me when I was researching for this article. I can&amp;rsquo;t see myself ever being able to remember the commands more quickly than just deleting the last two words or characters and re-typing them, but there you go!&lt;/p&gt;
&lt;h2 id="closing-thoughts"&gt;Closing Thoughts&lt;/h2&gt;
&lt;p&gt;If you are ever looking to go deeper, then search the web for &lt;em&gt;GNU Readline&lt;/em&gt;, which is the library used under the hood to handle the command line in many shells. You can actually configure lower level details of how all shells which use readline work, with the &lt;a href="https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File.html"&gt;&lt;code&gt;.inputrc&lt;/code&gt;&lt;/a&gt; configuration file.&lt;/p&gt;
&lt;p&gt;The great thing about learning these shortcuts is that they will work in any prompt which uses GNU Readline. This means everything you&amp;rsquo;ve learnt applies to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bash&lt;/li&gt;
&lt;li&gt;zsh&lt;/li&gt;
&lt;li&gt;The Python REPL&lt;/li&gt;
&lt;li&gt;The Node.js REPL&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And probably a whole bunch more&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;All of these shortcuts should be familiar to Emacs users. There is in fact a &amp;lsquo;Vi Mode&amp;rsquo; option for readline, which allows you to use vi commands to work with text. You can enter this mode with &lt;code&gt;set -o vi&lt;/code&gt;, I&amp;rsquo;ll likely come back to this in detail in a later article.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a great cheat sheet on emacs readline commands at &lt;a href="http://readline.kablamo.org/emacs.html"&gt;readline.kablamo.org/emacs&lt;/a&gt;, which is a very useful reference if you want to dig deeper. For this article I&amp;rsquo;ve tried to focus on what I think are the most useful commands (and transpose just so you can show off!).&lt;/p&gt;
&lt;p&gt;Hope that was useful! GIFs were made with &lt;a href="http://www.cockos.com/licecap/"&gt;LICEcap&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id="footnotes"&gt;Footnotes&lt;/h4&gt;
&lt;h4 id="references"&gt;References&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/GNU_Readline"&gt;Wikipedia: GNU Readline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File.html"&gt;GNU Org: Readline Init File&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://readline.kablamo.org/emacs.html"&gt;Kablamo.org: Readline Cheat Sheet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If you know of any more, please let me know and I&amp;rsquo;ll update the article!&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>A utility to help you wait for ports to open</title><link>https://dwmkerr.com/a-utility-to-help-you-wait-for-ports-to-open/</link><pubDate>Thu, 25 May 2017 22:15:00 +0000</pubDate><guid>https://dwmkerr.com/a-utility-to-help-you-wait-for-ports-to-open/</guid><description>&lt;p&gt;There are occasions where you might need to have scripts or commands which wait for TCP/IP ports to open before you continue.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve come across this need again and again when working with &lt;a href="https://dwmkerr.com/tag/microservices/"&gt;microservices&lt;/a&gt;, to make my life easier I&amp;rsquo;ve created a little utility called &lt;a href="https://github.com/dwmkerr/wait-port"&gt;wait-port&lt;/a&gt; which will wait for a port to open:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/wait-port"&gt;&lt;img src="images/wait-port.gif" alt="Wait Port Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s built in Node, the project is open source, open for contributions and ready to use:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/wait-port"&gt;github.com/dwmkerr/wait-port&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Installation and usage is pretty straightforward:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ npm install -g wait-port
wait-port@0.1.4
$ wait-port 8080
Waiting for localhost:8080.....
Connected!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can also install locally&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;This might be useful if you have a docker-compose workflow where you need to wait for a database to start up, want to run some automated tests against a server which can be slow to start, or have a complex set of interdependent services which need to start up in a specific order.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d be interested to know of any cases where people find this useful, so please share in the comments and I can add a &amp;lsquo;use cases&amp;rsquo; section to the project showing others how they might be able to save some time and energy with the utility!&lt;/p&gt;
&lt;h2 id="the-pure-shell-way"&gt;The Pure Shell Way&lt;/h2&gt;
&lt;p&gt;It is actually pretty easy to do this purely in bash. Here&amp;rsquo;s how you can wait for a port to open in a shell script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;until&lt;/span&gt; nc -w &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; 127.0.0.1 3000; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; sleep 1; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will be sufficient in many cases, the reason I created the utility is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I want something which is very readable in scripts (&lt;code&gt;wait-port 3000&lt;/code&gt; to me is more readable).&lt;/li&gt;
&lt;li&gt;I want to be able to specify an overall timeout (i.e. wait for up to 60 seconds) which requires adding more to the script.&lt;/li&gt;
&lt;li&gt;I need a different error code if the overall attempt to wait times out or fails for an unknown reason.&lt;/li&gt;
&lt;li&gt;I want to be able to optionally show some kind of progress (you can use the &lt;code&gt;--output&lt;/code&gt; flag to control the output from &lt;code&gt;wait-port&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;I know I need a few other features (being able to &amp;lsquo;snooze&amp;rsquo; after the port is opening, i.e. waiting for a little extra time, controllable intervals for trying the port etc, all of which can be easily added).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="testing-tip"&gt;Testing Tip!&lt;/h2&gt;
&lt;p&gt;One really useful tip which will be obvious to *nix pros but I wasn&amp;rsquo;t aware of is that you can create a server listening on a port with &lt;code&gt;netcat&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nc -l &lt;span style="color:#ae81ff"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is just the barest basics of what netcat can do, it&amp;rsquo;s a very powerful tool. This tip makes it very easy to test the &lt;code&gt;wait-port&lt;/code&gt; behaviour.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="footnotes"&gt;Footnotes&lt;/h3&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I hate installing things globally, if you are like me you&amp;rsquo;ll prefer local usage with something like: npm install wait-port &amp;amp;&amp;amp; ./node_modules/.bin/wait-port :3000&lt;/code&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Tips and Tricks for Beautifully Simple Mobile App CI</title><link>https://dwmkerr.com/tips-and-tricks-for-beautifully-simple-mobile-app-ci/</link><pubDate>Mon, 03 Apr 2017 11:14:58 +0000</pubDate><guid>https://dwmkerr.com/tips-and-tricks-for-beautifully-simple-mobile-app-ci/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to demonstrate some simple tips and tricks which will help you build and maintain beautifully simple mobile build pipelines. These techniques can be applied to different mobile app technologies and integrated into almost any build system:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/0-sample-index.png" alt="Sample App Index"&gt;&lt;/p&gt;
&lt;p&gt;Each tip is demonstrated in the sample apps in the &lt;a href="https://github.com/dwmkerr/beautifully-simple-app-ci"&gt;dwmkerr/beautifully-simple-app-ci&lt;/a&gt; repo.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#TheChallengesOfMobileAppCI"&gt;The Challenges of Mobile App CI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Tip1EmbraceMakefilesForConsistency"&gt;Tip 1 - Embrace Makefiles for Consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Tip2ControlVersionNumbersWithATouchCommand"&gt;Tip 2 - Control Version Numbers with a &amp;lsquo;Touch&amp;rsquo; Command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#tip3controlappiconswithalabelcommand"&gt;Tip 3 - Control App Icons with a &amp;lsquo;Label&amp;rsquo; Command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Tip4SupportConfigurableAppIds"&gt;Tip 4 - Support Configurable App Ids&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#Tip5DocumentDocumentDocument"&gt;Tip 5 - Document, Document, Document&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#/conclusion"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="the-challenges-of-mobile-app-ci"&gt;The Challenges of Mobile App CI&lt;/h2&gt;
&lt;p&gt;Conceptually, a mobile app CI pipeline is pretty simple:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/1-basic-ci.png" alt="Basic CI Pipeline"&gt;&lt;/p&gt;
&lt;p&gt;We take our code, perform some kind of validation (such as testing, linting, whatever), generate our artifacts and then deploy them to some devices.&lt;/p&gt;
&lt;p&gt;Often though there&amp;rsquo;s a bit more to it than that:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/2-basic-not-basic-1.png" alt="Basic CI is not Basic"&gt;&lt;/p&gt;
&lt;p&gt;Our source code has some metadata associated with it at the point in time you create your binaries, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The SHA, which uniquely identifies your exact location in the source history.&lt;/li&gt;
&lt;li&gt;The branch, which may have some &lt;em&gt;semantic&lt;/em&gt; meaning for your project, for example &lt;code&gt;master&lt;/code&gt; meaning &amp;lsquo;production&amp;rsquo; or &lt;code&gt;alpha&lt;/code&gt; meaning your current unstable public build.&lt;/li&gt;
&lt;li&gt;A tag, which may represent something like a semver, or may have more project-specific meaning.&lt;/li&gt;
&lt;li&gt;A version, which might be in something like a &lt;code&gt;package.json&lt;/code&gt; or embedded in your project files for iOS or Android.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When we build we have to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Think about how we test and validate&lt;/li&gt;
&lt;li&gt;Think about how we sign&lt;/li&gt;
&lt;li&gt;Handle package names and bundle ids, which can cause headaches if you are going to install multiple &lt;em&gt;versions&lt;/em&gt; of an app (e.g. dev and UAT builds)&lt;/li&gt;
&lt;li&gt;Consider build numbers and version number&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So even the &amp;lsquo;basic&amp;rsquo; CI isn&amp;rsquo;t all that basic. The rest of this article is a set of tips and techniques which I have found useful when developing mobile apps.&lt;/p&gt;
&lt;h2 id="tip-1---embrace-makefiles-for-consistency"&gt;Tip 1 - Embrace Makefiles for Consistency&lt;/h2&gt;
&lt;p&gt;There are a raft of platform and framework specific tools and interfaces we will have to use in mobile projects. XCode, Gradle, NPM, framework specific CLIs, tools such as Fastlane, etc etc.&lt;/p&gt;
&lt;p&gt;If you ensure that your main &amp;rsquo;entrypoint&amp;rsquo; to key operations is a recipe in a makefile, you can provide a degree of consistency to mobile projects. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;make build&lt;/code&gt; - Creates an IPA and APK, saving them to the &lt;code&gt;./artifacts&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make test&lt;/code&gt; - Runs all test suites.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make deploy&lt;/code&gt; - Deploys the binaries.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;makefile&lt;/code&gt; for such commands might look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;test:
# Run all the tests.
npm test
build:
# Create the apk, copy to artifacts.
cd android &amp;amp;&amp;amp; ./gradlew assembleRelease &amp;amp;&amp;amp; cd ..
cp -f ./android/app/build/outputs/apk/myapp.apk ./artifacts
# Create the ipa, copy to artifacts.
cd ./ios; fastlane gym --scheme &amp;#34;app&amp;#34; --codesigning_identity &amp;#34;$(CODE_SIGNING_IDENTITY)&amp;#34;; cd ../;
cp -f ./ios/myapp.ipa ./artifacts
deploy:
# Push to TestFairy.
curl https://app.testfairy.com/api/upload \
-F api_key=&amp;#39;$(API_KEY)&amp;#39; \
-F &amp;#34;file=@./artifacts/myapp.apk&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a slightly shortened snippet, you can see a variety of working examples in the git repo:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/beautifully-simple-app-ci"&gt;github.com/dwmkerr/beautifully-simple-app-ci&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The first sample in the above repo demonstrates using makefiles to handle key commands for a React Native app. In the example, CircleCI is used to handle automatic builds on code changes, and the apps themselves are distributed automatically to testers&amp;rsquo; devices with TestFairy.&lt;/p&gt;
&lt;p&gt;The nice feature is that the bulk of the logic is in the main repo source, in the &lt;code&gt;makefile&lt;/code&gt; - the CI tool simply orchestrates it. Developers can run &lt;em&gt;exactly&lt;/em&gt; the same commands on their local machine.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/dwmkerr/beautifully-simple-app-ci/blob/master/1_react_native_app/README.md"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt; immediately draws attention to the makefile commands:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/3-tip1-readme.png" alt="Screenshot of the README.md file"&gt;&lt;/p&gt;
&lt;p&gt;The makefiles do most of the work, that makes setting up CircleCI almost trivial. Here&amp;rsquo;s a snippet of its config:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Tell Circle where we keep our artifacts.
general:
artifacts:
- ./artifacts
# When we test, we build the android app and test it.
test:
override:
- make build-android
- make test
# If there are any changes to the master branch, push a new version
# of the app.
deployment:
master:
branch: [master]
commands:
- make deploy-android
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Our commands are android specific at this stage as Circle don&amp;rsquo;t support iOS builds on their free plan&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. Later samples which use other build systems demonstrate Android &lt;em&gt;and&lt;/em&gt; iOS.&lt;/p&gt;
&lt;p&gt;The CI automatically tests and builds whenever we have new code commits:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/4-tip1-circle.png" alt="Screenshot of CircleCI and the artifacts"&gt;&lt;/p&gt;
&lt;p&gt;Also, if a commit is made to the &lt;code&gt;master&lt;/code&gt; branch, our new app is automatically pushed to TestFairy, which can be configured to automatically update the test team:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/5-tip1-testfairy.png" alt="Screenshot of TestFairy"&gt;&lt;/p&gt;
&lt;p&gt;Makefile syntax is close enough to shell scripting that simple operations are generally straightforward&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to implement. The approach is also perfectly valid for server side code and almost any project.&lt;/p&gt;
&lt;p&gt;Teams with many projects can build consistent patterns and syntax for building. Take a look at the image below:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Simple-Docker-Image-CI.png" alt="Docker Workflow"&gt;&lt;/p&gt;
&lt;p&gt;This is from my article on &lt;a href="http://www.dwmkerr.com/simple-continuous-integration-for-docker-images/"&gt;Simple Continuous Integration for Docker Images&lt;/a&gt; - where exactly the same principles are applied.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Makefiles allow you to provide an entrypoint for common app CI tasks which is framework and toolkit agnostic&lt;/li&gt;
&lt;li&gt;Being able to run the individual &lt;em&gt;steps&lt;/em&gt; of a CI build on a local machine makes it easier for developers to work with the pipeline&lt;/li&gt;
&lt;li&gt;By having a CI platform only need to handle the orchestration of these simple steps, we are less tied to specific platforms and can reduce lock-in&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;ll see more interesting makefile recipes as we get into the other tips.&lt;/p&gt;
&lt;h2 id="tip-2---control-version-numbers-with-a-touch-command"&gt;Tip 2 - Control Version Numbers with a &amp;lsquo;Touch&amp;rsquo; command&lt;/h2&gt;
&lt;p&gt;iOS and Android apps have both a &lt;em&gt;version number&lt;/em&gt; and a &lt;em&gt;build number&lt;/em&gt;. We might have other files in our project with version numbers too (such as a &lt;code&gt;package.json&lt;/code&gt; file).&lt;/p&gt;
&lt;p&gt;It can be very useful to have a way of keeping these version numbers in sync. Again, we can use a makefile recipe:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make touch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This command will vary in implementation depending on your platform. For example, this would be all that is needed for a Cordova based project:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# The version in package.json is the &amp;#39;master&amp;#39; version.
VERSION ?= $(shell cat package.json | jq --raw-output .version)
BUILD_NUM ?= 0
touch:
$(info &amp;#34;Touching to version $(VERSION) and build number $(BUILD_NUM).&amp;#34;)
sed -i &amp;#34;&amp;#34; -e &amp;#39;s/android-versionCode=\&amp;#34;[0-9]*\&amp;#34;/android-versionCode=\&amp;#34;$(BUILD_NUM)\&amp;#34;/g&amp;#39; ./config.xml
sed -i &amp;#34;&amp;#34; -e &amp;#39;s/ios-CFBundleVersion=\&amp;#34;[0-9]*\&amp;#34;/ios-CFBundleVersion=\&amp;#34;$(BUILD_NUM)\&amp;#34;/g&amp;#39; ./config.xml
sed -i &amp;#34;&amp;#34; -e &amp;#39;s/version=\&amp;#34;[.0-9a-zA-Z]*\&amp;#34;/version=\&amp;#34;$(VERSION)&amp;#34;/g&amp;#39; ./config.xml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice we don&amp;rsquo;t really need complex tools for a job like this, &lt;code&gt;sed[^3]&lt;/code&gt; is sufficient to quickly make changes to config files.&lt;/p&gt;
&lt;p&gt;This works very nicely with build systems, many of which provide a build number as an environment variable. For example, we can add a build number with TravisCI like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;env:
- BUILD_NUM=$TRAVIS_BUILD_NUMBER
script:
- make touch
- make test
- make build-android
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To go into more detail, we&amp;rsquo;ll look at the second sample in the git repo, which is a Cordova App. This sample will always set the build number in both apps and the build version to whatever is present in the &lt;code&gt;package.json&lt;/code&gt; file. That means you can do things like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ npm version minor # Bump the version
v0.1.0
$ BUILD_NUM=3 make build &amp;amp;&amp;amp; make deploy # Build and deploy the apps
...
done
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And all of the version numbers and build numbers are updated and the apps are deployed. In this example project, they&amp;rsquo;re deployed to HockeyApp:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/6-hockey-app.png" alt="Screenshot of the newly versioned apps in HockeyApp"&gt;&lt;/p&gt;
&lt;p&gt;This build runs on TravisCI, so only builds the Android version. You can clone the code and build the iOS version (and deploy it) using the makefile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There will come a point in your project development where you&amp;rsquo;ll need to handle version numbers, having a command to explicitly deal with this adds rigour to this process&lt;/li&gt;
&lt;li&gt;Build numbers are just as important as version numbers during development, ensuring your CI build number is baked into your artifacts is critical for troubleshooting and control&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="tip-3---control-app-icons-with-a-label-command"&gt;Tip 3 - Control App Icons with a &amp;lsquo;Label&amp;rsquo; Command&lt;/h1&gt;
&lt;p&gt;When you are working in a larger team, it can be very useful to label your app icon so that team members know exactly what version of the app they are using. This is often the case if you are working in a team where features or bugfixes are being deployed rapidly.&lt;/p&gt;
&lt;p&gt;You might label your icons with build numbers, SHAs, branch names, versions, tags, or even something custom such as &amp;lsquo;QA&amp;rsquo; or &amp;lsquo;UAT&amp;rsquo; for different versions of your app. Here are a few examples:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/8-framed-labelled-icons.png" alt="Labelled Icons Screenshot"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve found this to be very useful, so created a command-line tool called &amp;lsquo;&lt;a href="github.com/dwmkerr/app-icon"&gt;app-icon&lt;/a&gt;&amp;rsquo; to help with the task:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/app-icon"&gt;github.com/dwmkerr/app-icon&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This tool has a &lt;code&gt;label&lt;/code&gt; command to add a label, and a &lt;code&gt;generate&lt;/code&gt; command to generate icons of all different sizes. This means you can add recipes like this to your &lt;code&gt;makefile&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;VERSION ?= $(shell cat package.json | jq --raw-output .version)
BUILD_NUM ?= 0 # This might come from Circle, Travis or Whatever...
label:
$(info Labeling icon with &amp;#39;$(VERSION)&amp;#39; and &amp;#39;$(BUILD_NUM)&amp;#39;...)
app-icon label -i base-icon.png -o icon.png --top $(VERSION) --bottom $(BUILD_NUM)
app-icon generate -i icon.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each sample app labels its icon in a different way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;a href="./1_react_native_app/"&gt;React Native App&lt;/a&gt; puts the short Git SHA on the bottom of the icon.&lt;/li&gt;
&lt;li&gt;The &lt;a href="./2_ionic_app/"&gt;Ionic App&lt;/a&gt; puts the &lt;code&gt;package.json&lt;/code&gt; version at the top of the icon.&lt;/li&gt;
&lt;li&gt;The &lt;a href="./3_native_app"&gt;Native App&lt;/a&gt; puts an environment label at the top of the icon, and the build number at the bottom.&lt;/li&gt;
&lt;li&gt;The &lt;a href="./4_xamarinapp"&gt;Xamarin App&lt;/a&gt; includes the configurable app environment (this is detailed in the next tip) and build number&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are references to each sample and the associated code in the &lt;code&gt;README.md&lt;/code&gt; at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/beautifully-simple-app-ci"&gt;github.com/dwmkerr/beautifully-simple-app-ci&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As a quick example, the Pure Native App runs this code prior to each build:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BUILD_NUM&lt;span style="color:#f92672"&gt;=&lt;/span&gt;BUDDYBUILD_BUILD_NUMBER make label
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This app uses BuddyBuild as a build system, meaning we can just drop this line in the &lt;a href="./buddybuild_postclone.sh"&gt;&lt;code&gt;buddybuild_postclone.sh&lt;/code&gt;&lt;/a&gt; script. You can see the labeled icons directly in the BuddyBuild UI:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/12-buddybuild-icons.png" alt="BuddyBuild Icons"&gt;&lt;/p&gt;
&lt;p&gt;The Android build is currently having some issues due to fonts being accessible by the labelling tool (which uses ImageMagick under the hood), with any luck this issue will be fixed soon. This seems to be an issue with the BuddyBuild ImageMagick installation rather than the labelling code itself, which is running fine on all of the other builds!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A little bit of time invested in managing your app icon can potentially save many hours if you are rapidly iterating on apps&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/dwmkerr/app-icon"&gt;&lt;code&gt;app-icon&lt;/code&gt;&lt;/a&gt; tool can help you quickly label and generate icons&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="tip-4---support-configurable-app-ids"&gt;Tip 4 - Support Configurable App Ids&lt;/h1&gt;
&lt;p&gt;Another trick I&amp;rsquo;ve found useful is to have a command which automatically updates your iOS Bundle ID or Android Application ID. This can be handy when you have multiple versions of an app (such as a QA build, dev build, UAT build or whatever).&lt;/p&gt;
&lt;p&gt;If you have users who need to have different versions of your app on their phones then this is actually a necessary step (at least for iOS), as you cannot have multiple versions of an app with the same ID installed.&lt;/p&gt;
&lt;p&gt;Often, I will aim to have a standard &amp;lsquo;base id&amp;rsquo;, such as:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;com.dwmkerr.myapp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then simply append whatever the &amp;lsquo;flavour&amp;rsquo; of my app is to the end of the id:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;com.dwmkerr.myapp_qa # The QA build...
com.dwmkerr.myapp_uat # The UAT build...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The base id is then reserved for the master build, which is what goes into production.&lt;/p&gt;
&lt;p&gt;Just like with all of the other tricks, I tend to use a recipe in the &lt;code&gt;makefile&lt;/code&gt; to do the heavy lifting, and then leave the build system to orchestrate the commands (we&amp;rsquo;ll see more of this later). Here&amp;rsquo;s how a recipe will typically look (this comes from the fourth sample, which is a Xamarin App):&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ENV ?= production
# Set the app id, with the &amp;#39;production&amp;#39; environment implying the unaltered &amp;#39;base&amp;#39; id.
ifeq ($(ENV),production)
APP_ID=com.dwmkerr.xamarinapp
else
APP_ID=com.dwmkerr.xamarinapp_$(ENV)
endif
name:
$(info Naming app &amp;#39;$(APP_ID)&amp;#39;...)
sed -i.bak &amp;#39;s/com.dwmkerr.xamarinapp.*&amp;lt;/$(APP_ID)&amp;lt;/&amp;#39; iOS/Info.plist
sed -i.bak &amp;#39;s/com.dwmkerr.xamarinapp.*\&amp;#34;/$(APP_ID)\&amp;#34;/&amp;#39; Droid/Properties/AndroidManifest.xml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This small recipe can be very useful in combination with other techniques. Ensuring your build respects the &lt;code&gt;ENV&lt;/code&gt; variable (or whatever you name your &amp;lsquo;flavour&amp;rsquo;) means that you can have different configurations for different environments, build multiple versions of the app, each with a distinct app icon, and distribute them to your team.&lt;/p&gt;
&lt;p&gt;In the screenshots below, you can see how the presence of the &lt;code&gt;ENV&lt;/code&gt; environment variable automatically updates the App ID (this is taken from the &lt;a href="./4_xamarinapp"&gt;Xamarin Sample&lt;/a&gt;, which orchestrates builds with Bitrise&lt;sup id="fnref1:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/9-bitrise.png" alt="The ENV Environment variable in Bitrise"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/10-bitrise-apps.png" alt="The built apps in Bitrise"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configurable App Ids allow you to maintain isolated builds of your app for specific environments, even on the same physical device&lt;/li&gt;
&lt;li&gt;This tip must be used with caution, some features (such as iOS push notifications) will not work if the bundle id is changed (it can also cause issues if your provisioning profile does not use a wildcard)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tip-5---document-document-document"&gt;Tip 5 - Document, Document, Document&lt;/h2&gt;
&lt;p&gt;Even teams which are great at documenting complex application code can sometimes be a bit lax when it comes to documenting build related code.&lt;/p&gt;
&lt;p&gt;Unfortunately, build related code will often need &lt;em&gt;more&lt;/em&gt; documentation than usual. Why is this?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is often &lt;em&gt;complex&lt;/em&gt; (spend any time working with the XCode commandline or provisioning profiles and you&amp;rsquo;ll likely agree)&lt;/li&gt;
&lt;li&gt;It is &lt;em&gt;rarely changed&lt;/em&gt; (often worked on heavily at the early stages of a project then not touched)&lt;/li&gt;
&lt;li&gt;It is &lt;em&gt;critical&lt;/em&gt; (when it breaks, teams are often blocked)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When something goes wrong with a build process, or needs to be changed, it is a real pain when only one person knows how the code works. Be rigorous with this code, make sure it is documented and reviewed, and share the knowledge around your team. I tend to like to have a table of commands as a quick index in the README.md file, and then heavily comment the code itself:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/11-document.png" alt="TODO"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In Summary&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be rigorous with documentation, when things go wrong with CI code then people are often blocked&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Most of these tips are fairly explicit, there are detailed examples in the sample project. Familiarity with these patterns and techniques can be useful, but perhaps the most valuable takeaway would be to embrace the following principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developers should be able to run all of the key CI steps on their local machine, to be able to understand, adapt and improve the process&lt;/li&gt;
&lt;li&gt;When building more complex features, we should create small, simple units of work which can be composed into larger pipelines&lt;/li&gt;
&lt;li&gt;Complexity, if needed, should be in in code - not in &amp;lsquo;black box&amp;rsquo; CI tools (such as esoteric features for specific CI providers or Jenkins plugins). For example, CircleCI offers a Git Short SHA environment variable - but you can grab a short SHA with &lt;code&gt;git log -1 --format=&amp;quot;%h&amp;quot;&lt;/code&gt;, and the second approach works anywhere&lt;/li&gt;
&lt;li&gt;Use CI platforms to &lt;em&gt;orchestrate&lt;/em&gt; work, use makefiles and scripts to handle logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this article has been useful, any thoughts or comments are always welcome!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I have successfully used this approach to build Android &lt;em&gt;and&lt;/em&gt; iOS from the same OSX build agent on their paid plan on a number of projects. The most straightforward way to do this is to have a single build run on OSX and create the Android app as well as the iOS app.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Perhaps straightforward is an overstatement, but getting those who are familiar with shell scripting will have few difficulties. Those who are not will find a learning curve, but it is &lt;em&gt;very&lt;/em&gt; useful to at least get the basics of shell scripting learnt.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Get up and running with OpenShift on AWS</title><link>https://dwmkerr.com/get-up-and-running-with-openshift-on-aws/</link><pubDate>Thu, 02 Feb 2017 07:47:00 +0000</pubDate><guid>https://dwmkerr.com/get-up-and-running-with-openshift-on-aws/</guid><description>&lt;p&gt;&lt;a href="https://www.openshift.com/"&gt;OpenShift&lt;/a&gt; is Red Hat&amp;rsquo;s platform-as-a-service offering for hosting and scaling applications. It&amp;rsquo;s built on top of Google&amp;rsquo;s popular &lt;a href="https://kubernetes.io/"&gt;Kubernetes&lt;/a&gt; system.&lt;/p&gt;
&lt;p&gt;Getting up and running with OpenShift Online is straightforward, as it is a cloud hosted solution. Setting up your own cluster is a little more complex, but in this article I&amp;rsquo;ll show you how to make it fairly painless.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/welcome.png" alt="OpenShift Login"&gt;&lt;/p&gt;
&lt;p&gt;The repo for this project is at: &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;github.com/dwmkerr/terraform-aws-openshift&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="creating-the-infrastructure"&gt;Creating the Infrastructure&lt;/h2&gt;
&lt;p&gt;OpenShift has some fairly specific requirements about what hardware it runs on&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. There&amp;rsquo;s also DNS to set up, as well as internet access and so on.&lt;/p&gt;
&lt;p&gt;All in all, for a bare-bones setup, you&amp;rsquo;ll need something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/network-diagram-2.png" alt="Network Diagram"&gt;&lt;/p&gt;
&lt;p&gt;Which is (deep breath):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A network&lt;/li&gt;
&lt;li&gt;A public subnet, with internet access via a gateway&lt;/li&gt;
&lt;li&gt;A master host, which will run the OpenShift master&lt;/li&gt;
&lt;li&gt;A pair of node hosts, which will run additional OpenShift nodes&lt;/li&gt;
&lt;li&gt;A hosted zone, which allows us to configure DNS&lt;/li&gt;
&lt;li&gt;A bastion, which allows us to SSH onto hosts, without directly exposing them&lt;/li&gt;
&lt;li&gt;Some kind of basic log aggregation, which I&amp;rsquo;m using CloudWatch for&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is not a production grade setup, which requires redundant masters and so on, but it provides the basics.&lt;/p&gt;
&lt;p&gt;Rather than setting this infrastructure up by hand, this is all scripted with &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt;. To set up the infrastructure, clone the &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;github.com/dwmkerr/terraform-aws-openshift&lt;/a&gt; repo:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ git clone git@github.com:dwmkerr/terraform-aws-openshift
...
Resolving deltas: 100% (37/37), done.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then use the terraform CLI&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to create the infrastructure:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ cd terraform-aws-openshift/
$ terraform get &amp;amp;&amp;amp; terraform apply
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ll be asked for a region, to deploy the network into, here I&amp;rsquo;m using &lt;code&gt;us-west-1&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screenshot-at-Feb-02-21-16-44.png" alt="Enter Region"&gt;&lt;/p&gt;
&lt;p&gt;After a few minutes the infrastructure will be set up:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/output.png" alt="Terraform complete"&gt;&lt;/p&gt;
&lt;p&gt;A quick glance at the AWS console shows the new hosts we&amp;rsquo;ve set up:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/aws.png" alt="AWS Console"&gt;&lt;/p&gt;
&lt;p&gt;The next step is to install OpenShift.&lt;/p&gt;
&lt;h2 id="installing-openshift"&gt;Installing OpenShift&lt;/h2&gt;
&lt;p&gt;There are a few different ways to install OpenShift, but the one we&amp;rsquo;ll use is called the &amp;lsquo;advanced installation&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&amp;rsquo;. This essentially involves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creating an &amp;lsquo;inventory&amp;rsquo;, which specifies the hosts OpenShift will be installed on and the installation options&lt;/li&gt;
&lt;li&gt;Downloading the advanced installation code&lt;/li&gt;
&lt;li&gt;Running the advanced installation Ansible Playbook&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To create the inventory, we just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sed &lt;span style="color:#e6db74"&gt;&amp;#34;s/\${aws_instance.master.public_ip}/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;terraform output master-public_ip&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/&amp;#34;&lt;/span&gt; inventory.template.cfg &amp;gt; inventory.cfg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This takes our &amp;lsquo;inventory template&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&amp;rsquo; and populates it with the public IP of our master node, which is recorded in a Terraform output variable.&lt;/p&gt;
&lt;p&gt;We can then copy the inventory to the bastion:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh-add ~/.ssh/id_rsa
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp ./inventory.cfg ec2-user@&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;terraform output bastion-public_dns&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;:~
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can again use the Terraform output variables, this time to get the bastion IP. Finally, we pipe our install script to the bastion host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat install-from-bastion.sh | ssh -A ec2-user@&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;terraform output bastion-public_dns&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s a &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift/issues/1"&gt;bug&lt;/a&gt; which means you might see &lt;code&gt;ansible-playbook: command not found&lt;/code&gt;, if so, just run the script again. The install script clones the installation scripts and runs them, using the inventory we&amp;rsquo;ve provided:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/ansible.png" alt="Ansible Output"&gt;&lt;/p&gt;
&lt;p&gt;This&amp;rsquo;ll probably take about 10 minutes to run. And that&amp;rsquo;s it, OpenShift is installed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;open &lt;span style="color:#e6db74"&gt;&amp;#34;https://&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;terraform output master-public_dns&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;:8443&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hit &amp;lsquo;advanced&amp;rsquo; and continue, as we&amp;rsquo;re using a self-signed certificate most browsers will complain:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/console1.png" alt="Invalid Certificate"&gt;&lt;/p&gt;
&lt;p&gt;Enter any username and password (the system is configured to allow anyone to access it by default) and you&amp;rsquo;ll be presented with the OpenShift console:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/console2.png" alt="OpenShift console"&gt;&lt;/p&gt;
&lt;p&gt;As the setup requires three t2.large instances, which are not available on the free plan, you might want to clean up when you are done with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;terraform destroy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Hopefully you&amp;rsquo;ve found this useful, there are more details and references on the README of the github repo:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;https://github.com/dwmkerr/terraform-aws-openshift&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Comments and feedback are always welcome!&lt;/p&gt;
&lt;hr&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See &lt;a href="https://docs.openshift.org/latest/install_config/install/prerequisites.html#system-requirements"&gt;https://docs.openshift.org/latest/install_config/install/prerequisites.html#system-requirements&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Use &amp;lsquo;brew install terraform&amp;rsquo;, full instructions in the &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift"&gt;README.md&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;See &lt;a href="https://docs.openshift.org/latest/install_config/install/advanced_install.html"&gt;https://docs.openshift.org/latest/install_config/install/advanced_install.html&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;See &lt;a href="https://github.com/dwmkerr/terraform-aws-openshift/blob/master/inventory.template.cfg"&gt;https://github.com/dwmkerr/terraform-aws-openshift/blob/master/inventory.template.cfg&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Creating a Resilient Consul Cluster for Docker Microservice Discovery with Terraform and AWS</title><link>https://dwmkerr.com/creating-a-resilient-consul-cluster-for-docker-microservice-discovery-with-terraform-and-aws/</link><pubDate>Mon, 09 Jan 2017 07:10:40 +0000</pubDate><guid>https://dwmkerr.com/creating-a-resilient-consul-cluster-for-docker-microservice-discovery-with-terraform-and-aws/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to show you how to create a resilient Consul cluster, using Terraform and AWS. We can use this cluster for microservice discovery and management. No prior knowledge of the technologies or patterns is required!&lt;/p&gt;
&lt;p&gt;The final code is at &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster"&gt;github.com/dwmkerr/terraform-consul-cluster&lt;/a&gt;. Note that it has evolved somewhat since the time of writing, see the Appendices at the end of the article for details.&lt;/p&gt;
&lt;h2 id="consul-terraform--aws"&gt;Consul, Terraform &amp;amp; AWS&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.consul.io/"&gt;Consul&lt;/a&gt; is a technology which enables &lt;em&gt;Service Discovery&lt;/em&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;, a pattern which allows services to locate each other via a central authority.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; is a technology which allows us to script the provisioning of infrastructure and systems. This allows us to practice the &lt;em&gt;Infrastructure as Code&lt;/em&gt; pattern. The rigour of code control (versioning, history, user access control, diffs, pull requests etc) can be applied to our systems.&lt;/p&gt;
&lt;p&gt;And why &lt;a href="https://aws.amazon.com/"&gt;AWS&lt;/a&gt;? We need to create many servers and build a network to see this system in action. We can simulate parts of this locally with tools such as &lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt;, but we can use the arguably most popular&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; IaaS platfom for this job at essentially zero cost, and learn some valuable skills which are readily applicable to other projects at the same time.&lt;/p&gt;
&lt;p&gt;A lot of what we will learn is not really AWS specific - and the Infrastructure as Code pattern which Terraform helps us apply allows us to apply these techniques easily with other providers.&lt;/p&gt;
&lt;h2 id="the-goal"&gt;The Goal&lt;/h2&gt;
&lt;p&gt;The goal is to create a system like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-0-goal.png" alt="Overall System Diagram"&gt;&lt;/p&gt;
&lt;p&gt;In a nutshell:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We have a set of homogenous Consul nodes&lt;/li&gt;
&lt;li&gt;The nodes form a cluster and automatically elect a leader&lt;/li&gt;
&lt;li&gt;The nodes span more than one availability zone, meaning the system is redundant and can survive the failure of an entire availability zone (i.e. data centre)&lt;/li&gt;
&lt;li&gt;The Consul UI is available to view via a gateway&lt;/li&gt;
&lt;li&gt;We have two example microservices which register themselves on the cluster, so we can actually see some registered services in the console&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As a quick caveat, in reality this setup would typically live in a private subnet, not directly accessible to the outside work except via public facing load balancers. This adds a bit more complexity to the Terraform setup but not much value to the walk-though. A network diagram of how it might look is below, I invite interested readers to try and move to this model as a great exercise to cement the concepts!&lt;/p&gt;
&lt;h2 id="step-1---creating-our-network"&gt;Step 1 - Creating our Network&lt;/h2&gt;
&lt;p&gt;The first logical step is to create the network itself. This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The network (in AWS terminology, this is a &lt;em&gt;VPC&lt;/em&gt; or &lt;em&gt;Virtual Private Cloud&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;The &amp;lsquo;public&amp;rsquo; subnet, which defines our IP ranges for hosts&lt;/li&gt;
&lt;li&gt;The internet gateway, which provides an entry/exit point for traffic from/to the internet&lt;/li&gt;
&lt;li&gt;The firewall rules, which define what traffic can come in and out of the network&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All together, that&amp;rsquo;s this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-1-network.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Our solution will be made more resilient by ensuring we host our Consul nodes across multiple &lt;em&gt;availability zones&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Creating a VPC and building a subnet is fairly trivial if you have done some network setup before or spent much time working with AWS, if not, you may be a little lost already. There&amp;rsquo;s a good course on Udemy&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; which will take you through the process of setting up a VPC which I recommend if you are interested in this, as it is quite hands on. It&amp;rsquo;ll also show you how to build a more &amp;lsquo;realistic&amp;rsquo; network, which also contains a private subnet and NAT, but that&amp;rsquo;s beyond the scope of this write-up. Instead, I&amp;rsquo;ll take you through the big parts.&lt;/p&gt;
&lt;h3 id="the-network"&gt;The Network&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re using AWS, we need to create a VPC. A VPC is a Virtual Private Cloud. The key thing is that it is &lt;em&gt;isolated&lt;/em&gt;. Things you create in this network will be able to talk to each other if you let them, but cannot communicate with the outside world, unless you specifically create the parts needed for them to do so.&lt;/p&gt;
&lt;p&gt;A private network is probably something you regularly use if you work in a company&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;. Most companies have their own internal network - when you use a computer on that network it can talk to other company computers (such as the company mail server). When you are off that network, you might not be able to access your company email (unless it is publicly available, like gmail, or over a VPN [and by accessing a VPN, you are actually &lt;em&gt;joining&lt;/em&gt; the network again, albeit remotely]).&lt;/p&gt;
&lt;p&gt;Perhaps the most immediately obvious part of a VPC is that &lt;em&gt;you control the IP addresses&lt;/em&gt;. You specify the &lt;em&gt;range&lt;/em&gt; of IP addresses which are available to give to machines on the network. When a machine joins, it is given an IP in that range. I&amp;rsquo;m not going to go into too much detail here, if you are interested let me know and I&amp;rsquo;ll write up an article on VPCs in detail!&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-3-vpc.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how I&amp;rsquo;d suggest scripting AWS infrastructure with Terraform if you haven&amp;rsquo;t done this before.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the AWS console to create what you want&lt;/li&gt;
&lt;li&gt;Search the Terraform documentation for the entity you want to create (e.g. &lt;a href="https://www.terraform.io/docs/providers/aws/r/vpc.html"&gt;VPC&lt;/a&gt;), &lt;em&gt;script&lt;/em&gt; the component and &lt;em&gt;apply&lt;/em&gt; the provisioning&lt;/li&gt;
&lt;li&gt;Compare the hand-made VPC to the script-made VPC, if the two are the same, you are done&lt;/li&gt;
&lt;li&gt;If the two are different, check the documentation and try again&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ensure you have an AWS account, and note your Secret Key and Access Key. We&amp;rsquo;ll need these to remotely control it. Here&amp;rsquo;s the terraform script to create a VPC:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Setup the core provider information.
provider &amp;#34;aws&amp;#34; {
access_key = &amp;#34;${var.access_key}&amp;#34;
secret_key = &amp;#34;${var.secret_key}&amp;#34;
region = &amp;#34;${var.region}&amp;#34;
}
// Define the VPC.
resource &amp;#34;aws_vpc&amp;#34; &amp;#34;consul-cluster&amp;#34; {
cidr_block = &amp;#34;10.0.0.0/16&amp;#34; // i.e. 10.0.0.0 to 10.0.255.255
enable_dns_hostnames = true
tags {
Name = &amp;#34;Consul Cluster VPC&amp;#34;
Project = &amp;#34;consul-cluster&amp;#34;
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This script uses &lt;a href="https://www.terraform.io/docs/configuration/variables.html"&gt;Terraform Variables&lt;/a&gt;, such as &lt;code&gt;var.access_key&lt;/code&gt;, which we keep in a &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/blob/master/variables.tf"&gt;variables.tf&lt;/a&gt; file. Terraform will use the default values defined in the file if they are present, or ask the user to supply them. Let&amp;rsquo;s build the network:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After supplying the values for the variables, Terraform will provision the network, using the AWS SDK internally.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-2-terraform-apply.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll see lots of info about what it is creating, then a success message.&lt;/p&gt;
&lt;h3 id="the-public-subnet"&gt;The Public Subnet&lt;/h3&gt;
&lt;p&gt;You don&amp;rsquo;t put hosts directly into a VPC, they need to go into a structure called a &amp;lsquo;subnet&amp;rsquo;, which is a &lt;em&gt;part&lt;/em&gt; of a VPC. Subnets get their own subset of the VPC&amp;rsquo;s available IP addresses, which you specify.&lt;/p&gt;
&lt;p&gt;Subnets are used to build &lt;em&gt;zones&lt;/em&gt; in a network. Why would you need this? Typically it is to manage security. You might have a &amp;lsquo;public zone&amp;rsquo; in which all hosts can be accessed from the internet, and a &amp;lsquo;private zone&amp;rsquo; which is inaccessible directly (and therefore a better location for hosts with sensitive data). You might have an &amp;lsquo;operator&amp;rsquo; zone, which only sysadmins can access, but they can use to get diagnostic information.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a common subnet layout for multi-tiered applications:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-4-subnets.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;The defining characteristics of zones is that they are used to create &lt;em&gt;boundaries&lt;/em&gt; to isolate hosts. These boundaries are normally secured by firewalls, traversed via gateways or NATs etc. We&amp;rsquo;re going to create two public subnets, one in each of the availability zones&lt;sup id="fnref1:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Create a public subnet for each AZ.
resource &amp;#34;aws_subnet&amp;#34; &amp;#34;public-a&amp;#34; {
vpc_id = &amp;#34;${aws_vpc.consul-cluster.id}&amp;#34;
cidr_block = &amp;#34;10.0.1.0/24&amp;#34; // i.e. 10.0.1.0 to 10.0.1.255
availability_zone = &amp;#34;ap-southeast-1a&amp;#34;
map_public_ip_on_launch = true
}
resource &amp;#34;aws_subnet&amp;#34; &amp;#34;public-b&amp;#34; {
vpc_id = &amp;#34;${aws_vpc.consul-cluster.id}&amp;#34;
cidr_block = &amp;#34;10.0.2.0/24&amp;#34; // i.e. 10.0.2.0 to 10.0.1.255
availability_zone = &amp;#34;ap-southeast-1b&amp;#34;
map_public_ip_on_launch = true
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With Terraform, resources can depend on each other. In this case, the subnets need to reference the ID of the VPC we want to place them in (so we use &lt;code&gt;aws_vpc.consul-cluster.id&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="the-internet-gateway-route-tables-and-security-groups"&gt;The Internet Gateway, Route Tables and Security Groups&lt;/h3&gt;
&lt;p&gt;The final parts of the network you can see in the &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/blob/master/network.tf"&gt;./infrastructure/network.tf&lt;/a&gt; script. These are the Internet Gateway, Route Table and Security Group resources. Essentially they are for controlling access between hosts and the internet. AWS have a &lt;a href="http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Scenario1.html"&gt;good guide&lt;/a&gt; if you are not familiar with these resources; they don&amp;rsquo;t add much to the article so I&amp;rsquo;ll leave you to explore on your own.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it for the network, we now have the following structure:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-1-network-1.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;If you want to see the code as it stands now, check the &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/tree/step-1"&gt;Step 1&lt;/a&gt; branch. Now we need to look at creating the hosts to install Consul on.&lt;/p&gt;
&lt;h2 id="step-2---creating-the-consul-hosts"&gt;Step 2 - Creating the Consul Hosts&lt;/h2&gt;
&lt;p&gt;The Consul documentation recommends running in a cluster or 3 or 5 nodes&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;. We want to set up a system which is self-healing - if we lose a node, we want to create a new one.&lt;/p&gt;
&lt;p&gt;Enter &lt;a href="http://docs.aws.amazon.com/autoscaling/latest/userguide/AutoScalingGroup.html"&gt;Auto-Scaling Groups&lt;/a&gt;. Auto-scaling groups allow us to define a template for an instance, and ask AWS to make sure there are always a certain number of these instances. If we lose an instance, a new one will be created to keep the group at the correct size&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;So we now need to create:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &amp;lsquo;Launch Configuration&amp;rsquo; which determines what instances our Auto-scaling Group creates&lt;/li&gt;
&lt;li&gt;A &amp;lsquo;user data script&amp;rsquo; which runs on newly created instances, which must install and start Consul&lt;/li&gt;
&lt;li&gt;An Auto-scaling group, configured to run five instances across the two public subnets&lt;/li&gt;
&lt;li&gt;A load balancer, configured to pass incoming requests for the Consul Admin console to the nodes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Or visually:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-5-cluster-basic-2.png" alt="Basic Cluster Diagram"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get to it.&lt;/p&gt;
&lt;h3 id="the-launch-configuration--auto-scaling-group"&gt;The Launch Configuration &amp;amp; Auto-scaling Group&lt;/h3&gt;
&lt;p&gt;The Launch Configuration will define the characteristics of our instances and the auto-scaling group determines the size of our cluster:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Launch configuration for the consul cluster auto-scaling group.
resource &amp;#34;aws_launch_configuration&amp;#34; &amp;#34;consul-cluster-lc&amp;#34; {
name_prefix = &amp;#34;consul-node-&amp;#34;
image_id = &amp;#34;${lookup(var.ami_ecs_optimised, var.region)}&amp;#34;
instance_type = &amp;#34;t2.micro&amp;#34;
security_groups = [&amp;#34;${aws_security_group.consul-cluster-vpc.id}&amp;#34;]
lifecycle {
create_before_destroy = true
}
}
// Auto-scaling group for our cluster.
resource &amp;#34;aws_autoscaling_group&amp;#34; &amp;#34;consul-cluster-asg&amp;#34; {
name = &amp;#34;consul-asg&amp;#34;
launch_configuration = &amp;#34;${aws_launch_configuration.consul-cluster-lc.name}&amp;#34;
min_size = 5
max_size = 5
vpc_zone_identifier = [
&amp;#34;${aws_subnet.public-a.id}&amp;#34;,
&amp;#34;${aws_subnet.public-b.id}&amp;#34;
]
lifecycle {
create_before_destroy = true
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A few key things to note:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I have omitted the &lt;code&gt;tag&lt;/code&gt; properties in the scripts for brevity&lt;/li&gt;
&lt;li&gt;The &amp;lsquo;image&amp;rsquo; for the launch configuration is looked up based on the region we&amp;rsquo;ve specified - we&amp;rsquo;re a basic linux image&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;We are using micro instances, which are free-tier eligible&lt;/li&gt;
&lt;li&gt;The auto-scaling group spans both availability zones.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once we run &lt;code&gt;terraform apply&lt;/code&gt;, we&amp;rsquo;ll see our auto-scaling group, which references the new launch configuration and works over multiple availability zones:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-6-lc-asg.png" alt="Auto scaling group and launch configuration"&gt;&lt;/p&gt;
&lt;p&gt;We can also see the new instances:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-7-instances.png" alt="Instances"&gt;&lt;/p&gt;
&lt;p&gt;These instances don&amp;rsquo;t do much yet though, we&amp;rsquo;ve not installed Docker or Consul.&lt;/p&gt;
&lt;h3 id="installing-consul-and-accessing-the-admin-interface"&gt;Installing Consul and Accessing the Admin Interface&lt;/h3&gt;
&lt;p&gt;To set up our instances we use a &amp;lsquo;userdata&amp;rsquo; script&amp;rsquo; A userdata runs once when an instance is created. We can create a script in our repository, and reference it in our Terraform files.&lt;/p&gt;
&lt;p&gt;We add a new file called &lt;code&gt;consul-node.sh&lt;/code&gt; to a &lt;code&gt;files&lt;/code&gt; folder. This script installs Docker and runs Consul:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;yum install -y docker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;usermod -a -G docker ec2-user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;service docker start
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get my IP address.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;curl http://169.254.169.254/latest/meta-data/local-ipv4&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Instance IP is: &lt;/span&gt;$IP&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Start the Consul server.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -d --net&lt;span style="color:#f92672"&gt;=&lt;/span&gt;host &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;consul &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; consul agent -server -ui &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -bind&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$IP&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -client&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -bootstrap-expect&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s a breakdown of what we&amp;rsquo;re doing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install Docker. These scripts run as root, so we add the ec2-user to the Docker group, meaning when we log in later on via SSH, we can run Docker&lt;/li&gt;
&lt;li&gt;Get our IP address. AWS provide a magic address (169.254.169.254) which lets you query data about your instance, see &lt;a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html"&gt;Instance Metadata &amp;amp; User Metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Run the Consul docker image in server mode, with the UI enabled, expecting only one instance&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The actual scripts contains more!&lt;/strong&gt; Getting userdata scripts right, testing and debugging them is tricky. See how I do it in detail in &lt;a href="#Appendix-1-Logging"&gt;Appendix 1: Logging&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we need to tell Terraform to include this script as part of the instance metadata. Here&amp;rsquo;s how we do that:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_launch_configuration&amp;#34; &amp;#34;consul-cluster-lc&amp;#34; {
/// ...add the line below....
user_data = &amp;#34;${file(&amp;#34;files/consul-node.sh&amp;#34;)}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When Consul is running with the &lt;code&gt;-ui&lt;/code&gt; option, it provides an admin UI. You can try it by running Consul locally with &lt;code&gt;docker run -p8500:8500 consul&lt;/code&gt; and navigating to http://localhost:8500/ui.&lt;/p&gt;
&lt;p&gt;We can install a load balancer in front of our auto-scaling group, to automatically forward incoming traffic to a host. Here&amp;rsquo;s the config:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_elb&amp;#34; &amp;#34;consul-lb&amp;#34; {
name = &amp;#34;consul-lb-a&amp;#34;
security_groups = [
&amp;#34;${aws_security_group.consul-cluster-vpc.id}&amp;#34;,
&amp;#34;${aws_security_group.web.id}&amp;#34;
]
subnets = [
&amp;#34;${aws_subnet.public-a.id}&amp;#34;,
&amp;#34;${aws_subnet.public-b.id}&amp;#34;
]
listener {
instance_port = 8500
instance_protocol = &amp;#34;http&amp;#34;
lb_port = 80
lb_protocol = &amp;#34;http&amp;#34;
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 3
target = &amp;#34;HTTP:8500/ui/&amp;#34;
interval = 30
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Blow-by-blow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a load balancer, with the same security groups as the rest of the VPC, but also a security group which allows web access&lt;/li&gt;
&lt;li&gt;Point to two subnets first subnet&lt;/li&gt;
&lt;li&gt;Forward HTTP 8500 traffic&lt;/li&gt;
&lt;li&gt;Configure a healthcheck&lt;sup id="fnref:9"&gt;&lt;a href="#fn:9" class="footnote-ref" role="doc-noteref"&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The final change we make is to add an &lt;code&gt;outputs.tf&lt;/code&gt; file, which lists all of the properties Terraform knows about which we want to save. All it includes is:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;output &amp;#34;consul-dns&amp;#34; {
value = &amp;#34;${aws_elb.consul-lb.dns_name}&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When we finally run &lt;code&gt;terraform apply&lt;/code&gt;, we see the public DNS of our load balancer:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-8-cluster-dns.png" alt="Screenshot showing &amp;rsquo;terraform apply&amp;rsquo; output, indicating our newly generated ELB&amp;rsquo;s public DNS"&gt;&lt;/p&gt;
&lt;p&gt;And running in a browser on port 8500 we see the Consul admin interface:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-9-admin-ui.png" alt="Screenshot showing the Consul admin interface"&gt;&lt;/p&gt;
&lt;p&gt;Every time we refresh we will likely see a different node. We&amp;rsquo;ve actually created five clusters each of one node - what we now need to do is connect them all together into a single cluster of five nodes.&lt;/p&gt;
&lt;p&gt;If you want to see the code as it stands now, check the &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/tree/step-2"&gt;Step 2&lt;/a&gt; branch.&lt;/p&gt;
&lt;h2 id="step-3---creating-the-cluster"&gt;Step 3 - Creating the Cluster&lt;/h2&gt;
&lt;p&gt;Creating the cluster is now not too much of a challenge. We will update the userdata script to tell the consul process we are expecting 5 nodes (via the &lt;a href="https://www.consul.io/docs/agent/options.html#_bootstrap_expect"&gt;&lt;code&gt;bootstrap-expect&lt;/code&gt;&lt;/a&gt; flag.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the updated script:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Get my IP address.
IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
echo &amp;#34;Instance IP is: $IP&amp;#34;
# Start the Consul server.
docker run -d --net=host \
--name=consul \
consul agent -server -ui \
-bind=&amp;#34;$IP&amp;#34; \
-client=&amp;#34;0.0.0.0&amp;#34; \
-bootstrap-expect=&amp;#34;5&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The problem is &lt;strong&gt;this won&amp;rsquo;t work&lt;/strong&gt;&amp;hellip; We need to tell each node the address of &lt;em&gt;another&lt;/em&gt; server in the cluster. For example, if we start five nodes, we should tell nodes 2-5 the address of node 1, so that the nodes can discover each other.&lt;/p&gt;
&lt;p&gt;The challenge is how do we get the IP of node 1? The IP addresses are determined by the network, we don&amp;rsquo;t preset them so cannot hard code them. Also, we can expect nodes to occasionally die and get recreated, so the IP addresses of nodes will in fact change over time.&lt;/p&gt;
&lt;h3 id="getting-the-ip-addresses-of-nodes-in-the-cluster"&gt;Getting the IP addresses of nodes in the cluster&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s a nice trick we can use here. We can ask AWS to give us the IP addresses of each host in the auto-scaling group. If we tell each node the addresses of the &lt;em&gt;other nodes&lt;/em&gt;, then they will elect a leader themselves&lt;sup id="fnref:10"&gt;&lt;a href="#fn:10" class="footnote-ref" role="doc-noteref"&gt;10&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-12-choose-leader-1.png" alt="Diagram showing how we decide on a leader IP"&gt;&lt;/p&gt;
&lt;p&gt;There are a couple of things we need to do to get this right. First, update the userdata script to provide the IPs of other nodes when we&amp;rsquo;re starting up, then update the &lt;strong&gt;role&lt;/strong&gt; of our nodes so that they have permissions to use the APIs we&amp;rsquo;re going to call.&lt;/p&gt;
&lt;h3 id="getting-the-cluster-ips"&gt;Getting the Cluster IPs&lt;/h3&gt;
&lt;p&gt;This is actually fairly straightforward. We update our userdata script to the below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# A few variables we will refer to later...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ASG_NAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;consul-asg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;REGION&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ap-southeast-1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;EXPECTED_SIZE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Return the id of each instance in the cluster.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; cluster-instance-ids &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Grab every line which contains &amp;#39;InstanceId&amp;#39;, cut on double quotes and grab the ID:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# &amp;#34;InstanceId&amp;#34;: &amp;#34;i-example123&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;#....^..........^..^.....#4.....^...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; aws --region&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$REGION&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; autoscaling describe-auto-scaling-groups &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --auto-scaling-group-name $ASG_NAME &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | grep InstanceId &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | cut -d &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; -f4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Return the private IP of each instance in the cluster.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; cluster-ips &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; id in &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cluster-instance-ids&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; aws --region&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$REGION&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; ec2 describe-instances &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --query&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Reservations[].Instances[].[PrivateIpAddress]&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --output&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --instance-ids&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$id&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Wait until we have as many cluster instances as we are expecting.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; COUNT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;cluster-instance-ids | wc -l&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$COUNT&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -lt &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$EXPECTED_SIZE&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$COUNT&lt;span style="color:#e6db74"&gt; instances in the cluster, waiting for &lt;/span&gt;$EXPECTED_SIZE&lt;span style="color:#e6db74"&gt; instances to warm up...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sleep &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get my IP address, all IPs in the cluster, then just the &amp;#39;other&amp;#39; IPs...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IP&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;curl http://169.254.169.254/latest/meta-data/local-ipv4&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mapfile -t ALL_IPS &amp;lt; &amp;lt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;cluster-ips&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OTHER_IPS&lt;span style="color:#f92672"&gt;=(&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;ALL_IPS[@]/&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;IP&lt;span style="color:#e6db74"&gt;}}&lt;/span&gt;/&lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Instance IP is: &lt;/span&gt;$IP&lt;span style="color:#e6db74"&gt;, Cluster IPs are: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;CLUSTER_IPS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, Other IPs are: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;OTHER_IPS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Start the Consul server.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -d --net&lt;span style="color:#f92672"&gt;=&lt;/span&gt;host &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;consul &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; consul agent -server -ui &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -bind&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$IP&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -retry-join&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;OTHER_IPS[0]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -retry-join&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;OTHER_IPS[1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -retry-join&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;OTHER_IPS[2]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -retry-join&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;OTHER_IPS[3]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -bootstrap-expect&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$EXPECTED_SIZE&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Right, here&amp;rsquo;s what&amp;rsquo;s going on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We create a few variables we&amp;rsquo;ll use repeatedly&lt;/li&gt;
&lt;li&gt;We create a &lt;code&gt;cluster-instance-ids&lt;/code&gt; function which returns the ID of each instance in the auto-scaling group&lt;/li&gt;
&lt;li&gt;We create a &lt;code&gt;cluster-ips&lt;/code&gt; function which returns the private IP address of each instance in the cluster.&lt;/li&gt;
&lt;li&gt;We wait until the auto-scaling group has our expected number of instances (it can take a while for them all to be created)&lt;/li&gt;
&lt;li&gt;We get the 5 IP addresses&lt;/li&gt;
&lt;li&gt;We remove our IP from the array, leaving us with the IPs of the &lt;em&gt;other&lt;/em&gt; nodes&lt;/li&gt;
&lt;li&gt;We start the Consul agent in server mode, expecting 5 nodes and offering the IP of each other agent&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The problem is, if we try to run the script we will fail, because calling the AWS APIs requires some permissions we don&amp;rsquo;t have. Let&amp;rsquo;s fix that.&lt;/p&gt;
&lt;h3 id="creating-a-role-for-our-nodes"&gt;Creating a Role for our nodes&lt;/h3&gt;
&lt;p&gt;Our nodes now have a few special requirements. They need to be able to query the details of an auto-scaling group and get the IP of an instance&lt;sup id="fnref:11"&gt;&lt;a href="#fn:11" class="footnote-ref" role="doc-noteref"&gt;11&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We will need to create a policy which describes the permissions we need, create a role, attach the policy to the role and then ensure our instances are assigned the correct role. This is &lt;code&gt;consul-node-role.tf&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// This policy allows an instance to discover a consul cluster leader.
resource &amp;#34;aws_iam_policy&amp;#34; &amp;#34;leader-discovery&amp;#34; {
name = &amp;#34;consul-node-leader-discovery&amp;#34;
path = &amp;#34;/&amp;#34;
policy = &amp;lt;&amp;lt;EOF
{
&amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,
&amp;#34;Statement&amp;#34;: [
{
&amp;#34;Sid&amp;#34;: &amp;#34;Stmt1468377974000&amp;#34;,
&amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,
&amp;#34;Action&amp;#34;: [
&amp;#34;autoscaling:DescribeAutoScalingInstances&amp;#34;,
&amp;#34;autoscaling:DescribeAutoScalingGroups&amp;#34;,
&amp;#34;ec2:DescribeInstances&amp;#34;
],
&amp;#34;Resource&amp;#34;: [
&amp;#34;*&amp;#34;
]
}
]
}
EOF
}
// Create a role which consul instances will assume.
// This role has a policy saying it can be assumed by ec2
// instances.
resource &amp;#34;aws_iam_role&amp;#34; &amp;#34;consul-instance-role&amp;#34; {
name = &amp;#34;consul-instance-role&amp;#34;
assume_role_policy = &amp;lt;&amp;lt;EOF
{
&amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,
&amp;#34;Statement&amp;#34;: [
{
&amp;#34;Action&amp;#34;: &amp;#34;sts:AssumeRole&amp;#34;,
&amp;#34;Principal&amp;#34;: {
&amp;#34;Service&amp;#34;: &amp;#34;ec2.amazonaws.com&amp;#34;
},
&amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,
&amp;#34;Sid&amp;#34;: &amp;#34;&amp;#34;
}
]
}
EOF
}
// Attach the policy to the role.
resource &amp;#34;aws_iam_policy_attachment&amp;#34; &amp;#34;consul-instance-leader-discovery&amp;#34; {
name = &amp;#34;consul-instance-leader-discovery&amp;#34;
roles = [&amp;#34;${aws_iam_role.consul-instance-role.name}&amp;#34;]
policy_arn = &amp;#34;${aws_iam_policy.leader-discovery.arn}&amp;#34;
}
// Create a instance profile for the role.
resource &amp;#34;aws_iam_instance_profile&amp;#34; &amp;#34;consul-instance-profile&amp;#34; {
name = &amp;#34;consul-instance-profile&amp;#34;
roles = [&amp;#34;${aws_iam_role.consul-instance-role.name}&amp;#34;]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Terraform is a little verbose here! Finally, we update our launch configuration to ensure that the instances assume this role.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;resource &amp;#34;aws_launch_configuration&amp;#34; &amp;#34;consul-cluster-lc&amp;#34; {
// Add this line!!
iam_instance_profile = &amp;#34;${aws_iam_instance_profile.consul-instance-profile.id}&amp;#34;
}
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s create the cluster again, with &lt;code&gt;terraform apply&lt;/code&gt;. When we log into the UI we should now see a cluster containing all five nodes:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-13-cluster.png" alt="Screenshot of the Consul UI, showing that the Consul server is running on five nodes in the Datacenter"&gt;&lt;/p&gt;
&lt;p&gt;This code is all in the &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/tree/step-3"&gt;Step 3&lt;/a&gt; branch.&lt;/p&gt;
&lt;p&gt;If you are familiar with Consul, this may be all you need. If not, you might be interested in seeing how we actually create a new instance to host a service, register it with Consul and query its address.&lt;/p&gt;
&lt;h2 id="step-4---adding-a-microservice"&gt;Step 4 - Adding a Microservice&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve created a docker image for as simple a microservice as you can get. It returns a quote from Futurama&amp;rsquo;s Zapp Brannigan. The image is tagged as &lt;code&gt;dwmkerr/zapp-service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On a new EC2 instance, running in either subnet, with the same roles as the Consul nodes, we run the following commands:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Install Docker
sudo su
yum update -y
yum install -y docker
service docker start
# Get my IP and the IP of any node in the server cluster.
IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
NODE_ID=$(aws --region=&amp;#34;ap-southeast-1&amp;#34; autoscaling describe-auto-scaling-groups --auto-scaling-group-name &amp;#34;consul-asg&amp;#34; \
| grep InstanceId \
| cut -d &amp;#39;&amp;#34;&amp;#39; -f4 \
| head -1)
NODE_IP=$(aws --region=&amp;#34;ap-southeast-1&amp;#34; ec2 describe-instances \
--query=&amp;#34;Reservations[].Instances[].[PrivateIpAddress]&amp;#34; \
--output=&amp;#34;text&amp;#34; \
--instance-ids=&amp;#34;$NODE_ID&amp;#34;)
# Run the consul agent.
docker run -d --net=host \
consul agent \
-bind=&amp;#34;$IP&amp;#34; \
-join=$NODE_IP
# Run registrator - any Docker images will then be auto registered.
docker run -d \
--name=registrator \
--net=host \
--volume=/var/run/docker.sock:/tmp/docker.sock \
gliderlabs/registrator:latest \
consul://localhost:8500
# Run the example microservice - registrator will take care of letting consul know.
docker run -d -p 5000:5000 dwmkerr/zapp-service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What&amp;rsquo;s going on here?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We grab our own IP address and the IP address of the first instance we find in the server cluster, using the same tricks as before&lt;/li&gt;
&lt;li&gt;We run the Consul agent - telling it the IP to use to join the cluster&lt;/li&gt;
&lt;li&gt;We run &lt;a href="https://github.com/gliderlabs/registrator"&gt;Registrator&lt;/a&gt;, a handy utility which will automatically register any new services we run to Consul&lt;/li&gt;
&lt;li&gt;We run a goofy sample microservice (which registrator will register for us)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we can check the Consul UI:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-15-sample-service.png" alt="The Consul UI showing a new service"&gt;&lt;/p&gt;
&lt;p&gt;And there we have it. Our new node joins the cluster (as a client), we can register a new service with Consul.&lt;/p&gt;
&lt;p&gt;We can call this service from any node in the subnet, seeing output like the below:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-x-zapp.png" alt="Screenshot of the Zapp service"&gt;&lt;/p&gt;
&lt;p&gt;In this example, I used a DNS SRV query to ask where the &lt;code&gt;zapp-service&lt;/code&gt; is, was told it was at &lt;code&gt;10.0.2.158&lt;/code&gt; on port &lt;code&gt;5000&lt;/code&gt;, then called the service, receiving a response. I can discover any service using this method, from any node. As services are added, removed, moved etc, I can ask Consul for accurate information on where to find them.&lt;/p&gt;
&lt;p&gt;Check the &lt;a href=""&gt;Step 4&lt;/a&gt; branch to see the code in its current state.&lt;/p&gt;
&lt;h2 id="step-5---spanner-throwing"&gt;Step 5 - Spanner Throwing&lt;/h2&gt;
&lt;p&gt;We can now try to throw some spanners in the works, to see how resilient the system is.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href="https://www.consul.io/docs/internals/consensus.html#deployment-table"&gt;Deployment Table&lt;/a&gt; from the Consul documentation, a cluster of five nodes means we have a quorum of three nodes (i.e. a minimum of three nodes are needed for a working system). This means we can tolerate the failure of two nodes.&lt;/p&gt;
&lt;p&gt;The easiest way to test this is to simply manually kill two nodes:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-16-terminate.png" alt="Screenshot showing two AWS instances being terminated"&gt;&lt;/p&gt;
&lt;p&gt;If we pick two random nodes, as above, and terminate them, we see the cluster determines that we have two failed nodes but will still function (if one was the leader, a new leader will be automatically elected):&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-17-node-failure.png" alt="Screenshot showing the cluster highlighting two failed nodes"&gt;&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s nice about this setup is that no manual action is needed to recover. Our load balancer will notice the nodes are unhealthy and stop forwarding traffic. Our auto-scaling group will see the nodes have terminated and create two new ones, which will join the cluster in the same way as the original nodes. Once they join, the load balancer will find them healthy and bring them back into rotation.&lt;/p&gt;
&lt;p&gt;We can see from the load balancer monitoring that it notices we have unhealthy nodes and also notices when new ones come into service:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-18-recovery-1.png" alt="Screenshot showing the load balancer monitoring"&gt;&lt;/p&gt;
&lt;p&gt;A quick check of the admin dashboard shows we now have a recovered system, with five healthy nodes:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-18b-recovered.png" alt="Screenshot showing recovered system"&gt;&lt;/p&gt;
&lt;p&gt;The nodes which were terminated are still listed as failing. After 72 hours Consul will stop trying to periodically reconnect to these nodes and completely remove them&lt;sup id="fnref:12"&gt;&lt;a href="#fn:12" class="footnote-ref" role="doc-noteref"&gt;12&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Hopefully this should provide a good starting point to think about building your own resilient and robust systems for services like Consul.&lt;/p&gt;
&lt;p&gt;Interesting areas to look into to extend the project would be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setting up alerts so that if we lose more than one node, we are informed&lt;/li&gt;
&lt;li&gt;Automating resilience tests by programatically bringing down servers and monitoring how long it takes the system to return to five nodes&lt;/li&gt;
&lt;li&gt;Instead of using a userdata script to set up a node, bake it into a new custom AMI with &lt;a href="https://www.packer.io/"&gt;Packer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adding alerts for if we lose three of more nodes, which always requires manual intervention (see &lt;a href="https://www.consul.io/docs/guides/outage.html"&gt;Outage Recovery&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As always, any questions or comments are welcome! All code is available at &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster"&gt;github.com/dwmkerr/terraform-consul-cluster&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="appendix-1-logging"&gt;Appendix 1: Logging&lt;/h2&gt;
&lt;p&gt;Small typos or mistakes in the userdata script are almost impossible to effectively diagnose. The scripts were actually built in the following way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Draft a script on my local machine which configures script logging and CloudWatch&lt;sup id="fnref:13"&gt;&lt;a href="#fn:13" class="footnote-ref" role="doc-noteref"&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;Spin up a new EC2 instance manually&lt;/li&gt;
&lt;li&gt;SSH onto the instance, and run the script line by line until I&amp;rsquo;m sure it&amp;rsquo;s right&lt;/li&gt;
&lt;li&gt;Ensure the logs are forwarded to CloudWatch, then add the more complex features and repeatedly test&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;rsquo;ve included CloudWatch logging in the code. In this write-up I&amp;rsquo;ve omitted this code as it is purely for diagnostics and doesn&amp;rsquo;t contribute to the main topic. The setup is in the &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/blob/master/files/consul-node.sh"&gt;&lt;code&gt;consul-node.sh&lt;/code&gt;&lt;/a&gt; and &lt;a href="%60https://github.com/dwmkerr/terraform-consul-cluster/blob/master/consul-node-role.tf"&gt;&lt;code&gt;consul-node-role.tf&lt;/code&gt;&lt;/a&gt; files.&lt;/p&gt;
&lt;p&gt;If you want more details, let me know, or just check the code. I would heartily recommend setting up logging like this for all but the most straightforward projects:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/img-19-cloudwatch-1.png" alt="Screenshot showing logs"&gt;&lt;/p&gt;
&lt;p&gt;Being able to diagnose issues like this is vital when working with distributed systems which may be generating many log files.&lt;/p&gt;
&lt;h2 id="appendix-2-modularisaton"&gt;Appendix 2: Modularisaton&lt;/h2&gt;
&lt;p&gt;I got some a great PR from &lt;a href="https://github.com/arehmandev"&gt;arehmandev&lt;/a&gt; which modularises the code. This makes it more reusable and cleans up the structure significantly. If you want to see the before/after, check the original PR at &lt;a href="https://github.com/dwmkerr/terraform-consul-cluster/pull/4"&gt;https://github.com/dwmkerr/terraform-consul-cluster/pull/4&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Further Reading&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.consul.io/docs/internals/consensus.html"&gt;Consul - Consensus Protocol&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sitano.github.io/2015/10/06/abt-consul-outage/"&gt;What you have to know about Consul and how to beat the outage problem&lt;/a&gt;, John Koepi&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This kind of pattern is critical in the world of microservices, where many small services will be running on a cluster. Services may die, due to errors or failing hosts, and be recreated on new hosts. Their IPs and ports may be ephemeral.It is essential that the system as a whole has a registry of where each service lives and how to access it. Such a registry must be &lt;em&gt;resilient&lt;/em&gt;, as it is an essential part of the system.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Most popular is a fairly loose term. Well ranked by Gartner and anecdotally with the largest infrastructure footprint. &lt;a href="https://www.gartner.com/doc/reprints?id=1-2G2O5FC&amp;amp;ct=150519&amp;amp;st=sb"&gt;https://www.gartner.com/doc/reprints?id=1-2G2O5FC&amp;amp;ct=150519&amp;amp;st=sb&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;This is AWS parlance again. An availabilty zone is an isolated datacenter. Theoretically, spreading nodes across AZs will increase resilience as it is less likely to have catastrophic failures or outages across multiple zones.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;I don&amp;rsquo;t get money from Udemy or anyone else for writing anything on this blog. All opinions are purely my own and influenced by my own experience, not sponsorship. Your milage may vary (yada yada) but I found the course quite good: &lt;a href="https://www.udemy.com/aws-certified-solutions-architect-associate/"&gt;https://www.udemy.com/aws-certified-solutions-architect-associate/&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;For more expert readers that may sound horribly patronising, I don&amp;rsquo;t mean it to be. For many less experienced technologists the basics of networking might be more unfamiliar!&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&amp;#160;&lt;a href="#fnref1:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;See &lt;a href="https://www.consul.io/docs/internals/consensus.html"&gt;https://www.consul.io/docs/internals/consensus.html&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;A common pattern is to actually make the group size dynamic, responding to events. For example, we could have a group of servers which increases in size if the average CPU load of the hosts stays above 80% for five minutes, and scales down if it goes below 10% for ten minutes. This is more common for app and web servers and not needed for our system.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;Specifically, the current latest &lt;a href="https://aws.amazon.com/amazon-linux-ami/"&gt;Amazon Linux AMI&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;Check the admin UI every 30 seconds, more than 3 seconds indicates a timeout and failure. Two failures in a row means an unhealthy host, which will be destroyed, two successes in a row for a new host means healthy, which means it will receive traffic.&amp;#160;&lt;a href="#fnref:9" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;This is a fairly sophisticated topic in itself, see &lt;a href="https://www.consul.io/docs/internals/consensus.html"&gt;Consul - Consensus Protocol&lt;/a&gt; for details.&amp;#160;&lt;a href="#fnref:10" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;In fact, we actually have more permissions required, because in the &amp;lsquo;real&amp;rsquo; code we also have logs forwarded to CloudWatch.&amp;#160;&lt;a href="#fnref:11" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:12"&gt;
&lt;p&gt;These nodes can be removed manually, see &lt;a href="https://www.consul.io/docs/commands/force-leave.html"&gt;Consul Force Leave&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:12" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:13"&gt;
&lt;p&gt;Amazon&amp;rsquo;s service for managing and aggregating logs&amp;#160;&lt;a href="#fnref:13" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Using Slack for Server Development</title><link>https://dwmkerr.com/using-slack-for-server-development/</link><pubDate>Fri, 18 Nov 2016 01:25:00 +0000</pubDate><guid>https://dwmkerr.com/using-slack-for-server-development/</guid><description>&lt;p&gt;I recently found a surprisingly helpful approach for server-side development which uses Slack in a creative way.&lt;/p&gt;
&lt;h2 id="the-problem"&gt;The Problem&lt;/h2&gt;
&lt;p&gt;The scenario can be roughly simplified to this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/0-problem.png" alt="Planned Architecture"&gt;&lt;/p&gt;
&lt;p&gt;We are building a mobile app and application server. This will take data from a user, transform it and then pass it to the enterprise system processing.&lt;/p&gt;
&lt;p&gt;The problem is that the enterprise system doesn&amp;rsquo;t exist yet!&lt;/p&gt;
&lt;p&gt;Now this is not too much of a challenge, the first thing we did was build a simple mock of the enterprise system in Node.js, so that we can at least talk to &lt;em&gt;something&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/0-problem-2.png" alt="The Mock System"&gt;&lt;/p&gt;
&lt;p&gt;So now we have the question - is our application server transforming the data correctly?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say that in our example we bring in three pieces of data from the UI - a first name, middle name and last name.&lt;/p&gt;
&lt;p&gt;Our enterprise system, in this case we&amp;rsquo;ll say it is a CRM system, only accepts a first name and last name. So in our app server, we are going to concatenate the middle name and last name.&lt;/p&gt;
&lt;p&gt;Our testers want to make sure that the enterprise system will receive the right data - but at the moment it is only a mock, we cannot log in a check the middle and last names have been combined properly. What to do?&lt;/p&gt;
&lt;h2 id="slack-to-the-rescue"&gt;Slack to the Rescue!&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s a trivial change to our mock server to send the received messages to Slack:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/1-slack.png" alt="Slack Diagram"&gt;&lt;/p&gt;
&lt;p&gt;Now our testers can input data in the mobile app and then watch a slack channel to see the data our application server will actually send to the enterprise system. They can verify the logic has been implemented correctly.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it might look - in the image below I am running my mock enterprise server, which has Swagger UI to show the mocked APIs and allow me to call them:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/4-swagger.png" alt="Swagger UI"&gt;&lt;/p&gt;
&lt;p&gt;The message is received on the server, sent to slack and we can check the result:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/5-slack.png" alt="Slack"&gt;&lt;/p&gt;
&lt;p&gt;Now this is obviously a trivial and contrived example, but Slack offers a lot of capabilities. Imagine you have a server which watermarks images, you could send an image file to render to the screen. There are a whole bunch of ways you can extend this use case.&lt;/p&gt;
&lt;p&gt;In the early stages of a project, where they may be many mocked systems, being able to see what they are doing can be really useful.&lt;/p&gt;
&lt;h2 id="setting-it-up"&gt;Setting it up&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve created a super-simple demo setup here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/slack-backend"&gt;github.com/dwmkerr/slack-backend&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how you go about it.&lt;/p&gt;
&lt;p&gt;Step 1: Set up a webhook on slack&lt;/p&gt;
&lt;p&gt;&lt;img src="images/1-menu.png" alt="Menu"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/1-incoming-webhook.png" alt="Webhook"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/3-hook.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Step 2: Use the HTTP APIs from Slack, or a client from your platform of choice to send the message:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create the Slack webhook based on our config.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;slack&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Slack&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;slack&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;setWebhook&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;https://hooks.slack.com/services/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&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Every time we&amp;#39;re about to handle a request, tell our friend Slack.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;server&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;ext&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;onPreHandler&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;reply&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Never bother logging any requests to swagger UI.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;/\/swaggerui\//&lt;/span&gt;) &lt;span style="color:#f92672"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;/\/swagger.json/&lt;/span&gt;) &lt;span style="color:#f92672"&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;/\/$/&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reply&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Send the Slack message.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;slack&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;webhook&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;text&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;`Request *&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;\`\`\`&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;\`\`\``&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;reply&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This approach was quick and easy to implement, hopefully others will find it useful!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Simple Continuous Integration for Docker Images</title><link>https://dwmkerr.com/simple-continuous-integration-for-docker-images/</link><pubDate>Thu, 03 Nov 2016 05:14:35 +0000</pubDate><guid>https://dwmkerr.com/simple-continuous-integration-for-docker-images/</guid><description>&lt;p&gt;In this article I&amp;rsquo;m going to demonstrate a few tips and tricks which can make your life easier when you are building or maintaining Dockerfiles.&lt;/p&gt;
&lt;h2 id="the-need-for-a-build-pipeline"&gt;The need for a Build Pipeline&lt;/h2&gt;
&lt;p&gt;Do we really need any kind of continuous integration or build pipeline for Dockerfiles?&lt;/p&gt;
&lt;p&gt;There will be cases when the answer is no. However, if the answer to any of the following questions is &amp;lsquo;yes&amp;rsquo;, it might be worth considering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Do you want others to be able to contribute to the Dockerfile, perhaps changing the image over time?&lt;/li&gt;
&lt;li&gt;Are there specific functionalities in your Dockerfiles which could break if altered?&lt;/li&gt;
&lt;li&gt;Do you expect to need to release updates to your Dockerfile?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Essentially, if we are looking at providing some kind of automated quality assurance and automation around building and releasing, then a build pipeline is not a bad idea.&lt;/p&gt;
&lt;h2 id="a-simple-build-pipeline"&gt;A simple Build Pipeline&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what a simple build pipeline could look like. This example is for a Docker Image I just created for local DynamoDB development - &lt;a href="https://github.com/dwmkerr/docker-dynamodb"&gt;dwmkerr/docker-dynamodb&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Simple-Docker-Image-CI.png" alt="Simple Continous Intergration Pipeline"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dissect what we&amp;rsquo;ve got here.&lt;/p&gt;
&lt;h3 id="the-dockerfile"&gt;The Dockerfile&lt;/h3&gt;
&lt;p&gt;This is the main &amp;lsquo;code&amp;rsquo; of the project if you like. The &lt;a href="https://github.com/dwmkerr/docker-dynamodb/blob/master/Dockerfile"&gt;Dockerfile&lt;/a&gt; is the recipe for the image we create.&lt;/p&gt;
&lt;h3 id="the-continuous-integration-service"&gt;The Continuous Integration Service&lt;/h3&gt;
&lt;p&gt;In this case, I am using &lt;a href="https://circleci.com/"&gt;CircleCI&lt;/a&gt;, however the approach described would work fine with most CI systems (such as Jenkins, TravisCI and TeamCity). There &lt;em&gt;is&lt;/em&gt; an option to use the &lt;a href="https://docs.docker.com/docker-hub/builds/"&gt;Docker Hub Automated Builds&lt;/a&gt;, but I&amp;rsquo;ve found this doesn&amp;rsquo;t give the flexibility I need (see &lt;a href="#appendix1whynotdockerhubautomatedbuilds"&gt;Why not Docker Hub Automated Builds&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Essentially the CI service needs to offer the option to have three distinct steps in the pipeline, each of which must pass for process to proceed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Deploy&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="the-build"&gt;The Build&lt;/h3&gt;
&lt;p&gt;We can build with tools, script files, whatever. At the moment, I am leaning towards &lt;a href="https://www.gnu.org/software/make/"&gt;makefiles&lt;/a&gt;. Normally I only need a few lines of shell script to do a build - anything more complex and the makefile can call a shell script. See also &lt;a href="#appendix2whymakefiles"&gt;Why Makefiles?&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what it might look like:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;build:
docker build -t dwmkerr/dynamodb:latest .
ifndef BUILD_NUM
$(warning No build number is defined, skipping build number tag.)
else
docker build -t dwmkerr/dynamodb:$(BUILD_NUM) .
endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This command just builds the &lt;code&gt;Dockerfile&lt;/code&gt; and tags it as &lt;code&gt;dwmkerr/dynamodb:lastest&lt;/code&gt;. If a &lt;code&gt;BUILD_NUM&lt;/code&gt; variable is present, we also create the tag &lt;code&gt;dwmkerr/dynamodb:BUILD_NUM&lt;/code&gt;. This means if we want to deploy to a service such as &lt;a href="https://aws.amazon.com/ecs/"&gt;Amazon ECS&lt;/a&gt; we can push a specific build by referring to the image with that tag.&lt;/p&gt;
&lt;h3 id="the-tests"&gt;The Tests&lt;/h3&gt;
&lt;p&gt;Again I&amp;rsquo;m relying on &lt;code&gt;make&lt;/code&gt;. I just want to be able to run &lt;code&gt;make test&lt;/code&gt; - if zero is returned I&amp;rsquo;m happy. If not, the pipeline should stop and I&amp;rsquo;ll check the output. Here&amp;rsquo;s my test command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;test: build
./test/basics.test.sh
./test/ephemeral.test.sh
./test/persistent.test.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not a thing of beauty, but it works. These scripts I&amp;rsquo;ll discuss a little bit later on, in the delightly titled &lt;a href="#appendix3whatarethesetestscripts"&gt;What are these test scripts&lt;/a&gt; section.&lt;/p&gt;
&lt;p&gt;For CircleCI, this is enough to have the main part of our pipeline. Here&amp;rsquo;s how the &lt;code&gt;circle.yml&lt;/code&gt; file looks at this stage:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;machine:
services:
- docker
environment:
# Set the build number, used in makefiles.
BUILD_NUM: $CIRCLE_BUILD_NUM
test:
override:
- make test
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;(Actually there&amp;rsquo;s a couple of other bits but they&amp;rsquo;re just to make sure circle uses the right version of Docker, &lt;a href="https://github.com/dwmkerr/docker-dynamodb/blob/master/circle.yml"&gt;see the full circle.yml file here&lt;/a&gt;).&lt;/p&gt;
&lt;h3 id="the-deployments"&gt;The Deployments&lt;/h3&gt;
&lt;p&gt;Deployments are trivial as all we need to do is push to the Docker Hub. The &lt;code&gt;make deploy&lt;/code&gt; command looks-a like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;deploy:
docker push dwmkerr/dynamodb:latest
ifndef BUILD_NUM
$(warning No build number is defined, skipping push of build number tag.)
else
docker push dwmkerr/dynamodb:$(BUILD_NUM)
endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;re pushing the &lt;code&gt;latest&lt;/code&gt; tag and &lt;code&gt;BUILD_NUM&lt;/code&gt; tag if present. To add this to the CircleCI pipeline, we just add the following to &lt;code&gt;circle.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;deployment:
master:
branch: master
commands:
- docker login -e $DOCKER_EMAIL -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
- make deploy
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we have a push to &lt;code&gt;master&lt;/code&gt;, we log in to Docker (using environment variables I configure in the CircleCI UI) and then run &lt;code&gt;make deploy&lt;/code&gt; to push our images.&lt;/p&gt;
&lt;h2 id="thats-it"&gt;That&amp;rsquo;s It&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s about it. This is a pretty simple approach, you can see it in action at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/docker-dynamodb"&gt;github.com/dwmkerr/docker-dynamodb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The rest of this post is a bit of a deep dive into some specific areas I found interesting.&lt;/p&gt;
&lt;h2 id="appendix-1-why-not-docker-hub-automated-builds"&gt;Appendix 1: Why not Docker Hub Automated Builds?&lt;/h2&gt;
&lt;p&gt;There are automated builds available in the Docker Hub:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/dockerhubbuilds.png" alt="Docker Hub Automated Builds"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not using this feauture at the moment, here&amp;rsquo;s a brief roundup of what I think are the current pros and cons:&lt;/p&gt;
&lt;p&gt;Pros&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You don&amp;rsquo;t have to goof around installing Docker on a CI platform.&lt;/li&gt;
&lt;li&gt;It allows you to update the description of your Docker image automatically, from the GitHub &lt;code&gt;README.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It allows you to associate the image with a specific GitHub repo (rather than just linking from the image description).&lt;/li&gt;
&lt;li&gt;Branch management - allowing tags to be built for specific branches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Cons&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It doesn&amp;rsquo;t &lt;em&gt;seem&lt;/em&gt; to support any kind of configurable gating, such as a running a test command prior to deploying.&lt;/li&gt;
&lt;li&gt;It doesn&amp;rsquo;t &lt;em&gt;seem&lt;/em&gt; to support any kind of triggering of downstream processes, such as updating environments, sending notifications or whatever.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The lack of ability to perform tests on the image before deploying it why I&amp;rsquo;m currently not using the service.&lt;/p&gt;
&lt;p&gt;By doing the testing in a CI system for every pull request and only merging PRs where the tests pass we could mitigate the risk here. This service is worth watching as I&amp;rsquo;m sure it will evolve quickly.&lt;/p&gt;
&lt;h2 id="appendix-2-why-makefiles"&gt;Appendix 2: Why Makefiles?&lt;/h2&gt;
&lt;p&gt;I started coding with a commandline compiler in DOS. When I used my first GUI (Borland Turbo C++) it felt like a huge leap:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/turbocpp.png" alt="Borland Turbo C++"&gt;&lt;/p&gt;
&lt;p&gt;Later on I moved onto Microsoft Visual C++ 4.2:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/visualcpp.png" alt="Visual C++ 4.2"&gt;&lt;/p&gt;
&lt;p&gt;And you cannot imagine the excitement when I got my boxed edition of Visual Studio .NET:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/visualstudiodotnet.jpg" alt="Visual Studio .NET"&gt;&lt;/p&gt;
&lt;p&gt;Wow!&lt;/p&gt;
&lt;p&gt;Anyway, I digress. GNU &lt;code&gt;make&lt;/code&gt; was invented by Leonardo Da Vinci in 1473 to allow you to build something from the commandline, using a fairly consistent syntax.&lt;/p&gt;
&lt;p&gt;It is near ubiquitous on *nix systems. I am increasingly using it as an &amp;rsquo;entry point&amp;rsquo; to builds, as I use variety of languages and platforms. Being able to know that most of the time:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;make build
make test
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Will build and test something is convenient. Makefiles actually are not that great to work with (see &lt;a href="http://stackoverflow.com/questions/448910/makefile-variable-assignment"&gt;this&lt;/a&gt;, &lt;a href="http://stackoverflow.com/questions/10121182/multiline-bash-commands-in-makefile"&gt;this&lt;/a&gt; and &lt;a href="http://www.conifersystems.com/whitepapers/gnu-make/"&gt;this&lt;/a&gt;). I&amp;rsquo;ve found as long as you keep the commands simple, they&amp;rsquo;re OK. For anything really complex, I normally have a &lt;code&gt;scripts/&lt;/code&gt; folder, but call the scripts &lt;em&gt;from&lt;/em&gt; the makefile, so that there&amp;rsquo;s still a simple entrypoint.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not entirely sold on makefiles, but they tend to be my default at the moment if I know I&amp;rsquo;m going to use the commandline for builds (for example, in Java projects I&amp;rsquo;ll often write a makefile to call Maven or Gradle).&lt;/p&gt;
&lt;p&gt;For things like Node.js, where you have commands like &lt;code&gt;npm test&lt;/code&gt; or &lt;code&gt;npm run xyz&lt;/code&gt; I &lt;em&gt;still&lt;/em&gt; sometimes use makefiles, using &lt;code&gt;npm&lt;/code&gt; for day-to-day dev tests (&lt;code&gt;npm start&lt;/code&gt;) and &lt;code&gt;make&lt;/code&gt; if it&amp;rsquo;s something more complex (e.g. &lt;code&gt;make deploy-sit&lt;/code&gt; to deploy to an SIT environment).&lt;/p&gt;
&lt;h2 id="appendix-3-what-are-these-test-scripts"&gt;Appendix 3: What are these test scripts?&lt;/h2&gt;
&lt;p&gt;You may have noticed:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;test: build
./test/basics.test.sh
./test/ephemeral.test.sh
./test/persistent.test.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What&amp;rsquo;s going on here?&lt;/p&gt;
&lt;p&gt;My Docker image is just a wrapper around &lt;a href="http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html"&gt;Amazon&amp;rsquo;s Local DynamoDB tool&lt;/a&gt;. I don&amp;rsquo;t really need to test that tool. But what I wanted to test was the capabilities which lie at the &lt;em&gt;intersection&lt;/em&gt; between &amp;rsquo;native&amp;rsquo; Docker and &amp;rsquo;native&amp;rsquo; DynamoDB.&lt;/p&gt;
&lt;p&gt;For example, I know Docker supports volume mapping. I know DynamoDB supports using a data directory, to allow persistent between runs. I want to test I can combine Docker volume mapping and the DynamoDB data directory features. I know Docker images should default to being ephemeral, I want to test this holds true by default for my image.&lt;/p&gt;
&lt;p&gt;Testing Docker is a little hard - I want to test that I can run containers, start, stop, check state before and after and so on. This is essentially an integration test, it can be tricky to make it truly isolated and deterministic.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve given it my best go with these scripts. Here&amp;rsquo;s an example for the &amp;rsquo;ephemeral&amp;rsquo; test, where I&amp;rsquo;m trying to assert that if I run a container, create a table, stop the container and run a new one, I no longer have the table. Here&amp;rsquo;s the test:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Bomb if anything fails.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set -e
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Kill any running dynamodb containers.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Cleaning up old containers...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker ps -a | grep dwmkerr/dynamodb | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt; | xargs docker rm -f &lt;span style="color:#f92672"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Run the container.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Checking we can run the container...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker run -d -p 8000:8000 dwmkerr/dynamodb&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sleep &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Create a table.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws dynamodb --endpoint-url http://localhost:8000 --region us-east-1 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; create-table &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --table-name Supervillains &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --attribute-definitions AttributeName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;name,AttributeType&lt;span style="color:#f92672"&gt;=&lt;/span&gt;S &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --key-schema AttributeName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;name,KeyType&lt;span style="color:#f92672"&gt;=&lt;/span&gt;HASH &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --provisioned-throughput ReadCapacityUnits&lt;span style="color:#f92672"&gt;=&lt;/span&gt;1,WriteCapacityUnits&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Clean up the container. On CircleCI the FS is BTRFS, so this might fail...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Stopping and restarting...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker stop $ID &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker rm $ID &lt;span style="color:#f92672"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ID&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;docker run -d -p 8000:8000 dwmkerr/dynamodb&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sleep &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# List the tables - there shouldn&amp;#39;t be any!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;COUNT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;aws dynamodb --endpoint-url http://localhost:8000 --region us-east-1 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list-tables &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | jq &lt;span style="color:#e6db74"&gt;&amp;#39;.TableNames | length&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; $COUNT -ne &lt;span style="color:#e6db74"&gt;&amp;#34;0&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo &lt;span style="color:#e6db74"&gt;&amp;#34;Expected to find no tables, found &lt;/span&gt;$COUNT&lt;span style="color:#e6db74"&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a bit dirty - it removes containers from the host, changes things and so on. But it works.&lt;/p&gt;
&lt;p&gt;I did experiment with running these tests &lt;em&gt;in a container&lt;/em&gt;, which has the benefit of giving you a clean host to start with, which you can throw away after each test.&lt;/p&gt;
&lt;p&gt;I had to give up after a little while due to time constraints, but will probably revisit this process. The benefits of running these integration tests in a container is that we get a degree of isolation from the host.&lt;/p&gt;
&lt;p&gt;If anyone is interested, my attempts so far are on this &lt;a href="https://github.com/dwmkerr/docker-dynamodb/pull/2"&gt;RFC Pull Request&lt;/a&gt;. Feel free to jump in!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Run Amazon DynamoDB locally with Docker</title><link>https://dwmkerr.com/run-amazon-dynamodb-locally-with-docker/</link><pubDate>Thu, 27 Oct 2016 08:06:00 +0000</pubDate><guid>https://dwmkerr.com/run-amazon-dynamodb-locally-with-docker/</guid><description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; Run DynamoDB locally using Docker:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -d -p 8000:8000 dwmkerr/dynamodb
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Try it out by opening the shell, &lt;a href="http://localhost:8000/shell"&gt;localhost:8000/shell&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/banner.jpg" alt="DynamoDB Shell"&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all there is to it!&lt;/p&gt;
&lt;h2 id="dynamodb"&gt;DynamoDB&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html"&gt;Amazon DynamoDB&lt;/a&gt; is a NoSQL database-as-a-service, which provides a flexible and convenient repository for your services.&lt;/p&gt;
&lt;p&gt;Building applications which use DynamoDB is straightforward, there are APIs and clients for many languages and platforms.&lt;/p&gt;
&lt;p&gt;One common requirement is to be able to run a local version of DynamoDB, for testing and development purposes. To do this, you need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hit the &lt;a href="http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html"&gt;DynamoDB Local&lt;/a&gt; documentation page&lt;/li&gt;
&lt;li&gt;Download an archive&lt;/li&gt;
&lt;li&gt;Extract it to a sensible location&lt;/li&gt;
&lt;li&gt;Run the extracted JAR, perhaps passing in some options&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This can be a little cumbersome if you regularly use DynamoDB, so here&amp;rsquo;s a easier way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -p 8000:8000 dwmkerr/dynamodb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;dwmkerr/dynamodb&lt;/code&gt; image runs the JAR in a container, exposing the database on port 8000 by default.&lt;/p&gt;
&lt;p&gt;You can see the &lt;a href="dockeri.co/image/dwmkerr/dynamodb"&gt;image on the Docker Hub&lt;/a&gt; and the source code at &lt;a href="https://github.com/dwmkerr/docker-dynamodb"&gt;github.com/dwmkerr/docker-dynamodb&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="customising-dynamodb"&gt;Customising DynamoDB&lt;/h2&gt;
&lt;p&gt;You can pass any of &lt;a href="http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html"&gt;the documented commandline flags to DynamoDB&lt;/a&gt;. There are instructions on the GitHub page. Here&amp;rsquo;s an example of how you can pass in a data directory, which allows DynamoDB data to be persisted after restarting a container (the image is ephemeral by default, as per &lt;a href="https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/"&gt;Dockerfile best practices&lt;/a&gt;).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -d -p 8000:8000 -v /tmp/data:/data/ dwmkerr/dynamodb -dbPath /data/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running DynamoDB in a container gives an extra degree of flexibility and can speed up your workflow too!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Effective Node.js Debugging</title><link>https://dwmkerr.com/effective-node-js-debugging/</link><pubDate>Sat, 03 Sep 2016 01:36:09 +0000</pubDate><guid>https://dwmkerr.com/effective-node-js-debugging/</guid><description>&lt;p&gt;If you are interested in improving your Node.js debugging skills, then check out my talk at the recent &lt;a href=""&gt;JSChannel 2016&lt;/a&gt; conference in Bangalore:&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/-iCygy2wGpM" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;Comments and observations are always welcome!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Testing the Docker for Mac Beta</title><link>https://dwmkerr.com/testing-the-docker-for-mac-beta/</link><pubDate>Fri, 03 Jun 2016 10:45:24 +0000</pubDate><guid>https://dwmkerr.com/testing-the-docker-for-mac-beta/</guid><description>&lt;p&gt;I&amp;rsquo;ve finally had a chance to install the new Docker for Mac Beta and give it a whirl. In this article I&amp;rsquo;m going to talk a bit about how Docker works, the challenges of running Docker on a Mac or Windows and how the new Beta helps.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Below: The welcome message for the new Docker for Mac app&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-03-at-20-33-20.png" alt="Docker for Mac Icon"&gt;&lt;/p&gt;
&lt;h1 id="so-what-is-docker-for-mac"&gt;So What is Docker for Mac?&lt;/h1&gt;
&lt;p&gt;If you don&amp;rsquo;t know what Docker is, check out my article &lt;a href="http://www.dwmkerr.com/learn-docker-by-building-a-microservice/"&gt;Learn Docker by Building a Microservice&lt;/a&gt; or the lovely &lt;a href="https://www.docker.com/what-docker"&gt;What is Docker&lt;/a&gt; page from the docs.&lt;/p&gt;
&lt;p&gt;You may be aware that Docker creates processes in isolated containers using some key Linux technologies which allow for low-level isolation (such as &lt;strong&gt;namespaces&lt;/strong&gt; and &lt;strong&gt;cgroups&lt;/strong&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;p&gt;This is described in detail on the &lt;a href="https://docs.docker.com/engine/understanding-docker/"&gt;Understand the Docker Architecture&lt;/a&gt; page, but essentially means we can do this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Docker-on-Ubuntu.png" alt="Docker Running on Ubuntu"&gt;&lt;/p&gt;
&lt;p&gt;Here I have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;My machine, called &lt;code&gt;Dave-Ubuntu&lt;/code&gt;, which is running Ubuntu&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;, with a local IP 192.168.0.1.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;docker&lt;/code&gt; executable, which I use to issue commands to&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip;the Docker Host, which runs the docker daemon, which actually does the work of starting/stopping/building containers and so on.&lt;/li&gt;
&lt;li&gt;Some containers in the Docker Host - one is based on a MySQL image and has a DB, one is based on a Node.js image and is running an app.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Docker host is actually my machine - I can connect using the loopback IP 127.0.0.1 (i.e. localhost). The containers also have the IP of the host. If I want to create and connect to a MySQL DB from my machine, I just type:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -d -e MYSQL_ROOT_PASSWORD=123 -p 3306:3306 mysql
mysql -uroot -p123 -h127.0.0.1
&amp;gt; show databases;
&amp;gt; ...etc...
&amp;gt; exit;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The container was created on my machine (in the host) and addressable using my loopback IP.&lt;/p&gt;
&lt;h2 id="so-what"&gt;So What?&lt;/h2&gt;
&lt;p&gt;This is all great, but things get a little harder on a Mac or Windows. MacOS and the Windows OS don&amp;rsquo;t have the same kernel level support for process isolation, control groups and so on, so the Docker Host cannot run on these operating systems. Instead, an extra layer and component is introduced:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Docker-on-MacOS.png" alt="Docker on OSX"&gt;&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s new?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Oracle VirtualBox has been installed to create and manage virtual machines.&lt;/li&gt;
&lt;li&gt;A virtual machine running Linux (called in this case a &amp;lsquo;docker machine&amp;rsquo;) called &amp;lsquo;default&amp;rsquo; has been created (by convention with the IP 192.168.99.100).&lt;/li&gt;
&lt;li&gt;This virtual machine runs Linux, so can perfectly happily act as the docker host.&lt;/li&gt;
&lt;li&gt;The docker host is still addressable as 127.0.0.1 - &lt;em&gt;from the virtual machine&lt;/em&gt; - from the outside world (i.e. my Mac) I have to use the virtual machine IP.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So this is how Docker works on a Mac or on Windows. Things are made seemless where possible, for example, all of the required components are installed when you install the &lt;a href="https://www.docker.com/products/docker-toolbox"&gt;Docker Toolbox&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="so-what-1"&gt;So What?&lt;/h2&gt;
&lt;p&gt;Well the problem here is that one of the big benefits of using docker is that it allows us to create development environments which are much closer to production environments (at least from a software point of view).&lt;/p&gt;
&lt;p&gt;This kind of breaks down if we are doing development on a Mac or on Windows - because we have introduced an additional component which is simply not going to be present in our production environment. What are the problems?&lt;/p&gt;
&lt;h3 id="1-localhost-vs-docker-machine-ip"&gt;1. Localhost vs docker-machine IP&lt;/h3&gt;
&lt;p&gt;Docker helps us be a lot more agnostic to our development box, but if I&amp;rsquo;m writing about how to interact with docker containers there&amp;rsquo;s a problem:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -d -p 8080:8080 my-app-server
curl http://localhost:8080/some-api-call
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This works on a Linux machine - it does not work on a Mac or Windows. On a Mac I need to run something like:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -d -p 8080:8080 my-app-server
curl http://$(docker-machine ip default)/some-api-call
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will &lt;em&gt;not&lt;/em&gt; work on Linux or Windows.&lt;/p&gt;
&lt;p&gt;Is this a big deal? Actually, kind of. What if I have an integration test which spins up some containers and runs calls against them - the test has to know about the execution environment. That&amp;rsquo;s a pain. An alternative is to run tests in a container and link them with something like docker-compose, but this is not ideal.&lt;/p&gt;
&lt;h3 id="2-terminal-hassle"&gt;2. Terminal Hassle&lt;/h3&gt;
&lt;p&gt;If I open a terminal and check to see what containers are running:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ll see nothing. If I try to run a container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -it mongo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ll get an error - because my docker instance cannot communicate with the host. I need to use a specially set up terminal to tell it to connect to the VM.&lt;/p&gt;
&lt;p&gt;Again, the Docker Toolkit is set up to try and make things easy. If I install the toolkit I can run an app called Docker Quickstart Terminal:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Quickstart.jpg" alt="Docker Quickstart Terminal"&gt;&lt;/p&gt;
&lt;p&gt;And this will open a terminal where I &lt;em&gt;can&lt;/em&gt; use these commands. It will also start the docker machine VM if it has to. It&amp;rsquo;s even smart enough to recognise if I have multiple terminal apps, such as iTerm, and ask which one I want to use.&lt;/p&gt;
&lt;p&gt;This problem is - this doesn&amp;rsquo;t always work smoothly. Sometimes it will seem that the machine has started but will still not accept commands. Typically a restart is needed in this scenario.&lt;/p&gt;
&lt;p&gt;Also, it&amp;rsquo;s an interruption. If you are running a terminal already and want to issue a quick command, it will fail, unless it was a terminal started with the Docker Quickstart app.&lt;/p&gt;
&lt;h3 id="3-inotify---in-container-development"&gt;3. inotify - In Container Development&lt;/h3&gt;
&lt;p&gt;If you recognise the term, you probably know the issue. If not, a little explanation is necessary.&lt;/p&gt;
&lt;p&gt;As you get more and more familiar with Docker, you will probably find that you are spending more and more time testing, building then running your image in a container. In fact, you might be changing a single code file and using the container as the dev test server on your machine.&lt;/p&gt;
&lt;p&gt;This fast gets painful - the container image takes time to build and slows down the development cycle. There&amp;rsquo;s a great technique in this scenario: &lt;strong&gt;In Container Development&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In container development is pretty much what it sounds like. Instead of editing your code on your machine, building an image and creating a container to debug, you simply create the container with what you need, &lt;strong&gt;mount your code&lt;/strong&gt; in to the container and run all of your development tooling from inside the container:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/In-Container-Development.png" alt="Docker In Container Development"&gt;&lt;/p&gt;
&lt;p&gt;In this diagram, I have my code locally on my machine. I have built a container which runs &lt;code&gt;nodemon&lt;/code&gt;, watching a directory on the container. That directory is actually just a volume containing my code which I have mounted into my container.&lt;/p&gt;
&lt;p&gt;This is a really nice technique - I can still code locally, but as I make changes, &lt;code&gt;nodemon&lt;/code&gt; serves up my new content.&lt;/p&gt;
&lt;p&gt;This specific example applies to Node.js, but can be applied to many scenarios.&lt;/p&gt;
&lt;p&gt;The problem is that many watcher tools like &lt;code&gt;nodemon&lt;/code&gt; use a kernel subsystem called &lt;code&gt;inotify&lt;/code&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; to get notifications when files change. But &lt;code&gt;inotify&lt;/code&gt; doesn&amp;rsquo;t work on virtualbox&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;. This means that this technique isn&amp;rsquo;t supported for Mac or Windows. There are however some tools which try and work around this with polling.&lt;/p&gt;
&lt;p&gt;So now we have another issue. The develop/test process might be nice on Linux, but for devs on other platforms the process is more clunky.&lt;/p&gt;
&lt;h1 id="docker-for-mac-and-windows-to-the-rescue"&gt;Docker for Mac and Windows to the rescue&lt;/h1&gt;
&lt;p&gt;The issues I&amp;rsquo;ve mentioned so far are the big ones which cause me problems personally, I&amp;rsquo;m sure there are others (please comment and let me know!).&lt;/p&gt;
&lt;p&gt;This is why there was rather a lot of interest in the new Docker Beta - one of the big features is that the Docker Machine is going away. In theory, we can use Docker on a Mac or Windows and have the same experience as on Linux.&lt;/p&gt;
&lt;h2 id="so-how"&gt;So how?&lt;/h2&gt;
&lt;p&gt;Virtualbox is gone. We still need a VM, but this VM is now a very lightweight Alpine Linux based image which runs on xhyve for MacOS and Hyper-V for Windows. All management of this VM is handled &lt;em&gt;by the docker executable&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If these are not familiar terms, &lt;a href="https://en.wikipedia.org/wiki/Alpine_Linux"&gt;Alpine Linux&lt;/a&gt; is an &lt;em&gt;extreeeemely&lt;/em&gt; lightweight Linux distro originally design to fit on a floppy disk (I think it clocks at around 5 MB now). &lt;a href="https://github.com/mist64/xhyve"&gt;xhyve&lt;/a&gt; is an &lt;em&gt;extremely&lt;/em&gt; lightweight hypervisor which allows FreeBSD and some other distros on OSX. &lt;a href="https://en.wikipedia.org/wiki/Hyper-V"&gt;Hyper-V&lt;/a&gt; is a native hypervisor for Windows Server which can run on Windows 8 onwards&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Using tools specifically designed for each platform (and with the help of both Apple and Microsoft), Docker have been able to make the experience much more seamless and smooth.&lt;/p&gt;
&lt;h1 id="trying-it-out"&gt;Trying It Out&lt;/h1&gt;
&lt;p&gt;Removing the three pain points discussed and a clean and simple setup process is what I&amp;rsquo;m looking at today, and here&amp;rsquo;s the results.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;Piece of cake. Download the beta, install, run, enter the beta key and pop, there&amp;rsquo;s the new docker:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-03-at-20-33-20-1.png" alt="Docker Welcome Message"&gt;&lt;/p&gt;
&lt;p&gt;The new status bar icon gives me a way to quickly see the status of the machine. Some of the commands hint at features to come, others offer the instructions needed. Settings are fairly basic, but I&amp;rsquo;m not sure what else you&amp;rsquo;d need:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-09-56.png" alt="Status Bar Screenshot 1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-10-07.png" alt="Status Bar Screenshot 2"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-10-23.png" alt="Status Bar Screenshot 3"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-44-04.png" alt="Status Bar Screenshot 4"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-44-12.png" alt="Status Bar Screenshot 5"&gt;&lt;/p&gt;
&lt;h3 id="1-localhost-vs-docker-machine-ip-1"&gt;1. Localhost vs docker-machine IP&lt;/h3&gt;
&lt;p&gt;Quickly bashing out the commands below shows that the virtual machine IP address issue is gone:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123 mysql
mysql -uroot -p123 -h127.0.0.1
&amp;gt; show databases;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-03-at-23-37-39.png" alt="Localhost Screenshot"&gt;&lt;/p&gt;
&lt;p&gt;Great news!&lt;/p&gt;
&lt;p&gt;How this works under the hood is a mystery to me. If anyone knows, I&amp;rsquo;d be interested and would like to update this writeup!&lt;/p&gt;
&lt;h3 id="2-terminal-hassle-1"&gt;2. Terminal Hassle&lt;/h3&gt;
&lt;p&gt;Quick and easy to test - running any terminal any way I like lets me access containers using the &lt;code&gt;docker&lt;/code&gt; executable - no magic needed:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-03-at-23-46-57.png" alt="Shells"&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a screenshot of iTerm3, the Terminal App and the Terminal App running &lt;code&gt;zsh&lt;/code&gt;, all of which are happily communicating with the docker deamon through the &lt;code&gt;docker&lt;/code&gt; app.&lt;/p&gt;
&lt;h3 id="3-in-container-development"&gt;3. In Container Development&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve not thrashed this one too hard, but gone for a quick sanity check. Throwing together probably my best ever node.js app&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;main.js&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;setInterval(function() {
console.log(&amp;#34;Goodbye, cruel world!&amp;#34;);
}, 1000);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and a simple dockerfile:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;FROM node:6
WORKDIR src/
ADD package.json .
RUN npm install
CMD npm start
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;is enough to test this. I can build then run the container, mounting the working directory into the &lt;code&gt;src&lt;/code&gt; volume on the container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker build -t incontainerdev .
docker run -it -v `pwd`:/src incontainerdev]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Immediately, I open a new window and change the source code and save (on my local Mac, not in the container). Voila:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Screen-Shot-2016-06-04-at-00-03-23.png" alt="Live Reloading"&gt;&lt;/p&gt;
&lt;p&gt;Live reloading works without a hitch! &lt;code&gt;nodemon&lt;/code&gt; picks up my changes, using &lt;code&gt;inotify&lt;/code&gt; from the VM (all through a lightweight userspace hypervisor).&lt;/p&gt;
&lt;p&gt;You know what is cool&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt;? &lt;strong&gt;I don&amp;rsquo;t even need Node.js installed to build this Node app!&lt;/strong&gt; The runtime is in the container, all of the execution happens in the container.&lt;/p&gt;
&lt;h1 id="thats-a-wrap"&gt;That&amp;rsquo;s a Wrap&lt;/h1&gt;
&lt;p&gt;That&amp;rsquo;s it for my initial impressions. From this point onwards I&amp;rsquo;m going to be using Docker for Mac heavily as I&amp;rsquo;ll do all of my work with it installed, so from time to time I may update this article with other observations.&lt;/p&gt;
&lt;p&gt;The key takeaway is: at the moment, Docker for Mac just &lt;em&gt;works&lt;/em&gt;. I&amp;rsquo;m using it in the same way I would on Ubuntu with no messing around. This is great, it seems like a simple thing but I&amp;rsquo;m guessing it was a lot of effort from the guys and girls at Docker, Microsoft and Apple.&lt;/p&gt;
&lt;p&gt;This is still a Beta, there&amp;rsquo;ll be bugs and they&amp;rsquo;ll be fixed. I can&amp;rsquo;t wait for the Beta to go fully into the wild, and see what exciting things people can do with it.&lt;/p&gt;
&lt;p&gt;As usual, any comments or observations are welcome!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Further Reading&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Namespaces: &lt;a href="http://man7.org/linux/man-pages/man7/namespaces.7.html"&gt;http://man7.org/linux/man-pages/man7/namespaces.7.html&lt;/a&gt;
cgroups: &lt;a href="http://man7.org/linux/man-pages/man7/cgroups.7.html"&gt;http://man7.org/linux/man-pages/man7/cgroups.7.html&lt;/a&gt;
Docker Execution Drivers: &lt;a href="https://blog.docker.com/2014/03/docker-0-9-introducing-execution-drivers-and-libcontainer/"&gt;https://blog.docker.com/2014/03/docker-0-9-introducing-execution-drivers-and-libcontainer/&lt;/a&gt;
inotify: &lt;a href="http://man7.org/linux/man-pages/man7/inotify.7.html"&gt;http://man7.org/linux/man-pages/man7/inotify.7.html&lt;/a&gt;
The challenges of in-container development on OSX: &lt;a href="http://hharnisc.github.io/2015/09/16/developing-inside-docker-containers-with-osx.html"&gt;http://hharnisc.github.io/2015/09/16/developing-inside-docker-containers-with-osx.html&lt;/a&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Read up on namespaces &lt;a href="http://man7.org/linux/man-pages/man7/namespaces.7.html"&gt;here&lt;/a&gt; and cgroups &lt;a href="http://man7.org/linux/man-pages/man7/cgroups.7.html"&gt;here&lt;/a&gt;. Docker can also use &lt;a href="https://en.wikipedia.org/wiki/LXC"&gt;LXC&lt;/a&gt; but no longer &lt;em&gt;has&lt;/em&gt; to, there&amp;rsquo;s a great write-up &lt;a href="https://blog.docker.com/2014/03/docker-0-9-introducing-execution-drivers-and-libcontainer/"&gt;here&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Surprise!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Read up on inotify &lt;a href="http://man7.org/linux/man-pages/man7/inotify.7.html"&gt;here&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;The issue will not be resolved: &lt;a href="https://www.virtualbox.org/ticket/10660"&gt;https://www.virtualbox.org/ticket/10660&lt;/a&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I&amp;rsquo;ve not used the Docker for Windows Beta yet so have not got first hand experience of it. I&amp;rsquo;ve also not looked into compatibility, from memory Hyper-V isn&amp;rsquo;t available on Home versions of Windows, but I might be wrong.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;Inspired by my first programming book, the excellent &lt;a href="http://www.amazon.com/C-Dummies-Dan-Gookin/dp/0764570684"&gt;C for Dummies&lt;/a&gt; by Dan Gookin.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;For a given definition of cool.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Is it worth persevering with Golang?</title><link>https://dwmkerr.com/is-it-worth-persevering-with-golang/</link><pubDate>Wed, 01 Jun 2016 22:10:40 +0000</pubDate><guid>https://dwmkerr.com/is-it-worth-persevering-with-golang/</guid><description>&lt;p&gt;I recently decided to try out &lt;a href="https://golang.org/"&gt;the Go Programming Language&lt;/a&gt;, by building a little project called &lt;a href="http://www.github.com/dwmkerr/google-it"&gt;google-it&lt;/a&gt; which let&amp;rsquo;s me run a google search from a terminal:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/google-it.gif" alt="google-it screenshot"&gt;&lt;/p&gt;
&lt;p&gt;The idea behind the project is simple - avoid jumping into a browser if you need to quickly look up something which you can find in the first line of a Google search result. The idea is to try and stay in the zone. For example, forgotten how to split panes in tmux?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;google-it &lt;span style="color:#e6db74"&gt;&amp;#34;split pane tmux&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Would probably show enough information to get going without leaving the terminal.&lt;/p&gt;
&lt;p&gt;Anyway, the project itself is not that useful&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; but it seemed like an ideal project to use as a learning exercise for a new language. After perhaps 10-20 hours of learning, coding and messing around, I&amp;rsquo;m wondering - is it worth persevering with Golang?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 3/6/2016&lt;/strong&gt; If you are learning too, check the &lt;a href="#tipsfornoobs"&gt;Tips for Noobs&lt;/a&gt; section at the end of the article, great tips from members of the community!&lt;/p&gt;
&lt;h2 id="why-go"&gt;Why Go&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;?&lt;/h2&gt;
&lt;p&gt;The decision to choose Go for this learning exercise was fairly arbitrary. For the last few years I&amp;rsquo;ve been using mainly interpretted languages or languages which use a platform (.NET, Node.js, Java etc) and the idea of going back to something which compiles into in good ol&amp;rsquo; binaries seemed appealing. I&amp;rsquo;d also heard a lot of good things about Go in general, mostly relating to simplicity, ease of use and concurrency.&lt;/p&gt;
&lt;h2 id="why-persevere"&gt;Why Persevere?&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s where I&amp;rsquo;m looking for guidance. I&amp;rsquo;ve collected some of my observations so far, and my overall experience with the language is uninspiring. Anyone who can comment on what makes Go great, or whether my ambivalence is justified will help me decide whether to build my next mess-around project in Go or move on to something else.&lt;/p&gt;
&lt;h2 id="frustrations-so-far"&gt;Frustrations So Far&lt;/h2&gt;
&lt;p&gt;Before I upset anyone, this is all just the opinion of a total Go noob with maybe 15 hours of coding time in Go. But I&amp;rsquo;ve been developing using a few different languages and platforms for while.&lt;/p&gt;
&lt;h3 id="folder-structure-is-way-too-opinionated"&gt;Folder structure is way too opinionated&lt;/h3&gt;
&lt;p&gt;Setup itself is easy, at least on unix or a Mac. But like many coders, I&amp;rsquo;m anal-retentive about how I like to organise things:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;~
└───repositories
├───github.com
├───dwmkerr
├───project1
├───etc
├───organisation1
├───etc
├───bitbucket.com
├───etc
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is how I structure my projects on all my machines, and it works for me.&lt;/p&gt;
&lt;p&gt;Go forces me to put all of my Go projects in the &lt;code&gt;$GOPATH&lt;/code&gt;, so now I have:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;└───repositories
├───github.com
├───etc
├───go
├───src
├───github.com
├───dwmkerr
├───goproject1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which unnecessarily spreads out my projects. Other thoughts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;My &lt;code&gt;src&lt;/code&gt; folder is increasingly cluttered with dependent modules, making it harder to find my own work.&lt;/li&gt;
&lt;li&gt;Even within the project folder, I have little flexibility. I&amp;rsquo;d like to have a &lt;code&gt;src&lt;/code&gt; folder to keep my code in, with just the &lt;code&gt;README.md&lt;/code&gt; at the root (leaving space for a &lt;code&gt;docs&lt;/code&gt; folder and others if necessary) - this cannot be done, so &lt;a href="https://github.com/dwmkerr/google-it"&gt;my root folder is cluttered&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Again, in the project folder itself, &lt;a href="https://www.reddit.com/r/golang/comments/2lq3it/is_there_a_way_to_arrange_go_code_into_multiple/"&gt;I cannot use sub-folders for code&lt;/a&gt;. Some might argue if you need subfolders you have too much code in one project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All in all it feels like there are a lot of constraints for structure and organisation, with little benefit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 3/6/2016&lt;/strong&gt; Steve Francia has rightly pointed out that points 2 and 3 are actually wrong, a project can be simply a &lt;code&gt;main.go&lt;/code&gt; file in the root and a set of submodules, see &lt;a href="http://www.dwmkerr.com/is-it-worth-persevering-with-golang/#comment-2708416211"&gt;this comment&lt;/a&gt; for details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 3/6/2016&lt;/strong&gt; A very nice way to separate internal and external go modules is described in &lt;a href="https://www.reddit.com/r/golang/comments/4m5it3/is_it_worth_persevering_with_golang/d3ssyts"&gt;this reddit thread&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="the-idiomatic-approach-to-error-handling-is-flawed"&gt;The idiomatic approach to error handling is flawed&lt;/h3&gt;
&lt;p&gt;This is likely to prove contentious.&lt;/p&gt;
&lt;p&gt;My code contains sections like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func LoadSettings() (Settings, error) {
var s Settings
exists, err := exists(GetSettingsPath())
if err != nil {
return s, err
}
if !exists {
return CreateDefaultSettings(), nil
}
raw, err := ioutil.ReadFile(GetSettingsPath())
if err != nil {
return s, err
}
json.Unmarshal(raw, &amp;amp;s)
return s, err
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I see smells:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;s&lt;/code&gt; structure is created even though I may not need it.&lt;/li&gt;
&lt;li&gt;Even worse, it is &lt;strong&gt;returned uninitialised&lt;/strong&gt; in error conditions.&lt;/li&gt;
&lt;li&gt;Repetitive code for dealing with error conditions for calls.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now I could avoid the first smell by returning a pointer to the structure, but that incures unnecessary complexity and heap allocations. Here I feel the language is forcing me to do something awful (return a structure I know is invalid) and expect the caller to deal with it.&lt;/p&gt;
&lt;p&gt;Even worse - the calling code now does this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;settings, err := LoadSettings()
if err != nil {
color.Red(&amp;#34;Error loading settings: &amp;#34;, err)
os.Exit(1)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;ve seen this in many places - nested calls passing the same error around, with little extra context, and eventually terminating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is what exceptions are for.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Native exceptions in languages handle this for you, giving stack information and killing the process by default.&lt;/p&gt;
&lt;p&gt;The &amp;lsquo;pass the error on to the caller approach&amp;rsquo; may not be the right way to go, but the Go blog suggests exactly this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.golang.org/error-handling-and-go"&gt;https://blog.golang.org/error-handling-and-go&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And to me it stinks a bit. If this is the truly desired idomatic approach, then why not supported it natively by the language? Here&amp;rsquo;s the same pseudo-code in F#:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let loadSettings =
let path = getSettingsPath()
match exists path with
| true -&amp;gt; path |&amp;gt; readFile |&amp;gt; readSettings
| _ -&amp;gt; createDefaultSettings
match loadSettings with
| Some settings -&amp;gt; // ..whatever
| None -&amp;gt; // ..deal with errors
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If &lt;code&gt;loadSettings&lt;/code&gt; can&amp;rsquo;t return settings, it doesn&amp;rsquo;t return settings. If the caller doesn&amp;rsquo;t handle the &amp;rsquo;no settings&amp;rsquo; scenario explicitly, the compiler will complain that there&amp;rsquo;s a case missing. In this case we have an approach which will warn the coder if they miss something.&lt;/p&gt;
&lt;h3 id="inconsistent-syntax"&gt;Inconsistent Syntax&lt;/h3&gt;
&lt;p&gt;A small one, but when I&amp;rsquo;m defining a structure I can do this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;type Link struct {
Id string
Uri string
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;but when I&amp;rsquo;m returning a structure, I need commas:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;return Link{
Id: strconv.Itoa(linkNumber),
Uri: item.Link,
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can see the benefit of &lt;strong&gt;allowing&lt;/strong&gt; a comma on the last line, to support quick refactoring, but &lt;strong&gt;forcing&lt;/strong&gt; it seems odd. Why commas for some constructs and not others&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;?&lt;/p&gt;
&lt;p&gt;Also, some more &amp;lsquo;unusual&amp;rsquo; syntax (depending on your background) is present, I assume to save space:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;something := createAndAssign()
// rather than
var something SomeType
something = assign()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But some space saving constructs such as ternary operators are missing:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// easy- c++, c#, java style
something := condition ? case1() : case2()
// easy- python style
something := case1() if condition else case2() // python
// hard - go style
var something SomeType
if condition {
something = case1()
} else {
something = case2()
}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="difficult-debugging"&gt;Difficult Debugging&lt;/h3&gt;
&lt;p&gt;For C, C++, .NET, Java and many other languages, debugging is pretty straightforward. For Node.js, you can just use the excellent Chrome debugging tools. For Go, it seems like it&amp;rsquo;s &lt;strong&gt;much&lt;/strong&gt; harder.&lt;/p&gt;
&lt;p&gt;In my limited time using the language, I avoided &lt;code&gt;gdb&lt;/code&gt; because it looked like a lot of work:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://golang.org/doc/gdb"&gt;https://golang.org/doc/gdb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I did see some projects like &lt;a href="https://github.com/mailgun/godebug"&gt;godebug&lt;/a&gt; which may ease the process but I was initially surprised by the effort needed to get into debugging.&lt;/p&gt;
&lt;p&gt;Commenter Sotirios Mantziaris &lt;a href="http://www.dwmkerr.com/is-it-worth-persevering-with-golang/#comment-2707804888"&gt;mentioned that delve provides a nice experience as a debugger&lt;/a&gt;, so this would be worth exploring.&lt;/p&gt;
&lt;h2 id="delights-so-far"&gt;Delights So Far&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s also worth talking about what I&amp;rsquo;ve liked or loved about Go so far.&lt;/p&gt;
&lt;h3 id="simple-tooling"&gt;Simple Tooling&lt;/h3&gt;
&lt;p&gt;A project can be nothing more than a single file, go knows how to build and install it. Compare that to Java, where you have a lot of &amp;lsquo;project&amp;rsquo; related stuff - Gradle stuff, Ant stuff, Maven stuff, xml project files stuff and it feels much cleaner.&lt;/p&gt;
&lt;p&gt;The tooling is intuitive, fast and works well if you are happy living in a terminal.&lt;/p&gt;
&lt;h3 id="fantastic-community"&gt;Fantastic Community&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve added this observation just recently, since writing the article I&amp;rsquo;ve had a &lt;strong&gt;huge&lt;/strong&gt; amount of positive input, describing how to improve my code, better understand Go idioms and where its sweet spots like.&lt;/p&gt;
&lt;p&gt;For someone new to a language, the community support is great and will really help people just getting into Go get advice and guidance.&lt;/p&gt;
&lt;h3 id="testing-as-a-first-class-citizen"&gt;Testing as a First Class Citizen&lt;/h3&gt;
&lt;p&gt;Testing is built in, which is great. Knowing that you can run &lt;code&gt;go test&lt;/code&gt; on a project and have a standard way of executing tests is really quite nice. I love &lt;code&gt;npm test&lt;/code&gt; for Node.js projects as it has helped standardise testing as a practice (checkout &lt;code&gt;npm install&lt;/code&gt; then &lt;code&gt;npm test&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;However, I did have to rely on a library, &lt;a href="https://github.com/smartystreets/goconvey"&gt;goconvey&lt;/a&gt;, to allow me to write tests in the more BBD structured style which I prefer:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;func TestSpec(t *testing.T) {
Convey(&amp;#34;The param loader&amp;#34;, t, func() {
Convey(&amp;#34;Should handle no params&amp;#34;, func() {
params, err := ParseParams([]string{})
So(params.ShowHelp.Present, ShouldEqual, false)
So(params.Results.Present, ShouldEqual, false)
So(params.Open.Present, ShouldEqual, false)
So(err, ShouldEqual, nil)
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But that&amp;rsquo;s a totally personal thing and I&amp;rsquo;m sure many others will prefer more &amp;lsquo;vanilla&amp;rsquo; tests.&lt;/p&gt;
&lt;h3 id="great-documentation"&gt;Great Documentation&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve found everything I&amp;rsquo;ve needed so far on &lt;a href="https://golang.org/doc/"&gt;Go&amp;rsquo;s own documentation&lt;/a&gt;. The documentation is clean, accessible and seems fairly complete from my limited interactions with it.&lt;/p&gt;
&lt;h3 id="delightful-vim-development-experience"&gt;Delightful Vim Development Experience&lt;/h3&gt;
&lt;p&gt;OK - this is not a language feature. But if you are a noob like me moving from this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/VimGoVanilla-1.jpg" alt="Vim vanilla"&gt;&lt;/p&gt;
&lt;p&gt;to this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/VimWithVimGo.jpg" alt="Vim with vim-go plugin screenshot"&gt;&lt;/p&gt;
&lt;p&gt;made a big difference. The excellent &lt;a href="https://github.com/fatih/vim-go"&gt;vim-go&lt;/a&gt; plugin gives syntax highlighting and supports some really useful commands. As a learner, regularly running &lt;code&gt;:GoLint&lt;/code&gt; is really helping me write more &amp;lsquo;conventional&amp;rsquo; Go.&lt;/p&gt;
&lt;h2 id="what-am-i-missing"&gt;What am I missing?&lt;/h2&gt;
&lt;p&gt;There are some things I know I haven&amp;rsquo;t had a chance to look at which may really be demonstrating the best parts of Go:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Concurrency patterns&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Writing web servers&lt;/li&gt;
&lt;li&gt;Godoc&lt;/li&gt;
&lt;li&gt;Debugging with Delve&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="should-i-continue"&gt;Should I continue?&lt;/h2&gt;
&lt;p&gt;At this stage I&amp;rsquo;m leaning towards moving on and trying something different, hoping that I&amp;rsquo;ll come back to Go later. Should I persevere with Go for my next project, would Go enthusiasts suggest so and what sort of project hits the &amp;lsquo;sweet spot&amp;rsquo; where Go is a really effective choice?&lt;/p&gt;
&lt;p&gt;An interesting comment by a colleague was: &amp;ldquo;I would say the usual &amp;lsquo;does it change the way you think about programming?&amp;rsquo;, if yes then persevere, if no then are you going to really leverage Go’s strengths (and find out weaknesses) in your project? If no then either change language or project.&amp;rdquo; was rather insightful.&lt;/p&gt;
&lt;p&gt;Any comments are welcome!&lt;/p&gt;
&lt;h2 id="can-you-help-me-get-better"&gt;Can you help me get better?&lt;/h2&gt;
&lt;p&gt;Any pull requests to my project or comments which show where I&amp;rsquo;ve gone wrong and what I could do to improve my experience and code would be welcome at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/google-it"&gt;https://github.com/dwmkerr/google-it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks!&lt;/p&gt;
&lt;h2 id="tips-for-noobs"&gt;Tips for Noobs!&lt;/h2&gt;
&lt;p&gt;Since publishing this article I&amp;rsquo;ve collected some useful tips from people who&amp;rsquo;ve commented or got in touch.&lt;/p&gt;
&lt;h4 id="use-gofmt-and-lint"&gt;Use gofmt and lint&lt;/h4&gt;
&lt;p&gt;The tool &lt;code&gt;gofmt&lt;/code&gt; will update your code to format it in a conventional go style. This&amp;rsquo;ll help you keep your code consistent with others&amp;rsquo;. Using a linter will also help you stay conventional - if you are using &lt;a href="https://github.com/fatih/vim-go"&gt;vim-go&lt;/a&gt; you can run it from vim with &lt;code&gt;:GoLint&lt;/code&gt;. Thanks @snoproblem!&lt;/p&gt;
&lt;h4 id="understand-where-go-is-a-ferrari"&gt;Understand where Go is a Ferrari&lt;/h4&gt;
&lt;p&gt;This I am still working on, and it&amp;rsquo;s certainly tricky for a noob. But commenter devdungeon pointed out that a project like mine is not a great use case for Go - Go excels at speed and concurrency. Projects where that is key are going to be more inspiring. See &lt;a href="http://www.dwmkerr.com/is-it-worth-persevering-with-golang/#comment-2708265044"&gt;this comment&lt;/a&gt; for more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Mainly because you have to sign up for the Google Cloud Platform to get an API key so you can perform searches, as Google have deprecated the free and easy search API.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=DvijZuvEiQo"&gt;https://www.youtube.com/watch?v=DvijZuvEiQo&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;SnoProblem describes why in &lt;a href="http://www.dwmkerr.com/is-it-worth-persevering-with-golang/#comment-2707767852"&gt;this comment&lt;/a&gt;&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Quick Tip: Sending Newlines with cURL</title><link>https://dwmkerr.com/quick-tip-sending-newlines-with-curl/</link><pubDate>Tue, 03 May 2016 22:12:28 +0000</pubDate><guid>https://dwmkerr.com/quick-tip-sending-newlines-with-curl/</guid><description>&lt;p&gt;Yikes, this took far too long to figure out!&lt;/p&gt;
&lt;p&gt;I have a service which takes plain text multi-line input and outputs an object for each line, something like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Line 1
Line 2
Line 3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[
{line: &amp;#34;Line 1&amp;#34;},
{line: &amp;#34;Line 2&amp;#34;},
{line: &amp;#34;Line 3&amp;#34;}
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There&amp;rsquo;s a bit more to it than that, but that&amp;rsquo;s the gist.&lt;/p&gt;
&lt;p&gt;I want to test my service with cURL, trying:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl --data &amp;#34;Line 1\nLine 2\nLine 3&amp;#34; \
-H &amp;#34;Content-Type: text/plain&amp;#34; localhost:3000/parse
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This did not work. Nor did some alternatives. And I really didn&amp;rsquo;t want to have to write the text to a file and load it in.&lt;/p&gt;
&lt;p&gt;Turns out there&amp;rsquo;s a nice little shell trick to let you use escape characters C style, use &lt;code&gt;$'some\ncontent'&lt;/code&gt; to use ANSI C escaping. Now you can cURL with newlines!&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;curl --data $&amp;#39;Line 1\nLine 2\nLine 3&amp;#39; \
-H &amp;#34;Content-Type: text/plain&amp;#34; localhost:3000/parse
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;h2 id="references"&gt;References&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/bash/manual/html_node/ANSI_002dC-Quoting.html"&gt;GNU Bash ANSI C Quoting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/8467424/echo-newline-in-bash-prints-literal-n"&gt;Stack Overflow - Echo Newline Bash Prints \n&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/3872427/how-to-send-line-break-with-curl"&gt;Stack Overflow - How to send line break with cURL&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description><category>CodeProject</category></item><item><title>Moving from React + Redux to Angular 2</title><link>https://dwmkerr.com/moving-from-react-redux-to-angular-2/</link><pubDate>Mon, 25 Apr 2016 09:45:00 +0000</pubDate><guid>https://dwmkerr.com/moving-from-react-redux-to-angular-2/</guid><description>&lt;p&gt;I&amp;rsquo;ve just finished working on a very large project written in React and Redux. The whole team were new to both and we loved them.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to share my experiences of experimenting in Angular 2 with you, from the point of view of someone who needs a pretty compelling reason to move away from my JSX and reducers.&lt;/p&gt;
&lt;h1 id="the-journey-so-far"&gt;The Journey So Far&lt;/h1&gt;
&lt;p&gt;Let me highlight a few key moments in my UI development experiences, to give a bit of context to my ramblings.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Journey.jpg" alt="The Journey So Far"&gt;&lt;/p&gt;
&lt;p&gt;Reading about redux was a lightbulb moment for me - rather than a complex framework it&amp;rsquo;s a simply library to help apply a few common sense functional programming principles - state is immutable, functions apply predictable transformations to data to produce new data.&lt;/p&gt;
&lt;p&gt;Learning React took a little bit of getting used to, but not too much, it was quite a bit more simple than Angular anyway.&lt;/p&gt;
&lt;p&gt;Long story short, simple React components and rigorous state management has so far resulted in the most manageable and well written very large scale UIs I&amp;rsquo;ve worked on so far - can Angular 2 compete with this?&lt;/p&gt;
&lt;h1 id="first-step-with-angular-2---folder-structure-typescript-sublime-text"&gt;First Step with Angular 2 - Folder Structure, Typescript, Sublime Text&lt;/h1&gt;
&lt;p&gt;I checked out &lt;a href="https://angular.io/docs/ts/latest/quickstart.html"&gt;the pretty neat &amp;lsquo;Getting Started&amp;rsquo; guide from Angular&lt;/a&gt; which promised to get me started in five minutes.&lt;/p&gt;
&lt;p&gt;It didn&amp;rsquo;t take five minutes, there&amp;rsquo;s a few gotchas, so I&amp;rsquo;m going to give a condensed guide here.&lt;/p&gt;
&lt;h2 id="step-1-the-folder-structure"&gt;Step 1: The Folder Structure&lt;/h2&gt;
&lt;p&gt;The first few steps of the angular guide creates the following folder structure:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;|-- angular2-starter
|-- tsconfig.json
|-- typings.json
|-- package.json
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the standard &lt;code&gt;package.json&lt;/code&gt; with some scripts ready to go. We also have &lt;code&gt;tsconfig.json&lt;/code&gt; to configure the typescript compiler and &lt;code&gt;typings.json&lt;/code&gt; to provide info to the compiler on where to get type information.&lt;/p&gt;
&lt;p&gt;You can check the code at this stage here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/angular2-starter/tree/step1"&gt;https://github.com/dwmkerr/angular2-starter/tree/step1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Step1.png" alt="Step 1 GitHub Screenshot"&gt;&lt;/p&gt;
&lt;h2 id="node--npm-issues"&gt;Node &amp;amp; NPM Issues&lt;/h2&gt;
&lt;p&gt;At this stage the quickstart says you can run &lt;code&gt;npm install&lt;/code&gt; and all will be well:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/npm-install.png" alt="npm install screenshot"&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm ERR! cb() never called!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not so good! For the record I&amp;rsquo;m using NPM 3.7.3 installed via homebrew. This looks like a bug in Beta 15 (see &lt;a href="https://github.com/angular/angular/issues/8053"&gt;Issue #8053&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I fixed this by using &lt;em&gt;n&lt;/em&gt; to upgrade my node version:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ node -v
v5.9.0
$ npm install -g n # install &amp;#39;n&amp;#39; node version manager
$ sudo n latest
installed : v5.11.0
$ node -v
v5.11.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now it &lt;code&gt;npm install&lt;/code&gt; runs OK.&lt;/p&gt;
&lt;h2 id="step-2-adding-components-and-configuring-sublime"&gt;Step 2: Adding Components and Configuring Sublime&lt;/h2&gt;
&lt;p&gt;The next steps of the walkthrough take us through adding an app component, a &lt;code&gt;main.ts&lt;/code&gt; file to bootstrap the application and an index file. You can check the updates here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/angular2-starter/tree/step2"&gt;https://github.com/dwmkerr/angular2-starter/tree/step2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Essentially we now have:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;|-- angular2-starter
|-- tsconfig.json
|-- typings.json
|-- package.json
|-- index.html
|-- styles.css
|-- app
|-- main.ts
|-- app.component.ts
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this stage, running &lt;code&gt;npm start&lt;/code&gt; gives us a browerserified app to play with:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Step2.png" alt="Step 2 Screenshot"&gt;&lt;/p&gt;
&lt;p&gt;Clear enough so far, although the code in Sublime is not looking so pretty:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Step2Sublime.png" alt="Step 2 Sublime Text Screenshot"&gt;&lt;/p&gt;
&lt;p&gt;Quickly installing the &lt;a href="https://github.com/Microsoft/TypeScript-Sublime-Plugin"&gt;TypeScript plugin&lt;/a&gt; from Microsoft[^n] seems to do the trick:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Step2SublimeFormatted.png" alt="Step 2 Sublime Text with TypeScript plugin"&gt;&lt;/p&gt;
&lt;p&gt;If you need more details, here&amp;rsquo;s a gist with the full setup for Sublime 3, assuming you&amp;rsquo;ve got nothing installed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gist.github.com/dwmkerr/04fa8b8c15d049d0381e7798a79bcc45"&gt;https://gist.github.com/dwmkerr/04fa8b8c15d049d0381e7798a79bcc45&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this stage the app will run, we can see the basics of the Angular 2 syntax and start experimenting.&lt;/p&gt;
&lt;h2 id="step-3-adding-some-components"&gt;Step 3: Adding some components&lt;/h2&gt;
&lt;p&gt;At this stage the quick started guide starts going into more detail, guiding you through the process of creating multiple components. I decided to go off on my own here, with the rough plan of being able to write a set of goals for the day and turn it into a check-list[^n].&lt;/p&gt;
&lt;p&gt;Within not much time I had the some basic components, input and output, bindings and so on. Some screenshots:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Goals-Screenshot-1.png" alt="Goals Screenshot 1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Goals-Screenshot-2-1.png" alt="Goals Screenshot 2"&gt;&lt;/p&gt;
&lt;p&gt;You can take a look at the code at this stage by checking out the &amp;lsquo;step3&amp;rsquo; branch:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/angular2-starter/tree/step3"&gt;github.com/dwmkerr/angular2-starter/tree/step3&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="thoughts-so-far"&gt;Thoughts so far&lt;/h1&gt;
&lt;p&gt;For now, that&amp;rsquo;s all I&amp;rsquo;ve got time for. I&amp;rsquo;ve had a chance to get a feel for Angular 2, I&amp;rsquo;m going to come back to this in a few weeks and integrate Redux, maybe swap out System.JS for Webpack and do some experimenting.&lt;/p&gt;
&lt;p&gt;Opinions[^n] so far?&lt;/p&gt;
&lt;h3 id="not-sold-on-typescript"&gt;Not Sold on TypeScript&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve used TypeScript in my mess around, rather than plain &amp;lsquo;ol JavaScript, to keep the experience authentic to the angular team&amp;rsquo;s goals of using TypeScript to help.&lt;/p&gt;
&lt;p&gt;So far, I&amp;rsquo;m not seeing an enormous benefit. Some of the extra information available to auto-completion in nice, but this is a tooling thing.&lt;/p&gt;
&lt;p&gt;JavaScript is not a static language, the TypeScript annotations I find slowing me down a little.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There&amp;rsquo;s so much extra domain specific &lt;em&gt;stuff&lt;/em&gt; in Angular 2 that people might be lost without it. But if your stuff is so complex you need to adapt the base language, is it &lt;strong&gt;too&lt;/strong&gt; complex?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="explicit-component-surface-areas-are-a-nice-idea"&gt;Explicit Component Surface Areas are a Nice Idea&lt;/h3&gt;
&lt;p&gt;When defining a component, you specify explicitly what comes &lt;em&gt;in&lt;/em&gt; (data) and what goes &lt;em&gt;out&lt;/em&gt; (events).&lt;/p&gt;
&lt;p&gt;This means that the surface area of a component (i.e. the part you touch if you interact with it programmatically) is well defined. This is a good thing.&lt;/p&gt;
&lt;p&gt;However, this is all handled with some pretty framework-specific stuff[^n]:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// e.g.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;GoalsBoxComponent&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Event we fire when the goals change.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Output&lt;/span&gt;() &lt;span style="color:#a6e22e"&gt;goalsChanged&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventEmitter&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Goal&lt;/span&gt;[]&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;EventEmitter&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// e.g.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;GoalListComponent&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Input is a set of goals to render.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Input&lt;/span&gt;() &lt;span style="color:#a6e22e"&gt;goals&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Goal&lt;/span&gt;[] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In a nutshell&amp;hellip;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Explicit component surface area is a cool idea.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;React does this too with the optional &lt;code&gt;propTypes&lt;/code&gt;, but it is not enforced. &lt;em&gt;However&lt;/em&gt;, how this is done in Angular has already gone through a few radical changes with some &lt;a href="https://github.com/angular/angular/pull/4435#issuecomment-144789359"&gt;lively debate&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="not-ready-for-production-yet"&gt;Not ready for production&amp;hellip; yet&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s no standardised, documented way to test a component - nuff said. But things are evolving quickly.&lt;/p&gt;
&lt;h3 id="framework-fatigue"&gt;Framework Fatigue&lt;/h3&gt;
&lt;p&gt;Comparing React to Angular is unfair, one is a view library, one is a framework. But it&amp;rsquo;s worth pointing out this is a pretty complex framework. There&amp;rsquo;s a &lt;strong&gt;lot&lt;/strong&gt; of very domain specific stuff. See this documentation for an example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;li&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ngFor&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;#hero of heroes&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From &lt;a href="https://angular.io/docs/ts/latest/tutorial/toh-pt2.html"&gt;the documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The (*) prefix to ngFor indicates that the &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element and its children constitute a master template.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;The # prefix before &amp;ldquo;hero&amp;rdquo; identifies the hero as a local template variable. We can reference this variable within the template to access a hero’s properties.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You&amp;rsquo;ll get used to it (if you have to), but I think it&amp;rsquo;s harder to &lt;em&gt;reason&lt;/em&gt; about than:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;render&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;div&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;props&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;goals&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;goal&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;li&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;goal&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;title&lt;/span&gt;}&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/li&amp;gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/div&amp;gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OK fair enough, JSX is very specific, but the &lt;strong&gt;logic&lt;/strong&gt; (mapping an iterable) is JavaScript.&lt;/p&gt;
&lt;h1 id="wrapping-up"&gt;Wrapping Up&lt;/h1&gt;
&lt;p&gt;That&amp;rsquo;s it, for now. Next steps are to experiment more, see if it will play nice with Redux and share the next set of opinions.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d love to hear what you think, so drop your comments below!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Footnotes&lt;/strong&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Learn Docker by building a Microservice</title><link>https://dwmkerr.com/learn-docker-by-building-a-microservice/</link><pubDate>Tue, 19 Apr 2016 08:54:39 +0000</pubDate><guid>https://dwmkerr.com/learn-docker-by-building-a-microservice/</guid><description>&lt;p&gt;If you are looking to get your hands dirty and learn all about &lt;a href="https://docker.com"&gt;Docker&lt;/a&gt;, then look no further!&lt;/p&gt;
&lt;p&gt;In this article I&amp;rsquo;m going to show you how Docker works, what all the fuss is about, and how Docker can help with a basic development task - building a microservice.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll use a simple Node.js service with a MySQL backend as an example, going from code running locally to containers running a microservice and database.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="images/Article.png" /&gt;
&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve read the article, you can find the source code here:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/node-docker-microservice"&gt;github.com/dwmkerr/node-docker-microservice&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="what-is-docker"&gt;What is Docker?&lt;/h2&gt;
&lt;p&gt;At its heart, Docker is software which lets you create an &lt;em&gt;image&lt;/em&gt; (which is a lot like a template for a virtual machine) and then run instances of that image in a &lt;em&gt;container&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Docker maintains a vast repository of images, called the &lt;a href="https://hub.docker.com"&gt;Docker Hub&lt;/a&gt; which you can use as starting points or as free storage for your own images. You can install Docker, choose an image you&amp;rsquo;d like to use, then run an instance of it in a container.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to build images, create containers from images and more in this article.&lt;/p&gt;
&lt;h3 id="install-docker"&gt;Install Docker&lt;/h3&gt;
&lt;p&gt;To follow along and use this article, you&amp;rsquo;ll need Docker.&lt;/p&gt;
&lt;p&gt;Check the installation guide for your platform, &lt;a href="https://docs.docker.com/engine/installation/"&gt;docs.docker.com/engine/installation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are on Mac or Windows, consider using a Virtual Machine. I use Parallels on Mac OS X to run an Ubuntu machine for most development activities. Being able to take snapshots, break things and then revert back is very handy when experimenting.&lt;/p&gt;
&lt;h3 id="try-it-out"&gt;Try It Out&lt;/h3&gt;
&lt;p&gt;Enter this command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -it ubuntu
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a bit of spinning, you&amp;rsquo;ll see a prompt like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@719059da250d:/#
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Try out a few commands and then exit the container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;root@719059da250d:/# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.4 LTS
Release: 14.04
Codename: trusty
root@719059da250d:/# exit
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This doesn&amp;rsquo;t look like much, but a lot has happened!&lt;/p&gt;
&lt;p&gt;What you are seeing is the bash shell of an &lt;em&gt;isolated&lt;/em&gt; container running Ubuntu, on your machine. It&amp;rsquo;s yours to place with - you can install things on it, run software, whatever you want.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a diagram and breakdown of what just happened (the digram is from the &lt;a href="https://docs.docker.com/v1.8/introduction/understanding-docker/"&gt;&amp;lsquo;Understanding the Architecture&amp;rsquo; Docker Documentation&lt;/a&gt;, which is great):&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Flow.png" alt="Docker Run Flow"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We issue a docker command:&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker&lt;/code&gt;: run the docker client&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run&lt;/code&gt;: the command to run a new container&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-it&lt;/code&gt;: option to give the container an interactive terminal&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ubuntu&lt;/code&gt;: the image to base the container on&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;The docker service running on the host (our machine) checks to see if we have a copy of the requested image locally- which there isn&amp;rsquo;t.&lt;/li&gt;
&lt;li&gt;The docker service checks the public registry (the docker hub) to see if there&amp;rsquo;s an image named &lt;code&gt;ubuntu&lt;/code&gt; available- which there is.&lt;/li&gt;
&lt;li&gt;The docker service downloads the image and stores it in its local cache of images (ready for next time).&lt;/li&gt;
&lt;li&gt;The docker service creates a new container, based on the &lt;code&gt;ubuntu&lt;/code&gt; image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Try any of these:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -it haskell
docker run -it java
docker run -it python
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;re not going to use &lt;a href="https://xkcd.com/1312/"&gt;Haskell&lt;/a&gt; today, but you can see, running an environment is very easy.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a snap to build images of our own, with our apps or services on them, databases, whatever we need. We can then run them on any machine with Docker installed - and the image will run in the same, predictable way. We can build our software &lt;em&gt;and the environment it runs on&lt;/em&gt; as code and deploy easily.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look into a simple microservice as an example.&lt;/p&gt;
&lt;h2 id="the-brief"&gt;The Brief&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re going to build a microservice which lets us manage a directory of email addresses to phone numbers, using Node.js and MySQL.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;For doing local development we&amp;rsquo;ll need to install MySQL and create a test database for us to&amp;hellip;&lt;/p&gt;
&lt;p&gt;&amp;hellip;nope.&lt;/p&gt;
&lt;p&gt;Creating a local database and running scripts on it is an easy start, but can get messy. Lots of uncontrolled stuff going on. It might work, we could even control it with some shell scripts checked in to our repo, but what if other developers already have MySQL installed? What if they have a database already with the creative name &amp;lsquo;users&amp;rsquo; which we want to create?&lt;/p&gt;
&lt;h3 id="step-1-creating-a-test-database-server---in-docker"&gt;Step 1: Creating a Test Database Server - in Docker&lt;/h3&gt;
&lt;p&gt;This is a great Docker use case. We might not want to run our production database in Docker (perhaps we&amp;rsquo;ll just use Amazon RDS for example), but we can spin up a clean MySQL database in no time as a Docker container for development - leaving our development machine clean and keeping everything we do controlled and repeatable.&lt;/p&gt;
&lt;p&gt;Run the following command:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run --name db -d -e MYSQL_ROOT_PASSWORD=123 -p 3306:3306 mysql:latest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This starts a MySQL instance running, allowing access through port 3306 using the root password 123.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker run&lt;/code&gt; tells the engine we want to run an image (the image comes at the end, &lt;a href="https://hub.docker.com/_/mysql/"&gt;mysql:vlatest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name db&lt;/code&gt; names this container &lt;code&gt;db&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-d&lt;/code&gt; (or &lt;code&gt;--detach&lt;/code&gt;) detach - i.e. run the container in the background.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e MYSQL_ROOT_PASSWORD=123&lt;/code&gt; (or &lt;code&gt;--env&lt;/code&gt;) environment variables - tells docker we want to provide an environment variable. The variable following it is what the MySQL image checks for setting the default root password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 3306:3306&lt;/code&gt; (or &lt;code&gt;--publish&lt;/code&gt; tells the engine that we want to map the port 3306 from inside the container to out port 3306.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last part is really important - even though that&amp;rsquo;s the MySQL default port, if we don&amp;rsquo;t tell docker explicitly we want to map it, it will block access through that port (because containers are isolated until you tell them you want access).&lt;/p&gt;
&lt;p&gt;The return value of this function is the &lt;em&gt;container id&lt;/em&gt;, a reference to the container which you can use to stop it, restart it, issue commands on it and so on. Let&amp;rsquo;s see which containers are running:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ docker ps
CONTAINER ID IMAGE ... NAMES
36e68b966fd0 mysql:latest ... db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The key information is the container ID, image and name. Let&amp;rsquo;s connect to this image and see what&amp;rsquo;s there:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;$ docker exec -it db /bin/bash
root@36e68b966fd0:/# mysql -uroot -p123
mysql&amp;gt; show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
+--------------------+
1 rows in set (0.01 sec)
mysql&amp;gt; exit
Bye
root@36e68b966fd0:/# exit
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is pretty clever too:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker exec -it db&lt;/code&gt; tells docker we want to execute a command on the container named &lt;code&gt;db&lt;/code&gt; (we could also use the id, or just the first few letters of the id). &lt;code&gt;-it&lt;/code&gt; ensures we have an interactive terminal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql -uroot -p123&lt;/code&gt; the command we actually run as a process in the container, which in this case is just the mysql client.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can create databases, tables, users, whatever we need.&lt;/p&gt;
&lt;h3 id="wrapping-up-the-test-database"&gt;Wrapping up the Test Database&lt;/h3&gt;
&lt;p&gt;Running MySQL inside a container has already introduced a few Docker tricks, but let&amp;rsquo;s pause here and move onto the service. For now, we&amp;rsquo;ll have create a &lt;code&gt;test-database&lt;/code&gt; folder with a script to start the database, stop the database and setup test data:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;test-database\setup.sql
test-database\start.sh
test-database\stop.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Start is simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Run the MySQL container, with a database named &amp;#39;users&amp;#39; and credentials&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# for a users-service user which can access it.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Starting DB...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run --name db -d &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -e MYSQL_ROOT_PASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;123&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -e MYSQL_DATABASE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;users -e MYSQL_USER&lt;span style="color:#f92672"&gt;=&lt;/span&gt;users_service -e MYSQL_PASSWORD&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;123&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -p 3306:3306 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mysql:latest
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Wait for the database service to start up.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Waiting for DB to start up...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec db mysqladmin --silent --wait&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30&lt;/span&gt; -uusers_service -p123 ping &lt;span style="color:#f92672"&gt;||&lt;/span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Run the setup script.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;Setting up initial data...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec -i db mysql -uusers_service -p123 users &amp;lt; setup.sql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This script runs the database image in a detached container (i.e. in the background), with a user set up to access a &lt;code&gt;users&lt;/code&gt; database, then waits for the database server to start up, then runs a &lt;code&gt;setup.sql&lt;/code&gt; script to set initial data.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;setup.sql&lt;/code&gt; is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;create&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;table&lt;/span&gt; directory (user_id INT &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NULL&lt;/span&gt; AUTO_INCREMENT &lt;span style="color:#66d9ef"&gt;PRIMARY&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;KEY&lt;/span&gt;, email TEXT, phone_number TEXT);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; directory (email, phone_number) &lt;span style="color:#66d9ef"&gt;values&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;homer@thesimpsons.com&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;+1 888 123 1111&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; directory (email, phone_number) &lt;span style="color:#66d9ef"&gt;values&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;marge@thesimpsons.com&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;+1 888 123 1112&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; directory (email, phone_number) &lt;span style="color:#66d9ef"&gt;values&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;maggie@thesimpsons.com&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;+1 888 123 1113&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; directory (email, phone_number) &lt;span style="color:#66d9ef"&gt;values&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;lisa@thesimpsons.com&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;+1 888 123 1114&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;insert&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;into&lt;/span&gt; directory (email, phone_number) &lt;span style="color:#66d9ef"&gt;values&lt;/span&gt; (&lt;span style="color:#e6db74"&gt;&amp;#39;bart@thesimpsons.com&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;+1 888 123 1115&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;stop.sh&lt;/code&gt; script will stop the container and remove it (containers are left around by docker by default so that they can be restared quickly, we don&amp;rsquo;t really need that feature for this example):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/sh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Stop the db and remove the container.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker stop db &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker rm db
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;re going to make this even more slick later on, simplifying this further. Check the code at this stage by looking at the &lt;a href="https://github.com/dwmkerr/node-docker-microservice/tree/step1"&gt;step1&lt;/a&gt; branch of the repo.&lt;/p&gt;
&lt;h3 id="step-2-creating-a-microservice-in-nodejs"&gt;Step 2: Creating a Microservice in Node.js&lt;/h3&gt;
&lt;p&gt;This article is really focused on learning Docker, so I&amp;rsquo;m not going to spend ages on the Node.js microservice. Instead, I&amp;rsquo;ll highlight the areas and takeaways.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;test-database/ # contains the code seen in Step 1
users-service/ # root of our node.js microservice
- package.json # dependencies, metadata
- index.js # main entrypoint of the app
- api/ # our apis and api tests
- config/ # config for the app
- repository/ # abstraction over our db
- server/ # server setup code
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s take this apart bit by bit. The first section to look at is &lt;code&gt;repository&lt;/code&gt;. It can be useful to wrap your database access in some kind of class or abstraction, to allow to mock it for testing purposes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// repository.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Exposes a single function - &amp;#39;connect&amp;#39;, which returns
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// a connected repository. Call &amp;#39;disconnect&amp;#39; on this object when you&amp;#39;re done.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;use strict&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mysql&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;mysql&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Class which holds an open connection to a repository
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// and exposes some simple functions for accessing data.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Repository&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;constructor&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;getUsers&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Promise((&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;query&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT email, phone_number FROM directory&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;results&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;An error occured getting the users: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;results&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; []).&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;getUserByEmail&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Promise((&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Fetch the customer.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;query&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;SELECT email, phone_number FROM directory WHERE email = ?&amp;#39;&lt;/span&gt;, [&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;], (&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;results&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;An error occured getting the user: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;results&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;length&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;undefined&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;results&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;results&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;disconnect&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connection&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;end&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// One and only exported function, returns a connected repo.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connect&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Promise((&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A host must be specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A user must be specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A password must be specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A port must be specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Repository&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;mysql&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;createConnection&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;connectionSettings&lt;/span&gt;)));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s probably a lot of better ways to do this! But basically we can create a &lt;code&gt;Repository&lt;/code&gt; object like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connect&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;users_service&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3306&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getUsers&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;users&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;users&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getUserByEmail&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;homer@thesimpsons.com&amp;#39;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...when you are done...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;disconnect&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There&amp;rsquo;s also a set of unit tests in the &lt;code&gt;repository/repository.spec.js&lt;/code&gt; file. Now that we&amp;rsquo;ve got a repo, we can create a server. This is &lt;code&gt;server/server.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// server.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;express&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;morgan&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;morgan&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Promise((&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Make sure we have a repository and port provided.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A server must be started with a connected repository.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;A server must be started with a port.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create the app, add some logging.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;express&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;use&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;morgan&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;dev&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Add the APIs to the app.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;../api/users&amp;#39;&lt;/span&gt;)(&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Start the app, creating a running server which we return.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;server&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;listen&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;server&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This module exposes a &lt;code&gt;start&lt;/code&gt; function, which we can use like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;server&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./server/server);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;server.start({port: 8080, repo: repository}).then((svr) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; // we&amp;#39;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ve&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;got&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;running&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;server&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that &lt;code&gt;server.js&lt;/code&gt; uses &lt;code&gt;api/users/js&lt;/code&gt;? Here it is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// users.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Defines the users api. Add to a server by calling:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// require(&amp;#39;./users&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;use strict&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Only export - adds the API to the app with the given options.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/users&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getUsers&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;users&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;users&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) =&amp;gt; { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;phoneNumber&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/search&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get the email.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;email&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;query&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Error(&lt;span style="color:#e6db74"&gt;&amp;#34;When searching for a user, the email must be specified, e.g: &amp;#39;/search?email=homer@thesimpsons.com&amp;#39;.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get the user from the repo.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getUserByEmail&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;404&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;User not found.&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;email&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;phoneNumber&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;phone_number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both of these files have unit tests adjacent to the source.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll need config. Rather than using a specialised library, a simple file will do the trick - &lt;code&gt;config/config.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// config.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Simple application configuration. Extend as needed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PORT&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DATABASE_HOST&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;users_service&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;123&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3306&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can &lt;code&gt;require&lt;/code&gt; config as needed. Currently, most config is hard coded, but as you can see from &lt;code&gt;port&lt;/code&gt; it&amp;rsquo;s easy to add environment variables as an option.&lt;/p&gt;
&lt;p&gt;Final step - stringing it together with an &lt;code&gt;index.js&lt;/code&gt; file which composes everything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// index.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Entrypoint to the application. Opens a repository to the MySQL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// server and starts the server.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;server&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./server/server&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./repository/repository&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./config/config&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Lots of verbose logging when we&amp;#39;re starting up...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;--- Customer Service---&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Connecting to customer repository...&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Log unhandled exceptions.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;uncaughtException&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Unhandled Exception&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;unhandledRejection&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Unhandled Rejection&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;connect&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Connected. Starting server...&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;server&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;((&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Server started successfully, running on port &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;port&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;close&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;repository&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;disconnect&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have a little error handling and beyond that we&amp;rsquo;re just loading config, creating a repo and starting our server.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the microservice. It allows us to get all users, or search a user:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;HTTP GET /users # gets all users
HTTP GET /search?email=homer@thesimpons.com # searches by email
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you checkout the code, you&amp;rsquo;ll see that there&amp;rsquo;s a few commands available for you:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd ./users-service
npm install # setup everything
npm test # unit test - no need for a test database running
npm start # run the server - you must have a test database running
npm run debug # run the server in debug mode, opens a browser with the inspector
npm run lint # check to see if the code is beautiful
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Asides from the code you&amp;rsquo;ve seen we have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Node Inspector for debugging&lt;/li&gt;
&lt;li&gt;Mocha/shoud/supertest for unit tests&lt;/li&gt;
&lt;li&gt;ESLint for linting&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;Run the test database with:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd test-database/
./start.sh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then the service with:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;cd ../users-service/
npm start
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can point your browser to &lt;a href="http://localhost:8123/users"&gt;localhost:8123/users&lt;/a&gt; and see it in action. If you are using Docker Machine (i.e. you&amp;rsquo;re on Mac or Windows) then &lt;code&gt;localhost&lt;/code&gt; won&amp;rsquo;t work, you need the IP of the docker machine instead. You can use &lt;code&gt;docker-machine ip&lt;/code&gt; to get it.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve whipped through building the service quickly. If you&amp;rsquo;d like to see this code before we continue, check the &lt;a href="https://github.com/dwmkerr/node-docker-microservice/tree/step2"&gt;step2&lt;/a&gt; branch.&lt;/p&gt;
&lt;h1 id="step-3-dockerising-our-microservice"&gt;Step 3: Dockerising our Microservice&lt;/h1&gt;
&lt;p&gt;OK now it gets fun!&lt;/p&gt;
&lt;p&gt;So we have a microservice which we can run on a dev box, as long as it has a compatible version of Node.js installed. What we&amp;rsquo;d like to do is set up our service so that we can create a &lt;em&gt;Docker Image&lt;/em&gt; from it, allowing us to deploy our service anywhere which supports docker.&lt;/p&gt;
&lt;p&gt;The way we do this is create a &lt;em&gt;Dockerfile&lt;/em&gt;. A Dockerfile is a recipe that tells the Docker engine how to build your image. We&amp;rsquo;ll create a simple Dockerfile in our &lt;code&gt;users-service&lt;/code&gt; directory and start to explore how we can adapt it to our needs.&lt;/p&gt;
&lt;h2 id="creating-the-dockerfile"&gt;Creating the Dockerfile&lt;/h2&gt;
&lt;p&gt;Create a new text file called &lt;code&gt;Dockerfile&lt;/code&gt; at &lt;code&gt;users-service/&lt;/code&gt; with the content below:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Use Node v4 as the base image.
FROM node:4
# Run node
CMD [&amp;#34;node&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now run the commands below to build the image and run the a container from it:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker build -t node4 . # Builds a new image
docker run -it node4 # Run a container with this image, interactive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let&amp;rsquo;s look at the build command first.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker build&lt;/code&gt; tell the engine we want to create a new image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t node4&lt;/code&gt; tag this image with the tag &lt;code&gt;node4&lt;/code&gt;. We can refer to this image by tag from now on.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.&lt;/code&gt; use the current directory to find the &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After some console output you&amp;rsquo;ll see we have a new image created. You can see all images on your system with &lt;code&gt;docker images&lt;/code&gt;. The next command should be fairly familiar from what we&amp;rsquo;ve done so far:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker run&lt;/code&gt; run a new container from an image.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-it&lt;/code&gt; use an interactive terminal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node4&lt;/code&gt; the tag of the image we want to use in the container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When we run this image, we get a node repl, check the current version like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;version&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;v4.4.0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is potentially different to the node version on your current machine.&lt;/p&gt;
&lt;h2 id="examining-the-dockerfile"&gt;Examining the Dockerfile&lt;/h2&gt;
&lt;p&gt;Looking at the dockerfile we can see quite easily what is going on:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;FROM node:4&lt;/code&gt; the first thing we specify in a Dockerfile is the base image. A quick google finds the &lt;a href="https://hub.docker.com/_/node/"&gt;node organisation page on the docker hub&lt;/a&gt; showing all of the available images. This is essentially bare bones ubuntu with node installed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMD [&amp;quot;node&amp;quot;]&lt;/code&gt; the &lt;code&gt;CMD&lt;/code&gt; command tells docker that this image should run the node executable. When the executable terminates, the container shuts down.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With the addition of a few more commands, we can update our Dockerfile so that it runs our service:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Use Node v4 as the base image.
FROM node:4
# Add everything in the current directory to our image, in the &amp;#39;app&amp;#39; folder.
ADD . /app
# Install dependencies
RUN cd /app; \
npm install --production
# Expose our server port.
EXPOSE 8123
# Run our app.
CMD [&amp;#34;node&amp;#34;, &amp;#34;/app/index.js&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only addition here is that we use the &lt;code&gt;ADD&lt;/code&gt; command to copy everything&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; in the current directory to a folder in the container called &lt;code&gt;app/&lt;/code&gt; . We then use &lt;code&gt;RUN&lt;/code&gt; to run a command in the image, which installs our modules. Finally, we &lt;code&gt;EXPOSE&lt;/code&gt; the server port, telling docker we intend to support inbound connections on &lt;code&gt;8123&lt;/code&gt;, then run our server code.&lt;/p&gt;
&lt;p&gt;Ensure the test-database service is running, then build and run the image again:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker build -t users-service .
docker run -it -p 8123:8123 users-service
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you navigate to &lt;code&gt;localhost:8123/users&lt;/code&gt; in a browser you should see an error, checking the console shows our container is reporting some issues:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;--- Customer Service---
Connecting to customer repository...
Connected. Starting server...
Server started successfully, running on port 8123.
GET /users 500 23.958 ms - 582
Error: An error occured getting the users: Error: connect ECONNREFUSED 127.0.0.1:3306
at Query._callback (/app/repository/repository.js:21:25)
at Query.Sequence.end (/app/node_modules/mysql/lib/protocol/sequences/Sequence.js:96:24)
at /app/node_modules/mysql/lib/protocol/Protocol.js:399:18
at Array.forEach (native)
at /app/node_modules/mysql/lib/protocol/Protocol.js:398:13
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yikes! So the connection from our &lt;code&gt;users-service&lt;/code&gt; container to the &lt;code&gt;test-database&lt;/code&gt; container is being refused. We might try running &lt;code&gt;docker ps&lt;/code&gt; to see all containers running:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;CONTAINER ID IMAGE PORTS NAMES
a97958850c66 users-service 0.0.0.0:8123-&amp;gt;8123/tcp kickass_perlman
47f91343db01 mysql:latest 0.0.0.0:3306-&amp;gt;3306/tcp db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;They&amp;rsquo;re both there, so what is going on?&lt;/p&gt;
&lt;h2 id="linking-containers"&gt;Linking Containers&lt;/h2&gt;
&lt;p&gt;The issue we&amp;rsquo;ve seen is actually to be expected. Docker containers are supposed to be isolated, so it wouldn&amp;rsquo;t make much sense if we could create connections between containers without us explicitly allowing it.&lt;/p&gt;
&lt;p&gt;Yes, we can connect from our machine (the host) to a container, because we&amp;rsquo;ve opened ports for that (using the &lt;code&gt;-p 8123:8123&lt;/code&gt; argument for example). If we allowed containers to talk to each other in the same way, then two containers running on the same machine would be able to communicate, even if the developers didn&amp;rsquo;t intend it, and that&amp;rsquo;s a recipe for disaster, especially when we might have a cluster of machines whos job it is to run containers from different applications.&lt;/p&gt;
&lt;p&gt;If we&amp;rsquo;re going to connect from one container to another, we need to &lt;em&gt;link&lt;/em&gt; them, which tells docker that we explicitly want to allow communication between the two. There are two ways of doing this, the first is the &amp;lsquo;old fasioned&amp;rsquo; but quite simple way, the second we&amp;rsquo;ll see a little later.&lt;/p&gt;
&lt;h3 id="linking-containers-with-the-link-parameter"&gt;Linking Containers with the &amp;rsquo;link&amp;rsquo; parameter&lt;/h3&gt;
&lt;p&gt;When we run a container, we can tell docker that we intend to connect to another container using the &lt;code&gt;link&lt;/code&gt; parameter. In our case, we can run our service correctly like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker run -it -p 8123:8123 --link db:db -e DATABASE_HOST=DB users-service
&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;docker run -it&lt;/code&gt; run a docker image in a container, with an interactive terminal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p 8123:8123&lt;/code&gt; map the host port 8123 to the container port 8123.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;link db:db&lt;/code&gt; link to the container named &lt;code&gt;db&lt;/code&gt; and refer to it as &lt;code&gt;db&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e DATABASE_HOST=db&lt;/code&gt; set the &lt;code&gt;DATABASE_HOST&lt;/code&gt; environment variable to &lt;code&gt;db&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;users-service&lt;/code&gt; the name of the image to run in our container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when we go to &lt;code&gt;localhost:8123/users&lt;/code&gt; everything works.&lt;/p&gt;
&lt;h4 id="how-it-works"&gt;How it works&lt;/h4&gt;
&lt;p&gt;Remember our config file for the service? It allowed us to specify a database host with an environment variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// config.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Simple application configuration. Extend as needed.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PORT&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8123&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;db&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;process&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;env&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;DATABASE_HOST&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;database&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;users&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;users_service&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;password&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;123&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3306&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When we run the container, we set this environment variable to &lt;code&gt;DB&lt;/code&gt;, which means we&amp;rsquo;re connecting to a host called &lt;code&gt;DB&lt;/code&gt;. This is &lt;em&gt;automatically&lt;/em&gt; set up for us by the docker engine when we link to a container.&lt;/p&gt;
&lt;p&gt;To see this in action, try running &lt;code&gt;docker ps&lt;/code&gt; to list all running containers. Look up the name of the container running the &lt;code&gt;users-service&lt;/code&gt;, which will be a random name such as &lt;code&gt;trusting_jang&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker ps
CONTAINER ID IMAGE ... NAMES
ac9449d3d552 users-service ... trusting_jang
47f91343db01 mysql:latest ... db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we can look at the hosts available on our container:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker exec trusting_jang cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 db 47f91343db01 # linking magic!!
172.17.0.3 ac9449d3d552
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Remember how &lt;code&gt;docker exec&lt;/code&gt; works? Choose a container name and then whatever follows is the command you&amp;rsquo;ll execute on the container, in our case &lt;code&gt;cat /etc/hosts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;OK the hosts file doesn&amp;rsquo;t have the &lt;code&gt;# linking magic!!&lt;/code&gt; comment, that&amp;rsquo;s so you can see - docker has added &lt;code&gt;db&lt;/code&gt; to our hosts file so we can refer to the linked container by hostname. This is one consequence of linking. Here&amp;rsquo;s the other:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker exec trusting_jang printenv | grep DB
DB_PORT=tcp://172.17.0.2:3306
DB_PORT_3306_TCP=tcp://172.17.0.2:3306
DB_PORT_3306_TCP_ADDR=172.17.0.2
DB_PORT_3306_TCP_PORT=3306
DB_PORT_3306_TCP_PROTO=tcp
DB_NAME=/trusting_jang/db
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From this command we can also see that when docker links a container, it also provides a set of environment variables with some helpful information. We know the host, tcp port and container name.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s step 3 complete - we have a MySQL database running happily in a container, we have a node.js microservice which we can run locally or in a container of its own, and we know how to link them together.&lt;/p&gt;
&lt;p&gt;You can check out how the code looks at this stage by going to the &lt;a href="https://github.com/dwmkerr/node-docker-microservice/tree/step3"&gt;step3&lt;/a&gt; branch.&lt;/p&gt;
&lt;h1 id="step-4-integration-testing-the-environment"&gt;Step 4: Integration Testing the Environment&lt;/h1&gt;
&lt;p&gt;We can now write an integration test which calls the actual server, running as a docker container, calling the containerised test database.&lt;/p&gt;
&lt;p&gt;Writing the integration test can be done in whatever language or on whatever platform you want, within reason, but to keep things simple I&amp;rsquo;m using Node.js as we&amp;rsquo;ve already seen Mocha and Supertest in our project.&lt;/p&gt;
&lt;p&gt;In a new folder, called &lt;code&gt;integration-tests&lt;/code&gt; we&amp;rsquo;ve got a single &lt;code&gt;index.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;supertest&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;supertest&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;should&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;should&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;describe&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;users-service&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;api&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;supertest&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;http://localhost:8123&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;it&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;returns a 200 for a known user&amp;#39;&lt;/span&gt;, (&lt;span style="color:#a6e22e"&gt;done&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;api&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/search?email=homer@thesimpsons.com&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;expect&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;done&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will check an API call and show the results of the test&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;As long as your &lt;code&gt;users-service&lt;/code&gt; and &lt;code&gt;test-database&lt;/code&gt; are running, the tests will pass. However, at this stage the services are getting a little harder to handle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We have to use a shell script to start and stop the database&lt;/li&gt;
&lt;li&gt;We have to remember a sequence of commands to start the users service against the database&lt;/li&gt;
&lt;li&gt;We have to use node directly to run the integration tests&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we&amp;rsquo;re a little more familiar with Docker we can fix these issues.&lt;/p&gt;
&lt;h3 id="simplifiying-the-test-database"&gt;Simplifiying the Test Database&lt;/h3&gt;
&lt;p&gt;Currently we have the following files for the test database:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;/test-database/start.sh
/test-database/stop.sh
/test-database/setup.sql
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we&amp;rsquo;re more familar with Docker, we can improve on this. Looking into the &lt;a href="https://hub.docker.com/_/mysql/"&gt;mysql image documentation&lt;/a&gt; on Docker Hub there&amp;rsquo;s a note which tells us any &lt;code&gt;.sql&lt;/code&gt; or &lt;code&gt;.sh&lt;/code&gt; file added to the image&amp;rsquo;s &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt; folder will be executed when setting up the DB.&lt;/p&gt;
&lt;p&gt;This means we can replace our &lt;code&gt;start.sh&lt;/code&gt; and &lt;code&gt;stop.sh&lt;/code&gt; scripts with a &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;FROM mysql:5
ENV MYSQL_ROOT_PASSWORD 123
ENV MYSQL_DATABASE users
ENV MYSQL_USER users_service
ENV MYSQL_PASSWORD 123
ADD setup.sql /docker-entrypoint-initdb.d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now to run our test database it is just:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker build -t test-database .
docker run --name db test-database
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="composing"&gt;Composing&lt;/h3&gt;
&lt;p&gt;Building and running each container is still somewhat time consuming. We can take things a step further with the &lt;a href="https://docs.docker.com/compose/"&gt;Docker Compose&lt;/a&gt; tool.&lt;/p&gt;
&lt;p&gt;Docker Compose lets you create a file which defines each container in your system, the relationships between them, and build or run them all.&lt;/p&gt;
&lt;p&gt;First, &lt;a href="https://docs.docker.com/compose/install/"&gt;install Docker Compose&lt;/a&gt;. Now create a new file in the root of your project called &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;version: &amp;#39;2&amp;#39;
services:
users-service:
build: ./users-service
ports:
- &amp;#34;8123:8123&amp;#34;
depends_on:
- db
environment:
- DATABASE_HOST=db
db:
build: ./test-database
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now check this out:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;docker-compose build
docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Docker Compose has built all of the images needed for our application, created containers fromthem, run them in the correct order and started the whole stack!&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;docker-compose build&lt;/code&gt; command builds each image which is listed in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;version: &amp;#39;2&amp;#39;
services:
users-service:
build: ./users-service
ports:
- &amp;#34;8123:8123&amp;#34;
depends_on:
- db
environment:
- DATABASE_HOST=db
db:
build: ./test-database
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; value for each of our services tells docker where to go to find the &lt;code&gt;Dockerfile&lt;/code&gt;. When we run &lt;code&gt;docker-compose up&lt;/code&gt;, docker starts all of our services. Notice from the &lt;code&gt;Dockerfile&lt;/code&gt; we can specify ports and dependencies. Actually, there&amp;rsquo;s a whole bunch of config we can change here.&lt;/p&gt;
&lt;p&gt;In another terminal, run &lt;code&gt;docker compose down&lt;/code&gt; to gracefully shut down the containers.&lt;/p&gt;
&lt;h1 id="winding-up"&gt;Winding Up&lt;/h1&gt;
&lt;p&gt;We&amp;rsquo;ve seen a lot of docker in this article, but there&amp;rsquo;s a lot more to it. I hope this has shown some of the interesting and useful things that you can use docker for in your workflow.&lt;/p&gt;
&lt;p&gt;As usual, questions and comments are welcomed! I&amp;rsquo;d also strongly recommend the document &lt;a href="https://docs.docker.com/engine/understanding-docker/"&gt;Understanding Docker&lt;/a&gt; to get a deeper understanding of how docker works.&lt;/p&gt;
&lt;p&gt;You can see the final source code for the project built in this article at &lt;a href="https://github.com/dwmkerr/node-docker-microservice"&gt;github.com/dwmkerr/node-docker-microservice&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="notes"&gt;Notes&lt;/h1&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Copying everything is actually a bad idea, because we will also copy the node_modules folder. Generally it is a better idea explicitly list the files or folders you want to copy, or use a .dockerignore file, which works just like the .gitignore file.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;If the server isn&amp;rsquo;t running, it will actually show a rather annoying exception, due to a bug in supertest, see &lt;a href="https://github.com/visionmedia/supertest/issues/314"&gt;github.com/visionmedia/supertest/issues/314&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Getting Started with React &amp; ES6</title><link>https://dwmkerr.com/getting-started-with-react/</link><pubDate>Mon, 07 Sep 2015 19:44:54 +0000</pubDate><guid>https://dwmkerr.com/getting-started-with-react/</guid><description>&lt;p&gt;Feeling like having a go with Facebook&amp;rsquo;s hugely popular &lt;a href="http://facebook.github.io/react/"&gt;React&lt;/a&gt; framework but not sure where to start?&lt;/p&gt;
&lt;p&gt;In this post I&amp;rsquo;m going to build a simple React application from scratch - using &lt;a href="http://es6-features.org/"&gt;ECMAScript 6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll put together the bare minimum skeleton of a site and keep the folder structure free of noise and clutter so that you can focus on the app code and not the tooling!&lt;/p&gt;
&lt;p&gt;The simple app we&amp;rsquo;ll build is at &lt;a href="https://github.com/dwmkerr/react-es6-starter"&gt;github.com/dwmkerr/react-es6-starter&lt;/a&gt;, or see &lt;a href="https://react-es6-starter.herokuapp.com"&gt;it live&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="building-the-code"&gt;Building the Code&lt;/h2&gt;
&lt;p&gt;Our goal will be to have a single &lt;code&gt;index.html&lt;/code&gt; file which includes our Javascript files. We&amp;rsquo;re aiming for something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Build-Process.png" alt="Build Process 1"&gt;&lt;/p&gt;
&lt;p&gt;But browsers don&amp;rsquo;t handle ES6 yet. So our loose files, which reference each other, are going to have to be transpiled into ES5 and bundled into a single file. We need a build process:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Build-Process-2.png" alt="Build Process 2"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="webpack.github.io"&gt;Webpack&lt;/a&gt; can handle all of this for us. Given an entrypoint file, webpack will traverse all of the &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;import&lt;/code&gt; statements and build a single bundle file. It also allows us to configure &amp;rsquo;loaders&amp;rsquo;, which let us pass these files through other tools:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Build-Process-3.png" alt="Build Process 3"&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ll need the following libraries:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="webpack.github.io"&gt;Webpack&lt;/a&gt; - the tool that handles the build process.&lt;/li&gt;
&lt;li&gt;&lt;a href="babeljs.io"&gt;Babel&lt;/a&gt; - an excellent ES6/ES7/JSX to ES5 transpiler.&lt;/li&gt;
&lt;li&gt;&lt;a href="github.com/babel/babel-loader"&gt;Babel Loader&lt;/a&gt; - the component which integrates Babel into our Webpack build.&lt;/li&gt;
&lt;li&gt;&lt;a href="github.com/ampedandwired/html-webpack-plugin"&gt;Html Webpack Plugin&lt;/a&gt; - a simple Webpack plugin which will copy our index file to our build folder and add a link to our Webpack bundle.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s install these modules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install --save webpack babel babel-loader html-webpack-plugin
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll also need a webpack config file. By default webpack expects a file named &lt;code&gt;webpack.config.js&lt;/code&gt; to be in the root of the project. But every tool under the sun wants to stick its config file in the root of our project, and most of the time they&amp;rsquo;re just in the way.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s put everything to do with our tooling in a &lt;code&gt;tooling&lt;/code&gt; folder instead. Create the file &lt;code&gt;webpack.config.js&lt;/code&gt; in a &lt;code&gt;tooling&lt;/code&gt; folder in the root of the project:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Defines the entrypoint of our application.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;entry&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;__dirname&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;../src/app.js&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Bundle to a ./build/public/bundle.js file.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;output&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;__dirname&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;../build/public&amp;#39;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;filename&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;bundle.js&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Use babel for anything that is *.js or *.jsx.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loaders&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/\.jsx?$/&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loader&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;babel-loader&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;include&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;__dirname&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;../src&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First we define our entry point - the first file which will actually be run if we run the final bundled script. This is the &lt;code&gt;app.js&lt;/code&gt; file we&amp;rsquo;ll create shortly. If &lt;code&gt;app.js&lt;/code&gt; includes other modules, Webpack will pick them up, if those modules include other modules, they will be picked up and so on.&lt;/p&gt;
&lt;p&gt;Next we specify that everything should be bundled into a &lt;code&gt;./build/public/bundle.js&lt;/code&gt; file (we&amp;rsquo;re going to use the convention that everything we can produce with our tools goes into &lt;code&gt;./build&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Finally, we specify that every file in &lt;code&gt;src&lt;/code&gt; which matches the &lt;code&gt;\.jsx?$&lt;/code&gt; regex will go through the babel loader.&lt;/p&gt;
&lt;h3 id="using-es6"&gt;Using ES6!&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ve actually got enough now to use ES6. Create a file in &lt;code&gt;src&lt;/code&gt; called &lt;code&gt;index.html&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#f92672"&gt;html&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then create a &lt;code&gt;src/app.js&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PI&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3.14&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;vals&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;].&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;x&lt;/span&gt; =&amp;gt; &lt;span style="color:#a6e22e"&gt;x&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;document.&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;innerText&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Pi is &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3.14&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; and vals is &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;vals&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run the command &lt;code&gt;./node_modules/.bin/webpack --config ./tooling/webpack.config.js&lt;/code&gt; and our code is built, transpiled an moved to the build folder.&lt;/p&gt;
&lt;p&gt;Now we could serve this folder using any basic webserver. We are already using webpack, so the webpack dev server will do the trick. It uses exactly the same config file as the webpack tool:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm install --save-dev webpack-dev-server
./node_modules/.bin/webpack-dev-server --config ./tooling/webpack.config --inline
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The inline reloads the page when the source changes. We don&amp;rsquo;t need to tell the server where the files are, it knows that from the webpack config.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s stick these commands in our &lt;code&gt;package.json&lt;/code&gt; for convenience:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;start&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;webpack-dev-server --config ./tooling/webpack.config.js --inline --quiet&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;webpack&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;webpack --config tooling/webpack.config.js&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can manually build with &lt;code&gt;npm run webpack&lt;/code&gt; and start our dev server with &lt;code&gt;npm start&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="adding-some-react"&gt;Adding some React&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s add a React component. Create a folder under &lt;code&gt;app&lt;/code&gt; called &lt;code&gt;home&lt;/code&gt; and add a &lt;code&gt;home.js&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Home&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Component&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;render&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;div&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;h1&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;React&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ES6&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Starter&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/h1&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;p&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Welcome&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;to&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;the&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ES6&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Starter&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;home&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;page&lt;/span&gt;&lt;span style="color:#f92672"&gt;!&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/p&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;/div&amp;gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is our first react component, which does nothing more than render some basic markup. We&amp;rsquo;ll use this as the starting point for our application.&lt;/p&gt;
&lt;p&gt;We can now take our &lt;code&gt;app.js&lt;/code&gt; file and render our Home component into the div. Here&amp;rsquo;s &lt;code&gt;app.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;react/addons&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Home&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;./home/home&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;render&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Home&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;, document.&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s all there is to it! We&amp;rsquo;ve got a clean and simple starting point to begin playing with React. Before we look into things like state management and routing, let&amp;rsquo;s look into testing what we have so far.&lt;/p&gt;
&lt;h3 id="testing"&gt;Testing&lt;/h3&gt;
&lt;p&gt;Even the most simple app would be incomplete without looking into how we will deal with the testing.&lt;/p&gt;
&lt;p&gt;Many will recommend the &lt;a href="https://facebook.github.io/jest/"&gt;Jest&lt;/a&gt; framework to test React applications. However, it&amp;rsquo;s a bit more to learn and has some problems with NodeJS v0.12, so until we get Node v4 I&amp;rsquo;m going to keep things simple.&lt;/p&gt;
&lt;p&gt;First, we&amp;rsquo;ll install &lt;a href="http://karma-runner.github.io/"&gt;Karma&lt;/a&gt; as a test runner. We&amp;rsquo;ll use &lt;a href="http://jasmine.github.io/"&gt;Jasmine&lt;/a&gt; as as framework to write test cases and &lt;a href="http://phantomjs.org/"&gt;PhantomJS&lt;/a&gt; as a headless browser in which our tests will run. This means we&amp;rsquo;ll need to add some more dev dependencies:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install --save-dev karma jasmine karma-webpack karma-jasmine karma-phantomjs-launcher
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now create a &lt;code&gt;karma.config.js&lt;/code&gt; file in our &lt;code&gt;tooling&lt;/code&gt; folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;exports&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;browsers&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;PhantomJS&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;files&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// We need to polyfill as PhantomJS doesn&amp;#39;t support &amp;#39;bind&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;../node_modules/babel-core/browser-polyfill.js&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;../**/*.spec.js&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;frameworks&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;jasmine&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;preprocessors&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;../**/*.spec.js&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;webpack&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;reporters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;progress&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;singleRun&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;webpack&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loaders&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/\.jsx?$/&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loader&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;babel-loader&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;include&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;path&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;__dirname&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;../src&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;webpackServer&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;noInfo&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So here we are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Loading a polyfill from babel core (sorry guys, one more &lt;code&gt;npm install --save-dev babel-core&lt;/code&gt;) which gives PhantomJS the &lt;code&gt;bind&lt;/code&gt; function (along with some others). This is needed as some of the testing code in the browser needs these features.&lt;/li&gt;
&lt;li&gt;Specifying that anything that ends in &lt;code&gt;.spec.js&lt;/code&gt; should be loaded.&lt;/li&gt;
&lt;li&gt;Running anything that ends in &lt;code&gt;.spec.js&lt;/code&gt; through webpack.&lt;/li&gt;
&lt;li&gt;Telling webpack to use babel.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Quite a bit of config, but we&amp;rsquo;re re-using the same webpack tooling as before. We run the code through webpack, which sends it through babel and builds ES5 we can test in the browser.&lt;/p&gt;
&lt;p&gt;With this in place, we can write a spec. Add &lt;code&gt;home.spec.js&lt;/code&gt; to the &lt;code&gt;home&lt;/code&gt; folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;react&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;jquery&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Home&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;./home.js&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;describe&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;Home&amp;#39;&lt;/span&gt;, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;it&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;should render to the DOM&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create the &amp;lt;Home /&amp;gt; react component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;component&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;render&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;Home&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&amp;gt;&lt;/span&gt;, document.&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Find the DOM element for the created component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;node&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;React&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;findDOMNode&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;component&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Check the DOM looks how we&amp;#39;d expect it to.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;expect&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;node&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;children&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;h1&amp;#39;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;text&lt;/span&gt;()).&lt;span style="color:#a6e22e"&gt;toEqual&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;React Redux Starter&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s going on here? We just ask React to render our Home component directly into the DOM. We get a component back from this call. We can then ask React to give us the DOM associatefd with the component and use familiar tools (jQuery!) to test the shape of the generated DOM.&lt;/p&gt;
&lt;p&gt;All that&amp;rsquo;s missing is the last of the dev dependencies we&amp;rsquo;ve missed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install --save-dev jquery phantomjs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can run tests directly on a Mac or Unix with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./node_modules/.bin/karma start ./tooling/karma.config.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For Windows use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;node_modules&lt;span style="color:#ae81ff"&gt;\.&lt;/span&gt;bin&lt;span style="color:#ae81ff"&gt;\k&lt;/span&gt;arma start tooling&lt;span style="color:#ae81ff"&gt;\k&lt;/span&gt;arma.config.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In fact, we&amp;rsquo;ll update our &lt;code&gt;package.json&lt;/code&gt; scripts so that this is the &lt;code&gt;test&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;./node_modules/.bin/karma start ./tooling/karma.config.js&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Done! This means we can run tests on any platform with NodeJS&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; using the same command - &lt;code&gt;npm test&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We now have a very simple setup which allows us to run tests. You can build on this - perhaps adding Jest later or a more sophisticated or React specific set of tools.&lt;/p&gt;
&lt;h3 id="adding-code-coverage"&gt;Adding Code Coverage&lt;/h3&gt;
&lt;p&gt;You might want to add some code coverage information to your project. This can be a little tricky when using ES6, as we need to make sure we report coverage of the original ES6 code, rather than the actual transpiled code which is instrumented.&lt;/p&gt;
&lt;p&gt;Fortunately, with the clean and simple setup we have built, adding code coverage is a snap.&lt;/p&gt;
&lt;p&gt;Our test runner, Karma, is built to quickly integrate with the code coverage tool &lt;a href="https://github.com/gotwarlost/istanbul"&gt;Istanbul&lt;/a&gt;, we just need to use the &lt;a href="https://github.com/karma-runner/karma-coverage"&gt;Karma Coverage&lt;/a&gt; plugin. Let&amp;rsquo;s install the two modules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install --save-dev istanbul karma-coverage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now with a small addition to our &lt;code&gt;karma.config.js&lt;/code&gt; file we will get a nice HTML coverage report. We need to update our &lt;code&gt;reporters&lt;/code&gt; config to include &lt;code&gt;coverage&lt;/code&gt; and specify coverage options in the &lt;code&gt;coverageReporter&lt;/code&gt; config.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;reporters&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#39;progress&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;coverage&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;coverageReporter&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dir&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;../build/coverage/&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;type&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;html&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you run &lt;code&gt;npm test&lt;/code&gt; now, you&amp;rsquo;ll get an HTML coverage report generated. The only problem is that it is for the transpiled code, which makes it almost useless. A customer instrumenter called isparta will help us here. We use isparta to get a report of the coverage of the original ES6 code. Two more modules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install --save-dev isparta isparta-instrumenter-loader
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then in our karma config we pass the orignal code through the insrtrumenter, before babel transpiles it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;webpack&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;preLoaders&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/\.jsx?$/&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exclude&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;/node_modules/&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;/\.spec\.js/&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loader&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;isparta-instrumenter-loader&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// everything else stays the same...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anything that is not a spec or from &lt;code&gt;node_modules&lt;/code&gt; gets instrumented. Now we have a ES6 code coverage report:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/CapturFiles_8.png" alt="Code Coverage Report"&gt;&lt;/p&gt;
&lt;p&gt;With this in place, you can go even further and integrate with other CI or publish to code quality systems (for example this repo integrates to &lt;a href="https://coveralls.io"&gt;coveralls.io&lt;/a&gt;). This is often used to show badges for repos:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://coveralls.io/github/dwmkerr/react-es6-starter?branch=master"&gt;&lt;img src="images/badge.svg" alt="Coverage Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Another use case is to gate checkins unless they maintain a certain code coverage threshhold.&lt;/p&gt;
&lt;h3 id="wrapping-up"&gt;Wrapping Up&lt;/h3&gt;
&lt;p&gt;This provides a very lean starting point for learning React. There&amp;rsquo;s no moving parts at the moment - no state management. We&amp;rsquo;ll get into that in later articles but right now you have a playground.&lt;/p&gt;
&lt;p&gt;You can set up CI in a flash, just sign up for a &lt;a href="https://travis-ci.org/"&gt;Travis&lt;/a&gt; account and use a &lt;code&gt;travis.yml&lt;/code&gt; like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;language&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;node_js&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;node_js&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;0.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This repo is all ready to push to &lt;a href="todo"&gt;Heroku&lt;/a&gt;, no Procfile is needed. Check out &lt;a href="todo"&gt;react-es6-starter.herokuapp.com&lt;/a&gt; to see the code in action.&lt;/p&gt;
&lt;p&gt;I hope you&amp;rsquo;ve found this article useful! Next time we&amp;rsquo;ll be getting into the details of managing state in React.&lt;/p&gt;
&lt;p&gt;Please fork the repo and have a play, let me know of any suggestions or improvements!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/react-es6-starter"&gt;github.com/dwmkerr/react-es6-starter&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="glossary-of-conventions"&gt;Glossary of Conventions&lt;/h3&gt;
&lt;p&gt;There are a few conventions that I personally use in most Javascript projects. The conventions used in this article which I think are valuable to consider using in many projects are:&lt;/p&gt;
&lt;h4 id="always-support-installteststart"&gt;Always support install/test/start&lt;/h4&gt;
&lt;p&gt;Everyone should always be able to checkout, install, test and run the code with the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install &lt;span style="color:#75715e"&gt;# installs everything needed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm test &lt;span style="color:#75715e"&gt;# lets the user know the code works right on their system!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm start &lt;span style="color:#75715e"&gt;# starts the code, lets the user know what to do next&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install should setup everything, and if code needs to be built to be testable, this should be a post-install hook.&lt;/p&gt;
&lt;p&gt;Test should be run next, as a user should be able to verify that the code works as expected on their system.&lt;/p&gt;
&lt;p&gt;Finally, when the user runs start, a dev server (as convention dictates we are in a dev mode by default (and production mode is set with a flag or environment variable) the server should start and a console message should show the user where to browse to.&lt;/p&gt;
&lt;hr&gt;
&lt;h5 id="footnotes"&gt;Footnotes&lt;/h5&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://www-03.ibm.com/press/us/en/pressrelease/47474.wss"&gt;IBM Mainframes&lt;/a&gt; anyone?&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>CodeProject</category></item><item><title>Manipulating JSON Web Tokens (JWTs)</title><link>https://dwmkerr.com/modifying-a-jwt-in-a-node-application/</link><pubDate>Tue, 24 Mar 2015 14:45:02 +0000</pubDate><guid>https://dwmkerr.com/modifying-a-jwt-in-a-node-application/</guid><description>&lt;p&gt;I&amp;rsquo;ve been writing a couple of web services lately that use &lt;a href="https://auth0.com/"&gt;Auth0&lt;/a&gt; for identity management. It&amp;rsquo;s a great platform that makes working with different identity providers a breeze.&lt;/p&gt;
&lt;p&gt;One thing that I couldn&amp;rsquo;t work out how to do at first was to quickly build a new JWT&lt;sup&gt;&lt;a href="#fn1" id="ref1"&gt;1&lt;/a&gt;&lt;/sup&gt; from an existing token. I wanted to take my current token, add some more data to it and return it to the user. So here&amp;rsquo;s a &amp;lsquo;why&amp;rsquo; and &amp;lsquo;how&amp;rsquo;.&lt;/p&gt;
&lt;h2 id="why"&gt;Why?&lt;/h2&gt;
&lt;p&gt;Why would you want to do this? A use case would be when you want to associate your a session with some data. For example, imagine a library gateway which offers access to a whole bunch of University libraries. First we authenticate. Then we ask for all of the libraries in the system. Then we ask for authorisation to use a specific library. We could put the library name in the token and pass it for every call onwards.&lt;/p&gt;
&lt;p&gt;It might look like this:&lt;/p&gt;
&lt;h4 id="1-authenticate"&gt;1. Authenticate&lt;/h4&gt;
&lt;p&gt;First, we authenticate, perhaps with a username and password.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;POST libraries.com/api/authenticate
{&amp;#34;usename&amp;#34;:&amp;#34;calculon&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;dramatic...pause&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then we can return a JWT if all is well:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{&amp;#34;jwt&amp;#34;:&amp;#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjYWxjdWxvbiJ9.VWkAafAMCxazY7uBlPTJoQwCBdUIy3T1d-C4TfxhAZQ&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="2-work-with-the-service"&gt;2. Work with the Service&lt;/h4&gt;
&lt;p&gt;We can put this JWT in an &lt;code&gt;Authorization&lt;/code&gt; header and start asking for protected resources:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET libraries.com/api/libraries
Authorization: Bearer eyJhb...AZQ
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;giving us:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[
{&amp;#34;name&amp;#34;: &amp;#34;Mars University Libary&amp;#34;, &amp;#34;slug&amp;#34;:&amp;#34;mul&amp;#34;},
{&amp;#34;name&amp;#34;: &amp;#34;Coney Island State Library&amp;#34;, &amp;#34;slug&amp;#34;:&amp;#34;cis&amp;#34;}
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Two libraries we can choose from. Now I want to present this choice to a user, but once they&amp;rsquo;ve made their choice I don&amp;rsquo;t want to change the libary again. I want to work with only one library in a session.&lt;/p&gt;
&lt;h4 id="3-add-data-to-the-token"&gt;3. Add Data to the Token&lt;/h4&gt;
&lt;p&gt;A nice thing we can do here is just create &lt;em&gt;another&lt;/em&gt; authentication method, which attempts to see if we are authorised to use the given library:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;POST libraries.com/api/libraries/mul/authorise
Authorization: Bearer eyJhb...AZQ
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the token is valid, we can check to see if the user is allowed to use this library. If so, we can return a &lt;em&gt;new&lt;/em&gt; token, which is associated with a &lt;em&gt;specific&lt;/em&gt; library:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;HTTP/1.1 200 OK
{&amp;#34;jwt&amp;#34;: &amp;#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjYWxjdWxvbiIsImxpYnJhcnkiOiJtdWwifQ.NM2pqRMkIp65u9unZnGIoyxK6v2A18730lPwSMrK93Q&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is a new token. Paste it into &lt;a href="https://jwt.io"&gt;jwt.io&lt;/a&gt;, you&amp;rsquo;ll see there&amp;rsquo;s a library code in the payload.&lt;/p&gt;
&lt;h4 id="4-work-with-the-service"&gt;4. Work with the service&lt;/h4&gt;
&lt;p&gt;Now I can call APIs like:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET libaries.com/api/books
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And my server can check the library in my token. If I have one, I return books from the given library, otherwise I return a 401.&lt;/p&gt;
&lt;h4 id="is-this-useful"&gt;Is this useful?&lt;/h4&gt;
&lt;p&gt;This specific example might not appeal, but you may well find as you write more complex services you want to at times add data to your token.&lt;/p&gt;
&lt;p&gt;The case above also shows how you can associate a session with a set of resources (in this case, a single library). This is useful if we know we&amp;rsquo;ll only work with a subset of resources. I want to choose a library once and work with that only. If you need to work with multiple libraries, it wouldn&amp;rsquo;t make sense.&lt;/p&gt;
&lt;h2 id="how"&gt;How?&lt;/h2&gt;
&lt;p&gt;If we are using Auth0, then we almost certainly have our token generated for us. The helper library &lt;a href="https://github.com/auth0/express-jwt"&gt;express-jwt&lt;/a&gt; will certainly let us make sure the token is valid, and put the payload of data on the &lt;code&gt;request.user&lt;/code&gt; object, but how can we create a new token &lt;em&gt;from the existing one&lt;/em&gt;?&lt;/p&gt;
&lt;p&gt;It turns out it&amp;rsquo;s really pretty easy, as we would expect as we are using open standards. Here&amp;rsquo;s the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;jwt&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;jsonwebtoken&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;extendToken&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;secret&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;extend&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Clone and extend the payload.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;body&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;parse&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;payload&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;prop&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;extend&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;extend&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;hasOwnProperty&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;prop&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;prop&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;extend&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;prop&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Sign the new token with our secret.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;jwt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;sign&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;), &lt;span style="color:#a6e22e"&gt;secret&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We have a function which takes a secret, the payload of an existing token, an object containing data to extend and that&amp;rsquo;s it. Here&amp;rsquo;s how you could use it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;expressJwt&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;express-jwt&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mySecret&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Buffer&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;walkinonsunshine&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;base64&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Middleware for protecting routes...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;requireAuth&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;expressJwt&lt;/span&gt;({&lt;span style="color:#a6e22e"&gt;secret&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mySecret&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/libraries/:lib/authorise&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;requireAuth&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// get the library, check the user has access...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lib&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;lib&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;checkLib&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;sub&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;lib&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;ok&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;next&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ok&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;401&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Access Denied.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;res&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;({&lt;span style="color:#a6e22e"&gt;jwt&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;extendToken&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;mySecret&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;user&lt;/span&gt;, {&lt;span style="color:#a6e22e"&gt;library&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lib&lt;/span&gt;})});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve extended the original token with some new data, resigned it and passed it back to the user. Future requests will automatically have the &lt;code&gt;req.user.lib&lt;/code&gt; field set (as the entire token payload is put by default on the &lt;code&gt;req.user&lt;/code&gt; object with the express-js middleware.&lt;/p&gt;
&lt;p&gt;Hopefully that&amp;rsquo;ll be of some use if you ever need to extend the payload of a JWT token in a Node app.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sup id="fn1"&gt;1. Json Web Token, read more at &lt;a href="http://jwt.io/"&gt;jwt.io&lt;/a&gt;. &lt;a href="#ref1"&gt;↩&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>The Best Module System for AngularJS Applications</title><link>https://dwmkerr.com/the-best-module-system-for-angularjs-applications/</link><pubDate>Wed, 18 Mar 2015 14:47:10 +0000</pubDate><guid>https://dwmkerr.com/the-best-module-system-for-angularjs-applications/</guid><description>&lt;p&gt;I was working on a small and simple application built with AngularJS the other day. As with most applications like this, I start with a single JavaScript file caled &lt;code&gt;app.js&lt;/code&gt; and no module system.&lt;/p&gt;
&lt;p&gt;In the past I&amp;rsquo;ve used RequireJS with AngularJS. It&amp;rsquo;s an awful mistake. It leads to a big jump in complexity with no benefts. Angular apps don&amp;rsquo;t work well with AMDs, so really your are using RequireJS to combine files into one big file.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sure there&amp;rsquo;s a good analogy with hammers and nails. Something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&amp;rsquo;s like banging nails into your face with a hammer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Maybe a bit extreme. But those who&amp;rsquo;ve used the two together may well be nodding sagely.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also used Browserify. I prefer this approach, the syntax is cleaner. But it&amp;rsquo;s still a pain.&lt;/p&gt;
&lt;p&gt;Ideally, I&amp;rsquo;d like to use ECMA6 modules. So another approach is to just use ECMA6 module syntax and then compile your code with something like Traceur. But that requires quite a bit of tooling, slows down your pipeline and you&amp;rsquo;re still not &lt;em&gt;really&lt;/em&gt; using modules.&lt;/p&gt;
&lt;p&gt;I think the best approach is this one from &lt;a href="https://medium.com/@dickeyxxx"&gt;Jeff Dicky&lt;/a&gt; on his post &lt;a href="https://medium.com/@dickeyxxx/best-practices-for-building-angular-js-apps-266c1a4a6917"&gt;Best Practices for Building Angular.js Apps&lt;/a&gt;. Just forget all of the module stuff and concatenate only.&lt;/p&gt;
&lt;p&gt;Start with this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;myproject
- app/
- css/
- vendor/
- index.html
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or whatever your preferred structure is. Then stick your main file in &lt;code&gt;app/&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;myproject
- app/
- app.js
- css/
- vendor/
- index.html
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Your &lt;code&gt;app.js&lt;/code&gt; file should define your main Angular module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;app&amp;#39;&lt;/span&gt;, []);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now just go ahead and concatenate everything in your &lt;code&gt;app/&lt;/code&gt; folder. Structure it however you want:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;myproject
- app/
- components/
- home/
- profile/
- app.js
- css/
- vendor/
- index.html
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Concat will put everything in the top level folder (i.e. &lt;code&gt;app.js&lt;/code&gt;) first. As long as you don&amp;rsquo;t put anything else in your top level folder (that comes before &amp;lsquo;a&amp;rsquo; alphabetically) then it doesn&amp;rsquo;t matter where you put your other files, as long as you define them without referencing any globals. So define your components like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;app&amp;#39;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;SomeController&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No fuss no muss. No requires, no exports.&lt;/p&gt;
&lt;p&gt;If you need a new service, write it and save it. Same for directives or controllers or filters. Add the source file and it&amp;rsquo;s included, no messing around.&lt;/p&gt;
&lt;p&gt;Keep it simple, don&amp;rsquo;t force another module system on top of angular&amp;rsquo;s, you don&amp;rsquo;t get much benenfit. And wait patiently until ECMA6 moves more into the mainstream and we can start using native modules. There&amp;rsquo;s less and less point in investing in some super-sophisticated complex fancy module system for a framework which in vNext will throw it all away and for a language which will finally get native modules.&lt;/p&gt;
&lt;h3 id="words-for-gulpers"&gt;Words for Gulpers&lt;/h3&gt;
&lt;p&gt;If you are a gulp user, here&amp;rsquo;s how a pipeline might look to concat your JavaScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gulp&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;jshint&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-jshint&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;stylish&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;jshint-stylish&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;uglify&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-uglify&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;rename&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-rename&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sourcemaps&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-sourcemaps&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;concat&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-concat&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ngAnnotate&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;require&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;gulp-ng-annotate&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Hints and builds all JavaScript.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gulp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;task&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;js&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gulp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;([&lt;span style="color:#e6db74"&gt;&amp;#39;./client/app/**/*.js&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;jshint&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;jshint&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reporter&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;stylish&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;jshint&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reporter&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;fail&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;sourcemaps&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;init&lt;/span&gt;({&lt;span style="color:#a6e22e"&gt;loadMaps&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;}))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;concat&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;app.js&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;gulp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dest&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./client/dist&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ngAnnotate&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;uglify&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;rename&lt;/span&gt;({&lt;span style="color:#a6e22e"&gt;suffix&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;.min&amp;#39;&lt;/span&gt;}))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;sourcemaps&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;write&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;pipe&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;gulp&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dest&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;./client/dist/&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Watch your app javascript folder and when it changes, you&amp;rsquo;ll hint everything, concat into a single distribution folder, annotate and uglify, as well as building full sourcemaps.&lt;/p&gt;
&lt;h3 id="what-about-other-stuff"&gt;What about other stuff?&lt;/h3&gt;
&lt;p&gt;For vendor code (jQuery, Bootstrap, whatever), don&amp;rsquo;t bother trying to be smart and require or import it. Just include it in your app with script tags. I wouldn&amp;rsquo;t go to the effort at trying to force some kind of smart module system on a language that doesn&amp;rsquo;t really support it - uf you can get away with avoiding it, do so.&lt;/p&gt;
&lt;p&gt;This is not an encouragement to be sloppy, this is just the easiest way to deal with the issue. The number of hours I&amp;rsquo;ve wasted tracking down &amp;lsquo;bugs&amp;rsquo; which were subtle issues to do with require.js or type-os has definitely made the approach above my preferred approach.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Fixing Memory Leaks in AngularJS and other JavaScript Applications</title><link>https://dwmkerr.com/fixing-memory-leaks-in-angularjs-applications/</link><pubDate>Tue, 03 Mar 2015 14:35:36 +0000</pubDate><guid>https://dwmkerr.com/fixing-memory-leaks-in-angularjs-applications/</guid><description>&lt;p&gt;Dealing with memory leaks in JavaScript applications can be a complex process. In this article I&amp;rsquo;m going to show you how to identify whether you have memory leaks, analyse them and ultimately resolve them.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using an AngularJS application to demonstrate the concepts and approaches, but much of this material applies to any JavaScript application.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#understandingmemoryleaks"&gt;Understanding Memory Leaks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;What is a Memory Leak?&lt;/li&gt;
&lt;li&gt;Why is a Memory Leak Bad?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#identifyingmemoryleaks"&gt;Identifying Memory Leaks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Method 1: The Wrong Way&lt;/li&gt;
&lt;li&gt;Method 2: The Timeline&lt;/li&gt;
&lt;li&gt;Method 3: Recording Heap Allocations&lt;/li&gt;
&lt;li&gt;Method 4: Heap Snapshots&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#analysingmemoryleaks"&gt;Analysing Memory Leaks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Analysing the leak in Scenario 2&lt;/li&gt;
&lt;li&gt;More on Graphs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#fixingmemoryleaks"&gt;Fixing Memory Leaks&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Three golden rules&lt;/li&gt;
&lt;li&gt;Anti-patterns to avoid&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#thefuture"&gt;The Future&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Weak Maps&lt;/li&gt;
&lt;li&gt;AngularJS 2&lt;/li&gt;
&lt;li&gt;Even Better Browsers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#appendices"&gt;Appendices&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Thanks&lt;/li&gt;
&lt;li&gt;Mysteries&lt;/li&gt;
&lt;li&gt;Futher Reading&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="understanding-memory-leaks"&gt;Understanding Memory Leaks&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve dealt with memory leaks before, or the patterns of memory usage we sometimes call memory leaks in memory managed applications, then you can probably skip to &lt;a href="#identifyingmemoryleaks"&gt;Identifying Memory Leaks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If not let&amp;rsquo;s start with some theory.&lt;/p&gt;
&lt;h3 id="what-is-a-memory-leak"&gt;What is a Memory Leak?&lt;/h3&gt;
&lt;p&gt;A memory leak, at least in the world of unmanaged applications, is what occurs when you allocate memory and forget to free it. In pseudo-code&lt;sup&gt;&lt;a href="#fn1" id="ref1"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;void leaky()
{
void* memory;
memory = malloc(1000);
/* malloc just gave us some memory, use it! */
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;memory&lt;/code&gt; will hold the address of the memory we&amp;rsquo;ve allocate. We use the memory, then the function ends. &lt;code&gt;memory&lt;/code&gt; goes out of scope and whatever address it held is lost - but we didn&amp;rsquo;t free the memory! Not only that, we&amp;rsquo;ve lost the address of it so can&amp;rsquo;t ever free it in the future - it&amp;rsquo;s &lt;em&gt;leaked&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This memory is lost to the application - we can&amp;rsquo;t release it. Only terminating the process will release it back to the operating system.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When we allocate memory and don&amp;rsquo;t release it when we are done, we have &amp;rsquo;leaked&amp;rsquo; that memory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So how do we get memory leaks in JavaScript applications? We don&amp;rsquo;t allocate memory directly, the engine does it for us, and it cleans it up afterwards as well&lt;sup&gt;&lt;a href="#fn2" id="ref2"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;If we hold on to objects longer than we need to, that will give us similar results. Let&amp;rsquo;s look at some code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ChessManager&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;moves&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;makeMove&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;move&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;moves&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;push&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;move&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;newGame&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;moves&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;clear&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we&amp;rsquo;ve got a bug - the &lt;code&gt;newGame&lt;/code&gt; function doesn&amp;rsquo;t clear the &lt;code&gt;ChessManager&lt;/code&gt;&amp;rsquo;s moves, it just throws a null reference exception. But we could use this class in our code. In theory if we keep on calling &lt;code&gt;makeMove&lt;/code&gt; we&amp;rsquo;ll just grow and grow the &lt;code&gt;moves&lt;/code&gt; array. This is a bug leading to memory that can&amp;rsquo;t be freed, even though we don&amp;rsquo;t need it.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s a contrived example of a JavaScript memory leak.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When we are finished with memory but don&amp;rsquo;t allow the garbage collector to clean it up, that&amp;rsquo;s a memory leak.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At least for the purposes of this discussion. We&amp;rsquo;ll see it&amp;rsquo;s a very easy thing to do.&lt;/p&gt;
&lt;h3 id="why-is-a-memory-leak-bad"&gt;Why is a Memory Leak bad?&lt;/h3&gt;
&lt;p&gt;It might seem obvious but let&amp;rsquo;s just make sure we&amp;rsquo;re explicit with everything. As we said in the initial definition, we allocate memory but don&amp;rsquo;t deallocate it.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;some&lt;/em&gt; circumstances, this is not necessarily a disaster, if we don&amp;rsquo;t leak too much too often, but there are circumstances where this is very serious.&lt;/p&gt;
&lt;p&gt;Memory leaks cause performance problems, slow down applications and can lead to a process terminating. There are some times when that&amp;rsquo;s really not good.&lt;/p&gt;
&lt;p&gt;Servers and high performance applications shouldn&amp;rsquo;t leak, especially as many should be expected to run for long periods of time. Mobile apps or apps for embedded systems will need to deal with fewer resources and will suffer if they leak. Any application an end user is expecting to use for a long time will cause a lot of frustration if it leaks.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s enough theory, let&amp;rsquo;s actually start looking at identifying memory leaks in the context of an AngularJS application.&lt;/p&gt;
&lt;h2 id="identifying-memory-leaks"&gt;Identifying Memory Leaks&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve created a sample app for showing photo albums which is leaky in parts. The app is at:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://dwmkerr.github.io/angular-memory-leaks/"&gt;dwmkerr.github.io/angular-memory-leaks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a very basic app with a fairly common set of components; Bootstrap, jQuery and AngularJS. We&amp;rsquo;re going to take a look at how we can identify whether this app suffers from memory leaks.&lt;/p&gt;
&lt;p&gt;You can run the app in your browser, or run it locally with the commands:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;git clone https://github.com/dwmkerr/angular-memory-leaks.git
cd angular-memory-leaks
npm install &amp;amp;&amp;amp; bower install
gulp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running gulp will serve the app, lint, and reload the browser when you change the code. The project page is at &lt;a href="https://github.com/dwmkerr/angular-memory-leaks"&gt;github.com/dwmkerr/angular-memory-leaks&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="method-1-the-wrong-way"&gt;Method 1: The Wrong Way&lt;/h3&gt;
&lt;p&gt;First, just be aware that the wrong way to look for leaks is by examing the memory usage of the Chrome process. While an increasing amount of memory usage &lt;em&gt;can&lt;/em&gt; indicate a leak, it is not reliable. Why?&lt;/p&gt;
&lt;p&gt;Well browsers can allocate memory and use it how they want to. A page it is rendering may no longer need as much memory as it needed before, but that doesn&amp;rsquo;t mean the browser needs to release it to the OS. It may just keep it to avoid having to re-allocate it later on.&lt;/p&gt;
&lt;h3 id="method-2-the-timeline"&gt;Method 2: The Timeline&lt;/h3&gt;
&lt;p&gt;Open the Chrome developer tools. Go to &amp;lsquo;Timeline&amp;rsquo; select &amp;lsquo;Memory&amp;rsquo; and hit &amp;lsquo;Record&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/StartRecording.png" alt="Start Recording"&gt;&lt;/p&gt;
&lt;p&gt;Now start using your application. After you are done, stop recording. You&amp;rsquo;ll see a graph of memory usage.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/MemoryUsage.png" alt="Memory Usage"&gt;&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;almost&lt;/strong&gt; exactly what we need. I&amp;rsquo;ll explain the almost shortly, but lets take a look at this graph.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We see a &lt;em&gt;Used JS Heap&lt;/em&gt; in blue. &lt;em&gt;Used&lt;/em&gt; is important here - Chrome is telling us that there may be more heap usage than shown in its actual process, but what we are seeing here is what is actually used by the page.&lt;/li&gt;
&lt;li&gt;We see documents (in this case a steady value of one document).&lt;/li&gt;
&lt;li&gt;We see DOM nodes. As I use the app the nodes increase, up until a certain point and then they drop.&lt;/li&gt;
&lt;li&gt;We see Listeners (i.e. even handlers). Again, these increase as I use the app and then drop.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So what should we be looking for in this graph? That depends on what our app is doing. But let&amp;rsquo;s imagine the we are navigating through different photo albums in the albums app. We&amp;rsquo;ll need more memory to see each album, but once we leave an album we don&amp;rsquo;t need that memory any more. So we should get a healthy saw-tooth pattern&lt;sup&gt;&lt;a href="#fn3" id="ref3"&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/TimelineSawtooth.png" alt="Timeline Sawtooth"&gt;&lt;/p&gt;
&lt;p&gt;Here we see that we use more and more memory, up until the point that Chrome garbage collects, then goes back to where we started. This is repeated again and again. This is a good sign - when Chrome garbage collects we go back to the same place we started, a strong indication we are not leaking much memory.&lt;/p&gt;
&lt;p&gt;If we are doing some work which simply needs more and more memory, and we don&amp;rsquo;t release it, we would expect to see steps instead&lt;sup&gt;&lt;a href="#fn4" id="ref4"&gt;4&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/TimelineSteps-1.png" alt="Timeline Steps"&gt;&lt;/p&gt;
&lt;p&gt;An example of this might be an infinite scroll situation. I&amp;rsquo;m looking through a vast photo album, and when I get to the bottom of the screen I load more images automatically. The ones I&amp;rsquo;ve loaded are still in the DOM so cannot be released. We see no saw-tooth because there&amp;rsquo;s no release of memory. However, this is not a memory leak - it&amp;rsquo;s just increasing memory usage. It does mean that if we allow the user to scroll too much we may run out of resources though.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;dangerous&lt;/strong&gt; case is the one below:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/TimelineLeakySawtooth.png" alt="Leaky Sawtooth"&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s imaging we&amp;rsquo;re using the application, navigating through albums, returning the the home page, looking through some more albums and so on. We keep using memory, and Chrome keeps on garbage collecting, but we never quite get back to where we started. We are trending towards increasing memory usage. This indicates we &lt;em&gt;might&lt;/em&gt; be leaking memory.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Might&lt;/em&gt; is not going to cut the mustard, we need to know categorically what is going on and whether we have a leak.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You said this is &amp;lsquo;almost&amp;rsquo; exactly what we need?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately, you cannot always trust this graph. See Mystery 1 for the ugly details. Suffice to say that what we&amp;rsquo;re seeing here is an indicator only, but for more detail we need to look at Method 3.&lt;/p&gt;
&lt;h3 id="method-3-recording-heap-allocations"&gt;Method 3: Recording Heap Allocations&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s look at a different way of seeing if we&amp;rsquo;ve got a leak, the &amp;lsquo;Heap Allocations&amp;rsquo; view. In the developer tools, go to &amp;lsquo;Profiles&amp;rsquo; and &amp;lsquo;Record Heap Allocations&amp;rsquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapAllocations.png" alt="Record Heap Allocations"&gt;&lt;/p&gt;
&lt;p&gt;When we record heap allocations we get a chart showing us spikes as we allocate memory. These spikes are initially blue (meaning Chrome is using the memory), then change to grey once the memory is freed. If we see spikes or sections of spikes that remain blue, we may have a problem.&lt;/p&gt;
&lt;p&gt;Try this, go to the Ablums app and start recording. Click on the &amp;lsquo;India&amp;rsquo; album, then go back to the home page. You should see a chart like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapAllocationsEx1.png" alt="Heap Allocations Example 1"&gt;&lt;/p&gt;
&lt;p&gt;So we start recording and nothing is being allocated. Then we click on the &amp;lsquo;India&amp;rsquo; album (point 1) and we get a few spikes, as chrome allocates memory needed for the content in the new page. Then we click back on the home page (point 2). Some of the memory used in the India album is released (it looks like about half). One spike of memory used for the home page is still in use (what we&amp;rsquo;d expect) and another spike or two seem to be freed. These other spikes might be memory used for the actual transition, for example in logic in the router.&lt;/p&gt;
&lt;p&gt;So this looks like we may have a problem in the album page. In fact, we can drag a selection box around those first three spikes and see what is &lt;em&gt;still&lt;/em&gt; in memory (i.e. what might be a potential leak) in the view below:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapAllocationsEx2.png" alt="Heap Allocations Example 2"&gt;&lt;/p&gt;
&lt;p&gt;Dissecting this view we have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A subset of the data, the blue spike from the album page which is still in use.&lt;/li&gt;
&lt;li&gt;The &amp;lsquo;Heap View&amp;rsquo;, which shows us different &lt;em&gt;types&lt;/em&gt; of data in memory. Don&amp;rsquo;t worry, we&amp;rsquo;ll see a lot more on this later.&lt;/li&gt;
&lt;li&gt;An instance of a specific type of data, in this case an instance of a JavaScript object.&lt;/li&gt;
&lt;li&gt;The retainers graph for the specific object.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We&amp;rsquo;re going to look into what all of this means in a lot of detail as we go through the article. For now, I&amp;rsquo;ll simply state what we&amp;rsquo;re seeing, by the end of the article you&amp;rsquo;ll be able to analyse this (and much more) yourself.&lt;/p&gt;
&lt;p&gt;In this snapshot we see a small amount of data still in use. A quick look through the data reveils we have data still in use which relats to the AngularJS template cache.&lt;/p&gt;
&lt;p&gt;This is good! It means this is probably not a leak. When I first visit the album page AngularJS is caching the template used to render it, so of course it stays in memory.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When analysing memory usage remember that caching, preloading and other optimisation techniques may cause some noise.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So if we have the albums page in a cache, in theory the next time we visit the page and then return to the home page, we should free a lot more of the memory (because the &lt;em&gt;new&lt;/em&gt; memory we allocate will be just for the page itself, not the cache which is already set up). Let&amp;rsquo;s try it. We&amp;rsquo;ll record going to the album page, back to the homepage, then the album page and back again:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapAllocationsEx3.png" alt="Heap Allocations Example 3"&gt;&lt;/p&gt;
&lt;p&gt;This is looking good.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We go to the &amp;lsquo;India&amp;rsquo; album. Some memory used is now freed, but much is still in use. As we saw, at least some of that is the template cache.&lt;/li&gt;
&lt;li&gt;We go back to the home page, lots of memory is used but by the time we&amp;rsquo;re done recording it&amp;rsquo;s almost entirely freed.&lt;/li&gt;
&lt;li&gt;We visit the India album a second time, requiring some memory almost all of which is freed.&lt;/li&gt;
&lt;li&gt;We go back to the home page. Some memory is used during the transition and to render the page, some of that is still in use (which is expected as the page is still open).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The heap allocations chart is exceptionally useful in identifying memory leaks, it has already led to some insights:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Initial loading of pages increases our &amp;lsquo;baseline&amp;rsquo; memory footprint due to data being added to caches (such as the AngularJS template cache).&lt;/li&gt;
&lt;li&gt;Subsequent loading of pages requires memory, but the vast majority of it is freed.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One thing we noticed from this brief analysis was that the initial result was slightly misleading. With the heap allocations view repeated operations can help you identify trends. In the Albums application I&amp;rsquo;ve actually set up part of the app to run repeated operations, so we can try to consistently test scenarions. The &amp;lsquo;scenarios&amp;rsquo; menu lets us run them. Let&amp;rsquo;s try running scenario 1.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario1.png" alt="Scenario 1"&gt;&lt;/p&gt;
&lt;p&gt;This scenario will navigate from &lt;code&gt;/&lt;/code&gt; (the home page) to &lt;code&gt;/nowhere&lt;/code&gt; ten times. &lt;code&gt;/nowhere&lt;/code&gt; isn&amp;rsquo;t matched by the router so takes us back to the home page. This has the effect of reloading the home page 20 times (just reloading doesn&amp;rsquo;t work, the router is smart enough to realise we&amp;rsquo;re staying on the same page).&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scneario1HeapAllocations.png" alt="Scenario 1 Heap Allocations"&gt;&lt;/p&gt;
&lt;p&gt;While you are recording the chart you can see peaks go from blue to grey as memory is freed. Let&amp;rsquo;s see what we&amp;rsquo;ve got.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Shows our first navigation, some memory is not freed. Everything before this is setup code.&lt;/li&gt;
&lt;li&gt;Our last navigation. Some memory still in use (as expected).&lt;/li&gt;
&lt;li&gt;A glance at memory in use shows some compiled code and system data (more on this later). At this stage we don&amp;rsquo;t need to worry, Chrome will allocate data like this when it needs to.&lt;/li&gt;
&lt;li&gt;It looks like the 11th page load didn&amp;rsquo;t free all of it&amp;rsquo;s memory. This is potential cause for worry.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Altogether this a very healthy looking scenario. The huge majority of what we allocate is freed, as we would hope. Small amounts of memory stay in use (mostly used under the hood by Chrome) and a small amount of memory after the 11th reload is not freed (a quick look suggests a timing issue, definitely something we&amp;rsquo;d want to investigate further in a real-world app). Our allocations are in the 50 KB to 100 KB range and we&amp;rsquo;re looking good.&lt;/p&gt;
&lt;p&gt;Before we say goodbye to the Heap Allocations view (for now) let&amp;rsquo;s do the same for Scenario 2 (moving from the home page to the top rated page 10 times).&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2HeapAllocations.png" alt="Scenario 2 Heap Allocations"&gt;&lt;/p&gt;
&lt;p&gt;We are not going to analyse this issue (yet!) but this is an example of a much less healthy chart. In this chart we seem to be allocating memory for each page view and not releasing it. This kind of chart definitely indicates that there could be problems.&lt;/p&gt;
&lt;p&gt;So we&amp;rsquo;ve seen the Heap Allocations view, which is a bit more sophisticated than the memory usage graph. Let&amp;rsquo;s look at the last way to analyse memory leaks - snapshots.&lt;/p&gt;
&lt;h3 id="method-4-heap-snapshots"&gt;Method 4: Heap Snapshots&lt;/h3&gt;
&lt;p&gt;The final method of identifying memory leaks is the most sophisticated and finely controlled. We will take snapshots at specific points in time and analyse the differences between them. To take a snapshot, we go to the Profiles view and choose &amp;lsquo;Take Heap Snapshot&amp;rsquo;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/TakeHeapSnapshot.png" alt="Take Heap Snapshot"&gt;&lt;/p&gt;
&lt;p&gt;When we take a heap snapshot Chrome simply records the details of all memory allocated.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remember: Taking a Snapshot &lt;strong&gt;always&lt;/strong&gt; runs garbage collection first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A heap snapshot shows you exactly the same kind of data you get in the Heap Allocations view, except that you are seeing ALL memory in use, not just objects which were allocated and are still alive:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapSnapshot1.png" alt="A Heap Snapshot"&gt;&lt;/p&gt;
&lt;p&gt;This view is very complete but not necessarily very useful. There&amp;rsquo;s some extra ways to see the data (if you change from &amp;lsquo;Summary&amp;rsquo; to another view or change &amp;lsquo;All Objects&amp;rsquo; but we&amp;rsquo;ll see that later).&lt;/p&gt;
&lt;p&gt;Staying on topic, we&amp;rsquo;ll not yet look in detail at what the data is that we are seeing, we&amp;rsquo;ll first look into identifying whether there are memory leaks - then we&amp;rsquo;ll look into tracking them down.&lt;/p&gt;
&lt;p&gt;Indivdiual snapshots are not so helpful for checking for leaks, but what is very helpful is the ability to compare memory used between snapshots.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take some snapshots, try this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the app.&lt;/li&gt;
&lt;li&gt;Navigate to the top rated page (caches should now be set up).&lt;/li&gt;
&lt;li&gt;Navigate to the home page. Take a snapshot.&lt;/li&gt;
&lt;li&gt;Navigate to the top rated page. Take a snapshot.&lt;/li&gt;
&lt;li&gt;Navigate to the home page. Take a snapshot.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we can do something really cool. Select snapshot 3, and choose to view data allocated between snapshot 1 and 2. This means we&amp;rsquo;re seeing data allocated for the top rated page, which is &lt;em&gt;still&lt;/em&gt; in use when we go back to the home page, i.e. probably leaked.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/SnapshotComparison.png" alt="Snapshot Comparison"&gt;&lt;/p&gt;
&lt;p&gt;So what are we seeing now?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We have three snapshots. The size of each one is shown. &lt;em&gt;Sometimes&lt;/em&gt; the very first one seems overly high. See Mystery 2. We have selected the 3rd snapshot and are therefore only able to see data still present in this snapshot.&lt;/li&gt;
&lt;li&gt;We are chosing to show only objects allocated between Snapshot 1 and 2, i.e. objects allocated to present the page. But we&amp;rsquo;re &lt;strong&gt;in&lt;/strong&gt; snapshot 3, so we&amp;rsquo;re seeing those objects which were allocated and are still present.&lt;/li&gt;
&lt;li&gt;Objects allocated are looking suspicious - we&amp;rsquo;ve got DOM elements. This doesn&amp;rsquo;t look good!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the best way to identify memory leaks. So now that we&amp;rsquo;ve seen how to identify whether we have memory leaks, or at least that we have a potential problem to analyse we can move onto step 2 - Analysing Memory Leaks.&lt;/p&gt;
&lt;h2 id="analysing-memory-leaks"&gt;Analysing Memory Leaks&lt;/h2&gt;
&lt;p&gt;If we think we have a memory leak, we need to be able to look at the heap data and see what&amp;rsquo;s going on. Whether we are seeing heap data from a selection of allocations from the Heap Allocations view or from the Heap Snapshots, we see the same kind of information:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapData.png" alt="Heap Data"&gt;&lt;/p&gt;
&lt;p&gt;Starting from the left we have the &amp;lsquo;Constructor&amp;rsquo; column. This is the type of object we have. Some of these objects we can see are JavaScript classes (constructed with a &lt;code&gt;new&lt;/code&gt; call to a function), such as &lt;code&gt;Scope&lt;/code&gt;. As well as our own classes, we have some special classes of data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(compiled code): Represents JavaScript code compiled by Chrome. Consider this internal - we have no control over it.&lt;/li&gt;
&lt;li&gt;(array): Internally used array object. Again, internal.&lt;/li&gt;
&lt;li&gt;Array: A JavaScript array. Often we have a &lt;em&gt;lot&lt;/em&gt; of data in arrays.&lt;/li&gt;
&lt;li&gt;Object: A plain old JavaScript object.&lt;/li&gt;
&lt;li&gt;(closure): A closure.&lt;/li&gt;
&lt;li&gt;system / Context: The underlying data require to call a function, for example the actual data used by a closure.&lt;/li&gt;
&lt;li&gt;system: Internally used data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also plenty of objects that are created by Chrome, such as &lt;code&gt;HTMLDivElement&lt;/code&gt;, which is a wrapper around the internally used (native) DOM object.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dissect some of these objects in detail. Running &lt;strong&gt;Scenario 3&lt;/strong&gt; allocates some data and puts it on the &lt;code&gt;window&lt;/code&gt; object. This is really trivial data but shows a lot. You can use the Heap Allocations View or Heap Snapshots to see the data. I&amp;rsquo;ve taken three snapshots (once before pressing OK, once after the data is allocated, and the final one when the last modal is closed):&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapDataAnalysis2.png" alt="Heap Data Analysis Part 1"&gt;&lt;/p&gt;
&lt;p&gt;This data has come from the code below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a class which will hold heap data. Makes it easier
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// to find the data in Chrome.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HeapData&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a heap data object.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HeapData&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a function that multiplies two numbers.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiply&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a &amp;#39;multiply by&amp;#39; function, which curries the above
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// to generate a function which multiplies by a constant. This
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// will involve closures.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiplyBy&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiply&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Add some data to our heap data object.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;fry&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Philip J. Fry&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;zoidberb&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;John &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Zoidberg&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;character&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;firstName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Amy&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secondName&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Wong&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiplyBy&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;multiplyBy100&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;multiplyBy&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;doubledNumber&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;18&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;multipliedNumber&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;multiplyBy100&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;div&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; document.&lt;span style="color:#a6e22e"&gt;createElement&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Put the heap data on the window, it is now pinned to a GC root.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;window.&lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;heapData&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve got a little bit of everything here, some code, some closures, some objects and a DOM element.&lt;/p&gt;
&lt;p&gt;As we&amp;rsquo;ve put most of this data on the &lt;code&gt;heapData&lt;/code&gt; object, which is an instance of &lt;code&gt;HeapData&lt;/code&gt; we can easily find the object:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapDataAnalysis3.png" alt="Heap Data Analysis 3"&gt;&lt;/p&gt;
&lt;p&gt;So we can see the &lt;code&gt;HeapData&lt;/code&gt; constructor, expanding it we see an &lt;em&gt;instance&lt;/em&gt; of &lt;code&gt;HeapData&lt;/code&gt;. The &lt;code&gt;@420269&lt;/code&gt; is a unique ID assigned by Chrome. If we have lots of heap data objects, we can use this to distinguish between them when we&amp;rsquo;re looking at other parts of the snapshot. What else do we see?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Distance&lt;/strong&gt;. How far the instance is from a GC Root. A GC root is anything that can &amp;lsquo;pin&amp;rsquo; objects, for example the &lt;code&gt;window&lt;/code&gt; object which holds globals. If put something on &lt;code&gt;window&lt;/code&gt; it will never be freed, this is what makes it a GC root. Our distance is 2 as we have &lt;code&gt;HeapData&lt;/code&gt; (constructor) to &lt;code&gt;heapData&lt;/code&gt; (instance) to &lt;code&gt;window&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Objects count&lt;/strong&gt;. Only valid for the top level nodes, this shows us how many objects of the specified type we have. We have 1 &lt;code&gt;HeapData&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shallow Size&lt;/strong&gt;. The size of the data that is directly allocated for the object. Compare this to &lt;em&gt;Retained Size&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retained Size&lt;/strong&gt;. The size of data this object is retaining. For example, out &lt;code&gt;heapData&lt;/code&gt; instance holds a reference to an object which contains two fields &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;secondName&lt;/code&gt;. Our shallow size includes enough data for the reference, the retained size includes the full retained size of the retained object.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice that our instance of &lt;code&gt;HeapData&lt;/code&gt; is highlighted in yellow? That&amp;rsquo;s a convenience from Chrome, it&amp;rsquo;s showing us objects which are &lt;strong&gt;directly accessible&lt;/strong&gt; from JavaScript. Our object can be accessed via &lt;code&gt;window.heapData&lt;/code&gt;, therefore it&amp;rsquo;s directly accessible. Other objects we&amp;rsquo;ve created might not be (for example, a variable used in a closure exists and is on the heap, but not directly accessible).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see some other data we allocated:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapDataAnalysis4-1.png" alt="Heap Data Analysis 4"&gt;&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re looking at closures. We have two closures in yellow next to each other, clicking on one shows the retainer graph. What is going on here?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Our closure is not a simple thing. It has code (of course), which takes up memory. We won&amp;rsquo;t look into this in detail. It has shared function data (again, internally used and not worth looking into). We also have a reference to a &lt;code&gt;__proto__&lt;/code&gt; (a function object has a prototype!). Finally, we have the context, which contains enough data to call the function. If we look in to the context we will not see much, as our function contains numbers which Chrome can simply store in the code. However, if we use references in closures we&amp;rsquo;ll actually see them in the context.&lt;/li&gt;
&lt;li&gt;We also have the retainers. Our closure is referenced via a variable called &lt;code&gt;multiplyBy100&lt;/code&gt;, which itself is referenced by &lt;code&gt;heapData&lt;/code&gt;, which if referenced by the &lt;code&gt;window&lt;/code&gt; GC root.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;multiplyBy100&lt;/code&gt; variable is &lt;em&gt;also&lt;/em&gt; dominated by the second element of an array with id &lt;code&gt;@227339&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last thing we&amp;rsquo;ll look at in this snapshot is the div element.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/HeapDataAnalysis5.png" alt="Heap Data Analyis 5"&gt;&lt;/p&gt;
&lt;p&gt;We can see the div element is retained by the &lt;code&gt;div&lt;/code&gt; variable in the &lt;code&gt;heapData&lt;/code&gt; object. We can also see it is made up of a prototype and some native object. The native object shows no size - don&amp;rsquo;t be fooled. That just means its taking up no JavaScript heap memory. It is still using memory (just in V8 engine not the JavaScript code).&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s important to note here is that the element is shown in red. This means it&amp;rsquo;s &lt;strong&gt;detached&lt;/strong&gt;. So it exists, is referenced (and therefore cannot be garbage collected) but is not in the DOM. This is not necessarily a problem, but lots of detached DOM elements is often a bad sign, especially if the number is increasing.&lt;/p&gt;
&lt;p&gt;The rest of the data you can look through yourself. You&amp;rsquo;ll notice some interesting things, such as how concatenated strings work, but the important stuff we&amp;rsquo;ve now seen.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s move on to analyising the first potential memory leak we discovered - the transition to the Top Rated page of the albums app.&lt;/p&gt;
&lt;h3 id="analysing-the-leak-in-scenario-2"&gt;Analysing the leak in Scenario 2&lt;/h3&gt;
&lt;p&gt;We saw that &lt;strong&gt;Scenario 2&lt;/strong&gt; (switching to and from the &amp;rsquo;top rated&amp;rsquo; view) seemed to leak memory. Let&amp;rsquo;s use the heap snapshot comparison view to analyse this further. The steps are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to the home page.&lt;/li&gt;
&lt;li&gt;Navigate to the top rated page (setting up the cache).&lt;/li&gt;
&lt;li&gt;Navigate to the home page, take a snapshot.&lt;/li&gt;
&lt;li&gt;Navigate to the top rated page, take a snapshot.&lt;/li&gt;
&lt;li&gt;Navigate to the home page, take a snapshot.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can now look at the memory allocated between 1 and 2 which is present in 3 (i.e. what we allocated for the top rated view and potentially leaked):&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Snapshot1.png" alt="Scenario 2 Snapshot 1"&gt;&lt;/p&gt;
&lt;p&gt;Some things jump out immediately:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We have gone from 7.5 to 8.4 to 8.5 MB. We are changing from one view to another - and ending in the same place that we started. We &lt;strong&gt;should&lt;/strong&gt; be going back to 7.5 MB.&lt;/li&gt;
&lt;li&gt;We&amp;rsquo;ve got a lot of objects still hanging around, not just system data like compiled code, but HTML elements, detached DOM elements, &lt;code&gt;Promise&lt;/code&gt; objects, &lt;code&gt;n.fn.init&lt;/code&gt; objects and so on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This looks like a classic leak situation. Let&amp;rsquo;s start by looking at some objects we recognise. There are some &lt;code&gt;Scope&lt;/code&gt; objects near the top of the chart, let&amp;rsquo;s look at those.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario1Part2.png" alt="Scenario 2 Part 2"&gt;&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve got some &lt;code&gt;Scope&lt;/code&gt; objects, three in fact. These objects contain the usual AngularJS fields such as &lt;code&gt;$parent&lt;/code&gt;, the only field which distinguishes this scope is the &lt;code&gt;album&lt;/code&gt; field. If we look at out &lt;code&gt;aml-rated-album&lt;/code&gt; directive it looks like it could be the isolated scope for this directive:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;directive&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;amlRatedAlbum&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;restrict&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;E&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;album&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;=&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#75715e"&gt;// etc
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This scope has an &lt;code&gt;album&lt;/code&gt; field. There are three albums so it looks likely these are the three albums in the top rated page, the scopes stil in memory. What retains them?&lt;/p&gt;
&lt;p&gt;Looking at the retainers (at &lt;strong&gt;2&lt;/strong&gt;) we don&amp;rsquo;t see much. We&amp;rsquo;re retained by a &lt;code&gt;$$ChildScope&lt;/code&gt;, which also retained by a &lt;code&gt;$$ChildScope&lt;/code&gt; object. In fact we have quite a complex graph of objects.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When we leak a scope in AngularJS, we leak a huge graph of objects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Scopes know about their parents. They also know about their children, and siblings. If we inadvertantly pin a scope to a GC root, we &lt;strong&gt;will probably leak almost all of the scopes in the page&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Why? The graph below should show why. I &amp;rsquo;leak&amp;rsquo; a scope, and by doing so I retain all of the other scopes, because they are connected. Having a connected graph of scopes is required for angular to work, but it means that we we are extremely susceptible to leaking a &lt;strong&gt;lot&lt;/strong&gt; of data.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/ScopeLeakGraph1.png" alt="Scope leak graph"&gt;&lt;/p&gt;
&lt;p&gt;This graph shows &lt;code&gt;$parent&lt;/code&gt; retained relationships, but don&amp;rsquo;t forget scopes also know about their children and their siblings, so real graph is even more highly connected.&lt;/p&gt;
&lt;p&gt;So just grabbing a specific scope is not good enough. We need to try and be a little bit more specific. Let&amp;rsquo;s try starting from an element instead. Here we take a look at a div element and its retainers:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Part3.png" alt="Scenario 2 Part 3"&gt;&lt;/p&gt;
&lt;p&gt;Resting the mouse over the instance of a leaked &lt;code&gt;HTMLElement&lt;/code&gt; shows a bit of data about it, it&amp;rsquo;s a &lt;code&gt;aml-rated-album&lt;/code&gt; and it is detached. Definitely a symptom of our leak. Let&amp;rsquo;s see the retainers:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Part4-1.png" alt="Scenario 2 Part 4"&gt;&lt;/p&gt;
&lt;p&gt;Ouch. This is nasty. Again, we are not seeing much that is particularly useful. We have a long graph of retainers starting with the &lt;code&gt;compileNode&lt;/code&gt; function, we also have an array in a &lt;code&gt;n.fn.init&lt;/code&gt; function. To cut a long story short, we&amp;rsquo;re are not going to easily find the root cause here. But I will share some hints.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;jQuery isn&amp;rsquo;t leaking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We will end up seeing so much jQuery stuff it is natural to wonder whether jQuery is leaking. Almost certainly not. In the graph about &lt;code&gt;n.fn.init&lt;/code&gt; is just a jQuery selector, held onto by &lt;code&gt;$$element&lt;/code&gt;. No surprise - all angular elements are jQuery or jQuery light objects. We&amp;rsquo;ve leaked an element, it just happens to be wrapped in a jQuery selector. (You might see a different type of graph, probably due to the jQuery 1 + AngularJS 1.2 combination, we&amp;rsquo;ll see it later).&lt;/p&gt;
&lt;p&gt;You may see low level arrays containing data associated with a scope in jQuery, again, don&amp;rsquo;t worry. It&amp;rsquo;s the jQuery data cache (which we&amp;rsquo;ll also see later), which is associating elements to scopes.&lt;/p&gt;
&lt;p&gt;We can try and work through this graph, but let&amp;rsquo;s try another tack.&lt;/p&gt;
&lt;p&gt;It looks like we&amp;rsquo;re probably leaking the whole of the top rated view. We&amp;rsquo;re probably leaking the main scope for the view, created by the &lt;code&gt;TopRatedController&lt;/code&gt;. Let&amp;rsquo;s see if we can find it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can find objects you think are leaking by tagging them with classes!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is a neat trick. Let&amp;rsquo;s add a couple of lines to our top rated controller:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;app&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;TopRatedController&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$interval&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create a class, assign it to the scope. This&amp;#39;ll help us
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// see if $scope is leaked.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TopRatedControllerTag&lt;/span&gt;() {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;__tag&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TopRatedControllerTag&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// etc...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when we run the analysis again, we can search in the snapshot for &lt;code&gt;TopRatedControllerTag&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Part5.png" alt="Scenario 2 Part 5"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We search for &amp;lsquo;Tag&amp;rsquo;, finding one instance of the &lt;code&gt;TopRatedControllerTag&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Bingo - it is retained by a Scope, with id &lt;code&gt;@534851&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s look at this scope in more detail. Right click on it and choose &amp;lsquo;Review in Summary View&amp;rsquo;, so we can see what is retaining it:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Part6.png" alt="Scenario 2 Part 6"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We can now see the root scope for the actual view.&lt;/li&gt;
&lt;li&gt;We can see the usual pattern of &lt;code&gt;$$ChildScope&lt;/code&gt; and &lt;code&gt;$parent&lt;/code&gt; properties, but what else have we got?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Intestingly we can see that our scope is also retained by a &lt;strong&gt;context variable called $scope&lt;/strong&gt;. How do I know it is a context variable? It&amp;rsquo;s in blue, part of the colour coding (see Mystery 3).&lt;/p&gt;
&lt;p&gt;What is a context variable?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;closure&lt;/strong&gt; is a function which refers to a variable outside of its definition. A &lt;strong&gt;context variable&lt;/strong&gt; is the variable stored in a function context. A &lt;strong&gt;function context&lt;/strong&gt; contains the environment for a closure, which is the data required to execute it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So basically we have a closure which refers to a variable called &lt;code&gt;$scope&lt;/code&gt;, which is the root scope of our view. We can see in detail the closure:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/Scenario2Part7.png" alt="Scenario 2 Part 7"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;$scope&lt;/code&gt; is retained by a &lt;code&gt;context&lt;/code&gt; for a closure.&lt;/li&gt;
&lt;li&gt;The closure is in the &lt;code&gt;refresh&lt;/code&gt; function (this is why the &lt;code&gt;context&lt;/code&gt; is retained by &lt;code&gt;refresh&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We can open the function and examine it for issues. There&amp;rsquo;s an &lt;code&gt;$http.get&lt;/code&gt; which has as closure which uses &lt;code&gt;$scope&lt;/code&gt;, but alarmingly there is an &lt;code&gt;$interval&lt;/code&gt; registered to run every 10 seconds, which is never deregistered. The interval callback uses another &lt;code&gt;$http.get&lt;/code&gt;, with a closure that uses &lt;code&gt;$scope&lt;/code&gt;. This is the problem.&lt;/p&gt;
&lt;p&gt;A simple timeout we forgot to deregister has a closure on &lt;code&gt;$scope&lt;/code&gt;. &lt;code&gt;$scope&lt;/code&gt; can therefore never be cleaned up, because it is retained by a context.&lt;/p&gt;
&lt;p&gt;Some important takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The framework hides implementation details. Often useful, but in this case it made finding the leak a problem.&lt;/li&gt;
&lt;li&gt;This example seems contrived, but how how often do you have a closure using &lt;code&gt;$scope&lt;/code&gt; in a controller? In real world apps all of the time time, callbacks to ajax requests, event handlers, promise functions etc.&lt;/li&gt;
&lt;li&gt;A leak of a small object that contains the &lt;strong&gt;data&lt;/strong&gt; for three albums has leaked a &lt;strong&gt;large graph&lt;/strong&gt; of other objects, and even &lt;strong&gt;DOM elements&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Leaks are not incremental. You don&amp;rsquo;t get an accumulation of small leaks, one small leak can retain a huge graph.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s talk about this a bit more.&lt;/p&gt;
&lt;h3 id="dealing-with-object-graphs"&gt;Dealing with Object Graphs&lt;/h3&gt;
&lt;p&gt;We saw before that a chain of retainers can pin an object, such as a scope, to a GC root. We also saw that AngularJS scopes are part of a highly connected graph, meaning that if we leak part of it, we probably leak it all:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/ScopeLeakGraph1-1.png" alt="Scope Leak Graph 1"&gt;&lt;/p&gt;
&lt;p&gt;However, things can get worse. Remember how in an angular app you can get the scope for an element with &lt;code&gt;$(selector).scope()&lt;/code&gt;? This connection between a scope an an element is maintained in the jQuery data cache. This lets us associate arbitrary data with an element. This introduces another layer of connectivity:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/ScopeLeakGraph2.png" alt="Scope Leak Graph 2"&gt;&lt;/p&gt;
&lt;p&gt;In this graph, we see the jQuery data cache entries (in grey) associating DOM elements to scopes, introducing more connectivity.&lt;/p&gt;
&lt;p&gt;We can see here an alarming increase in the size and potential complexity of the graph. We&amp;rsquo;ve got DOM elements in play now. The chances are that if you are reading this you are dealing with a memory leak in your app, if it&amp;rsquo;s noticable enough for you to deal with it, you probably have a non-trivial graph.&lt;/p&gt;
&lt;p&gt;So how do we fix memory leaks? I&amp;rsquo;ll show three general approaches and how to use each one.&lt;/p&gt;
&lt;h2 id="fixing-memory-leaks"&gt;Fixing Memory Leaks&lt;/h2&gt;
&lt;p&gt;Fixing memory leaks is hard. As we have seen our problem is highly connected graphs. If we have a part of the graph we want to free for garbage collection (such as a scope and all of it&amp;rsquo;s children, such as a view or directive) then we must not retain that graph of objects. This means if you have (for example) three problems that lead to retaining a graph, you have to fix &lt;strong&gt;all of the problems&lt;/strong&gt; before the leak goes away.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s generalise the best practices first into three rules, see patterns we should follow for each of them and then look at anti-patterns to avoid.&lt;/p&gt;
&lt;h3 id="three-golden-rules"&gt;Three Golden Rules&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Rule 1: Understand the framework and lifecycle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you are using a framework like AngularJS, you &lt;strong&gt;must&lt;/strong&gt; understand the lifecycle of the objects you are dealing with. Unless you understand how the framework tries to clean up, you may make mistakes that stop it from working.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rule 2: Be careful at the interface between short and long lived objects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Whenever you see an interface between a short and long lived object, be extra careful. For example, if you have a directive talking to a service, make sure the service cannot retain the directive through closures, callbacks or any references. Services will last for the lifetime of the application, so they are the sort of object which can inadvertantly retain short lived objects.&lt;/p&gt;
&lt;p&gt;Other long lived objects exist but may be more subtle, the interface between AngularJS and other libraries can be a risky area, if other libraries maintain long lived state.&lt;/p&gt;
&lt;p&gt;Finally, consider this. The isolated scope for a directive (for example) may inadvertantly be long lived - if it is leaked. That leads us to Rule 3.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Rule 3: Disconnect the graph.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can be defensive by manually disconnecting graphs of objects. This can aid if you have a memory leak you cannot resolve. By disconnecting the graph, the garbage collector will at least be able to attempt to clean up parts of it.&lt;/p&gt;
&lt;p&gt;AngularJS should attempt to do this for you, for example when scopes are destroyed the links to other scopes are severed. But you can also do this yourself. Disconnecting the graph is not always as simple as emptying arrays or nulling objects, it can mean nulling closures and context variables too&lt;sup&gt;&lt;a href="#fn5" id="ref5"&gt;5&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;The anti-patterns which follow are all violations of these rules.&lt;/p&gt;
&lt;h3 id="anti-patterns-to-avoid"&gt;Anti-Patterns to Avoid&lt;/h3&gt;
&lt;p&gt;Whether or not your app is suffering from memory leaks, avoid these patterns.&lt;/p&gt;
&lt;h4 id="poorly-managed-event-handlers"&gt;Poorly Managed Event Handlers&lt;/h4&gt;
&lt;p&gt;Consider a trivial example in a directive link:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;attrs&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;selected&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We register an event handler. We&amp;rsquo;ve now built a closure which will have a context, which retains the &lt;code&gt;scope&lt;/code&gt;. If we don&amp;rsquo;t deregister this event handler, we retain the closure, the context, the scope, and then basically everything in the universe.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;attrs&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;selected&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;$destroy&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;off&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// deregister all event handlers
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; Angular &lt;em&gt;should&lt;/em&gt; handle this. It is supposed to deregister event handlers on elements it manages. In my experience this isn&amp;rsquo;t always the case, although it seems cases when this doesn&amp;rsquo;t happen are fewer and fewer as bugs get fixed in the framework. Anyway, Rule 3 - disconnect.&lt;/p&gt;
&lt;h4 id="poorly-managed-watchers"&gt;Poorly Managed Watchers&lt;/h4&gt;
&lt;p&gt;Watchers or angular event handlers, basically the same as above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;someEvent&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;refresh&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, Angular should clean this up if you forget to, but the advice is always do it yourself. Angular watchers return a deregister function.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cleanup&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;someEvent&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;refresh&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;$destroy&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cleanup&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Rule 1 - know the framework and how lifecycle is handled. &lt;code&gt;$destroy&lt;/code&gt; is sent to a scope specifically to allow it to be cleaned up.&lt;/p&gt;
&lt;h4 id="callback-functions-on-services"&gt;Callback Functions on Services&lt;/h4&gt;
&lt;p&gt;Services (or other long lived objects) should typically not take callback functions. Imagine a &amp;lsquo;user service&amp;rsquo;, allowing a scope to discover if the user has changed their name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;UserService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;onNameChange&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;newName&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;userName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;newName&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the service (a long lived object) takes a closure with a context to a short lived object, the scope. Unless the service is written absolutely correctly, we run the risk of the service retaining the scope. Remember, services are singletons and as such the interface between services and scopes is one that requires careful management.&lt;/p&gt;
&lt;p&gt;There are two fixes I would suggest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix 1: For a one-off operation, use a promise&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// change and name and wait for the result
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;UserService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;changeName&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Fry&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;newName&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;newName&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The notification service returns a promise (a short lived object) which holds the closure. If we get things wrong, we are less likely to leak the scope. Plus, promises are typically easy to work with once you&amp;rsquo;ve got the hang of them&lt;sup&gt;&lt;a href="#fn6" id="ref6"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix 2: For notifications, use broadcasts&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// more like our original example
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;NotificationService:ChangeName&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Some will say to not overuse broadcasts as they can be expensive. They can, so use them judiciously. But remember, they&amp;rsquo;re provided by the framework, typically lead to fairly loose coupling and are probably managing clean up as well or better than a hand-rolled mechanism in a service. Rule 2 - don&amp;rsquo;t tie short lived objects to long lived objects.&lt;/p&gt;
&lt;h2 id="the-future"&gt;The Future&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s a wrap. Hopefully this article will grow and improve with feedback from the community. To wind up, lets look at a few things that are on their way which will touch on these issues.&lt;/p&gt;
&lt;h3 id="weak-maps"&gt;Weak Maps&lt;/h3&gt;
&lt;p&gt;Finally, in ECMAScript 6 we will get a WeakMap&lt;sup&gt;&lt;a href="#fn7" id="ref7"&gt;7&lt;/a&gt;&lt;/sup&gt; object. This is &lt;em&gt;ideal&lt;/em&gt; for something like the jQuery data cache. A weak map uses weak references (not natively supported in JavaScript). This means that we can map a DOM element to a scope in a weak map, but the map entry doesn&amp;rsquo;t retain the element or scope. If the element or scope is cleaned up, the map entry is removed. This means internal structures to aid with frameworks don&amp;rsquo;t need to necessarily retain object graphs.&lt;/p&gt;
&lt;h3 id="angularjs-2"&gt;AngularJS 2&lt;/h3&gt;
&lt;p&gt;Simplifications to the framework in 2.0 and usage of native features like web components mean less complex framework code and less scope for issues. Consider even the usage of classes in Angular 2.0. We don&amp;rsquo;t decorate a scope object (of type &lt;code&gt;Object&lt;/code&gt;) we create an instance of a class. Easier to see in the heap view.&lt;/p&gt;
&lt;h3 id="even-better-browsers"&gt;Even Better Browsers&lt;/h3&gt;
&lt;p&gt;SPA frameworks are driving improvements to browsers. Frameworks like Angular lead to more SPAs. More SPAs mean we find more bugs and edge cases in browsers. Many memory leak issues in AngularJS have led to fixes in V8.&lt;/p&gt;
&lt;h2 id="appendices"&gt;Appendices&lt;/h2&gt;
&lt;p&gt;Beware any write up long enough to need appendices.&lt;/p&gt;
&lt;h3 id="thanks"&gt;Thanks&lt;/h3&gt;
&lt;p&gt;Much of my understanding here came from working with others on real-world issues. I would like to thank the following people for their advice and insights:&lt;/p&gt;
&lt;p&gt;James Denning, Shaun Bohannon, Arnaud Rebts, Colin Montgomery, Jon Hamshaw, Christian Lilley, Maarten De Wilde&lt;/p&gt;
&lt;p&gt;There are others I have worked on with in this area, if I have forgotten to mention you please let me know.&lt;/p&gt;
&lt;h3 id="mysteries"&gt;Mysteries&lt;/h3&gt;
&lt;p&gt;After a large amount of time spent investigating memory leaks, there are still some things which to me are a mystery. If anyone can shed some light, I&amp;rsquo;d be interested to know.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mystery 1: False Charts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As mentioned earlier we cannot always trust the timeline, it is not uncommon to see the memory usage in the timeline increase, even though the size of snapshots seems to be staying constant. This may be related to AngularJS Issue &lt;a href="https://github.com/angular/angular.js/issues/4864"&gt;DOM Nodes Leaking&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mystery 2: Odd Snapshot Sizes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is not uncommon for the first snapshot to be large, and then subsequent snapshots to all be a bit smaller (even without any state changes). Why this is the case I do not know. To test, run an angular app and take some snapshots without doing anything in between. You&amp;rsquo;ll normally see (for example) 9 MB, 9MB, 9MB. However, it is not uncommon to see 15 MB, 9MB, 9MB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mystery 3: Where&amp;rsquo;s the colour coding documentation?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Chrome documentation states that the colour coding key for elements in the heap snapshot is available in the tool. I can&amp;rsquo;t find it anywhere, so had to research to find the details.&lt;/p&gt;
&lt;h3 id="further-reading"&gt;Further Reading&lt;/h3&gt;
&lt;p&gt;Still not had enough? Try these.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/devtools/docs/heap-profiling#basics"&gt;Profiling Memory Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/devtools/docs/memory-analysis-101"&gt;Memory Analysis 101&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/devtools/docs/heap-profiling-containment"&gt;Heap profile containment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/devtools/docs/tips-and-tricks"&gt;Dev tools tips &amp;amp; tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.chrome.com/devtools/docs/javascript-memory-profiling"&gt;JavaScript Memory Profiling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management"&gt;Memory Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/"&gt;Taming the Unicorn&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sup id="fn1"&gt;1. This is C actually, but the syntax isn&amp;rsquo;t important, just the logic of what we&amp;rsquo;re doing.&lt;a href="#ref1"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn2"&gt;2. In JavaScript as in most managed languages, the mechanism by which this happens is reference counting and garbage collection. There&amp;rsquo;s a superb description at &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management"&gt;JavaScript Memory Management&lt;/a&gt;.&lt;a href="#ref2"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn3"&gt;3. Try it yourself with this &lt;a href="http://jsfiddle.net/dwmkerr/2LzxgLb4/"&gt;fiddle for a sawtooth pattern&lt;/a&gt;.&lt;a href="#ref3"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn4"&gt;4. Try it yourself with this &lt;a href="http://jsfiddle.net/dwmkerr/9dmpp5te/"&gt;fiddle for a &amp;lsquo;steps&amp;rsquo; pattern&lt;/a&gt;.&lt;a href="#ref4"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn5"&gt;5. See &lt;a href="https://github.com/dwmkerr/angular-modal-service/commit/79998ca98101798608bdb914aecbd44f3ccbaa7a"&gt;this commit&lt;/a&gt; in my Angular Modal Service for an example of how nulling context variables (i.e. disconnecting the graph) solved a memory leak. This is a good example of how are it can be, after large amounts of analysis I still haven&amp;rsquo;t discovered &lt;strong&gt;why&lt;/strong&gt; this was needed, but it solved the problem. It may relate to Mystery 4.&lt;a href="#ref5"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn6"&gt;6. See my article &lt;a href="http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/"&gt;Promises in AngularJS - The Definitive Guide&lt;/a&gt; if you are not sure how to use them.&lt;a href="#ref6"&gt;↩&lt;/a&gt;&lt;/sup&gt;
&lt;sup id="fn7"&gt;7. More details at &lt;a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap"&gt;https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap&lt;/a&gt;&lt;a href="#ref7"&gt;↩&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>The Only AngularJS Modal Service You'll Ever Need</title><link>https://dwmkerr.com/the-only-angularjs-modal-service-youll-ever-need/</link><pubDate>Mon, 16 Jun 2014 00:48:12 +0000</pubDate><guid>https://dwmkerr.com/the-only-angularjs-modal-service-youll-ever-need/</guid><description>&lt;p&gt;If you need modals in an AngularJS application, look no further. I&amp;rsquo;ll show you how to use the &lt;a href="https://github.com/dwmkerr/angular-modal-service"&gt;Angular Modal Service&lt;/a&gt; to add Bootstrap Modals or your own custom modals to your application.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jsfiddle.net/dwmkerr/8MVLJ/"&gt;See it in a fiddle&lt;/a&gt; or check out &lt;a href="http://dwmkerr.github.io/angular-modal-service"&gt;a full set of samples online&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="contents"&gt;Contents&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;[Using the Angular Modal Service](#UsingTheAngular ModalService)&lt;/li&gt;
&lt;li&gt;&lt;a href="#AQuickExample"&gt;A Quick Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#DesignGoals"&gt;Design Goals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#HowItWorks"&gt;How It Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#WrappingUp"&gt;Wrapping Up&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="using-the-angular-modal-service"&gt;Using the Angular Modal Service&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s how you can use the Angular Modal Service to add a bootstrap modal to your application.&lt;/p&gt;
&lt;h4 id="step-1-install-with-bower"&gt;Step 1: Install with Bower&lt;/h4&gt;
&lt;p&gt;Install the service with bower:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;bower install angular-modal-service --save
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you don&amp;rsquo;t use bower, just get the source directly from the &lt;a href="https://github.com/dwmkerr/angular-modal-service/tree/master/dst"&gt;&lt;code&gt;dst&lt;/code&gt;&lt;/a&gt; folder of the repo.&lt;/p&gt;
&lt;h4 id="step-2-include-the-javascript"&gt;Step 2: Include the JavaScript&lt;/h4&gt;
&lt;p&gt;Include the JavaScript from the &lt;code&gt;dst&lt;/code&gt; folder or require it with require.js:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;script&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;bower_components\angular-modal-service\dst\angular-modal-service.min.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="step-3-add-it-as-a-dependency"&gt;Step 3: Add it as a dependency&lt;/h4&gt;
&lt;p&gt;Make sure the &lt;code&gt;angularModalService&lt;/code&gt; module is listed as a required module for your application:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;app&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;myApp&amp;#39;&lt;/span&gt;, [&lt;span style="color:#e6db74"&gt;&amp;#39;angularModalService&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="step-4-show-the-modal"&gt;Step 4: Show the Modal&lt;/h4&gt;
&lt;p&gt;Inject &lt;code&gt;ModalService&lt;/code&gt; into any controller, directive or service and call the &lt;code&gt;showModal&lt;/code&gt; function to show a modal:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-language" data-lang="language"&gt;app.controller(&amp;#39;SampleController&amp;#39;, function($scope, ModalService) {
ModalService.showModal({
templateUrl: &amp;#34;template.html&amp;#34;,
controller: &amp;#34;ModalController&amp;#34;
}).then(function(modal) {
//it&amp;#39;s a bootstrap element, use &amp;#39;modal&amp;#39; to show it
modal.element.modal();
modal.close.then(function(result) {
console.log(result);
});
});
);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code loads the HTML from &lt;code&gt;template.html&lt;/code&gt;, adds it to the DOM, creates a scope for it and creates an instance of a &lt;code&gt;ModalController&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When this is done, the promise returned by the &lt;code&gt;showModal&lt;/code&gt; function resolves and you get a &lt;code&gt;modal&lt;/code&gt; object. This object contains the element created. If it&amp;rsquo;s a Bootstrap modal just call &lt;code&gt;modal&lt;/code&gt; to show it, if it&amp;rsquo;s a custom one you can show it by changing its CSS styles or using whatever APIs are provided. There&amp;rsquo;s an example ofa custom modal in &lt;a href="http://dwmkerr.github.io/angular-modal-service/"&gt;the samples&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id="step-5-close-the-modal"&gt;Step 5: Close the Modal&lt;/h4&gt;
&lt;p&gt;The controller that is created always has one extra parameter injected into it - a function called &lt;code&gt;close&lt;/code&gt;. Call this function to close the modal, anything you pass to it is passed to the caller as the &lt;code&gt;result&lt;/code&gt; object.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-language" data-lang="language"&gt;app.controller(&amp;#39;ModalController&amp;#39;, function($scope, close) {
// when you need to close the modal, call close
close(&amp;#34;Success!&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can pass a number of milliseconds to wait before destroying the DOM element as an optional second parameter to &lt;code&gt;close&lt;/code&gt; - this is useful if the closing of the modal is animated and you don&amp;rsquo;t want it to disappear before the animation completes.&lt;/p&gt;
&lt;h2 id="a-quick-example"&gt;A Quick Example&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a fiddle of the modal service in action:&lt;/p&gt;
&lt;iframe width="100%" height="300" src="http://jsfiddle.net/dwmkerr/8MVLJ/embedded/result,js,html" allowfullscreen="allowfullscreen" frameborder="0"&gt;&lt;/iframe&gt;
&lt;p&gt;One thing to note in this examples is that the template is just declared in the DOM - this works fine because the service always checks the template cache before attempting to load it from the server.&lt;/p&gt;
&lt;p&gt;There are more examples at &lt;a href="http://dwmkerr.github.io/angular-modal-service/"&gt;dwmkerr.github.io/angular-modal-service&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="design-goals"&gt;Design Goals&lt;/h2&gt;
&lt;p&gt;There are some other services for handling modals out there, notably &lt;a href="https://github.com/Fundoo-Solutions/angularjs-modal-service"&gt;Fundoo&amp;rsquo;s Modal Service&lt;/a&gt; and a few others. However, the design goals for my service were slightly different:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No link to bootstrap&lt;/strong&gt;. Bootstrap modals are complex with lots of options - if you want to use them then that&amp;rsquo;s great, the service should work with them, but the complexity of the options for Bootstrap Modals should not increase the complexity of the service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extremely simple code&lt;/strong&gt;. It&amp;rsquo;s rare you&amp;rsquo;ll write something that it will suit everyone&amp;rsquo;s need. Rather than trying to please everyone, I want a service that is simple enough to understand so that it can be easily adapted by others.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So the core goal here is simplicity - if others can understand the code, then they can more effectively decide whether it&amp;rsquo;s what they need, or build upon it.&lt;/p&gt;
&lt;p&gt;With these design goals in mind I built the angular modal service.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m going to walk through a slightly simplified version of the code because it actually illustrates quite a few important concepts when working with AngularJS.&lt;/p&gt;
&lt;p&gt;One of the things that&amp;rsquo;s useful to know is that this service creates a DOM element, builds a scope for it and instantiates a controller for it - what we&amp;rsquo;re doing is &lt;em&gt;very&lt;/em&gt; similar to what AngularJS does behind the scenes when a directive is created.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s dive in. We&amp;rsquo;re going to define a service, so we need a module.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;module&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;angularModalService&amp;#39;&lt;/span&gt;, []);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have our module, we can define our service. I tend to write services in the form of classes, but this is a personal choice - it&amp;rsquo;s just as valid to return a javascript object that contains functions and data.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;module&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;factory&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;ModalService&amp;#39;&lt;/span&gt;, [&lt;span style="color:#e6db74"&gt;&amp;#39;$document&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$compile&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$controller&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$http&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$rootScope&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$q&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;$timeout&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$document&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$compile&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$controller&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$rootScope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$timeout&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I need a lot of injected components, we&amp;rsquo;ll see why as we continue. I also use the explicit form of the function which takes the parameters as strings - this is the only safe way to write an injected function if you are minifying code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;body&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$document&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;find&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;body&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ModalService&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I use the &lt;code&gt;$document&lt;/code&gt; object to get the body element, which the modal will be appended to. I then create a class function and record &lt;code&gt;this&lt;/code&gt; as self, so that I can refer to the class instance in callbacks and so on.&lt;/p&gt;
&lt;p&gt;The next part of the code creates a function that will return the template, given either a raw template string or a template url. The reason we wrap this function like this is that the operation will either be synchronous or asynchronous, and I don&amp;rsquo;t want the caller to care. So we use promises to wrap the logic.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getTemplate&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;defer&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;({&lt;span style="color:#a6e22e"&gt;method&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;url&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;cache&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;No template or templateUrl has been specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If any of this seems confusing, check out my article &lt;a href="http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/"&gt;AngularJS Promises - The Definitive Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now to the main function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;showModal&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;defer&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;showModal&lt;/code&gt; function is going to have to do all sorts of async work - loading the template from the server and so on. So we are going to create a &lt;code&gt;deferred&lt;/code&gt; object and build a promise to return to the caller.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;No controller has been specified.&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we validate that a controller has been passed in as part of the options. Notice how just like in &lt;code&gt;getTemplate&lt;/code&gt; we use the &lt;code&gt;reject&lt;/code&gt; function to deal with error cases. Again, if error handling with promises seems unfamiliar, check out &lt;a href="http://www.dwmkerr.com/promises-in-angularjs-the-definitive-guide/"&gt;AngularJS Promises - The Definitive Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next we deal with the template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;getTemplate&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve used the &lt;code&gt;getTemplate&lt;/code&gt; function to get the template, sync or async it doesn&amp;rsquo;t matter, our logic is the same.&lt;/p&gt;
&lt;p&gt;Now we can build a new scope for our modal.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalScope&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$rootScope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$new&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll refer to this a lot later on. Now for some cleverness.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;closeDeferred&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;defer&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalScope&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;undefined&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$timeout&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;closeDeferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, &lt;span style="color:#a6e22e"&gt;delay&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This requires some explanation. First, we create a new &lt;code&gt;deferred&lt;/code&gt; object. This is going to be used to build a promise that is resolved when the modal closes.&lt;/p&gt;
&lt;p&gt;Now we build an &lt;code&gt;input&lt;/code&gt; object. This contains parameters we want to inject to the controller we&amp;rsquo;re going to create. Any parameters the controller needs, such as &lt;code&gt;$element&lt;/code&gt;, &lt;code&gt;$timeout&lt;/code&gt; or whatever will be injected by angular. We&amp;rsquo;re just going to make sure that the &lt;code&gt;$scope&lt;/code&gt; that is injected is the one we&amp;rsquo;ve just created, and that we also inject a function called &amp;lsquo;close&amp;rsquo;. This function simply resolves the promise we&amp;rsquo;ve created after a specified timeout.&lt;/p&gt;
&lt;p&gt;This means that any controller for a modal can take &lt;code&gt;close&lt;/code&gt; as a parameter, and we&amp;rsquo;ll inject the function that resolves the promise. This promise is returned to the consumer so that they can take action when the modal closes. We also allow the controller to pass a variable to &lt;code&gt;close&lt;/code&gt; which is passed to the &lt;code&gt;resolve&lt;/code&gt; function as well.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;inputName&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;inputName&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;options&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;inputName&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without the this code, the service is close to useless. What we do here is allow the caller to provide extra inputs to the controller. Imagine we have a list of items, maybe books for a library program, and when the use clicks on one we want to show a modal. The code that shows the modal needs to pass the selected book to the modal controller - by adding it to the &lt;code&gt;inputs&lt;/code&gt; object, the book can be injected into the controller. This allows to client to pass data &lt;strong&gt;to&lt;/strong&gt; the controller, with the parameter of the &lt;code&gt;close&lt;/code&gt; function used to return data &lt;strong&gt;from&lt;/strong&gt; the controller.&lt;/p&gt;
&lt;p&gt;Ready for some lower level Angular?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalController&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$controller&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;inputs&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalElementTemplate&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;angular&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;template&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;linkFn&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$compile&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;modalElementTemplate&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalElement&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;linkFn&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;modalScope&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Four innocuous lines that are actually quite complex.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, we create an instance of the controller with name &lt;code&gt;controller&lt;/code&gt;. Regardless of what AngularJS injects, we provide &lt;code&gt;inputs&lt;/code&gt; to be injected as well.&lt;/li&gt;
&lt;li&gt;Now we turn our raw template html into an AngularJS DOM element. AngularJS always works with jQuery or jQuery Lite elements, the &lt;code&gt;angular.element&lt;/code&gt; function takes raw HTML and turns it into a DOM element we can work with.&lt;/li&gt;
&lt;li&gt;Now we &lt;code&gt;$compile&lt;/code&gt; the element. This step goes over the DOM and expands all directives. We&amp;rsquo;re turning raw DOM elements into elements that are expanded into directives, but we haven&amp;rsquo;t yet linked this set of elements into a scope. This is the first step of the compile/link process.&lt;/li&gt;
&lt;li&gt;Finally, we can link the element. The &lt;code&gt;$compile&lt;/code&gt; function returns a link function which we call with a scope to link the DOM elements (fully expanded) to the specified scope.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is very similar to AngularJS actually handles directives itself - creating a scope, loading a template, turning it into an element, compiling it and linking it.&lt;/p&gt;
&lt;p&gt;Why are compile and link separate steps? Think of it like this, the work that is done in compile is actually identical for each instance of a directive (or modal in our case). It&amp;rsquo;s not related to an &lt;em&gt;instance&lt;/em&gt; of a directive or modal, it&amp;rsquo;s just expanding the elements and directives. So this work can be done once only, saving a lot of time - then we just call link to create an &lt;em&gt;instance&lt;/em&gt; of our element, bound to a specific scope. So link logic is always per instance (you have a scope, you can &lt;code&gt;$watch&lt;/code&gt; and so on) whereas compile logic is per &lt;em&gt;type&lt;/em&gt; of directive.&lt;/p&gt;
&lt;p&gt;Based on this, we could in fact cache the results of the compile function on a per-template basis, as they can be reused and linked to a scope as necessary. However this is an optimisation that is currently left out.&lt;/p&gt;
&lt;p&gt;Now we can add the fully built element to the DOM and build our return object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;body&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;append&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;modalElement&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modal&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalController&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;scope&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalScope&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;element&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;modalElement&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;closeDeferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We return the four things the caller might need - the controller, scope, element and close promise. When the close promise is resolved, we also want to clean up:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;modal&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;close&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;modalScope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;$destroy&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;modalElement&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;modal&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So when &lt;code&gt;close&lt;/code&gt; is resolved, whatever happens we&amp;rsquo;ll destroy the scope and clean up the DOM. Now we can resolve our promise with the &lt;code&gt;modal&lt;/code&gt; object we&amp;rsquo;ve built&amp;hellip;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;and we can pass errors that occured during &lt;code&gt;getTemplate&lt;/code&gt; to the caller and finally return the promise we&amp;rsquo;ve built.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! With this design we handle errors correctly, can pass data to and from the modal, clean up after ourselves and make sure that units of asynchronous work are handled with the standard pattern of promises.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;I hope you&amp;rsquo;ve found the service and some of the details of the code useful, as always comments are welcome, fork the code and have a play - let me know if you think of improvements or have questions,&lt;/p&gt;</description><category>CodeProject</category></item><item><title>AngularJS Promises - The Definitive Guide</title><link>https://dwmkerr.com/promises-in-angularjs-the-definitive-guide/</link><pubDate>Wed, 07 May 2014 12:06:55 +0000</pubDate><guid>https://dwmkerr.com/promises-in-angularjs-the-definitive-guide/</guid><description>&lt;p&gt;Promises are a core feature of AngularJS - whether you understand them or not, if you use AngularJS you&amp;rsquo;ve almost certainly been using them for a while.&lt;/p&gt;
&lt;p&gt;In this post I&amp;rsquo;m going to explain what promises are, how they work, where they&amp;rsquo;re used and finally how to use them effectively.&lt;/p&gt;
&lt;p&gt;Once we&amp;rsquo;ve got the core understanding of promises, we&amp;rsquo;ll look at some more advanced functionality - chaining and resolving promises when routing.&lt;/p&gt;
&lt;h4 id="contents"&gt;Contents&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="#whatarepromises"&gt;What are Promises?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#howdopromiseswork"&gt;How do Promises Work?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#arealworldexample"&gt;A Real World Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#promisessuccesserrorthen"&gt;Promises - Success, Error, Then&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#advancedpromiseschaining"&gt;Advanced Promises - Chaining&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#advancedpromisesrouting"&gt;Advanced Promises - Routing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#advancedpromisestipstricks"&gt;Advanced Promises - Tips &amp;amp; Tricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#thefutureofpromises"&gt;The Future of Promises&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#wrappingup"&gt;Wrapping Up&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="what-are-promises"&gt;What are Promises?&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m going to try and be as succinct as possible - if anyone has a shorter, clearer description, let me know!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A promise represents the eventual result of an operation. You can use a promise to specify what to do when an operation eventually succeeds or fails.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So let&amp;rsquo;s see this in action. Look at the code below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This code uses the &lt;code&gt;$http&lt;/code&gt; service to perform an HTTP GET on the url &amp;lsquo;/api/my/name&amp;rsquo;. Let&amp;rsquo;s say that this is an api we&amp;rsquo;ve implemented on our server that returns the name of the logged in user.&lt;/p&gt;
&lt;p&gt;Now a common mistake for JavaScript newcomers might be to assume that the function returns the name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// The WRONG way!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It doesn&amp;rsquo;t - and in fact it can&amp;rsquo;t. An HTTP request has to be executed, it&amp;rsquo;ll take a while before it returns - it might not return at all if there are errors. Remember, when we make requests in JavaScript we&amp;rsquo;re using &lt;strong&gt;ajax&lt;/strong&gt; which is &lt;em&gt;&lt;strong&gt;asynchronous&lt;/strong&gt; javascript and xml&lt;/em&gt;. The key word here is asynchronous - we return control to the browser, let it make a request and give it a function to call when the request completes.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s see how you actually make the request.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Your name is: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The request failed with response &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; and status code &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we use the promise object to specify what to do when the request succeeds, or when it fails. Remember, the functions we pass to &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; will be called later - when this block is finished executing we don&amp;rsquo;t have the name, we&amp;rsquo;ve just specified what to do when we &lt;em&gt;do&lt;/em&gt; eventually get it - or what to do if we fail to get it.&lt;/p&gt;
&lt;p&gt;As a convenience, the &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; functions actually just return the promise, so we can simplify the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Your name is: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The request failed with response &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; and status code &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In fact, &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; are special functions added to a promise by &lt;code&gt;$http&lt;/code&gt; - normally with promises we just use &lt;code&gt;then&lt;/code&gt;, which takes the success function as the first parameter and the error function as the second:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* success */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Your name is: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* failure */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The request failed: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ll see more about the difference between &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;then&lt;/code&gt; later.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all there is to it - a promise lets us specify what to do as the result of an operation.&lt;/p&gt;
&lt;h2 id="how-do-promises-work"&gt;How do Promises Work?&lt;/h2&gt;
&lt;p&gt;Promises are not actually complicated, they&amp;rsquo;re objects that contain a reference to functions to call when something fails or succeeds.&lt;/p&gt;
&lt;p&gt;Under the hood, AngularJS actually wires up a promise for an HTTP request in a way a bit like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;request&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;XMLHttpRequest&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;addEventListener&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;load&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// complete the promise
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}, &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;addEventListener&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// fail the promise
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}, &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;open&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;request&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;this is pseudo-code, but the idea is that its the browser that calls us back, via the event listeners, then AngularJS can just call the appropriate method on the promise.&lt;/p&gt;
&lt;p&gt;Now in AngularJS, the promises are created with the &lt;code&gt;$q&lt;/code&gt; service (we&amp;rsquo;ll see exactly how to do this shortly), but why &lt;code&gt;$q&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The reason the service is named &lt;code&gt;$q&lt;/code&gt; is that AngularJS&amp;rsquo; promise implementation is based on Kris Kowal&amp;rsquo;s promise mechanism, which is called &amp;lsquo;Q&amp;rsquo;. You can see the library at &lt;a href="https://github.com/kriskowal/q"&gt;github.com/kristkowal/q&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This was a deliberate decision, as the Q library is widely used and well understood by the community. We&amp;rsquo;re going to see a little bit later what the future of promises is in AngularJS and actually in ECMAScript 6.&lt;/p&gt;
&lt;h3 id="a-real-world-example"&gt;A Real World Example&lt;/h3&gt;
&lt;p&gt;In this example we&amp;rsquo;ll create a service that gets the user&amp;rsquo;s name, just like in our examples. However, to make it interesting, we&amp;rsquo;ll set our service up so that the first time we get the name from the server, and then afterwards we&amp;rsquo;ll return a cached copy.&lt;/p&gt;
&lt;p&gt;This means we&amp;rsquo;ll have to build our code to deal with the asynchronous case (the first one) and the more trivial synchronous case (getting the name from the cache).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at a pure asynchronous implementation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;factory&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;NameService&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create a class that represents our name service.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// getName returns a promise which when
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// fulfilled returns the name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/my/name&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s how it looks in a fiddle - just click &amp;lsquo;Result&amp;rsquo; to see it working. You can click on &amp;lsquo;Update&amp;rsquo; name to get the name, but each time it sends a request. This is what we&amp;rsquo;ll change next.&lt;/p&gt;
&lt;iframe width="100%" height="300" src="http://jsfiddle.net/dwmkerr/4GjtR/embedded/js,html,result" allowfullscreen="allowfullscreen" frameborder="0"&gt;&lt;/iframe&gt;
&lt;p&gt;Now let&amp;rsquo;s update our service so that we hit the server only if we haven&amp;rsquo;t already cached the name. I&amp;rsquo;ll build the service blow by blow, then we can see a fiddle of it working.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;factory&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;NameService&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create a class that represents our name service.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Initially the name is unknown....
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;so first we create a service which is in the form of a class. It has a name field which is initially null.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create a deferred operation.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$q&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;defer&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in the &lt;code&gt;getName&lt;/code&gt; function we start by creating a &lt;code&gt;deferred&lt;/code&gt; object, using the &lt;code&gt;$q&lt;/code&gt; service. This object contains the promise we&amp;rsquo;ll return, and has some helper functions to let us build the promise.&lt;/p&gt;
&lt;p&gt;We create a deferred object because whether we use ajax or not, we want the consumer to use the promise - even if we &lt;em&gt;can&lt;/em&gt; return straightaway in some circumstances (when we have the name) we can&amp;rsquo;t in all - so the caller must always expect a promise.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;!==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; (from Cache!)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If we already have the name, we can just &lt;code&gt;resolve&lt;/code&gt; the deferred object immediately - this is the easy case. I&amp;rsquo;ve added &amp;lsquo;from cache&amp;rsquo; to the name so we can see when it comes from the cache compared to the server.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; You can resolve a promise even before you return it. It still works fine for the consumer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, we can handle the case if we don&amp;rsquo;t already have the name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Get the name from the server.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/my/name/&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;self&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; (from Server!)&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;reject&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if we get success from the server, we can &lt;code&gt;resolve&lt;/code&gt; the promise. Otherwise, we &lt;code&gt;reject&lt;/code&gt; it, which means failure.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Call &lt;code&gt;resolve&lt;/code&gt; on a deferred object to complete it successfully, call &lt;code&gt;reject&lt;/code&gt; to fail it with an error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, we just return the promise we&amp;rsquo;ve built with &lt;code&gt;deferred&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;deferred&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And that&amp;rsquo;s it! You can see it in action below, press &amp;lsquo;Update Name&amp;rsquo; a few times and you&amp;rsquo;ll see it uses the cache.&lt;/p&gt;
&lt;iframe width="100%" height="300" src="http://jsfiddle.net/dwmkerr/LeZU4/embedded/result,html,js" allowfullscreen="allowfullscreen" frameborder="0"&gt;&lt;/iframe&gt;
&lt;p&gt;How do we use this? We&amp;rsquo;ll it&amp;rsquo;s simple, here&amp;rsquo;s a controller that uses the service we&amp;rsquo;ve built:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;MainController&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// We have a name on the code, but it&amp;#39;s initially empty...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// We have a function on the scope that can update the name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;updateName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* success function */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* error function */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Failed to get the name, result is &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now there&amp;rsquo;s something different here. Before, we might have used the &lt;code&gt;error&lt;/code&gt; or &lt;code&gt;success&lt;/code&gt; function of the promise. But here we use &lt;code&gt;then&lt;/code&gt;. Why is that?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;success&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; are functions on a promise that AngularJS adds for us when using &lt;code&gt;$http&lt;/code&gt; or &lt;code&gt;$resource&lt;/code&gt;. They&amp;rsquo;re not standard, you won&amp;rsquo;t find them on other promises.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So we&amp;rsquo;ve seen how promises work, what they are and so on, now we&amp;rsquo;ll look into this success/error/then stuff.&lt;/p&gt;
&lt;h2 id="promises---success-error-then"&gt;Promises - Success, Error, Then&lt;/h2&gt;
&lt;p&gt;Now we know that &lt;code&gt;$http&lt;/code&gt; returns a promise, and we know that we can call &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; on that promise. It would be sensible to think that these functions are a standard part of promise - but they&amp;rsquo;re not!&lt;/p&gt;
&lt;p&gt;When you are using a promise, the function you should call is &lt;code&gt;then&lt;/code&gt;. &lt;code&gt;then&lt;/code&gt; takes two parameters - a callback function for success and a callback function for failure. Taking a look at our original &lt;code&gt;$http&lt;/code&gt; example, we can rewrite it to use this function.
So this code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Your name is: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The request failed with response &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34; and status code &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;becomes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/api/my/name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Your name is: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The request failed: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We &lt;strong&gt;can&lt;/strong&gt; use &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; when using &lt;code&gt;$http&lt;/code&gt; - it&amp;rsquo;s convenient. For one thing, the &lt;code&gt;error&lt;/code&gt; function gives us a response and status (and more) and the &lt;code&gt;success&lt;/code&gt; function gives us the response data (rather than the full response object).&lt;/p&gt;
&lt;p&gt;But remember that it&amp;rsquo;s not a standard part of a promise. You can can add your own versions of these functions to promises you build yourself if you want:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;success&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fn&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fn&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;headers&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;fn&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fn&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;headers&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;promise&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;this is exactly how angular does it.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s the advice?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; with &lt;code&gt;$http&lt;/code&gt; promises if you want to - just remember they&amp;rsquo;re not standard, and the parameters are different to those for &lt;code&gt;that&lt;/code&gt; callbacks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So if you change your code so that your promise is not returned from &lt;code&gt;$http&lt;/code&gt;, as we did in the earlier example when we load data from a cache, your code will break if you expect &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; to be there.&lt;/p&gt;
&lt;p&gt;A safe approach is to use &lt;code&gt;then&lt;/code&gt; wherever possible.&lt;/p&gt;
&lt;h2 id="advanced-promises---chaining"&gt;Advanced Promises - Chaining&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;ve had your fill of promises for now, you can skip to &lt;a href="#thefutureofpromises"&gt;The Future of Promises&lt;/a&gt; or &lt;a href="#wrappingup"&gt;Wrapping Up&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One useful aspect of promises is that the &lt;code&gt;then&lt;/code&gt; function returns the promise itself. This means that you can actually &lt;em&gt;chain&lt;/em&gt; promises, to create conscise blocks of logic that are executed at the appropriate times, without lots of nesting.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s consider an example where we need to fetch the user&amp;rsquo;s name from the backend, but we have to use separate requests to get their profile information and then their application permissions.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;permissions&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/user/name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the username, get the profile.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/profile/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the profile, now get the permissions.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/security/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the permissions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;permissions&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The full user details are: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have a series of asynchronous calls that we can coordinate without having lots of nested callbacks.&lt;/p&gt;
&lt;p&gt;We can also greatly simplify error handling - let&amp;rsquo;s see the example again, with an exception thrown in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/user/name&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the username, get the profile.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$http&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/profile/&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;username&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the profile, now get the permissions.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;throw&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Oh no! Something failed!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Store the permissions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;permissions&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;response&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;The full user details are: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;JSON&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;stringify&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;details&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;An error occured: &amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can use &lt;code&gt;catch(callback)&lt;/code&gt; - which is actually just shorthand for &lt;code&gt;then(null, callback)&lt;/code&gt;. There&amp;rsquo;s even a &lt;code&gt;finally&lt;/code&gt; - which is executed whether or not the operations fail or succeed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use &lt;code&gt;catch&lt;/code&gt; and for error handling with promises - and use &lt;code&gt;finally&lt;/code&gt; for logic that&amp;rsquo;s executed after success OR failure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The composition of promises can simplify complicated code - particularly when you add in error handling!&lt;/p&gt;
&lt;p&gt;One final point to make which is not quite related to chaining but does relate to multiple promises is &lt;code&gt;$q.all&lt;/code&gt;. &lt;code&gt;all&lt;/code&gt; can be used to build a single promise from a set of promises.&lt;/p&gt;
&lt;p&gt;You can pass an array of promises to &lt;code&gt;all&lt;/code&gt; and you get back a single promise - which is resolved when all of the promises it contains resolve. This can be useful if you are building complex methods that may have to perform multiple asynchronous tasks - such as multiple ajax calls.&lt;/p&gt;
&lt;h2 id="advanced-promises---routing"&gt;Advanced Promises - Routing&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a particular area of AngularJS that uses promises to great effect, and that&amp;rsquo;s the router.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s imagine we have a router like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$routeProvider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;home.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;MainController&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/profile&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;profile.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ProfileController&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we have two routes. The home route takes us to the home page, with the &lt;code&gt;MainController&lt;/code&gt;, and the profile route takes us to the user&amp;rsquo;s profile page.&lt;/p&gt;
&lt;p&gt;Our ProfileController uses our funky name service:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;ProfileController&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The problem is, &lt;strong&gt;until the name service gets the name from the backend, the name is null&lt;/strong&gt;. This means if our view binds to the name, it&amp;rsquo;ll flicker - first it&amp;rsquo;s empty then its set.&lt;/p&gt;
&lt;p&gt;What we&amp;rsquo;d like to do is actully say to the router - &amp;ldquo;I&amp;rsquo;m going to go to this view, but only when you can tell me my name&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;We can do this with the &lt;em&gt;resolves&lt;/em&gt; in the router, here&amp;rsquo;s how it works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Create a function that uses the NameService
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// to return the getName promise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NameService&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$routeProvider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/home.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;MainController&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/profile&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/profile.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ProfileController&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* only navigate when we&amp;#39;ve resolved these promises */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;so now we have a &lt;em&gt;resolve&lt;/em&gt; on the route - when we go to the profile page the router will wait until the promise returned by &lt;code&gt;getName&lt;/code&gt; resolves, then it will pass the result into the controller, as the parameter called &lt;code&gt;name&lt;/code&gt;. Now our controller looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;app&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;ProfileController&amp;#39;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$scope&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Much better! And also &lt;strong&gt;much&lt;/strong&gt; more testable.&lt;/p&gt;
&lt;p&gt;One thing you may wonder - why do I use &lt;code&gt;getName&lt;/code&gt; as the resolve function instead of just using &lt;code&gt;NameService.getName&lt;/code&gt; directly?&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s because the route is set up in a &lt;code&gt;config&lt;/code&gt; function - and that function cannot have services injected. However, a resolve function &lt;strong&gt;can&lt;/strong&gt;, so we just use a function and let AngularJS inject the &lt;code&gt;NameService&lt;/code&gt; for us.&lt;/p&gt;
&lt;p&gt;Now for an important statement:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the first thing your controller does is fetch data from the server, it&amp;rsquo;s probably wrong.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Why? Because if your controller needs data, inject it - let the router ensure the data is ready. Then you don&amp;rsquo;t have controllers in an invalid state as they&amp;rsquo;re loading - and your controllers become easier to test.&lt;/p&gt;
&lt;p&gt;Be aware of &lt;code&gt;resolve&lt;/code&gt; for routes - it&amp;rsquo;s a great way to handle loading of required data, authentication and other things that you might be putting into the wrong place.&lt;/p&gt;
&lt;p&gt;You can see the example above in action here:&lt;/p&gt;
&lt;iframe width="100%" height="300" src="http://jsfiddle.net/dwmkerr/m29pe/embedded/result,js,html" allowfullscreen="allowfullscreen" frameborder="0"&gt;&lt;/iframe&gt;
&lt;p&gt;What&amp;rsquo;s cool is we can also see our caching logic by going to and from the Home and Profile pages. The promises are keeping our code clean and testable.&lt;/p&gt;
&lt;p&gt;As a final note on promises when routing, you can specify multiple resolves if you need to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$routeProvider&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;when&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/profile&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;templateUrl&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;/profile.html&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;controller&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;ProfileController&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* only navigate when we&amp;#39;ve resolved these promises */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolve&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getName&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getProfile&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;anythingElse&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getAnythingElse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;in this case each resolve is injected into the controller.&lt;/p&gt;
&lt;h2 id="advanced-promises---tips--tricks"&gt;Advanced Promises - Tips &amp;amp; Tricks&lt;/h2&gt;
&lt;p&gt;This section just contains some tips and tricks you might find useful when working with promises.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Promises in directives are not resolved automatically since AngularJS 1.2. Previously, if you passed a promise to a directive with an &amp;lsquo;=&amp;rsquo; binding, AngularJS would resolve the promise for you, this is no longer the case.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="the-future-of-promises"&gt;The Future of Promises&lt;/h2&gt;
&lt;p&gt;So promises are a core part of AngularJS and to use the framework effectively, you must understand how to use them and how they work. But what is the future of promises?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s almost certain that promises are going to become a &lt;strong&gt;native&lt;/strong&gt; feature of JavaScript, they are part of the proposed ECMAScript 6 specification.&lt;/p&gt;
&lt;p&gt;The functionality of the &lt;code&gt;q&lt;/code&gt; library and AngularJS&amp;rsquo; implementation of promises are very similar indeed to the proposed specification, but be aware that when promises become standard, AngularJS is most likely to adapt their own promises to work like native promises.&lt;/p&gt;
&lt;p&gt;You can read more at &lt;a href="http://www.html5rocks.com/en/tutorials/es6/promises/"&gt;html5rocks.com/en/tutorials/es6/promises/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Just be aware that you&amp;rsquo;ll see promises more and more, in other frameworks and in vanilla JavaScript.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;I hope this post has been useful to understanding promises. Any feedback is always good, so let me know if anything is unclear or could be improved. To finish this article, here are some useful links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Q library&lt;/strong&gt; &lt;a href="https://github.com/kriskowal/q"&gt;github.com/kriskowal/q&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The AngularJS &lt;code&gt;$q&lt;/code&gt; Service&lt;/strong&gt; &lt;a href="https://docs.angularjs.org/api/ng/service/$q"&gt;docs.angularjs.org/api/ng/service/$q&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Promises in ECMAScript 6&lt;/strong&gt; &lt;a href="http://www.html5rocks.com/en/tutorials/es6/promises/"&gt;html5rocks.com/en/tutorials/es6/promises/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;XmlHttpRequest, which we used in an example&lt;/strong&gt; &lt;a href="https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest"&gt;developer.mozilla.org/en/docs/Web/API/XMLHttpRequest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And also some interesting discussions:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://spion.github.io/posts/why-i-am-switching-to-promises.html"&gt;Why I am switching to promises&lt;/a&gt; - Written by Gorgi Kosev, great article describing why a switch from callbacks to promises can be a very good thing in NodeJS applications.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/"&gt;Callbacks, sychronous and asynchronous&lt;/a&gt; - From Havoc, this post contains many useful points for API writers who are using callbacks or promises. One key takeaway is to &lt;strong&gt;never&lt;/strong&gt; do what a sample in this article does which is resolve a promise either synchronously or asynchronously, as it leads to code which can be difficult to reason about. I&amp;rsquo;ll be mentioning this more in a later update which will explain the problem and solution.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony"&gt;Designing for Asynchrony&lt;/a&gt; - Written by Isaac Z. Schlueter, this post is another great one for API designers that takes a look into asynchrony.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Better Specifications</title><link>https://dwmkerr.com/better-specifications/</link><pubDate>Mon, 05 May 2014 14:58:43 +0000</pubDate><guid>https://dwmkerr.com/better-specifications/</guid><description>&lt;p&gt;Specifications are absolutely key to the success of a project.&lt;/p&gt;
&lt;p&gt;Unless you have a good definition of what your project is &lt;em&gt;supposed to be&lt;/em&gt;, there&amp;rsquo;s no way you can deliver it. A specification is the contract between you and the client, the basis for technical designs, quality assurance test plans, operational readiness, and much more.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to talk about how different teams do specs, what works and what doesn&amp;rsquo;t work. I&amp;rsquo;m going to make the statement that &lt;strong&gt;the better that specifications are handled, understood and controlled, the better for everyone&lt;/strong&gt; - Project Managers, developers, testers, operational teams and customers.&lt;/p&gt;
&lt;p&gt;If there&amp;rsquo;s anyone who&amp;rsquo;s on the fence about whether specifications are important or not, &lt;a href="http://www.joelonsoftware.com/articles/fog0000000036.html"&gt;Joel Spolsky&amp;rsquo;s series on Painless Specifications&lt;/a&gt; is a must read, he makes the point exceedingly well and I&amp;rsquo;d definitely recommend reading the articles.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to describe an approach to functional specifications that I think has a lot going for it. To developers it should look very familiar and (hopefully) appealing. To non-developers, the approach may not be so familiar, but the potential benefits should speak for themselves.&lt;/p&gt;
&lt;h3 id="specs-right-now"&gt;Specs Right Now&lt;/h3&gt;
&lt;p&gt;OK - the introductions have been made. So how do we represent specs at the moment? Here are some common ways.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Text files&lt;/strong&gt;. Specs are text? Well, sometimes. They might also be images, sometimes diagrams help too. But text can work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Word documents&lt;/strong&gt;. A little like the above, but with the facility to use tables and images and so on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web Pages&lt;/strong&gt;. Perhaps more common nowadays, specs can be written on online systems like Sharepoint (just documents really, but with better multi-user support), Google Docs or Bug Tracking/Wiki type systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BDD Tools&lt;/strong&gt;. Whether using tools or not, BDD specs are files that state functionality in a more strongly defined format.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests&lt;/strong&gt;. Kind of similar to the above - depending on what you&amp;rsquo;re writing or the tech expertise of team members, some teams opt for the bulk of their specs in the form of tests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of these systems have pros and cons. Some have many more cons, and some balance out better on different types of projects.&lt;/p&gt;
&lt;h2 id="a-better-way"&gt;A Better Way&lt;/h2&gt;
&lt;p&gt;So here&amp;rsquo;s a suggestion of a better way.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Specs are markdown files in a repository. That&amp;rsquo;s it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now I&amp;rsquo;m going to show you why this is a good idea.&lt;/p&gt;
&lt;h3 id="specs-are-text-ish"&gt;Specs are text. Ish.&lt;/h3&gt;
&lt;p&gt;Pure plain text is a bit hard for specs - it can be helpful to have bullet points, headings and so on. But word processing tools are not always the best answer - you&amp;rsquo;re tied to a specific tool, and unless you&amp;rsquo;re working with people who all know how styles, track changes or the specifics of the tool work, you can spend more time messing around with formatting than you&amp;rsquo;d like.&lt;/p&gt;
&lt;p&gt;Try comparing and rationalising two versions of a complex document that have gone out of sync, it&amp;rsquo;s a nightmare.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a snippet of a markdown spec:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Overview
--------
This spec is for the login screen of the application. The overall design of
the screen is in the file &amp;#39;mockup.png&amp;#39;.
The Controls
------------
* The &amp;#39;username&amp;#39; box accepts alphanumeric characters.
* The limit of the &amp;#39;username&amp;#39; box is 120 characters.
* The tooltip for the username box is &amp;#39;Enter Username&amp;#39;.
* The password box accepts any characters and is limited to 120 characters.
* The tooltip for the password box is &amp;#39;Enter password&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&amp;rsquo;s readable in it&amp;rsquo;s raw form. This is how it looks on GitHub:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LoginScreenSpec.png" alt="Login Screen Spec"&gt;&lt;/p&gt;
&lt;p&gt;See the real thing at &lt;a href="https://github.com/dwmkerr/better-specs/blob/master/login/login.md"&gt;github.com/dwmkerr/better-specs/blob/master/login/login.md&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;(These specs are just made up ones for this article).&lt;/p&gt;
&lt;p&gt;The formatting is readable even in plain text mode, but many modern day tools understand how to show markdown in a slightly nicer way (I&amp;rsquo;m writing this post in markdown - &lt;a href="https://ghost.org/"&gt;Ghost&lt;/a&gt; uses markdown for blog posts). You can print it and understand it, email it to recipients in plain text mode, and importantly there&amp;rsquo;s no fiddling with formatting.&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t used markdown before, give it a try on &lt;a href="https://stackedit.io/"&gt;StackEdit&lt;/a&gt;, it&amp;rsquo;s an online markdown editor - you can have a play with it.&lt;/p&gt;
&lt;h3 id="specs-are-version-controlled"&gt;Specs are Version Controlled&lt;/h3&gt;
&lt;p&gt;Specs need to be version controlled. You need to be able to see a history of a spec, who changed it, when they changed it and what the change was.&lt;/p&gt;
&lt;p&gt;Even more useful - you should be able to diff specs - what changed between two versions? This is hard to do with programs like word, with version control systems like git, it&amp;rsquo;s a piece of cake. Take a look:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LoginScreenDiff.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;We can see easily who changed this spec and when, we can compare revisions.&lt;/p&gt;
&lt;p&gt;This not just a powerful feature - it&amp;rsquo;s a required one. Developers and testers can see what has been added, removed or modified. So can project managers.&lt;/p&gt;
&lt;p&gt;This is a diff that developers will understand - the red and green make it clear for non-devs too. Modern systems like GitHub can take it further:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LoginNiceDiff.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Pretty clear what&amp;rsquo;s going on.&lt;/p&gt;
&lt;h3 id="pull-requests"&gt;Pull Requests&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s take the version control aspect further. A spec should have one owner, one person who can press the button and say &amp;lsquo;yes, this is in&amp;rsquo;. There needs to be one owner, because a change to a spec is like a change to a contract - it could represent hours of developer time, cost increases, release date issues and more. There&amp;rsquo;s impact, there are consequences.&lt;/p&gt;
&lt;p&gt;So using services like GitHub or Bitbucket can help here. Anyone can make changes to a spec, but a single approver has to review those changes and decide whether to bring them in.&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LockoutLogic.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/better-specs/pull/1/files?short_path=d645d03#diff-d645d03c7459b43c1e030eeb13d1245b"&gt;See this pull request on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pull requests support comments, discussions, diffs and all sorts of useful things. No one can get something into the spec until the approver has OK&amp;rsquo;d it.&lt;/p&gt;
&lt;p&gt;It also gives the approver a single point where they can action the changes, perhaps raising a task for a developer to work on the changes.&lt;/p&gt;
&lt;p&gt;That pull request can also be &lt;em&gt;linked to&lt;/em&gt; later - in the bug tracking or feature management system, an issue to implement the changes in the pull request can be made - and the implementer always has a link to the &lt;em&gt;exact&lt;/em&gt; change.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t put the change to a spec in a work item. A work item &lt;em&gt;refers&lt;/em&gt; to a change in the spec.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is very useful. In the pull request shown (which you can &lt;a href="https://github.com/dwmkerr/better-specs/pull/1/files?short_path=d645d03#diff-d645d03c7459b43c1e030eeb13d1245b"&gt;see here&lt;/a&gt;) has one innocuous extra line (which we see in green) stating that the user is locked out after three failed password attempts. This isn&amp;rsquo;t in the spec, not until we accept the pull request. And in this case instead we can comment to the person who raised it:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LoginPullReqComments.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I had to make a contrived example and fake a discussion between myself and myself, but I think seeing how this looks helps make the point.&lt;/p&gt;
&lt;h3 id="branches"&gt;Branches&lt;/h3&gt;
&lt;p&gt;Another great thing about keeping specs as plain text and using a version control system is that you can take advantage of branching. This means that different people can be working on proposed changes to specs or new features in their own isolated branches - the work they&amp;rsquo;re doing won&amp;rsquo;t be interfered with by others.&lt;/p&gt;
&lt;p&gt;When the time comes to bring the proposal into the spec, again a pull request can be made and the coordinator can deal with any conflicts that may have arisen since the branch was made.&lt;/p&gt;
&lt;p&gt;At the time the BA or whoever it might be is working on their specs, they still have full version control, a history and so on, but are not interfering with the &amp;lsquo;master&amp;rsquo; version of the specs that others in the project see.&lt;/p&gt;
&lt;h3 id="what-about-rich-media"&gt;What About Rich Media?&lt;/h3&gt;
&lt;p&gt;Keep your rich media. Keep images, tables and diagrams - just check them in alongside the spec and link to them from the spec. You still get version control, pull requests and a history. Depending on the media you might even get a nice diff representation - check out this commit:&lt;/p&gt;
&lt;p&gt;&lt;img src="images/LoginImageDiff.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/dwmkerr/better-specs/commit/8a1224812db2ae4909dfb5a30f8483b1ca96ed18?diff-0=0-100"&gt;See the actual commit here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can see the image before, after or even use the slider to see what has changed. The rich media is &lt;em&gt;supporting&lt;/em&gt; media - it&amp;rsquo;s still the spec that&amp;rsquo;s boss.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t throw away your powerpoint presentations, visio files or anything else, but make the spec king and these files support it. Got a big table of information? Do it in excel or google sheets, that&amp;rsquo;s the right tool for the job - but version control it with the spec.&lt;/p&gt;
&lt;h3 id="persuading-others"&gt;Persuading Others&lt;/h3&gt;
&lt;p&gt;The hard thing about this approach might be persuading those who are not used to it to use it.&lt;/p&gt;
&lt;p&gt;To developers, I imagine this approach has immediate appeal. We know that an email chain of timestamped word documents or powerpoint presentations is a nightmare to maintain as it evolves, and that versin control systems are critical to managing shared work.&lt;/p&gt;
&lt;p&gt;To business people who are not used to version control or markdown, it might seem like a nerdy, inefficient way of doing things. But the points above are quite compelling. If you have strong arguments for or against, comment and I&amp;rsquo;ll update the article.&lt;/p&gt;
&lt;h3 id="useful-resources"&gt;Useful Resources&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s worth pulling together some useful further reading.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.joelonsoftware.com/articles/fog0000000036.html"&gt;Joel Spolsky&amp;rsquo;s series on Painless Specifications&lt;/a&gt; - A well written series on why specs are so important.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; - The official homepage&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackedit.io/"&gt;StackEdit&lt;/a&gt; - Have a play writing markdown and see it rendered.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="final-thoughts"&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve never used this approach in a team - I am writing specs for a side project like this at the moment, but this is a one-man project (for now).&lt;/p&gt;
&lt;p&gt;Have you tried this approach? Any success stories or experiences of it failing? I&amp;rsquo;d love to know, so comment if you&amp;rsquo;ve got some experience on this one.&lt;/p&gt;
&lt;p&gt;P.S. - This page was written with markdown!&lt;/p&gt;</description><category>CodeProject</category></item><item><title>CodeProject MVP</title><link>https://dwmkerr.com/codeproject-mvp/</link><pubDate>Tue, 17 Jan 2012 07:25:00 +0000</pubDate><guid>https://dwmkerr.com/codeproject-mvp/</guid><description>&lt;p&gt;As a great start to the new year I have been made a CodeProject MVP!&lt;/p&gt;
&lt;p&gt;Have a look at my index of articles below:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=4259528"&gt;&lt;a href="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=4259528"&gt;http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=4259528&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Any suggestions for future articles are more than appreciated.&lt;/p&gt;</description><category>CodeProject</category></item><item><title>CodeProject Competition</title><link>https://dwmkerr.com/codeproject-competition/</link><pubDate>Thu, 06 Oct 2011 07:30:00 +0000</pubDate><guid>https://dwmkerr.com/codeproject-competition/</guid><description>&lt;p&gt;My Solitaire and Spider Solitaire in WPF article is in two CodeProject competitions this month. The article is at:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.codeproject.com/Articles/252152/Solitaire-and-Spider-Solitaire-for-WPF"&gt;https://www.codeproject.com/Articles/252152/Solitaire-and-Spider-Solitaire-for-WPF&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you think the article is worthy of a vote, then please go to the voting page for either of the two competitions!&lt;/p&gt;
&lt;p&gt;Best C# Article: &lt;a href="http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1209"&gt;&lt;a href="http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1209"&gt;http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1209&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Best Overall Article: &lt;a href="http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1212"&gt;&lt;a href="http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1212"&gt;http://www.codeproject.com/script/Surveys/VoteForm.aspx?srvid=1212&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;</description><category>CodeProject</category></item><item><title>Solitaire and Spider Solitaire on the CodeProject</title><link>https://dwmkerr.com/solitaire-and-spider-solitaire-on-the-codeproject/</link><pubDate>Mon, 12 Sep 2011 06:01:00 +0000</pubDate><guid>https://dwmkerr.com/solitaire-and-spider-solitaire-on-the-codeproject/</guid><description>&lt;p&gt;I have uploaded a new article on the CodeProject, a step by step tutorial showing how to create Solitaire and Spider Solitaire for WPF with the help of Apex.&lt;/p&gt;
&lt;p&gt;The article is available at: &lt;a href="http://www.codeproject.com/KB/WPF/solitaire.aspx"&gt;http://www.codeproject.com/KB/WPF/solitaire.aspx&lt;/a&gt;&lt;/p&gt;</description><category>CodeProject</category></item></channel></rss>