<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on Indistinct Talk</title>
        <link>https://ljones.id.au/posts/</link>
        <description>Recent content in Posts on Indistinct Talk</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-au</language>
        <lastBuildDate>Mon, 14 Aug 2023 04:00:00 +0000</lastBuildDate>
        <atom:link href="https://ljones.id.au/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>Roll on</title>
            <link>https://ljones.id.au/posts/2023/08/roll-on/</link>
            <pubDate>Mon, 14 Aug 2023 04:00:00 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2023/08/roll-on/</guid>
            <description>Long time no see! What&amp;rsquo;s new, you ask?
 Feels funny posting more than a year after my last post, starting with the same sentence. 😅
It is what it is though. I&amp;rsquo;ve been thinking about blogging but just haven&amp;rsquo;t made the time. So very belatedly, here&amp;rsquo;s the latest.
 The new job is no longer new. I&amp;rsquo;m primarily working with Google Cloud Platform (GCP) and GitLab. The new house is no longer new.</description>
            <content type="html"><![CDATA[<blockquote>
<p>Long time no see! What&rsquo;s new, you ask?</p>
</blockquote>
<p>Feels funny posting more than a year after my last post, starting with the same sentence. 😅</p>
<p>It is what it is though. I&rsquo;ve been thinking about blogging but just haven&rsquo;t made the time. So very belatedly, here&rsquo;s the latest.</p>
<ul>
<li>The new job is no longer new. I&rsquo;m primarily working with Google Cloud Platform (GCP) and GitLab.</li>
<li>The new house is no longer new. I&rsquo;m so happy to be able to live where I do!</li>
<li>I have picked up two professional certifications in 2023 so far, the GCP <a href="https://cloud.google.com/certification/cloud-digital-leader">Cloud Digital Leader</a> and <a href="https://cloud.google.com/certification/cloud-architect">Cloud Architect</a>.</li>
<li>So much learning about 3D printers, printing and CAD. I now own four printers. D:</li>
<li>I had an international holiday. It was a bit of a disaster, quite literally. I ended up stuck in a hotel while a tropical cyclone passed.</li>
<li>I went to like, 5 weddings. Love is in the air!</li>
</ul>
<p>I&rsquo;m still learning new things about just about everything, all the time. My life feels very busy and yet when I look back at the days, weeks and months past, less has been achieved than I hoped. I wonder how people with kids get anything done, haha! My sleep is bad enough already. D:</p>
]]></content>
        </item>
        
        <item>
            <title>A few life changes</title>
            <link>https://ljones.id.au/posts/2022/06/a-few-life-changes/</link>
            <pubDate>Sat, 04 Jun 2022 03:49:16 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2022/06/a-few-life-changes/</guid>
            <description>Long time no see! What&amp;rsquo;s new, you ask?
Well, a few things;
 Bought and moved into a house with my partner, Built gadgets to improve life in said house, Finally built the RigidBot Big 3D printer that has been sitting unassembled at my parents house for some 6-7 years, 😬 Started a new job.  On a completely unrelated note and in no particular order, I&amp;rsquo;ve started learning a lot about;</description>
            <content type="html"><![CDATA[<p>Long time no see! What&rsquo;s new, you ask?</p>
<p>Well, a few things;</p>
<ul>
<li>Bought and moved into a house with my partner,</li>
<li>Built gadgets to improve life in said house,</li>
<li>Finally built the <a href="https://www.kickstarter.com/projects/1650950769/rigidbot-3d-printer">RigidBot Big 3D printer</a> that has been sitting unassembled at my parents house for some 6-7 years, 😬</li>
<li>Started a new job.</li>
</ul>
<p>On a completely unrelated note and in no particular order, I&rsquo;ve started learning a lot about;</p>
<ul>
<li>house maintenance and renovation,</li>
<li>micro-controllers and electronics in general,</li>
<li>3D printer construction/assembly, upgrading and <em>troubleshooting</em>, 😓</li>
<li>Google Cloud Platform.</li>
</ul>
<p>To say it has been busy is somewhat of an understatement. Everything has a steep learning curve, which is tiring but rewarding. The awkward part is knowing that I have still barely scratched the surface.</p>
<p>Time and persistence will pay off, but hard slog is still there.</p>
<p>I&rsquo;m planning to write up some of the projects as blog posts, so keep an eye out for those sometime this year. 😅</p>
]]></content>
        </item>
        
        <item>
            <title>DigitalOcean Kubernetes Challenge 2021 - Extras</title>
            <link>https://ljones.id.au/posts/2022/01/digitalocean-kubernetes-challenge-2021-extras/</link>
            <pubDate>Fri, 07 Jan 2022 09:41:16 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2022/01/digitalocean-kubernetes-challenge-2021-extras/</guid>
            <description>So I actually forgot about this post; I thought I had taken it out of draft status but oopsie-daisy. In the end, I only got around to adding a Grafana stack including Loki to the DOKS cluster.
In my defence, it has been very busy. I&amp;rsquo;ll be posting about that soon.
 Here are some of the extra bibs and bobs that I put on my DOKS 2021 Challenge cluster.</description>
            <content type="html"><![CDATA[<p>So I actually forgot about this post; I thought I had taken it out of draft status but oopsie-daisy. In the end, I only got around to adding a Grafana stack including Loki to the <a href="/posts/2021/11/digitalocean-kubernetes-challenge-2021-challenge-complete/">DOKS</a> cluster.</p>
<p>In my defence, it has been <em>very</em> busy. I&rsquo;ll be posting about that soon.</p>
<hr>
<p>Here are some of the extra bibs and bobs that I put on my DOKS 2021 Challenge cluster.</p>
<p>Some extra tools are needed here, or at least, I make use of them.</p>
<ul>
<li><a href="https://kubernetes.io/docs/reference/kubectl/overview/">kubectl</a>, &lsquo;lets you control Kubernetes clusters&rsquo;</li>
<li><a href="https://docs.digitalocean.com/reference/doctl/">doctl</a>, <strong>optional</strong> &lsquo;the official CLI tool for interacting with DO resources&rsquo;</li>
<li><a href="https://k9scli.io">k9s</a>, <strong>optional</strong> a helpful TUI for interacting with k8s resources</li>
</ul>
<p>You can download the kubeconfig for the cluster from the DO gui, doctl or extract it from Terraform state. I&rsquo;ll be using doctl just because I wanted to try it.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ brew install doctl
<span style="color:#f92672">==</span>&gt; Downloading https://ghcr.io/v2/homebrew/core/doctl/manifests/1.67.0
<span style="color:#75715e">######################################################################## 100.0%</span>
<span style="color:#f92672">==</span>&gt; Downloading https://ghcr.io/v2/homebrew/core/doctl/blobs/sha256:7c235dbbf49483325e38b76cb72263ec9301e1a6ffd958ee5d15
<span style="color:#f92672">==</span>&gt; Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sha256:7c235dbbf49483325e38b76cb72263ec930
<span style="color:#75715e">######################################################################## 100.0%</span>
<span style="color:#f92672">==</span>&gt; Pouring doctl--1.67.0.x86_64_linux.bottle.tar.gz
<span style="color:#f92672">==</span>&gt; Caveats
zsh completions have been installed to:
  /home/linuxbrew/.linuxbrew/share/zsh/site-functions
<span style="color:#f92672">==</span>&gt; Summary
🍺  /home/linuxbrew/.linuxbrew/Cellar/doctl/1.67.0: <span style="color:#ae81ff">9</span> files, 24.6MB

$ doctl auth init
Please authenticate doctl <span style="color:#66d9ef">for</span> use with your DigitalOcean account. You can generate a token in the control panel at https://cloud.digitalocean.com/account/api/tokens

Enter your access token:
Validating token... OK

$ doctl kubernetes cluster kubeconfig save 898db296-575e-4112-a651-4d0c429c5b28
Notice: Adding cluster credentials to kubeconfig file found in <span style="color:#e6db74">&#34;/home/liam/.kube/config&#34;</span>
Notice: Setting current-context to <span style="color:#66d9ef">do</span>-sgp1-do-k8s-2021

$ kubectl version
Client Version: version.Info<span style="color:#f92672">{</span>Major:<span style="color:#e6db74">&#34;1&#34;</span>, Minor:<span style="color:#e6db74">&#34;20&#34;</span>, GitVersion:<span style="color:#e6db74">&#34;v1.20.4&#34;</span>, GitCommit:<span style="color:#e6db74">&#34;e87da0bd6e03ec3fea7933c4b5263d151aafd07c&#34;</span>, GitTreeState:<span style="color:#e6db74">&#34;clean&#34;</span>, BuildDate:<span style="color:#e6db74">&#34;2021-02-18T16:12:00Z&#34;</span>, GoVersion:<span style="color:#e6db74">&#34;go1.15.8&#34;</span>, Compiler:<span style="color:#e6db74">&#34;gc&#34;</span>, Platform:<span style="color:#e6db74">&#34;linux/amd64&#34;</span><span style="color:#f92672">}</span>
Server Version: version.Info<span style="color:#f92672">{</span>Major:<span style="color:#e6db74">&#34;1&#34;</span>, Minor:<span style="color:#e6db74">&#34;21&#34;</span>, GitVersion:<span style="color:#e6db74">&#34;v1.21.5&#34;</span>, GitCommit:<span style="color:#e6db74">&#34;aea7bbadd2fc0cd689de94a54e5b7b758869d691&#34;</span>, GitTreeState:<span style="color:#e6db74">&#34;clean&#34;</span>, BuildDate:<span style="color:#e6db74">&#34;2021-09-15T21:04:16Z&#34;</span>, GoVersion:<span style="color:#e6db74">&#34;go1.16.8&#34;</span>, Compiler:<span style="color:#e6db74">&#34;gc&#34;</span>, Platform:<span style="color:#e6db74">&#34;linux/amd64&#34;</span><span style="color:#f92672">}</span>
</code></pre></div><p>Okay, good to go.</p>
<h2 id="loki--grafana-stack">Loki / Grafana stack</h2>
<p>Update main.tf and apply.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#ae81ff">resource &#34;helm_release&#34; &#34;loki&#34; {</span>
  <span style="color:#ae81ff">name       = &#34;loki&#34;</span>
  <span style="color:#ae81ff">repository = &#34;https://grafana.github.io/helm-charts&#34;</span>
  <span style="color:#ae81ff">chart      = &#34;loki-stack&#34;</span>
  <span style="color:#ae81ff">version    = &#34;2.5.0&#34;</span>

  <span style="color:#ae81ff">namespace        = &#34;loki&#34;</span>
  <span style="color:#ae81ff">create_namespace = true</span>

  <span style="color:#ae81ff">set {</span>
    <span style="color:#ae81ff">name  = &#34;grafana.enabled&#34;</span>
    <span style="color:#ae81ff">value = &#34;true&#34;</span>
  }

  <span style="color:#ae81ff">set {</span>
    <span style="color:#ae81ff">name  = &#34;prometheus.enabled&#34;</span>
    <span style="color:#ae81ff">value = &#34;true&#34;</span>
  }
}
</code></pre></div><p>Get the admin password for the initial Grafana login, and port forward so that you have access to Grafana locally.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ kubectl get secret --namespace loki loki-grafana -o jsonpath<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;{.data.admin-password}&#34;</span> | base64 --decode ; echo
&lt;password output here&gt;

$ kubectl port-forward --namespace loki service/loki-grafana 8080:80
Forwarding from 127.0.0.1:8080 -&gt; <span style="color:#ae81ff">3000</span>
Forwarding from <span style="color:#f92672">[</span>::1<span style="color:#f92672">]</span>:8080 -&gt; <span style="color:#ae81ff">3000</span>
</code></pre></div><p>Hit <a href="http://localhost:8080">http://localhost:8080</a> and you should be staring at a Grafana login page. Success! Deep diving on Grafana is a bit much for this post, but have a play around in the explore tab on the left after you login. You&rsquo;ll already have logs (thanks to <a href="https://grafana.com/oss/loki/">Loki</a>) and cluster metrics (thanks to <a href="https://grafana.com/oss/prometheus/">Prometheus</a>) available to view.</p>
<p>One fun thing I did was check what output there was in Falco when I opened up a shell on my Loki pod. I used the LogQL search term <code>{job=&quot;falco/falco&quot;}</code>, i.e. give me all the logs for the falco job in the falco namespace, hit search and voila!</p>
<pre><code class="language-log" data-lang="log">2021-11-27T07:38:52.832748533Z stdout F 07:38:52.830148939: Notice A shell was spawned in a container with an attached terminal (user=&lt;NA&gt; user_loginuid=-1 k8s.ns=loki k8s.pod=loki-0 container=269597121f05 shell=sh parent=&lt;NA&gt; cmdline=sh terminal=34816 container_id=269597121f05 image=docker.io/grafana/loki) k8s.ns=loki k8s.pod=loki-0 container=269597121f05
2021-11-27T07:38:52.832551682Z stdout F 07:38:52.830014807: Notice A shell was spawned in a container with an attached terminal (user=&lt;NA&gt; user_loginuid=-1 k8s.ns=loki k8s.pod=loki-0 container=269597121f05 shell=sh parent=&lt;NA&gt; cmdline=sh -c command -v bash &gt;/dev/null &amp;&amp; exec bash || exec sh terminal=34816 container_id=269597121f05 image=docker.io/grafana/loki) k8s.ns=loki k8s.pod=loki-0 container=269597121f05
</code></pre><p>Maybe something like <code>count_over_time({job=&quot;falco/falco&quot;} |= &quot;A shell was spawned in a container with an attached terminal&quot;[1m])</code> will be a useful one to keep an eye on. 🤔</p>
]]></content>
        </item>
        
        <item>
            <title>Gitea, Golang, GoReleaser and Drone CI - Part 4</title>
            <link>https://ljones.id.au/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/</link>
            <pubDate>Sat, 18 Dec 2021 05:57:40 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/</guid>
            <description>Gitea, Golang, GoReleaser and Drone CI - Part 1 Gitea, Golang, GoReleaser and Drone CI - Part 2 Gitea, Golang, GoReleaser and Drone CI - Part 3 Gitea, Golang, GoReleaser and Drone CI - Part 4 &amp;lt;- You are Here   Alrighty, time to add some Drone CI to the mix to get those automatic releases happening. First things first, let&amp;rsquo;s start Drone CI and get it connected to Gitea.</description>
            <content type="html"><![CDATA[<ul>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/">Gitea, Golang, GoReleaser and Drone CI - Part 1</a></li>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/">Gitea, Golang, GoReleaser and Drone CI - Part 2</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/">Gitea, Golang, GoReleaser and Drone CI - Part 3</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/">Gitea, Golang, GoReleaser and Drone CI - Part 4</a> &lt;- You are Here</li>
</ul>
<hr>
<p>Alrighty, time to add some Drone CI to the mix to get those automatic releases happening. First things first, let&rsquo;s start Drone CI and get it connected to Gitea. Drone CI provide reasonable <a href="https://docs.drone.io/server/provider/gitea/">documentation</a>, so I won&rsquo;t go into all the details but basically:</p>
<ol>
<li>Create an OAuth app in Gitea.
<ul>
<li>Make note of the <code>Client Secret</code> when shown to you, as it&rsquo;s the only time it&rsquo;ll be shown.</li>
<li>Make sure you provide the appropriate Redirect URI. In our case, this should be <a href="http://drone:8080/login">http://drone:8080/login</a>.</li>
</ul>
</li>
<li>Create a secret, however you want really. We&rsquo;ll use this for the `</li>
<li>Add the below to our existing docker-compose file.
<ul>
<li>Be sure to provide the appropriate <code>Client ID</code>, <code>Client Secret</code> and <code>DRONE_RPC_SECRET</code>. Make something up for that last one, it can be whatever you want. Ideally something strong. 😄</li>
</ul>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml">  <span style="color:#f92672">drone</span>:
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">drone/drone:2</span>
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">drone</span>
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_SERVER_HOST=drone:8080</span>
      - <span style="color:#ae81ff">DRONE_SERVER_PROTO=http</span>
      - <span style="color:#ae81ff">DRONE_GITEA_CLIENT_ID=&lt;your-client-id-here&gt;</span>
      - <span style="color:#ae81ff">DRONE_GITEA_CLIENT_SECRET=&lt;your-client-secret-here&gt;</span>
      - <span style="color:#ae81ff">DRONE_GITEA_SERVER=http://gitea:3000</span>
      - <span style="color:#ae81ff">DRONE_RPC_SECRET=&lt;your-rpc-secret-here&gt;</span>
      - <span style="color:#ae81ff">DRONE_USER_CREATE=username:gitea,admin:true</span>
    <span style="color:#f92672">volumes</span>:
      - <span style="color:#ae81ff">drone:/data</span>
    <span style="color:#f92672">ports</span>:
      - <span style="color:#ae81ff">8080</span>:<span style="color:#ae81ff">80</span>
    <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>

  <span style="color:#f92672">runner</span>:
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">drone/drone-runner-docker:1</span>
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">runner</span>
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_RPC_PROTO=http</span>
      - <span style="color:#ae81ff">DRONE_RPC_HOST=drone</span>
      - <span style="color:#ae81ff">DRONE_RPC_SECRET=&lt;your-rpc-secret-here&gt;</span>
      - <span style="color:#ae81ff">DRONE_RUNNER_CAPACITY=2</span>
      - <span style="color:#ae81ff">DRONE_RUNNER_NAME=jovial-easley</span>
    <span style="color:#f92672">volumes</span>:
      - <span style="color:#ae81ff">/var/run/docker.sock:/var/run/docker.sock</span>
    <span style="color:#f92672">depends_on</span>:
      - <span style="color:#ae81ff">drone</span>
    <span style="color:#f92672">restart</span>: <span style="color:#ae81ff">unless-stopped</span>

</code></pre></div><p>Let&rsquo;s start Drone CI now.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ docker-compose up -d
Recreating gitea ... <span style="color:#66d9ef">done</span>
Creating drone   ... <span style="color:#66d9ef">done</span>
Creating runner  ... <span style="color:#66d9ef">done</span>
</code></pre></div><p>Hit <a href="http://drone:8080">http://drone:8080</a> and you should see <code>Hello, Welcome to Drone.</code>. Awesome! Click continue and you should be redirected to Gitea with a prompt to authorize drone to access your account. Accept and you should be redirected back to Drone to <code>Complete your Drone Registration.</code>. I actually just leave these blank and hit submit, it&rsquo;s fine.</p>
<p>Sweet, now I can see <code>gitea/crimp-city</code> listed under repositories. Click on it and then on <code>Activate Repository</code>. When you do this, Drone CI creates a webhook in Gitea, telling it to send it an update when a commit is made. You can ignore everything else for now.</p>
<p>So Gitea knows it should tell Drone CI when this repo gets a new commit. Let&rsquo;s test this out. Create a <code>.drone.yml</code> file in the root of the <code>crimp-city</code> repo.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#75715e"># .drone.yml</span>

<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">pipeline</span>
<span style="color:#f92672">name</span>: <span style="color:#ae81ff">default</span>

<span style="color:#f92672">steps</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">run</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">golang</span>
    <span style="color:#f92672">commands</span>:
      - <span style="color:#ae81ff">go run main.go</span>
</code></pre></div><p>Add, commit and push this change up to Gitea.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git add .drone.yml
$ git commit -m <span style="color:#e6db74">&#34;Add drone ci config&#34;</span>
<span style="color:#f92672">[</span>master 693d656<span style="color:#f92672">]</span> Add drone ci config
 <span style="color:#ae81ff">1</span> file changed, <span style="color:#ae81ff">10</span> insertions<span style="color:#f92672">(</span>+<span style="color:#f92672">)</span>
 create mode <span style="color:#ae81ff">100644</span> .drone.yml
$ git push
Enumerating objects: 8, <span style="color:#66d9ef">done</span>.
Counting objects: 100% <span style="color:#f92672">(</span>8/8<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Delta compression using up to <span style="color:#ae81ff">12</span> threads
Compressing objects: 100% <span style="color:#f92672">(</span>6/6<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Writing objects: 100% <span style="color:#f92672">(</span>6/6<span style="color:#f92672">)</span>, <span style="color:#ae81ff">686</span> bytes | 686.00 KiB/s, <span style="color:#66d9ef">done</span>.
Total <span style="color:#ae81ff">6</span> <span style="color:#f92672">(</span>delta 2<span style="color:#f92672">)</span>, reused <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, pack-reused <span style="color:#ae81ff">0</span>
remote: . Processing <span style="color:#ae81ff">1</span> references
remote: Processed <span style="color:#ae81ff">1</span> references in total
To http://gitea:3000/gitea/crimp-city.git
   4836926..693d656  master -&gt; master
</code></pre></div><p>I checked the build tab in Drone and&hellip; nothing. Huh, that&rsquo;s odd. I venture over to the <code>crimp-city</code> repo in Gitea, open the settings and webhooks tab, scrolling down to the bottom and, ah. A failed delivery.</p>
<pre><code class="language-log" data-lang="log">Delivery: Post &quot;http://drone:8080/hook?secret=kzpOpiciNP5TJDv2x3nKh4lb3MzNiI3n&quot;: dial tcp 172.23.0.3:8080: connect: connection refused
</code></pre><p>Oh. Yes. More network fun. Gitea is using the internal docker-compose network, where Drone is actually listening on port 80 and not 8080. We can fix this easily by scrolling back up the page and updated the <code>Target URL</code> to <code>http://drone/hook?secret=kzpOpiciNP5TJDv2x3nKh4lb3MzNiI3n</code>.</p>
<p>To test this is now working, we can add a README to <code>crimp-city</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-md" data-lang="md"># crimp-city

Testing that Gitea can talk to Drone CI!
</code></pre></div><p>Do the git add/commit/push dance and then check that Drone CI builds tab again. Sweet! Success. I can click through and view output from the two stages of the build, the <code>clone</code> and the <code>run</code> step.</p>
<p>Time to make it actually do releases though. Update the Drone CI config file.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#75715e"># .drone.yml</span>

<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">pipeline</span>
<span style="color:#f92672">name</span>: <span style="color:#ae81ff">default</span>

<span style="color:#f92672">steps</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">fetch</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">docker:git</span>
    <span style="color:#f92672">commands</span>:
      - <span style="color:#ae81ff">git fetch --tags</span>

  <span style="color:#75715e"># we don&#39;t have tests, awkward</span>
  <span style="color:#75715e"># - name: test</span>
  <span style="color:#75715e">#   image: golang:1.17</span>
  <span style="color:#75715e">#   volumes:</span>
  <span style="color:#75715e">#     - name: deps</span>
  <span style="color:#75715e">#       path: /go</span>
  <span style="color:#75715e">#   commands:</span>
  <span style="color:#75715e">#     - go test -race -v ./... -cover</span>

  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">release</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">golang:1.17</span>
    <span style="color:#f92672">environment</span>:
      <span style="color:#f92672">GITEA_TOKEN</span>:
        <span style="color:#f92672">from_secret</span>: <span style="color:#ae81ff">gitea_token</span>
    <span style="color:#f92672">volumes</span>:
      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">deps</span>
        <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/go</span>
    <span style="color:#f92672">commands</span>:
      - <span style="color:#ae81ff">curl -sL https://git.io/goreleaser | bash</span>
    <span style="color:#f92672">when</span>:
      <span style="color:#f92672">event</span>:
        - <span style="color:#ae81ff">tag</span>

<span style="color:#f92672">volumes</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">deps</span>
    <span style="color:#f92672">temp</span>: {}

</code></pre></div><p>Why do we have the <code>fetch</code> step? Per the GoRelease Drone CI <a href="https://goreleaser.com/ci/drone/?h=drone">documentation</a>:</p>
<blockquote>
<p>By default, drone does not fetch tags. plugins/git is used with default values, in most cases we&rsquo;ll need overwrite the clone step enabling tags in order to make goreleaser work correctly.</p>
</blockquote>
<p>One thing we&rsquo;ll need to do is set a secret in Drone CI, so that GoReleaser can talk to Gitea. You may remember this as our <code>GITEA_TOKEN</code>. You can use the same one as before or create a new dedicated secret. If it was production, I&rsquo;d do the latter.</p>
<p>To set the secret, go into Drone CI, click through to the <code>crimp-city</code> repo, Settings -&gt; Secrets -&gt; <code>+ New Secret</code>. Name it <code>gitea_token</code> and the value to the secret, then create.</p>
<p>Git add/commit/push the updated Drone CI config. Time to check what happened in the latest build logs from the runner. We have our <code>clone</code> and <code>fetch</code> steps but no <code>release</code>? This is because set to only run that step when the commit has a tag. Let&rsquo;s try that out now.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git tag -a v0.1.1 -m <span style="color:#e6db74">&#34;Testing Drone CI and GoReleaser&#34;</span>
$ git push origin v0.1.1
Enumerating objects: 1, <span style="color:#66d9ef">done</span>.
Counting objects: 100% <span style="color:#f92672">(</span>1/1<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Writing objects: 100% <span style="color:#f92672">(</span>1/1<span style="color:#f92672">)</span>, <span style="color:#ae81ff">180</span> bytes | 180.00 KiB/s, <span style="color:#66d9ef">done</span>.
Total <span style="color:#ae81ff">1</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, reused <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, pack-reused <span style="color:#ae81ff">0</span>
remote: . Processing <span style="color:#ae81ff">1</span> references
remote: Processed <span style="color:#ae81ff">1</span> references in total
To http://gitea:3000/gitea/crimp-city.git
 * <span style="color:#f92672">[</span>new tag<span style="color:#f92672">]</span>         v0.1.1 -&gt; v0.1.1
</code></pre></div><p>Check out the builds again annnnnnnnd&hellip;</p>
<p><img src="https://media.giphy.com/media/yidUzHzEdhzOnqhFBK/giphy.gif" alt="Woman celebrating" title="Booyakasha!"></p>
<p>It ran through the <code>release</code> stage as well! And if we check Gitea, we can see that there are now two releases. Awesome!</p>
<hr>
<p>Here we conclude. I actually finished writing a thing. Word. I&rsquo;m considering creating video tutorials and putting them up on YouTube because <em>man</em>, is it effort writing all this out. That&rsquo;s probably my bad for not tactically using screenshots though, so, I apologise. Get in touch on Twitter or elsewhere if you have any questions or difficulties.</p>
<p>Peace out and I&rsquo;ll see you in my next post.</p>
]]></content>
        </item>
        
        <item>
            <title>Hugo, GitLab Pipelines and You</title>
            <link>https://ljones.id.au/posts/2021/12/hugo-gitlab-pipelines-and-you/</link>
            <pubDate>Thu, 09 Dec 2021 11:51:47 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/12/hugo-gitlab-pipelines-and-you/</guid>
            <description>Just a short post this time, with something I recently setup with Hugo and GitLab, but it easily applies to any other static site generator and CI/CD runner1.
 So as you may already know or have guessed, this blog is built using Hugo, and is hosted on GitLab! There&amp;rsquo;s documentation on this already, so I won&amp;rsquo;t rehash that2. No, I&amp;rsquo;m here to talk about posting, in the future.
When you&amp;rsquo;re writing a post in Hugo, you can set the post date to a time in the future, but unless you have the buildFuture configuration set, the Hugo won&amp;rsquo;t build that post.</description>
            <content type="html"><![CDATA[<p>Just a short post this time, with something I recently setup with <a href="https://gohugo.io/">Hugo</a> and <a href="https://gitlab.com/">GitLab</a>, but it easily applies to any other static site generator and CI/CD runner<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<hr>
<p>So as you may already know or have guessed, this blog is built using <a href="https://gohugo.io/">Hugo</a>, and is hosted on <a href="https://gitlab.com/">GitLab</a>! There&rsquo;s <a href="https://gohugo.io/hosting-and-deployment/hosting-on-gitlab/">documentation on this already</a>, so I won&rsquo;t rehash that<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. No, I&rsquo;m here to talk about posting, <em>in the future</em>.</p>
<p>When you&rsquo;re writing a post in <a href="https://gohugo.io/">Hugo</a>, you can set the post date to a time in the future, but unless you have the <code>buildFuture</code> configuration set, the <a href="https://gohugo.io/">Hugo</a> won&rsquo;t build that post.</p>
<p>Why would you want to set a date in the future? Maybe you know you&rsquo;ll be away and unable to post for a few weeks, or you&rsquo;re just feeling productive and write a heap of content. Instead of overwhelming your readers with a tower of posts released on the same day, you could stagger that content over several weeks.</p>
<p>But here&rsquo;s the thing. With the default configuration following the documentation linked earlier, the blog only builds when the pipeline runs, which means you making a new commit to the <code>main</code> branch.</p>
<p>When I&rsquo;m away, I&rsquo;m away! 😎 🌴 I can&rsquo;t be triggering these builds!</p>
<p>Happily, <a href="https://gitlab.com/">GitLab</a> lets you set timer. They&rsquo;re called <a href="https://docs.gitlab.com/ee/ci/pipelines/schedules.html#pipeline-schedules">pipeline schedules</a>, and you can either set one with the ever powerful <a href="https://docs.gitlab.com/ee/topics/cron/">cron</a> syntax or one of their default options. I&rsquo;m using <code>0 20 * * *</code> in the UTC timezone, meaning about 4am local.</p>
<p>So I can just write, set the date I want and make sure to remove the draft status and&hellip; fresh posts for the people when they rise and shine! ☕</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Probably.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Unless you want me to! Let me know on Twitter or something.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Gitea, Golang, GoReleaser and Drone CI - Part 3</title>
            <link>https://ljones.id.au/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/</link>
            <pubDate>Sat, 04 Dec 2021 04:55:38 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/</guid>
            <description>Gitea, Golang, GoReleaser and Drone CI - Part 1 Gitea, Golang, GoReleaser and Drone CI - Part 2 Gitea, Golang, GoReleaser and Drone CI - Part 3 &amp;lt;- You are Here Gitea, Golang, GoReleaser and Drone CI - Part 4   Howdy again, hope you&amp;rsquo;re all going well. Let&amp;rsquo;s get right to it.
 GoReleaser is already installed in the devcontainer environment for you, let&amp;rsquo;s check that it&amp;rsquo;s happy.</description>
            <content type="html"><![CDATA[<ul>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/">Gitea, Golang, GoReleaser and Drone CI - Part 1</a></li>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/">Gitea, Golang, GoReleaser and Drone CI - Part 2</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/">Gitea, Golang, GoReleaser and Drone CI - Part 3</a> &lt;- You are Here</li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/">Gitea, Golang, GoReleaser and Drone CI - Part 4</a></li>
</ul>
<hr>
<p>Howdy again, hope you&rsquo;re all going well. Let&rsquo;s get right to it.</p>
<hr>
<p>GoReleaser is already installed in the devcontainer environment for you, let&rsquo;s check that it&rsquo;s happy.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ pwd
/workspaces/go-goreleaser-drone-example/crimp-city
$ goreleaser -h
GoReleaser is a release automation tool <span style="color:#66d9ef">for</span> Go projects.
Its goal is to simplify the build, release and publish steps <span style="color:#66d9ef">while</span> providing
variant customization options <span style="color:#66d9ef">for</span> all steps.

GoReleaser is built <span style="color:#66d9ef">for</span> CI tools, you only need to download and execute it
in your build script. Of course, you can also install it locally <span style="color:#66d9ef">if</span> you wish.

You can also customize your entire release process through a
single .goreleaser.yml file.

Usage:
  goreleaser <span style="color:#f92672">[</span>command<span style="color:#f92672">]</span>

Available Commands:
  build       Builds the current project
  check       Checks <span style="color:#66d9ef">if</span> configuration is valid
  completion  generate the autocompletion script <span style="color:#66d9ef">for</span> the specified shell
  help        Help about any command
  init        Generates a .goreleaser.yml file
  jsonschema  outputs goreleaser<span style="color:#960050;background-color:#1e0010">&#39;</span>s JSON schema
  release     Releases the current project

Flags:
      --debug     Enable debug mode
  -h, --help      help <span style="color:#66d9ef">for</span> goreleaser
  -v, --version   version <span style="color:#66d9ef">for</span> goreleaser

Use <span style="color:#e6db74">&#34;goreleaser [command] --help&#34;</span> <span style="color:#66d9ef">for</span> more information about a command.
</code></pre></div><p>Excellent, everything seems to be in order here. Let&rsquo;s generate the default config.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ goreleaser init
   • Generating .goreleaser.yml file
   • config created; please edit accordingly to your needs file<span style="color:#f92672">=</span>.goreleaser.yml
</code></pre></div><p>It should look something like the below, though I&rsquo;ve made some small changes to make it only build for 64-bit linux hosts.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#f92672">before</span>:
  <span style="color:#f92672">hooks</span>:
    - <span style="color:#ae81ff">go mod tidy</span>
<span style="color:#f92672">builds</span>:
  - <span style="color:#f92672">env</span>:
      - <span style="color:#ae81ff">CGO_ENABLED=0</span>
    <span style="color:#f92672">goos</span>:
      - <span style="color:#ae81ff">linux</span>
    <span style="color:#f92672">goarch</span>:
      - <span style="color:#ae81ff">amd64</span>
<span style="color:#f92672">archives</span>:
  - <span style="color:#f92672">replacements</span>:
      <span style="color:#f92672">linux</span>: <span style="color:#ae81ff">Linux</span>
      <span style="color:#f92672">amd64</span>: <span style="color:#ae81ff">x86_64</span>
<span style="color:#f92672">checksum</span>:
  <span style="color:#f92672">name_template</span>: <span style="color:#e6db74">&#39;checksums.txt&#39;</span>
<span style="color:#f92672">snapshot</span>:
  <span style="color:#f92672">name_template</span>: <span style="color:#e6db74">&#34;{{ incpatch .Version }}-next&#34;</span>
<span style="color:#f92672">changelog</span>:
  <span style="color:#f92672">sort</span>: <span style="color:#ae81ff">asc</span>
  <span style="color:#f92672">filters</span>:
    <span style="color:#f92672">exclude</span>:
      - <span style="color:#e6db74">&#39;^docs:&#39;</span>
      - <span style="color:#e6db74">&#39;^test:&#39;</span>
</code></pre></div><p>As usual, let&rsquo;s test this works before moving on.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ goreleaser release --snapshot --rm-dist
   • releasing...
   • loading config file       file<span style="color:#f92672">=</span>.goreleaser.yml
   • loading environment variables
   • getting and validating git state
      • ignoring errors because this is a snapshot error<span style="color:#f92672">=</span>git doesn<span style="color:#960050;background-color:#1e0010">&#39;</span>t contain any tags. Either add a tag or use --snapshot
      • building...               commit<span style="color:#f92672">=</span>9dac672bfae18f00162a9c96a6bc87197c652d4b latest tag<span style="color:#f92672">=</span>v0.0.0
      • pipe skipped              error<span style="color:#f92672">=</span>disabled during snapshot mode
   • parsing tag
   • running before hooks
      • running                   hook<span style="color:#f92672">=</span>go mod tidy
   • setting defaults
      • snapshotting
      • github/gitlab/gitea releases
      • project name
      • loading go mod information
      • building binaries
      • universal binaries
      • creating source archive
      • archives
      • linux packages
      • snapcraft packages
      • calculating checksums
      • signing artifacts
      • signing docker images
      • docker images
      • docker manifests
      • artifactory
      • blobs
      • homebrew tap formula
      • gofish fish food cookbook
      • scoop manifests
      • discord
      • reddit
      • slack
      • teams
      • twitter
      • smtp
      • mattermost
      • milestones
      • telegram
   • snapshotting
      • building snapshot...      version<span style="color:#f92672">=</span>0.0.1-next
   • checking ./dist
      • --rm-dist is set, cleaning it up
   • loading go mod information
   • writing effective config file
      • writing                   config<span style="color:#f92672">=</span>dist/config.yaml
   • building binaries
      • building                  binary<span style="color:#f92672">=</span>/workspaces/go-goreleaser-drone-example/crimp-city/dist/crimp-city_linux_amd64/crimp-city
   • archives
      • creating                  archive<span style="color:#f92672">=</span>dist/crimp-city_0.0.1-next_Linux_x86_64.tar.gz
   • calculating checksums
      • checksumming              file<span style="color:#f92672">=</span>crimp-city_0.0.1-next_Linux_x86_64.tar.gz
   • release succeeded after 0.29s
</code></pre></div><p>Okay, good, and what about the binary that it actually produced?</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ ./dist/crimp-city_linux_amd64/crimp-city

█░█ █▀▀ █░░ █░░ █▀█   █░█░█ █▀█ █▀█ █░░ █▀▄
█▀█ ██▄ █▄▄ █▄▄ █▄█   ▀▄▀▄▀ █▄█ █▀▄ █▄▄ █▄▀ version 0.0.1-next
</code></pre></div><p>Nice! But what&rsquo;s this? The version has changed from <code>dev</code> to <code>0.0.1-next</code>. Why? Well, Go lets you pass in variables at build time, and GoReleaser has some defaults that it will pass in.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#75715e"># Custom ldflags templates.</span>
<span style="color:#75715e"># Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`.</span>
</code></pre></div><p>Normally GoReleaser will do some magic with the most recent git tag for the version, but we don&rsquo;t currently have a tag, so it supplies <code>0.0.1-next</code> for us.</p>
<p>So, we have a Go program and a local working GoReleaser config. Let&rsquo;s make things official and actually make a release in Gitea. We&rsquo;ll need to create an API token for GoReleaser to use, and tell GoReleaser how to reach Gitea.</p>
<blockquote>
<p>GoReleaser requires an API token to deploy the artifacts to Gitea. You can create one in Settings | Applications | Generate New Token page of your Gitea instance.</p>
<p>This token should be added to the environment variables as GITEA_TOKEN.</p>
</blockquote>
<p>Set the token you just created.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">export GITEA_TOKEN<span style="color:#f92672">=</span>&lt;your-token-here&gt;
</code></pre></div><p>Update the <code>.goreleaser.yml</code> configuration.</p>
<blockquote>
<p><del>Now, I think there&rsquo;s a bug in the current version of GoReleaser. I&rsquo;ve raised an <a href="https://github.com/goreleaser/goreleaser/issues/2704">issue</a> for it, but basically, the release block included below shouldn&rsquo;t be necessary. If you&rsquo;re following along, try without it and if you get a 404 error, add it in.</del></p>
<p>I found a bug in GoReleaser, and it was fixed almost immediately! Awesome work. If you&rsquo;re on v1.1.0 of GoReleaser, you can leave the commented part of the block below as is. If you&rsquo;re on a lower version, you need to uncomment it. Or just upgrade. 🤷</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#f92672">gitea_urls</span>:
  <span style="color:#f92672">api</span>: <span style="color:#ae81ff">http://gitea:3000/api/v1/</span>
  <span style="color:#f92672">download</span>: <span style="color:#ae81ff">http://gitea:3000</span>
<span style="color:#75715e"># release:</span>
<span style="color:#75715e">#   gitea:</span>
<span style="color:#75715e">#     owner: gitea</span>
<span style="color:#75715e">#     name: crimp-city</span>
</code></pre></div><p>Now commit the changes, tag the commit and push it to Gitea.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git add .goreleaser.yml
$ git commit -m <span style="color:#e6db74">&#34;Update gitea goreleaser config&#34;</span>
<span style="color:#f92672">[</span>master 4836926<span style="color:#f92672">]</span> Update gitea goreleaser config
 <span style="color:#ae81ff">1</span> file changed, <span style="color:#ae81ff">4</span> insertions<span style="color:#f92672">(</span>+<span style="color:#f92672">)</span>
$ git tag -a v0.1 -m <span style="color:#e6db74">&#34;First release&#34;</span>
$ git push origin v0.1
Total <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, reused <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, pack-reused <span style="color:#ae81ff">0</span>
remote: . Processing <span style="color:#ae81ff">1</span> references
remote: Processed <span style="color:#ae81ff">1</span> references in total
To http://gitea:3000/gitea/crimp-city.git
 * <span style="color:#f92672">[</span>new tag<span style="color:#f92672">]</span>         v0.1 -&gt; v0.1
$ goreleaser release --rm-dist
   • releasing...
   • loading config file       file<span style="color:#f92672">=</span>.goreleaser.yml
   • loading environment variables
   • getting and validating git state
      • building...               commit<span style="color:#f92672">=</span>4836926dc37b8e8cac913f7f0e6f8ae5d2376c06 latest tag<span style="color:#f92672">=</span>v0.1
   • parsing tag
   • running before hooks
      • running                   hook<span style="color:#f92672">=</span>go mod tidy
   • setting defaults
      • snapshotting
      • scm releases
      • project name
      • loading go mod information
      • building binaries
      • universal binaries
      • creating source archive
      • archives
      • linux packages
      • snapcraft packages
      • calculating checksums
      • signing artifacts
      • signing docker images
      • docker images
      • docker manifests
      • artifactory
      • blobs
      • homebrew tap formula
      • krew plugin manifest
      • gofish fish food cookbook
      • scoop manifests
      • discord
      • reddit
      • slack
      • teams
      • twitter
      • smtp
      • mattermost
      • milestones
      • linkedin
      • telegram
   • checking ./dist
      • --rm-dist is set, cleaning it up
   • loading go mod information
   • writing effective config file
      • writing                   config<span style="color:#f92672">=</span>dist/config.yaml
   • generating changelog
      • writing                   changelog<span style="color:#f92672">=</span>dist/CHANGELOG.md
   • building binaries
      • building                  binary<span style="color:#f92672">=</span>/workspaces/go-goreleaser-drone-example/crimp-city/dist/crimp-city_linux_amd64/crimp-city
   • archives
      • creating                  archive<span style="color:#f92672">=</span>dist/crimp-city_0.1_Linux_x86_64.tar.gz
   • calculating checksums
      • checksumming              file<span style="color:#f92672">=</span>crimp-city_0.1_Linux_x86_64.tar.gz
   • publishing
      • scm releases
         • creating or updating release repo<span style="color:#f92672">=</span> tag<span style="color:#f92672">=</span>v0.1
         • Gitea release created     id<span style="color:#f92672">=</span><span style="color:#ae81ff">3</span>
         • uploading to release      file<span style="color:#f92672">=</span>dist/checksums.txt name<span style="color:#f92672">=</span>checksums.txt
         • uploading to release      file<span style="color:#f92672">=</span>dist/crimp-city_0.1_Linux_x86_64.tar.gz name<span style="color:#f92672">=</span>crimp-city_0.1_Linux_x86_64.tar.gz
   • announcing
   • release succeeded after 1.44s
</code></pre></div><p>Great success. If you go browse to the releases page for your repo<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, you&rsquo;ll see a new release, including:</p>
<ul>
<li>a changelog of all the commits</li>
<li>zipped archives of the source code</li>
<li>a zipped copy of the <code>crimp-city</code> binary</li>
<li>a <code>checksums.txt</code> so that people can confirm their copy of the downloaded binary is legitimate.</li>
</ul>
<p>How good is that? And you remember how we had the version embedded in the binary output? Yep, that&rsquo;s using the git tag we set.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ ./dist/crimp-city_linux_amd64/crimp-city

█░█ █▀▀ █░░ █░░ █▀█   █░█░█ █▀█ █▀█ █░░ █▀▄
█▀█ ██▄ █▄▄ █▄▄ █▄█   ▀▄▀▄▀ █▄█ █▀▄ █▄▄ █▄▀ version 0.1
</code></pre></div><p>Nice. Alright, that&rsquo;s all for now. Next time we&rsquo;ll get Drone CI to automatically create a release for us when we push a tag to Gitea.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Something like <a href="http://gitea:3000/gitea/crimp-city/releases">http://gitea:3000/gitea/crimp-city/releases</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Gitea, Golang, GoReleaser and Drone CI - Part 2</title>
            <link>https://ljones.id.au/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/</link>
            <pubDate>Sat, 27 Nov 2021 01:05:25 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/</guid>
            <description>Gitea, Golang, GoReleaser and Drone CI - Part 1 Gitea, Golang, GoReleaser and Drone CI - Part 2 &amp;lt;- You are Here Gitea, Golang, GoReleaser and Drone CI - Part 3 Gitea, Golang, GoReleaser and Drone CI - Part 4   In this post, we&amp;rsquo;ll create our Gitea container, a git repo to hold our code, and a very basic hello world program in Go.
I&amp;rsquo;m going to assume you already have Docker Desktop1, vscode and Remote - Containers extension installed.</description>
            <content type="html"><![CDATA[<ul>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/">Gitea, Golang, GoReleaser and Drone CI - Part 1</a></li>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/">Gitea, Golang, GoReleaser and Drone CI - Part 2</a> &lt;- You are Here</li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/">Gitea, Golang, GoReleaser and Drone CI - Part 3</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/">Gitea, Golang, GoReleaser and Drone CI - Part 4</a></li>
</ul>
<hr>
<p>In this post, we&rsquo;ll create our Gitea container, a git repo to hold our code, and a very basic hello world program in Go.</p>
<p>I&rsquo;m going to assume you already have <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, <a href="https://code.visualstudio.com/">vscode</a> and <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers">Remote - Containers</a> extension installed. If you don&rsquo;t, go do that now. I also assume some basic knowledge about git and how to use git repository hosting services like Github.</p>
<hr>
<p>I&rsquo;ve provided a vscode devcontainer configuration which includes the tools you&rsquo;ll need for the entire project, so to make things easy I recommend you use that.</p>
<p>Start by cloning my <a href="https://github.com/iJebus/go-goreleaser-drone-example">example repository</a> and then checkout <code>10d412a</code>, this commit includes only the necessary devcontainer and docker-compose for bringing up Gitea. Finally, open the folder in vscode.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">git clone https://github.com/iJebus/go-goreleaser-drone-example.git
cd go-goreleaser-drone-example
git checkout 10d412a
code .
</code></pre></div><p>You should be prompted by vscode to <em>reopen</em> in a devcontainer, which you should do. The devcontainer will then build and finally you&rsquo;ll be ready to go. Time to bring up Gitea.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#75715e"># use the terminal on the docker host or inside the vscode devcontainer, it&#39;s the same one either way</span>
$ docker-compose up -d
Starting gitea ... <span style="color:#66d9ef">done</span>
</code></pre></div><p>If you followed my preferred method from the previous post and have updated your <code>/etc/hosts</code> file, you should now access Gitea at <a href="http://gitea:3000">http://gitea:3000</a>. You&rsquo;ll see a lot of configuration which you can ignore, just scroll to the bottom and hit <code>Install Gitea</code>. There will be a brief loading screen, after which you should click <code>Register</code> in the top right. I recommend the username <code>gitea</code>, and later steps will assume this, but all other values can be set however you like.</p>
<p>Create a new project in Gitea. This is the project that&rsquo;s going to host our Golang project, including the GoReleaser and Drone CI configuration. I&rsquo;m going bouldering later today so I&rsquo;ll call mine <code>crimp-city</code>. Clone this project somewhere in the devcontainer<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, as the devcontainer already has the current latest version of Go installed.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ pwd
/workspaces/go-goreleaser-drone-example
$ git clone http://gitea:3000/gitea/crimp-city.git
Cloning into <span style="color:#e6db74">&#39;crimp-city&#39;</span>...
warning: You appear to have cloned an empty repository.
</code></pre></div><p>Now to create our basic go program.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ cd crimp-city
$ go mod init crimp-city
go: creating new go.mod: module crimp-city
$ touch main.go
</code></pre></div><p>Open <code>main.go</code> in vscode and fill it with something like the below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>

<span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;dev&#34;</span>

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">`
</span><span style="color:#e6db74">█░█ █▀▀ █░░ █░░ █▀█   █░█░█ █▀█ █▀█ █░░ █▀▄
</span><span style="color:#e6db74">█▀█ ██▄ █▄▄ █▄▄ █▄█   ▀▄▀▄▀ █▄█ █▀▄ █▄▄ █▄▀ version %s
</span><span style="color:#e6db74">`</span>, <span style="color:#a6e22e">version</span>)
}
</code></pre></div><p>Let&rsquo;s test that works.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ go run main.go

█░█ █▀▀ █░░ █░░ █▀█   █░█░█ █▀█ █▀█ █░░ █▀▄
█▀█ ██▄ █▄▄ █▄▄ █▄█   ▀▄▀▄▀ █▄█ █▀▄ █▄▄ █▄▀ version dev
</code></pre></div><p>Sweet! Let&rsquo;s commit these back to Gitea and call it a day!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ git add go.mod main.go
$ git commit -m <span style="color:#e6db74">&#34;initial commit&#34;</span>
<span style="color:#f92672">[</span>master <span style="color:#f92672">(</span>root-commit<span style="color:#f92672">)</span> 9dac672<span style="color:#f92672">]</span> initial commit
 <span style="color:#ae81ff">2</span> files changed, <span style="color:#ae81ff">15</span> insertions<span style="color:#f92672">(</span>+<span style="color:#f92672">)</span>
 create mode <span style="color:#ae81ff">100644</span> go.mod
 create mode <span style="color:#ae81ff">100644</span> main.go
$ git push
Enumerating objects: 4, <span style="color:#66d9ef">done</span>.
Counting objects: 100% <span style="color:#f92672">(</span>4/4<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Delta compression using up to <span style="color:#ae81ff">12</span> threads
Compressing objects: 100% <span style="color:#f92672">(</span>3/3<span style="color:#f92672">)</span>, <span style="color:#66d9ef">done</span>.
Writing objects: 100% <span style="color:#f92672">(</span>4/4<span style="color:#f92672">)</span>, <span style="color:#ae81ff">424</span> bytes | 424.00 KiB/s, <span style="color:#66d9ef">done</span>.
Total <span style="color:#ae81ff">4</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, reused <span style="color:#ae81ff">0</span> <span style="color:#f92672">(</span>delta 0<span style="color:#f92672">)</span>, pack-reused <span style="color:#ae81ff">0</span>
remote: . Processing <span style="color:#ae81ff">1</span> references
remote: Processed <span style="color:#ae81ff">1</span> references in total
To http://gitea:3000/gitea/crimp-city.git
 * <span style="color:#f92672">[</span>new branch<span style="color:#f92672">]</span>      master -&gt; master
</code></pre></div><p>Goovy, see you in the next post where we&rsquo;ll be implementing GoReleaser.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Or some sort of container runtime, .e.g. podman.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>The container, not the <code>.devcontainer</code> folder.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>DigitalOcean Kubernetes Challenge 2021 - Challenge Complete!</title>
            <link>https://ljones.id.au/posts/2021/11/digitalocean-kubernetes-challenge-2021-challenge-complete/</link>
            <pubDate>Thu, 25 Nov 2021 12:08:55 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/digitalocean-kubernetes-challenge-2021-challenge-complete/</guid>
            <description>Howdy, here we are with my follow up post on the DigitalOcean Kubernetes Challenge 2021, in which I&amp;rsquo;ll be creating a Kubernetes cluster on DigitalOcean and then deploying Falco to it. I&amp;rsquo;ll be using Terraform (TF) to do as much as possible because Infrastructure as Code is my jam.
First steps I created a repository on GitHub to hold the relevant code, if you go look now you should see the completed project.</description>
            <content type="html"><![CDATA[<p>Howdy, here we are with my follow up post on the <a href="../digitalocean-kubernetes-challenge-2021-challenge-accepted">DigitalOcean Kubernetes Challenge 2021</a>, in which I&rsquo;ll be creating a Kubernetes cluster on DigitalOcean and then deploying <a href="https://falco.org/">Falco</a> to it. I&rsquo;ll be using <a href="https://www.terraform.io/">Terraform (TF)</a> to do as much as possible because Infrastructure as Code is my jam.</p>
<h2 id="first-steps">First steps</h2>
<p>I created a <a href="https://github.com/iJebus/do-k8s-2021">repository</a> on GitHub to hold the relevant code, if you go look now you should see the completed project. Noice.</p>
<p>Accepting the challenge and signing up for a DigitalOcean (DO) account was pretty simple. There&rsquo;s a little asterisk on the billing dashboard mentioning that it&rsquo;s updated daily, so don&rsquo;t be surprised when applying coupons and not seeing the <code>Remaining account credits</code> field update. Despite the warning, I found this a little disappointing. I got the message saying the credits were applied, so, why can&rsquo;t the dashboard reflect that?</p>
<p>As is pretty standard for me these days, one of the first things I did was add a vscode devcontainer configuration so that I have a nice portable contained<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> environment for working in. I can&rsquo;t tell you how nice I find being able to move from one computer to another, pull the repo and basically be ready to go. That said, it&rsquo;s actually a bit overkill for this project because I only actually needed the terraform binary. You&rsquo;ll see why soon.</p>
<p>Finally, I took a bit of a peek at the TF documentation for the <a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs">DO provider</a>. I saw the provider needs an API token to be able to interact with DO on my behalf, which makes total sense, so I created a <code>Personal Access Token</code> called <code>do-k8s-2021</code> from the <a href="https://cloud.digitalocean.com/account/api/tokens">API</a> page. I&rsquo;m only using the token with this project, so I can confidently delete it in future and know what will be impacted. Anyway, I copied down the token temporarily as it&rsquo;s only shown once.</p>
<p>To make things a little more interesting, I decided to use <a href="https://app.terraform.io">Terraform Cloud (TFC)</a> for the first time<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. This involved creating a new workspace named <code>do-k8s-2021</code> to keep things consistent, and selected the <code>CLI-driven workflow</code> option. I was provided with instructions and sample TF code to get started, and everything just worked<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. You might be asking why, at this point.</p>
<p>Well, I&rsquo;m glad you asked.</p>
<ol>
<li>It&rsquo;s something that I wanted to learn more about and get practical experience using.</li>
<li>It makes development even more portable.
<ul>
<li>Sure, I could store the terraform state in the repo using the local backend, but this is a public project so anyone could see it<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>, and I feel like it&rsquo;s a bad practice anyway. The solution? Use a remote state backend.</li>
<li>Generally I&rsquo;m using AWS, so I use S3 and DynamoDB for state and state-locking respectively, but this project isn&rsquo;t on AWS. It&rsquo;s on DigitalOcean. What to do? 🤔</li>
<li>Terraform Cloud provides <a href="https://www.terraform.io/docs/cloud/overview.html#remote-state-management-data-sharing-and-run-triggers">remote state</a> for free. Even better, I can <a href="https://www.terraform.io/docs/cloud/workspaces/variables.html">securely store</a> the DO token here as well. That means I don&rsquo;t have to worry about how to distribute the token between development machines. Noice.</li>
</ul>
</li>
</ol>
<h2 id="digitalocean">DigitalOcean</h2>
<p>So, what did I have at this point?</p>
<ul>
<li>A DO account</li>
<li>A TFC account and workspace, configured with my DO <code>Personal Access Token</code> exported as a sensitive <code>Workspace variable</code> called <code>DIGITALOCEAN_TOKEN</code></li>
<li>A nearly empty <code>main.tf</code></li>
</ul>
<p>First things first, I needed to set up the DO provider in TF so I updated the <code>main.tf</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#75715e"># main.tf
</span><span style="color:#75715e"></span><span style="color:#a6e22e">terraform</span> {
  <span style="color:#a6e22e">backend</span> <span style="color:#e6db74">&#34;remote&#34;</span> {
    <span style="color:#a6e22e">organization</span> = <span style="color:#e6db74">&#34;ljones&#34;</span>

    <span style="color:#a6e22e">workspaces</span> {
      <span style="color:#a6e22e">name</span> = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
    }
  }
  <span style="color:#a6e22e">required_providers</span> {
    <span style="color:#a6e22e">digitalocean</span> = {
      <span style="color:#a6e22e">source</span> = <span style="color:#e6db74">&#34;digitalocean/digitalocean&#34;</span>
      <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;~&gt; 2.16.0&#34;</span>
    }
  }
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;digitalocean&#34;</span> {}
</code></pre></div><p>The <code>~&gt;</code> means that the DO provider can update to any <code>2.16.x</code> version. Side node, it&rsquo;s important to run <code>terraform init</code> after updating providers so that new providers are downloaded (if running locally) and the <code>.terraform.lock.hcl</code> file is created. The TL;DR is that this file keeps helps keep things consistent, and it should be committed to version control with the rest of the code.</p>
<p>Before I went slapping more TF code in there, I had a look around the DO console. Featured quite prominently was <code>first-project</code>. Huh, indeed. What&rsquo;s that?</p>
<blockquote>
<p>Projects let you organize your DigitalOcean resources (like Droplets, Spaces, and load balancers) into groups that fit the way you work. You can create projects that align with the applications, environments, and clients that you host on DigitalOcean.</p>
</blockquote>
<p>Yep, cool, I definitely want one of those to house all the resources associated with this, uh, project. Back to the documentation again and lo, there is indeed a <a href="https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/project">resource for managing DO projects</a>. I added the below to my <code>main.tf</code> and ran a <code>plan</code> operation.</p>
<blockquote>
<p>Note, I usually have <code>terraform</code> aliased to <code>tf</code> for convenience, so don&rsquo;t be surprised when you see <code>tf</code> being used in the terminal!</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;digitalocean_project&#34;</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span> {
  <span style="color:#a6e22e">name</span>        = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;DigitalOcean Kubernetes Challenge 2021&#34;</span>
  <span style="color:#a6e22e">purpose</span>     = <span style="color:#e6db74">&#34;Deploy a security and compliance system&#34;</span>
}
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ tf plan
Running plan in the remote backend. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

To view this run in a browser, visit:
https://app.terraform.io/app/ljones/do-k8s-2021/runs/run-bVsGoY2hdCxXkpDY

Waiting <span style="color:#66d9ef">for</span> the plan to start...

Terraform v1.0.11
on linux_amd64
Configuring remote state backend...
Initializing Terraform configuration...

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  <span style="color:#75715e"># digitalocean_project.do-k8s-2021 will be created</span>
  + resource <span style="color:#e6db74">&#34;digitalocean_project&#34;</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span> <span style="color:#f92672">{</span>
      + created_at  <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + description <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;DigitalOcean Kubernetes Challenge 2021&#34;</span>
      + environment <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Development&#34;</span>
      + id          <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + is_default  <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + name        <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
      + owner_id    <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + owner_uuid  <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + purpose     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Deploy a security and compliance system&#34;</span>
      + resources   <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
      + updated_at  <span style="color:#f92672">=</span> <span style="color:#f92672">(</span>known after apply<span style="color:#f92672">)</span>
    <span style="color:#f92672">}</span>

Plan: <span style="color:#ae81ff">1</span> to add, <span style="color:#ae81ff">0</span> to change, <span style="color:#ae81ff">0</span> to destroy.
</code></pre></div><p>Plan looked good, time for a run.</p>
<blockquote>
<p>Another neat thing about using TFC is that my TF <code>plan|apply</code> actions were shown live in the web UI as well as my terminal. It also keeps a history of these actions. This is less important maybe for a 1-person project but I see real value for teams.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ tf apply
Running apply in the remote backend. Output will stream here. Pressing Ctrl-C
will cancel the remote apply <span style="color:#66d9ef">if</span> it<span style="color:#e6db74">&#39;s still pending. If the apply started it
</span><span style="color:#e6db74">will stop streaming the logs, but will not stop the apply running remotely.
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Preparing the remote apply...
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">To view this run in a browser, visit:
</span><span style="color:#e6db74">https://app.terraform.io/app/ljones/do-k8s-2021/runs/run-QnJoQ2wVpHnqt1iv
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Waiting for the plan to start...
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Terraform v1.0.11
</span><span style="color:#e6db74">on linux_amd64
</span><span style="color:#e6db74">Configuring remote state backend...
</span><span style="color:#e6db74">Initializing Terraform configuration...
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Terraform used the selected providers to generate the following execution
</span><span style="color:#e6db74">plan. Resource actions are indicated with the following symbols:
</span><span style="color:#e6db74">  + create
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Terraform will perform the following actions:
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">  # digitalocean_project.do-k8s-2021 will be created
</span><span style="color:#e6db74">  + resource &#34;digitalocean_project&#34; &#34;do-k8s-2021&#34; {
</span><span style="color:#e6db74">      + created_at  = (known after apply)
</span><span style="color:#e6db74">      + description = &#34;DigitalOcean Kubernetes Challenge 2021&#34;
</span><span style="color:#e6db74">      + environment = &#34;Development&#34;
</span><span style="color:#e6db74">      + id          = (known after apply)
</span><span style="color:#e6db74">      + is_default  = (known after apply)
</span><span style="color:#e6db74">      + name        = &#34;do-k8s-2021&#34;
</span><span style="color:#e6db74">      + owner_id    = (known after apply)
</span><span style="color:#e6db74">      + owner_uuid  = (known after apply)
</span><span style="color:#e6db74">      + purpose     = &#34;Deploy a security and compliance system&#34;
</span><span style="color:#e6db74">      + resources   = (known after apply)
</span><span style="color:#e6db74">      + updated_at  = (known after apply)
</span><span style="color:#e6db74">    }
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Plan: 1 to add, 0 to change, 0 to destroy.
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">Do you want to perform these actions in workspace &#34;do-k8s-2021&#34;?
</span><span style="color:#e6db74">  Terraform will perform the actions described above.
</span><span style="color:#e6db74">  Only &#39;</span>yes<span style="color:#960050;background-color:#1e0010">&#39;</span> will be accepted to approve.

  Enter a value: yes

digitalocean_project.do-k8s-2021: Creating...
digitalocean_project.do-k8s-2021: Creation complete after 2s <span style="color:#f92672">[</span>id<span style="color:#f92672">=</span>3401883b-cbc0-4889-ad45-a8d30051cd28<span style="color:#f92672">]</span>

Apply complete! Resources: <span style="color:#ae81ff">1</span> added, <span style="color:#ae81ff">0</span> changed, <span style="color:#ae81ff">0</span> destroyed.
</code></pre></div><blockquote>
<p>I&rsquo;m not going to show the plan/apply output every time, by the way. I figure it might be helpful for those that haven&rsquo;t used it before though.</p>
</blockquote>
<p>I checked in the DO console and&hellip; <em>drum roll</em>&hellip; success! Terraform hadn&rsquo;t been lying to me after all.</p>
<h2 id="kubernetes-k8s">Kubernetes (k8s)</h2>
<p>It was about this point that I found out Kubernetes on DO is called DigitalOcean Kubernetes or DOKS, and this entire exercise should have been called doks-challenge-2021 or something like that. Oh well.</p>
<p>At this point I had:</p>
<ul>
<li>DO project ready</li>
<li>TFC operational</li>
<li>A mostly empty <code>main.tf</code></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#a6e22e">terraform</span> {
  <span style="color:#a6e22e">backend</span> <span style="color:#e6db74">&#34;remote&#34;</span> {
    <span style="color:#a6e22e">organization</span> = <span style="color:#e6db74">&#34;ljones&#34;</span>

    <span style="color:#a6e22e">workspaces</span> {
      <span style="color:#a6e22e">name</span> = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
    }
  }
  <span style="color:#a6e22e">required_providers</span> {
    <span style="color:#a6e22e">digitalocean</span> = {
      <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;digitalocean/digitalocean&#34;</span>
      <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;~&gt; 2.16.0&#34;</span>
    }
  }
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;digitalocean&#34;</span> {}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;digitalocean_project&#34;</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span> {
  <span style="color:#a6e22e">name</span>        = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;DigitalOcean Kubernetes Challenge 2021&#34;</span>
  <span style="color:#a6e22e">purpose</span>     = <span style="color:#e6db74">&#34;Deploy a security and compliance system&#34;</span>
}
</code></pre></div><p>The next step on my journey was actually deploying a Kubernetes cluster. At this point I just started skimming through the DO documentation on k8s, particularly <a href="https://docs.digitalocean.com/products/kubernetes/how-to/create-clusters/">cluster creation</a>. I saw there was a HA-cluster option but nah, this isn&rsquo;t that serious. I read up a little on DO vpcs, regions, block storage and load balancers. Surprisingly<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> they don&rsquo;t have an Australian region, but they do have Singapore and as I&rsquo;m on the western coast of Australia, that&rsquo;s pretty much as good as the eastern coast where all the cloud providers host their datacentres.</p>
<p>Then it was back to the TF DO documentation, eventually leading to these updates to <code>main.tf</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;digitalocean_project&#34;</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span> {
  <span style="color:#a6e22e">name</span>        = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;DigitalOcean Kubernetes Challenge 2021&#34;</span>
  <span style="color:#a6e22e">purpose</span>     = <span style="color:#e6db74">&#34;Deploy a security and compliance system&#34;</span>
  <span style="color:#a6e22e">resources</span> = [
    <span style="color:#e6db74">&#34;do:kubernetes:</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">id</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e"> # this is a bit tedious
</span><span style="color:#75715e"></span>  ]
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;digitalocean_vpc&#34;</span> <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span> {
  <span style="color:#a6e22e">name</span>   = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
  <span style="color:#a6e22e">region</span> = <span style="color:#e6db74">&#34;sgp1&#34;</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">data</span> <span style="color:#e6db74">&#34;digitalocean_kubernetes_versions&#34;</span> <span style="color:#e6db74">&#34;doks&#34;</span> {
  <span style="color:#a6e22e">version_prefix</span> = <span style="color:#e6db74">&#34;1.21.&#34;</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;digitalocean_kubernetes_cluster&#34;</span> <span style="color:#e6db74">&#34;doks&#34;</span> {
  <span style="color:#a6e22e">name</span>         = <span style="color:#e6db74">&#34;do-k8s-2021&#34;</span>
  <span style="color:#a6e22e">region</span>       = <span style="color:#e6db74">&#34;sgp1&#34;</span>
  <span style="color:#a6e22e">auto_upgrade</span> = <span style="color:#66d9ef">true</span>
  <span style="color:#a6e22e">version</span>      = data.<span style="color:#a6e22e">digitalocean_kubernetes_versions</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">latest_version</span>

  <span style="color:#a6e22e">vpc_uuid</span> = <span style="color:#a6e22e">digitalocean_vpc</span>.<span style="color:#a6e22e">do</span><span style="color:#f92672">-</span><span style="color:#a6e22e">k8s</span><span style="color:#ae81ff">-2021</span>.<span style="color:#a6e22e">id</span>

  <span style="color:#a6e22e">maintenance_policy</span> {
    <span style="color:#a6e22e">start_time</span> = <span style="color:#e6db74">&#34;16:00&#34;</span>
    <span style="color:#a6e22e">day</span>        = <span style="color:#e6db74">&#34;friday&#34;</span>
  }

  <span style="color:#a6e22e">node_pool</span> {
    <span style="color:#a6e22e">name</span>       = <span style="color:#e6db74">&#34;autoscale-worker-pool&#34;</span>
    <span style="color:#a6e22e">size</span>       = <span style="color:#e6db74">&#34;s-1vcpu-2gb&#34;</span><span style="color:#75715e"> # 1 cpu, 2gb ram (1gb useable)
</span><span style="color:#75715e"></span>    <span style="color:#a6e22e">auto_scale</span> = <span style="color:#66d9ef">true</span>
    <span style="color:#a6e22e">min_nodes</span>  = <span style="color:#ae81ff">1</span>
    <span style="color:#a6e22e">max_nodes</span>  = <span style="color:#ae81ff">3</span><span style="color:#75715e"> # this is the maximum allowed nodes on my new account without requesting an increase
</span><span style="color:#75715e"></span>  }
}
</code></pre></div><p>Applying, which creates the cluster, took about 6 minutes for me though that may vary for you depending on region and node size. It worked without a hitch though, noice.</p>
<p>The last thing I wanted to do was get the new k8s cluster into TF, so I can deploy manifests or helm charts. More reading of documentation, though this is actually something I&rsquo;m already doing over in AWS EKS land so there wasn&rsquo;t much to learn.</p>
<ul>
<li>Added the <code>kubernetes</code> tf provider</li>
<li>Added the <code>helm</code> tf provider</li>
<li>Initialised tf again</li>
<li>Tested creating/destroying a namespace via tf just to make sure everything was 👌</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf">  <span style="color:#a6e22e">required_providers</span> {
    ...
    <span style="color:#a6e22e">helm</span> = {
      <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/helm&#34;</span>
      <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;~&gt; 2.4.0&#34;</span>
    }
    <span style="color:#a6e22e">kubernetes</span> = {
      <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/kubernetes&#34;</span>
      <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;~&gt; 2.6.0&#34;</span>
    }
  }

...
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;kubernetes&#34;</span> {
  <span style="color:#a6e22e">host</span>                   = <span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">host</span>
  <span style="color:#a6e22e">token</span>                  = <span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">token</span>
  <span style="color:#a6e22e">cluster_ca_certificate</span> = base64decode(<span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">cluster_ca_certificate</span>)
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;helm&#34;</span> {
  <span style="color:#a6e22e">kubernetes</span> {
    <span style="color:#a6e22e">host</span>                   = <span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">host</span>
    <span style="color:#a6e22e">token</span>                  = <span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">token</span>
    <span style="color:#a6e22e">cluster_ca_certificate</span> = base64decode(<span style="color:#a6e22e">digitalocean_kubernetes_cluster</span>.<span style="color:#a6e22e">doks</span>.<span style="color:#a6e22e">kube_config</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">cluster_ca_certificate</span>)
  }
}
</code></pre></div><h2 id="falco">Falco</h2>
<p>Here we are, the final chapter. It was time to install Falco. At this point I had:</p>
<ul>
<li>DOKS operational and ready to be interacted with via tf</li>
</ul>
<blockquote>
<p>You may have noticed that I&rsquo;m still only using a single file, <code>main.tf</code>.</p>
<p>I&rsquo;m a fan of keeping things together until splitting them out actually makes my life easier, but this is maybe a style no-no for terraform. Sometimes empty files are used just to indicate the absence of that type of resource, for example, an empty <code>outputs.tf</code> indicating a module doesn&rsquo;t have outputs.</p>
<p>For a small greenfield project where I&rsquo;m the only one working on it, it really doesn&rsquo;t matter though.</p>
</blockquote>
<p>I went digging around and found the <a href="https://github.com/falcosecurity/charts/tree/master/falco">helm repository</a> and accompanying documentation. Looked like it should run a basic install without any tweaking of the chart values, so I left everything to default.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;helm_release&#34;</span> <span style="color:#e6db74">&#34;falco&#34;</span> {
  <span style="color:#a6e22e">name</span>       = <span style="color:#e6db74">&#34;falco&#34;</span>
  <span style="color:#a6e22e">repository</span> = <span style="color:#e6db74">&#34;https://falcosecurity.github.io/charts&#34;</span>
  <span style="color:#a6e22e">chart</span>      = <span style="color:#e6db74">&#34;falco&#34;</span>
  <span style="color:#a6e22e">version</span>    = <span style="color:#e6db74">&#34;1.16.2&#34;</span>

  <span style="color:#a6e22e">namespace</span>        = <span style="color:#e6db74">&#34;falco&#34;</span>
  <span style="color:#a6e22e">create_namespace</span> = <span style="color:#66d9ef">true</span>
}
</code></pre></div><p>The tf apply was successful, and accessing the k8s dashboard via the DO console, I could see Falco had installed and was operational!</p>
<pre><code class="language-log" data-lang="log">* Setting up /usr/src links from host
* Running falco-driver-loader for: falco version=0.30.0, driver version=3aa7a83bf7b9e6229a3824e3fd1f4452d1e95cb4
* Running falco-driver-loader with: driver=module, compile=yes, download=yes
* Unloading falco module, if present
* Trying to load a system falco module, if present
* Looking for a falco module locally (kernel 4.19.0-17-cloud-amd64)
* Trying to download a prebuilt falco module from https://download.falco.org/driver/3aa7a83bf7b9e6229a3824e3fd1f4452d1e95cb4/falco_debian_4.19.0-17-cloud-amd64_1.ko
* Download succeeded
* Success: falco module found and inserted
Mon Nov 22 11:33:41 2021: Falco version 0.30.0 (driver version 3aa7a83bf7b9e6229a3824e3fd1f4452d1e95cb4)
Mon Nov 22 11:33:41 2021: Falco initialized with configuration file /etc/falco/falco.yaml
Mon Nov 22 11:33:41 2021: Loading rules from file /etc/falco/falco_rules.yaml:
Mon Nov 22 11:33:41 2021: Loading rules from file /etc/falco/falco_rules.local.yaml:
Mon Nov 22 11:33:42 2021: Starting internal webserver, listening on port 8765
11:33:42.054459000: Notice Container with sensitive mount started (user=daemon user_loginuid=0 command=container:9f7394213f5d k8s.ns=kube-system k8s.pod=do-node-agent-tf8t9 container=9f7394213f5d image=docker.io/digitalocean/do-agent:3 mounts=/proc:/host/proc::false:private,/sys:/host/sys::false:private,/:/host/root::false:rslave,/var/lib/kubelet/pods/a8eac910-fb2e-40ea-bc13-2cfd181d8750/volumes/kubernetes.io~projected/kube-api-access-ctq56:/var/run/secrets/kubernetes.io/serviceaccount::false:private,/var/lib/kubelet/pods/a8eac910-fb2e-40ea-bc13-2cfd181d8750/etc-hosts:/etc/hosts::true:private,/var/lib/kubelet/pods/a8eac910-fb2e-40ea-bc13-2cfd181d8750/containers/do-node-agent/21e37c0b:/dev/termination-log::true:private) k8s.ns=kube-system k8s.pod=do-node-agent-tf8t9 container=9f7394213f5d
...
</code></pre><p>Thing is, nothing much was going on really. I had an essentially empty cluster at this point, so that made sense, but still! Happily, the falco helm chart actually has the ability to generate fake events. To do so, I added the below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;helm_release&#34;</span> <span style="color:#e6db74">&#34;falco&#34;</span> {
...
  <span style="color:#a6e22e">set</span> {
    <span style="color:#a6e22e">name</span>  = <span style="color:#e6db74">&#34;fakeEventGenerator.enabled&#34;</span>
    <span style="color:#a6e22e">value</span> = <span style="color:#e6db74">&#34;true&#34;</span>
  }
}
</code></pre></div><p>Tailing the logs once again, there was plenty going on now.</p>
<pre><code class="language-log" data-lang="log">11:49:55.808634583: Warning Sensitive file opened for reading by trusted program after startup (user=root user_loginuid=-1 command=httpd --loglevel info run ^syscall.ReadSensitiveFileUntrusted$ --sleep 6s parent=httpd file=/etc/shadow parent=httpd gparent=event-generator container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:55.911658311: Error File below a known binary directory opened for writing (user=root user_loginuid=-1 command=event-generator run --loop ^syscall file=/bin/created-by-event-generator parent=event-generator pcmdline=event-generator run --loop ^syscall gparent=&lt;NA&gt; container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:55.911718964: Error File below known binary directory renamed/removed (user=root user_loginuid=-1 command=event-generator run --loop ^syscall pcmdline=event-generator run --loop ^syscall operation=unlinkat file=&lt;NA&gt; res=0 dirfd=-100(AT_FDCWD) name=/bin/created-by-event-generator flags=0  container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.013072532: Notice System user ran an interactive command (user=bin user_loginuid=-1 command=login container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.294084849: Debug Shell spawned by untrusted binary (user=root user_loginuid=-1 shell=bash parent=httpd cmdline=bash -c ls &gt; /dev/null pcmdline=httpd --loglevel info run ^helper.RunShell$ gparent=httpd ggparent=event-generator aname[4]=event-generator aname[5]=&lt;NA&gt; aname[6]= aname[7]= container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.501997391: Error File below /etc opened for writing (user=root user_loginuid=-1 command=event-generator run --loop ^syscall parent=event-generator pcmdline=event-generator run --loop ^syscall file=/etc/created-by-event-generator program=event-generator gparent=&lt;NA&gt; ggparent= gggparent= container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.602813830: Error File created below /dev by untrusted program (user=root user_loginuid=-1 command=event-generator run --loop ^syscall file=/dev/created-by-event-generator container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.703339933: Warning Sensitive file opened for reading by non-trusted program (user=root user_loginuid=-1 program=event-generator command=event-generator run --loop ^syscall file=/etc/shadow parent=event-generator gparent=&lt;NA&gt; ggparent= gggparent= container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:56.963228274: Notice Database-related program spawned process other than itself (user=root user_loginuid=-1 program=ls parent=mysqld container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
11:49:57.233889340: Notice Known system binary sent/received network traffic (user=root user_loginuid=-1 command=sha1sum --loglevel info run ^helper.NetworkActivity$ connection=10.244.0.83:49851-&gt;10.2.3.4:8192 container_id=64b9a21e112e image=docker.io/falcosecurity/event-generator) k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e k8s.ns=falco k8s.pod=falco-event-generator-84d64cb8fb-j9tpl container=64b9a21e112e
</code></pre><p>Tailing the actual event generator pod, I saw corresponding logs.</p>
<pre><code class="language-log" data-lang="log">INFO sleep for 100ms                               action=syscall.WriteBelowEtc
INFO writing to /etc/created-by-event-generator    action=syscall.WriteBelowEtc
INFO sleep for 100ms                               action=syscall.SystemUserInteractive
INFO run command as another user                   action=syscall.SystemUserInteractive cmdArgs=&quot;[]&quot; cmdName=/bin/login user=daemon
INFO sleep for 100ms                               action=syscall.SystemProcsNetworkActivity
INFO spawn as &quot;sha1sum&quot;                            action=syscall.SystemProcsNetworkActivity args=&quot;^helper.NetworkActivity$&quot;
INFO sleep for 100ms                               action=helper.NetworkActivity as=sha1sum
INFO action executed                               action=helper.NetworkActivity as=sha1sum
INFO sleep for 100ms                               action=syscall.ReadSensitiveFileTrustedAfterStartup
INFO spawn as &quot;httpd&quot;                              action=syscall.ReadSensitiveFileTrustedAfterStartup args=&quot;^syscall.ReadSensitiveFileUntrusted$ --sleep 6s&quot;
INFO sleep for 6s                                  action=syscall.ReadSensitiveFileUntrusted as=httpd
INFO action executed                               action=syscall.ReadSensitiveFileUntrusted as=httpd
INFO sleep for 100ms                               action=syscall.WriteBelowBinaryDir
INFO writing to /bin/created-by-event-generator    action=syscall.WriteBelowBinaryDir
</code></pre><p>I didn&rsquo;t actually need this running fake events forever, so I reverted the helm chart back to defaults and applied again.</p>
<h2 id="job-done">Job done</h2>
<p>That&rsquo;s actually the challenge complete. As you can see, Terraform and managed Kubernetes providers like DigitalOcean make it pretty easy to get up and running quickly.</p>
<p>Considering I&rsquo;ve still got a month or so to go, I&rsquo;ll probably try some of the other challenges, or extend this one a bit further. Getting Loki and Grafana going with Falco sounds cool 🤔</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Did you see what I did there 😎&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Okay so I might have kicked the tires when it first launched, but this will be the first real-world use&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>Have I mentioned how much I appreciate HashiCorp?&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>That&rsquo;s bad&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p>Well, to me at least&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>DigitalOcean Kubernetes Challenge 2021 - Challenge Accepted!</title>
            <link>https://ljones.id.au/posts/2021/11/digitalocean-kubernetes-challenge-2021-challenge-accepted/</link>
            <pubDate>Sat, 20 Nov 2021 01:24:37 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/digitalocean-kubernetes-challenge-2021-challenge-accepted/</guid>
            <description>In my lifelong tradition of taking on new things before finishing anything, I&amp;rsquo;ve decided to take on the DigitalOcean Kubernetes Challenge. It should be useful for practicing my Terraform and Kubernetes-fu while also learning about the DigitalOcean platform. Specifically, I&amp;rsquo;m going to execute the Falco related challenge.
 Deploy a security and compliance system
When you deploy an image you may also deploy a security threat, and images can become quickly insecure as new threats are discovered and disclosed.</description>
            <content type="html"><![CDATA[<p>In my lifelong tradition of taking on new things before finishing anything, I&rsquo;ve decided to take on the <a href="https://www.digitalocean.com/community/pages/kubernetes-challenge">DigitalOcean Kubernetes Challenge</a>. It should be useful for practicing my Terraform and Kubernetes-fu while also learning about the DigitalOcean platform. Specifically, I&rsquo;m going to execute the <a href="https://falco.org/">Falco</a> related challenge.</p>
<blockquote>
<p><strong>Deploy a security and compliance system</strong></p>
<p>When you deploy an image you may also deploy a security threat, and images can become quickly insecure as new threats are discovered and disclosed. To continuously monitor and detect threats, you can deploy a tool like Falco.</p>
</blockquote>
<p>I&rsquo;ll be making a separate post on my process and experience, which I&rsquo;ll link to from here once complete. Update: <a href="../digitalocean-kubernetes-challenge-2021-challenge-complete">here it is</a>!</p>
<p>As a general comment, I&rsquo;ve been really happy to see DO go from strength to strength. Competition in the marketplace is always<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> a good thing, and I think they were early arrivals in a now increasingly crowded group of low/lower-complexity alternatives to AWS.</p>
<p>If you&rsquo;re interested in particpating too, use my <a href="https://m.do.co/c/ec6ba7af928e">referral code</a> for an extra $100 credit. Not too shabby!</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>I&rsquo;m not an economist. 😅&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>HashiCorp Vault Associate Study Plan - 1/10</title>
            <link>https://ljones.id.au/posts/2021/11/hashicorp-vault-associate-study-plan-1/10/</link>
            <pubDate>Thu, 11 Nov 2021 13:13:01 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/hashicorp-vault-associate-study-plan-1/10/</guid>
            <description>Oh boy, here I go again, studying and writing notes for a HashiCorp cert. This time it&amp;rsquo;s the Vault Associate. As before, hopefully they&amp;rsquo;re of use to me, and you!
I&amp;rsquo;ll be posting each major exam area individually, to keep things human.
 1. Compare authentication methods 1a. Describe authentication methods  Auth methods are how Vault assigns identity/policy to a user Multiple auth methods available, covering many use cases  e.</description>
            <content type="html"><![CDATA[<p>Oh boy, here I go again, studying and writing notes for a HashiCorp cert. This time it&rsquo;s the <a href="https://learn.hashicorp.com/tutorials/vault/associate-study?in=vault/associate-cert">Vault Associate</a>. As before, hopefully they&rsquo;re of use to me, and you!</p>
<p>I&rsquo;ll be posting each major exam area individually, to keep things human.</p>
<hr>
<h2 id="1-compare-authentication-methods">1. Compare authentication methods</h2>
<h3 id="1a-describe-authentication-methods">1a. Describe authentication methods</h3>
<ul>
<li>Auth methods are how Vault assigns identity/policy to a user</li>
<li>Multiple auth methods available, covering many use cases
<ul>
<li>e.g. GitHub auth method is useful for people, AppRole is useful for machines</li>
</ul>
</li>
<li>Auth methods are enabled/disabled by CLI or API</li>
<li>All auth methods are mounted under the <code>/auth</code> prefix, however the path can be customised</li>
<li>The same auth method can be mounted multiple times</li>
<li>After a client authenticates against an auth method, they are issued a token. The token may have attached policy. The policy is mapped at auth time</li>
<li>It&rsquo;s not possible to force a user to auth against multiple auth methods, though MFA is possible with some backends</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh"><span style="color:#75715e"># enable the userpass auth method</span>
$ vault auth enable userpass
Success! Enabled userpass auth method at: userpass/

<span style="color:#75715e"># enable the userpass auth method at another path</span>
$ vault auth enable -path<span style="color:#f92672">=</span>my-login userpass
Success! Enabled userpass auth method at: my-login/

<span style="color:#75715e"># an alternate way of enabling an auth method</span>
$ vault write sys/auth/my-auth type<span style="color:#f92672">=</span>userpass
Success! Data written to: sys/auth/my-auth

<span style="color:#75715e"># list auth methods</span>
$ vault auth list
Path         Type        Accessor                  Description
----         ----        --------                  -----------
my-auth/     userpass    auth_userpass_23a812e6    n/a
my-login/    userpass    auth_userpass_57e8bdd5    n/a
token/       token       auth_token_8fe964a8       token based credentials
userpass/    userpass    auth_userpass_ae1dbd20    n/a
</code></pre></div><h3 id="1b-choose-an-authentication-method-based-on-use-case">1b. Choose an authentication method based on use case</h3>
<p>Q. A developer wants to login to the Vault UI to check that some secrets exist. All the developers in the organisation are using GitHub
A. Use the GitHub auth method</p>
<p>Q. An operations engineer needs to configure a new auth method on a vault server. The organisation uses Microsoft AD to manage their staff identities
A. Use the LDAP auth method</p>
<p>Q. A pod on Kubernetes needs to authenticate against Vault to pull secrets needed to run an application
A. Use the Kubernetes auth method</p>
<h3 id="1c-differentiate-human-vs-system-auth-methods">1c. Differentiate human vs. system auth methods</h3>
<ul>
<li>Human auth methods are typically intended to be accessed by the CLI or UI</li>
<li>System auth methods typically use the API</li>
</ul>
<hr>
<p>Here ends part one! 1c. looks a little skinny to me but I really didn&rsquo;t get much more from the docs. I&rsquo;ll add to it if more comes up later.</p>
]]></content>
        </item>
        
        <item>
            <title>HashiCorp Certified Terraform Associate</title>
            <link>https://ljones.id.au/posts/2021/11/hashicorp-certified-terraform-associate/</link>
            <pubDate>Tue, 09 Nov 2021 12:25:08 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/hashicorp-certified-terraform-associate/</guid>
            <description>I sat the exam for this today and passed with 87%, not as high as I&amp;rsquo;d been getting on practice exams but obviously good enough. In preparation, I had been writing up the study notes that I&amp;rsquo;ve provided below. They&amp;rsquo;re mostly condensed from the Hashicorp study guide so if wording sounds familiar, well, it&amp;rsquo;s probably copy-pasta.
Note I didn&amp;rsquo;t actually complete them before attempting the exam so you&amp;rsquo;ll still need to check the Hashicorp resources, but maybe they&amp;rsquo;ll still be of use to someone.</description>
            <content type="html"><![CDATA[<p>I sat the exam for this today and passed with 87%, not as high as I&rsquo;d been getting on practice exams but obviously good enough. In preparation, I had been writing up the study notes that I&rsquo;ve provided below. They&rsquo;re mostly condensed from the Hashicorp study guide so if wording sounds familiar, well, it&rsquo;s probably copy-pasta.</p>
<p>Note I didn&rsquo;t actually complete them before attempting the exam so you&rsquo;ll still need to check the Hashicorp resources, but maybe they&rsquo;ll still be of use to someone.</p>
<p>Good luck if that&rsquo;s you and you&rsquo;re planning to do the exam soon!</p>
<hr>
<p>I use <a href="https://www.terraform.io">Terraform</a> on a daily basis so while I&rsquo;m on the certification bandwagon I may as well go with the obvious choices. This post will be my study guide, as mapped to the <a href="https://learn.hashicorp.com/tutorials/terraform/associate-review?in=terraform/certification">exam objectives</a>.</p>
<p>Many thanks go to the comprehensive documentation and tutorials provided by Hashicorp, for Terraform and the rest of their products. ❤️</p>
<h2 id="1-understand-infrastructure-as-code-iac-concepts">1. Understand Infrastructure as Code (IaC) concepts</h2>
<h3 id="1a-explain-what-iac-is">1a. Explain what IaC is</h3>
<p>Infrastructure as Code (IaC) is the concept of describing infrastructure (e.g. compute resources, networks, firewalls etc) in human-readable declarative configuration files.</p>
<h3 id="1b-describe-advantages-of-iac-patterns">1b. Describe advantages of IaC patterns</h3>
<ul>
<li>Idempotent; safe to apply code multiple times without resulting in unwanted/duplicate infrastructure. If part of a deploy fails, you can safely resolve and apply again.</li>
<li>Consistent; no opportunity for an engineer to accidentally miss a step while building out infrastructure.</li>
<li>Repeatable; the same code can be applied across many environments, or used to build/tear down the same environment (e.g. dev environments).</li>
<li>Predictable; only the indicated changes are applied.</li>
<li>Commitable (is that even a word?); the IaC can be checked into version control systems (e.g. git)
<ul>
<li>Enables GitOps flows, where changes can automatically be applied provided that they pass tests.</li>
<li>Allows for pull requests, easy review of planned changes.</li>
<li>Easily view history of changes to infrastructure, simple to roll back if required.</li>
</ul>
</li>
</ul>
<h2 id="2-understand-terraforms-purpose-vs-other-iac">2. Understand Terraform&rsquo;s purpose (vs other IaC)</h2>
<h3 id="2a-explain-multi-cloud-and-provider-agnostic-benefits">2a. Explain multi-cloud and provider-agnostic benefits</h3>
<ul>
<li>Multi-cloud helps provide extreme HA/fault-tolerance across services/regions.</li>
<li>Provider-agnostisic benefits
<ul>
<li>Single control plane for multi-cloud; action changes in multiple clouds with a single command.</li>
<li>Single consistent configuration syntax for operators to learn.</li>
</ul>
</li>
</ul>
<h3 id="2b-explain-the-benefits-of-state">2b. Explain the benefits of state</h3>
<ul>
<li>Maps resources to the real world, i.e. state allows Terraform to map <code>resource &quot;aws_instance&quot; &quot;foo&quot;</code> to <code>i-abcd1234</code>.</li>
<li>Tracks metadata, e.g. dependency order between resources.</li>
<li>Performance; while it&rsquo;s possible to refresh the entire state per <code>plan</code> operation in smaller infrastructures, this becomes less feasible at scale due to the time required and potential API limits. To address this issue, the state file can be used as a cache via <code>-refresh=false</code>, and/or plan resources can be targeted/limited via <code>-target</code>.</li>
<li>Syncing of state between users; use of a fully-featured state backend (e.g. S3 + DynamoDB, Consul) that &lsquo;locks&rsquo; the state will prevent <code>apply</code> operations from occuring simultaneously. TL;DR, single point of truth.</li>
</ul>
<h2 id="3-understand-terraform-basics">3. Understand Terraform basics</h2>
<h3 id="3a-handle-terraform-and-provider-installation-and-versioning">3a. Handle Terraform and provider installation and versioning</h3>
<p>Terraform installation</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># Compiled from source</span>
git clone https://github.com/hashicorp/terraform.git
cd terraform
go install

<span style="color:#75715e"># Platform specific pre-compilied binaries available on the Terraform website</span>
wget https://releases.hashicorp.com/terraform/1.0.3/terraform_1.0.3_darwin_amd64.zip

<span style="color:#75715e"># Platform specific package management tools</span>

<span style="color:#75715e">## Homebrew on OS X</span>
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

<span style="color:#75715e">## Chocolatey on Windows</span>
choco install terraform

<span style="color:#75715e"># Apt/yum/dnf on various Linux.</span>

<span style="color:#75715e">## Ubuntu/Debian</span>
sudo apt-get update <span style="color:#f92672">&amp;&amp;</span> sudo apt-get install -y gnupg software-properties-common curl
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
sudo apt-add-repository <span style="color:#e6db74">&#34;deb [arch=amd64] https://apt.releases.hashicorp.com </span><span style="color:#66d9ef">$(</span>lsb_release -cs<span style="color:#66d9ef">)</span><span style="color:#e6db74"> main&#34;</span>
sudo apt-get update <span style="color:#f92672">&amp;&amp;</span> sudo apt-get install terraform
</code></pre></div><p>Terraform versioning</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#75715e"># versions.tf
</span><span style="color:#75715e"></span><span style="color:#a6e22e">terraform</span> {
  <span style="color:#a6e22e">required_version</span> <span style="color:#e6db74">&#34;&gt;= 0.14&#34;</span>
}
</code></pre></div><ul>
<li>Provider definition and version definition</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#75715e"># main.tf
</span><span style="color:#75715e"></span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;aws&#34;</span> {
  <span style="color:#a6e22e">region</span> = <span style="color:#e6db74">&#34;us-west-2&#34;</span>
}<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e"># versions.tf
</span><span style="color:#75715e"></span><span style="color:#a6e22e">terraform</span> {
  <span style="color:#a6e22e">required_providers</span> {
    <span style="color:#a6e22e">aws</span> = {
        <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/aws&#34;</span>
        <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;&gt;= 2.0.0&#34;</span>
    }
  }
}
</code></pre></div><p>Provider installation and versioning via <code>.terraform.lock.hcl</code>.</p>
<ul>
<li>Running <code>init</code> will download/install modules as necessary, and store specific versioning information in the <code>.terraform.lock.hcl</code> file.
<ul>
<li>This file should be version-controlled to ensure consistency across teams and ephemeral Terraform execution environments.</li>
<li>This file should not be directly modified.</li>
</ul>
</li>
<li>Running <code>init -upgrade</code> will upgrade all providers within given version constraints.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/random from the dependency lock file
- Installing hashicorp/aws v2.50.0...
- Installed hashicorp/aws v2.50.0 <span style="color:#f92672">(</span>signed by HashiCorp<span style="color:#f92672">)</span>

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running <span style="color:#e6db74">&#34;terraform plan&#34;</span> to seeany changes that are required <span style="color:#66d9ef">for</span> your infrastructure. All Terraform commands should now work.

$ cat .terraform.lock.hcl
<span style="color:#75715e"># This file is maintained automatically by &#34;terraform init&#34;.</span>
<span style="color:#75715e"># Manual edits may be lost in future updates.</span>

provider <span style="color:#e6db74">&#34;registry.terraform.io/hashicorp/aws&#34;</span> <span style="color:#f92672">{</span>
  version     <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;2.50.0&#34;</span>
  constraints <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;&gt;= 2.0.0&#34;</span>
  hashes <span style="color:#f92672">=</span> <span style="color:#f92672">[</span>
    <span style="color:#e6db74">&#34;h1:aKw4NLrMEAflsl1OXCCz6Ewo4ay9dpgSpkNHujRXXO8=&#34;</span>,
    <span style="color:#75715e">## ...</span>
    <span style="color:#e6db74">&#34;zh:fdeaf059f86d0ab59cf68ece2e8cec522b506c47e2cfca7ba6125b1cd06b8680&#34;</span>,
  <span style="color:#f92672">]</span>
<span style="color:#f92672">}</span>
</code></pre></div><h3 id="3b-describe-plug-in-based-architecture">3b. Describe plug-in based architecture</h3>
<ul>
<li>Terraform Core reads configuration files and builds up a resource dependency graph.</li>
<li>Terraform plugins (i.e. providers and provisioners) bridge the Core to respective target APIs, .e.g. the AWS provider plugin implements S3 resources via calls to the <a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_Operations_Amazon_Simple_Storage_Service.html">S3 API</a>.</li>
<li><code>plan</code> and <code>update</code> actions cause Core to ask the provider to perform an action via an RPC interface. The provider then attempts a CRUD action against the target API&rsquo;s client library.</li>
<li>Terraform is mainly used against cloud infrastructure but providers can be used to interface with any API to manage any resource.</li>
</ul>
<h3 id="3c-demonstrate-using-multiple-providers">3c. Demonstrate using multiple providers</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#75715e"># main.tf
</span><span style="color:#75715e"></span><span style="color:#66d9ef">provider</span> <span style="color:#e6db74">&#34;aws&#34;</span> {
  <span style="color:#a6e22e">region</span> = <span style="color:#e6db74">&#34;us-west-2&#34;</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;random_pet&#34;</span> <span style="color:#e6db74">&#34;petname&#34;</span> {
  <span style="color:#a6e22e">length</span>    = <span style="color:#ae81ff">5</span>
  <span style="color:#a6e22e">separator</span> = <span style="color:#e6db74">&#34;-&#34;</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_s3_bucket&#34;</span> <span style="color:#e6db74">&#34;sample&#34;</span> {
  <span style="color:#a6e22e">bucket</span> = <span style="color:#a6e22e">random_pet</span>.<span style="color:#a6e22e">petname</span>.<span style="color:#a6e22e">id</span>
  <span style="color:#a6e22e">acl</span>    = <span style="color:#e6db74">&#34;public-read&#34;</span>
  <span style="color:#a6e22e">region</span> = <span style="color:#e6db74">&#34;us-west-2&#34;</span>
}<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e"># versions.tf
</span><span style="color:#75715e"></span><span style="color:#a6e22e">terraform</span> {
  <span style="color:#a6e22e">required_providers</span> {
    <span style="color:#a6e22e">random</span> = {
      <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/random&#34;</span>
      <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;3.0.0&#34;</span>
    }

    <span style="color:#a6e22e">aws</span> = {
        <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/aws&#34;</span>
        <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;&gt;= 2.0.0&#34;</span>
    }
  }
}
</code></pre></div><h3 id="3d-describe-how-terraform-finds-and-fetches-providers">3d. Describe how Terraform finds and fetches providers</h3>
<p>When <code>init</code> is run, Terraform will attempt to download providers from the Terraform registry.</p>
<ul>
<li>If a provider is not on the registry or the registry is known to be unreachable (e.g. due to corporate firewalls), you can override the default registry by providing an &lsquo;Explicit Installation Method Configuration&rsquo; in the Terraform CLI config (<code>.terraformrc</code> or <code>terraform.rc</code>).</li>
<li>If the default registry fails and an override hasn&rsquo;t been provided, Terraform checks creates an <em>implied</em> override that checks for the provider in some default locations in the user path.</li>
</ul>
<p>When the provider has been found, <code>init</code> will download the provider into a subdirectory of the working directory so that each working directory is self-contained. This can lead to unneccessary duplication, so a provider cache can be specified via <code>plugin_cache_dir</code> in the Terraform CLI config.</p>
<ul>
<li>Terraform will not create the cache directory for you.</li>
<li>Terraform checks against the registry, then against the cache to see if it has a copy. If it does not, Terraform will download the plugin into the cache directory before copying it into the working directory. It will use symlinks where possible.</li>
<li>The plugin cache directory cannot be referenced by an explicit or implied install configuration.</li>
<li>The plugin cache directory does not self clean and will need to be manually cleaned over time.</li>
</ul>
<h3 id="3e-explain-when-to-use-and-not-use-provisioners-and-when-to-use-local-exec-or-remote-exec">3e. Explain when to use and not use provisioners and when to use local-exec or remote-exec</h3>
<blockquote>
<p>Provisioners can be used to model specific actions on the local machine or on a remote machine in order to prepare servers or other infrastructure objects for service.</p>
</blockquote>
<p>Provisioners should be used a last resort.</p>
<ul>
<li>Terraform cannot model provisoners as part of a <code>plan</code> as they technically could be doing anything.</li>
<li>Many more moving parts than are normally involved, e.g. direct network access to remote servers, credentials to login, potentially require pre-existing software to be installed on the remote.</li>
</ul>
<p>Potential use cases for <code>local-exec</code> or <code>remote-exec</code>.</p>
<ul>
<li>When new functionality has been introduced to a service that is not yet implemented in their provider plugin.</li>
<li>Passing data into virtual machines and other compute resources; however, often cloud services will provide their own mechanism for this e.g. <code>user_data</code> for AWS EC2.</li>
<li>Running configuration management software; however, Hashicorp recommend a custom image build process instead e.g. through the use of <a href="https://packer.io/">Packer</a>.</li>
</ul>
<h2 id="4-use-the-terraform-cli-outside-of-core-workflow">4. Use the Terraform CLI (outside of core workflow)</h2>
<h3 id="4a-given-a-scenario-choose-when-to-use-terraform-fmt-to-format-code">4a. Given a scenario: choose when to use terraform fmt to format code</h3>
<blockquote>
<p>The terraform fmt command is used to rewrite Terraform configuration files to a canonical format and style. This command applies a subset of the Terraform language style conventions, along with other minor adjustments for readability.</p>
<p>The format command scans the current directory for configuration files and rewrites your Terraform configuration files to the recommended format.</p>
</blockquote>
<ul>
<li><code>fmt</code> and the format/style applied should always be used, basically. It is intentionally opinionated to ensure consistency across teams and projects.</li>
<li>The canonical format may have minor changes between versions so Hashicorp recommend running <code>fmt</code> after upgrading.</li>
<li><code>fmt</code> will detect errors in your code and is useful for validation</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ terraform fmt
Error: Invalid character

  on main.tf line 46, in resource <span style="color:#e6db74">&#34;aws_instance&#34;</span> <span style="color:#e6db74">&#34;web_app&#34;</span>:
  46:     Name <span style="color:#f92672">=</span> $var.name-learn

This character is not used within the language.
</code></pre></div><h3 id="4b-given-a-scenario-choose-when-to-use-terraform-taint-to-taint-terraform-resources">4b. Given a scenario: choose when to use terraform taint to taint Terraform resources</h3>
<blockquote>
<p>The terraform taint command informs Terraform that a particular object has become degraded or damaged. Terraform represents this by marking the object as &ldquo;tainted&rdquo; in the Terraform state, in which case Terraform will propose to replace it in the next plan you create.</p>
<p>Replacing a resource is also useful in cases where a user manually changes a setting on a resource or when you need to update a provisioning script. This allows you to rebuild specific resources and avoid a full terraform destroy operation on your configuration.</p>
</blockquote>
<p><code>taint</code> is deprecated, instead use <code>-replace=&quot;resource&quot;</code> with <code>plan</code> or <code>apply</code> commands. The latter will allow you to see the full effect of the change, i.e. tainting a single resource may force the recreation of one or more other resources, which will be visible in the plan.</p>
<h3 id="4c-given-a-scenario-choose-when-to-use-terraform-import-to-import-existing-infrastructure-into-your-terraform-state">4c. Given a scenario: choose when to use terraform import to import existing infrastructure into your Terraform state</h3>
<blockquote>
<p>The terraform <code>import</code> command is used to import existing resources into Terraform.</p>
</blockquote>
<p><code>import</code> helps Terraform assume control over existing infrastructure or resources otherwise created outside of Terraform (and thus are not in the state file). This is as opposed to manually editing the state file which is not recommended.</p>
<p>Through <code>import</code>, Terraform can generate the appropriate code to represent your existing resources.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># Create an empty resource in terraform</span>
$ cat main.tf
resource <span style="color:#e6db74">&#34;docker_container&#34;</span> <span style="color:#e6db74">&#34;web&#34;</span> <span style="color:#f92672">{}</span>

<span style="color:#75715e"># Import the existing resource into terraform</span>
terraform import docker_container.web <span style="color:#66d9ef">$(</span>docker inspect --format<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;{{.ID}}&#34;</span> hashicorp-learn<span style="color:#66d9ef">)</span>

<span style="color:#75715e"># Check the state contains the resource as expected</span>
$ terraform show
<span style="color:#75715e"># docker_container.web:</span>
resource <span style="color:#e6db74">&#34;docker_container&#34;</span> <span style="color:#e6db74">&#34;web&#34;</span> <span style="color:#f92672">{</span>
  command <span style="color:#f92672">=</span> <span style="color:#f92672">[</span>
    <span style="color:#e6db74">&#34;nginx&#34;</span>,
    <span style="color:#e6db74">&#34;-g&#34;</span>,
    <span style="color:#e6db74">&#34;daemon off;&#34;</span>,
  <span style="color:#f92672">]</span>
<span style="color:#75715e"># ...</span>

<span style="color:#75715e"># Generate configuration from the state</span>
terraform show -no-color &gt; main.tf
$ cat main.tf
<span style="color:#75715e"># docker_container.web:</span>
resource <span style="color:#e6db74">&#34;docker_container&#34;</span> <span style="color:#e6db74">&#34;web&#34;</span> <span style="color:#f92672">{</span>
  command <span style="color:#f92672">=</span> <span style="color:#f92672">[</span>
    <span style="color:#e6db74">&#34;nginx&#34;</span>,
    <span style="color:#e6db74">&#34;-g&#34;</span>,
    <span style="color:#e6db74">&#34;daemon off;&#34;</span>,
  <span style="color:#f92672">]</span>
<span style="color:#75715e"># ...</span>
</code></pre></div><p>Note that dumping the state this way may contain attributes that cannot actually be assigned via the configuration. Be sure to run a <code>plan</code> to detect any issues and modify the resource as required.</p>
<h3 id="4d-given-a-scenario-choose-when-to-use-terraform-workspace-to-create-workspaces">4d. Given a scenario: choose when to use terraform workspace to create workspaces</h3>
<blockquote>
<p>Each Terraform configuration has an associated backend that defines how operations are executed and where persistent data such as the Terraform state are stored.</p>
<p>The persistent data stored in the backend belongs to a workspace. Initially the backend has only one workspace, called &ldquo;default&rdquo;, and thus there is only one Terraform state associated with that configuration.</p>
<p>Certain backends support multiple named workspaces, allowing multiple states to be associated with a single configuration. The configuration still has only one backend, but multiple distinct instances of that configuration to be deployed without configuring a new backend or changing authentication credentials.</p>
</blockquote>
<ul>
<li>Workspaces can be used to create multiple instances of infrastructure from the same Terraform configuration.</li>
<li>Workspaces are less suitable for separating environments, e.g. development from production, as each environment should have its own configuration, backend and workspaces.
<ul>
<li>They <em>may</em> be appropriate in this case, in the interests of avoiding duplicating code, if there is very little drift between the environments.</li>
<li>When different credentials and backends are required between environments, splitting environments by using directories is preferable, at the risk of duplicating code and allowing drift to creep in. Modules should be used to address both those issues, however.</li>
</ul>
</li>
<li>Workspaces are more suitable for creating short-lived infrastructures used during development, e.g. a developer might branch the main trunk, create a new workspace for testing/developing that branch and then delete the workspace after the branch is merged into main.</li>
<li>Workspaces can be referenced as variables, .e.g. <code>count = &quot;${terraform.workspace == &quot;default&quot; ? 5 : 1}&quot;</code></li>
</ul>
<h3 id="4e-given-a-scenario-choose-when-to-use-terraform-state-to-view-terraform-state">4e. Given a scenario: choose when to use terraform state to view Terraform state</h3>
<blockquote>
<p>Terraform stores information about your infrastructure in a state file. This state file keeps track of resources created by your configuration and maps them to real-world resources.</p>
<p>The terraform <code>state</code> command is used for advanced state management. As your Terraform usage becomes more advanced, there are some cases where you may need to modify the Terraform state. Rather than modify the state directly, the terraform state commands can be used in many cases instead.</p>
</blockquote>
<ul>
<li><code>state</code> commands work the same with local and remote state backends.</li>
<li>All modifying <code>state</code> commands create a backup file, and this behaviour cannot be disabled.</li>
<li><code>state</code> has been designed to be friendly with other CLI tools.</li>
</ul>
<p>Really, <code>state</code> should be used for all state file interactions.</p>
<h3 id="4f-given-a-scenario-choose-when-to-enable-verbose-logging-and-what-the-outcomevalue-is">4f. Given a scenario: choose when to enable verbose logging and what the outcome/value is</h3>
<blockquote>
<p>Terraform has detailed logs which can be enabled by setting the TF_LOG environment variable to any value. This will cause detailed logs to appear on stderr.</p>
</blockquote>
<ul>
<li>You can set TF_LOG to one of the log levels TRACE, DEBUG, INFO, WARN or ERROR to change the verbosity of the logs.</li>
<li>Setting TF_LOG to JSON outputs logs at the TRACE level or higher, and uses a parseable JSON encoding as the formatting.</li>
<li>Logging can be enabled separately for terraform itself and the provider plugins using the TF_LOG_CORE or TF_LOG_PROVIDER environment variables. These take the same level arguments as TF_LOG, but only activate a subset of the logs.</li>
<li>To persist logged output you can set TF_LOG_PATH in order to force the log to always be appended to a specific file when logging is enabled. Note that even when TF_LOG_PATH is set, TF_LOG must be set in order for any logging to be enabled.</li>
</ul>
<p>Verbose logging is useful when you&rsquo;re encountering issues with either Terraform Core or a plugin. In particular, it&rsquo;s expected you&rsquo;ll use these logs to determine where a crash or issue is caused, before opening an issue on the appropriate github repository. The TRACE logging level should be used for these logs.</p>
<h2 id="5-interact-with-terraform-modules">5. Interact with Terraform modules</h2>
<h3 id="5a-contrast-module-source-options">5a. Contrast module source options</h3>
<blockquote>
<p>The Terraform Registry is integrated directly into Terraform, so a Terraform configuration can refer to any module published in the registry.</p>
<p>Each module in the registry is versioned. These versions syntactically must follow semantic versioning. In addition to pure syntax, we encourage all modules to follow the full guidelines of semantic versioning.</p>
</blockquote>
<ul>
<li>Module sources.
<ul>
<li>Terraform Registry modules.
<ul>
<li>The default registry.</li>
<li>Only shows verified modules by default.</li>
<li>Source strings of the form <code>&lt;NAMESPACE&gt;/&lt;NAME&gt;/&lt;PROVIDER&gt;</code>. For example: <code>hashicorp/consul/aws</code>.</li>
</ul>
</li>
<li>Private Registry modules.
<ul>
<li>Terraform Cloud provides a private registry, as an example.</li>
<li>Ideal for sharing modules within or across an organisation.</li>
<li>Public modules from the Terraform Registry can be added to a Private Registry to provide a curated list of approved modules.</li>
<li>May need to provide credentials for access.</li>
<li>Source strings of the form <code>&lt;HOSTNAME&gt;/&lt;NAMESPACE&gt;/&lt;NAME&gt;/&lt;PROVIDER&gt;</code>. This is the same format as the public registry, but with an added hostname prefix.</li>
</ul>
</li>
<li>Local modules.
<ul>
<li>Terraform treats any local directory referenced in the source argument of a module block as a module.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="5b-interact-with-module-inputs-and-outputs">5b. Interact with module inputs and outputs</h3>
<blockquote>
<ul>
<li>Input variables are like function arguments.</li>
<li>Output values are like function return values.</li>
<li>Local values are like a function&rsquo;s temporary local variables.</li>
</ul>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">module</span> <span style="color:#e6db74">&#34;vpc&#34;</span> {
  <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;terraform-aws-modules/vpc/aws&#34;</span>
  <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;2.21.0&#34;</span><span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e">  # everything below this point is a module input
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">name</span> = var.<span style="color:#a6e22e">vpc_name</span>
  <span style="color:#a6e22e">cidr</span> = var.<span style="color:#a6e22e">vpc_cidr</span>

  <span style="color:#a6e22e">azs</span>             = var.<span style="color:#a6e22e">vpc_azs</span>
  <span style="color:#a6e22e">private_subnets</span> = var.<span style="color:#a6e22e">vpc_private_subnets</span>
  <span style="color:#a6e22e">public_subnets</span>  = var.<span style="color:#a6e22e">vpc_public_subnets</span>

  <span style="color:#a6e22e">enable_nat_gateway</span> = var.<span style="color:#a6e22e">vpc_enable_nat_gateway</span>

  <span style="color:#a6e22e">tags</span> = var.<span style="color:#a6e22e">vpc_tags</span>
}
</code></pre></div><p>For the given module inputs above, there are corresponding <code>input variables</code> inside the module.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">variable</span> <span style="color:#e6db74">&#34;name&#34;</span> {
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;Name to be used on all the resources as identifier&#34;</span>
  <span style="color:#a6e22e">type</span>        = <span style="color:#a6e22e">string</span>
  <span style="color:#a6e22e">default</span>     = <span style="color:#e6db74">&#34;&#34;</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">variable</span> <span style="color:#e6db74">&#34;cidr&#34;</span> {
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;The CIDR block for the VPC. Default value is a valid CIDR, but not acceptable by AWS and should be overridden&#34;</span>
  <span style="color:#a6e22e">type</span>        = <span style="color:#a6e22e">string</span>
  <span style="color:#a6e22e">default</span>     = <span style="color:#e6db74">&#34;0.0.0.0/0&#34;</span>
}
</code></pre></div><blockquote>
<p>The resources defined in a module are encapsulated, so the calling module cannot access their attributes directly. However, the child module can declare output values to selectively export certain values to be accessed by the calling module.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">output</span> <span style="color:#e6db74">&#34;vpc_id&#34;</span> {
  <span style="color:#a6e22e">description</span> = <span style="color:#e6db74">&#34;The ID of the VPC&#34;</span>
  <span style="color:#a6e22e">value</span>       = concat(<span style="color:#a6e22e">aws_vpc</span>.<span style="color:#a6e22e">this</span>.<span style="color:#f92672">*</span>.<span style="color:#a6e22e">id</span>, [<span style="color:#e6db74">&#34;&#34;</span>])[<span style="color:#ae81ff">0</span>]
}
</code></pre></div><p>This output could then be access via <code>module.vpc.vpc_id</code>.</p>
<h3 id="5c-describe-variable-scope-within-moduleschild-modules">5c. Describe variable scope within modules/child modules</h3>
<blockquote>
<p>Within the module that declared a variable, its value can be accessed from within expressions as <code>var.&lt;NAME&gt;</code>, where <code>&lt;NAME&gt;</code> matches the label given in the declaration block</p>
<p>The value assigned to a variable can only be accessed in expressions within the module where it was declared.</p>
</blockquote>
<p>TL;DR, variables are scoped to the module they are declared in and need to redeclared in child modules if they need to be passed down.</p>
<p>Locals can be useful for merging values or other variables. Alternatively they can be used to create short references to otherwise long variables or values.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#a6e22e">locals</span> {<span style="color:#75715e">
</span><span style="color:#75715e">  # Ids for multiple sets of EC2 instances, merged together
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">instance_ids</span> = concat(<span style="color:#a6e22e">aws_instance</span>.<span style="color:#a6e22e">blue</span>.<span style="color:#f92672">*</span>.<span style="color:#a6e22e">id</span>, <span style="color:#a6e22e">aws_instance</span>.<span style="color:#a6e22e">green</span>.<span style="color:#f92672">*</span>.<span style="color:#a6e22e">id</span>)<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e">  # merging variables
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">name</span><span style="color:#f92672">-</span><span style="color:#a6e22e">prefix</span> = <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span>var.<span style="color:#a6e22e">project_name</span><span style="color:#e6db74">}</span><span style="color:#e6db74">-</span><span style="color:#e6db74">${</span>var.<span style="color:#a6e22e">environment</span><span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span><span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e">  # shortening a long value
</span><span style="color:#75715e"></span>  <span style="color:#a6e22e">vpc_id</span> = data.<span style="color:#a6e22e">terraform_remote_state</span>.<span style="color:#a6e22e">test_project</span>.<span style="color:#a6e22e">outputs</span>.<span style="color:#a6e22e">vpc_id</span>
}
<span style="color:#66d9ef">
</span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_s3_bucket&#34;</span> <span style="color:#e6db74">&#34;default&#34;</span> {
  <span style="color:#a6e22e">bucket</span> = <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">local</span>.<span style="color:#a6e22e">name</span><span style="color:#f92672">-</span><span style="color:#a6e22e">prefix</span><span style="color:#e6db74">}</span><span style="color:#e6db74">-bucket&#34;</span>
  <span style="color:#a6e22e">acl</span>    = <span style="color:#e6db74">&#34;private&#34;</span>

  <span style="color:#a6e22e">tags</span> = {
    <span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">local</span>.<span style="color:#a6e22e">name</span><span style="color:#f92672">-</span><span style="color:#a6e22e">prefix</span><span style="color:#e6db74">}</span><span style="color:#e6db74">-bucket&#34;</span>
  }
}
</code></pre></div><h3 id="5d-discover-modules-from-the-public-terraform-module-registry">5d. Discover modules from the public Terraform Module Registry</h3>
<p>I don&rsquo;t really know what to put for this one. Browse and get comfortable with <a href="https://registry.terraform.io/browse/modules">https://registry.terraform.io/browse/modules</a>.</p>
<h3 id="5e-defining-module-version">5e. Defining module version</h3>
<blockquote>
<ul>
<li>Hashicorp recommend explicitly constraining the acceptable version numbers to avoid unexpected or unwanted changes.
<ul>
<li>The following operators are valid:</li>
<li><code>= (or no operator)</code>: Allows only one exact version number. Cannot be combined with other conditions.</li>
<li><code>!=</code>: Excludes an exact version number.</li>
<li><code>&gt;, &gt;=, &lt;, &lt;=</code>: Comparisons against a specified version, allowing versions for which the comparison is true. &ldquo;Greater-than&rdquo; requests newer versions, and &ldquo;less-than&rdquo; requests older versions.</li>
<li><code>~&gt;</code>: Allows only the rightmost version component to increment. For example, to allow new patch releases within a specific minor release, use the full version number: ~&gt; 1.0.4 will allow installation of 1.0.5 and 1.0.10 but not 1.1.0. This is usually called the pessimistic constraint operator.</li>
</ul>
</li>
<li>Terraform will use the newest installed version of the module that meets the constraint; if no acceptable versions are installed, it will download the newest version that meets the constraint.</li>
<li>Version constraints are supported only for modules installed from a module registry, such as the public Terraform Registry or Terraform Cloud&rsquo;s private module registry. Other module sources can provide their own versioning mechanisms within the source string itself, or might not support versions at all. In particular, modules sourced from local file paths do not support <code>version</code>; since they&rsquo;re loaded from the same source repository, they always share the same version as their caller.</li>
</ul>
</blockquote>
<ul>
<li>Less than and equal to operators can be changed to create an acceptable range, e.g. <code>&gt;= 1.2.0, &lt; 2.0.0</code></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#66d9ef">module</span> <span style="color:#e6db74">&#34;consul&#34;</span> {
  <span style="color:#a6e22e">source</span>  = <span style="color:#e6db74">&#34;hashicorp/consul/aws&#34;</span>
  <span style="color:#a6e22e">version</span> = <span style="color:#e6db74">&#34;0.0.5&#34;</span>

  <span style="color:#a6e22e">servers</span> = <span style="color:#ae81ff">3</span>
}
</code></pre></div><h2 id="6-navigate-terraform-workflow">6. Navigate Terraform workflow</h2>
<h3 id="6a-describe-terraform-workflow--write---plan---create-">6a. Describe Terraform workflow ( Write -&gt; Plan -&gt; Create )</h3>
<blockquote>
<p>The core Terraform workflow has three steps:</p>
<ul>
<li>Write - Author infrastructure as code.</li>
<li>Plan - Preview changes before applying.</li>
<li>(version control commit)</li>
<li>Apply - Provision reproducible infrastructure.</li>
<li>(version control push)</li>
</ul>
</blockquote>
<p>When working with Terraform as an individual, these can all happen locally and have changes pushed to version control at the end of the cycle. As teams grow and managing secrets/credentials becomes more difficult, it&rsquo;s normal to introduce a CI system. You can then lower individuals access to sensitive details while still allowing them to work with them. It also allows for branching/merge-request workflows where team members can review and approve changes before they are applied. An example flow might look like the below.</p>
<ul>
<li>(version control branch)</li>
<li>Write code.</li>
<li>(version control commit)</li>
<li>Write code.</li>
<li>(version control commit)</li>
<li>(version control push)</li>
<li>CI Plan</li>
<li>(team-member reviews plan output and approves)</li>
<li>(version control merge)</li>
<li>CI Plan</li>
<li>(team-member reviews merged plan output and approves)</li>
<li>CI Apply</li>
</ul>
<p>This can add time to the development workflow so be pragmatic and build what suits your company and situation.</p>
<h3 id="6b-initialize-a-terraform-working-directory-terraform-init">6b. Initialize a Terraform working directory (terraform init)</h3>
<blockquote>
<p>When you create a new configuration — or check out an existing configuration from version control — you need to initialize the directory with terraform init.
Initializing a configuration directory downloads and installs the providers defined in the configuration, which in this case is the aws provider.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/random from the dependency lock file
- Installing hashicorp/aws v2.50.0...
- Installed hashicorp/aws v2.50.0 <span style="color:#f92672">(</span>signed by HashiCorp<span style="color:#f92672">)</span>

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running <span style="color:#e6db74">&#34;terraform plan&#34;</span> to seeany changes that are required <span style="color:#66d9ef">for</span> your infrastructure. All Terraform commands should now work.
</code></pre></div><h3 id="6c-validate-a-terraform-configuration-terraform-validate">6c. Validate a Terraform configuration (terraform validate)</h3>
<blockquote>
<p>The <code>terraform validate</code> command validates the configuration files in a directory, referring only to the configuration and not accessing any remote services such as remote state, provider APIs, etc.</p>
<p>Validate runs checks that verify whether a configuration is syntactically valid and internally consistent, regardless of any provided variables or existing state. It is thus primarily useful for general verification of reusable modules, including correctness of attribute names and value types.</p>
<p>It is safe to run this command automatically, for example as a post-save check in a text editor or as a test step for a re-usable module in a CI system.</p>
<p>Validation requires an initialized working directory with any referenced plugins and modules installed.</p>
</blockquote>
<ul>
<li><code>plan</code> includes an implied validation check.</li>
</ul>
<h3 id="6d-generate-and-review-an-execution-plan-for-terraform-terraform-plan">6d. Generate and review an execution plan for Terraform (terraform plan)</h3>
<blockquote>
<p>The <code>terraform plan</code> command creates an execution plan. By default, creating a plan consists of:</p>
<ul>
<li>Reading the current state of any already-existing remote objects to make sure that the Terraform state is up-to-date.</li>
<li>Comparing the current configuration to the prior state and noting any differences.</li>
<li>Proposing a set of change actions that should, if applied, make the remote objects match the configuration.</li>
</ul>
<p>You can use the optional <code>-out=FILE</code> option to save the generated plan to a file on disk, which you can later execute by passing the file to terraform apply as an extra argument. This two-step workflow is primarily intended for when running Terraform in automation.
If you run terraform plan without the -out=FILE option then it will create a speculative plan, which is a description of a the effect of the plan but without any intent to actually apply it.
In teams that use a version control and code review workflow for making changes to real infrastructure, developers can use speculative plans to verify the effect of their changes before submitting them for code review. However, it&rsquo;s important to consider that other changes made to the target system in the meantime might cause the final effect of a configuration change to be different than what an earlier speculative plan indicated, so you should always re-check the final non-speculative plan before applying to make sure that it still matches your intent.</p>
</blockquote>
<h3 id="6e-execute-changes-to-infrastructure-with-terraform-terraform-apply">6e. Execute changes to infrastructure with Terraform (terraform apply)</h3>
<blockquote>
<p>If you are using Terraform directly in an interactive terminal and you expect to apply the changes Terraform proposes, you can alternatively run terraform apply directly. By default, the &ldquo;apply&rdquo; command automatically generates a new plan and prompts for you to approve it.</p>
</blockquote>
<h3 id="6f-destroy-terraform-managed-infrastructure-terraform-destroy">6f. Destroy Terraform managed infrastructure (terraform destroy)</h3>
]]></content>
        </item>
        
        <item>
            <title>Gitea, Golang, GoReleaser and Drone CI - Part 1</title>
            <link>https://ljones.id.au/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/</link>
            <pubDate>Sun, 07 Nov 2021 02:08:27 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/</guid>
            <description>Gitea, Golang, GoReleaser and Drone CI - Part 1 &amp;lt;- You are Here Gitea, Golang, GoReleaser and Drone CI - Part 2 Gitea, Golang, GoReleaser and Drone CI - Part 3 Gitea, Golang, GoReleaser and Drone CI - Part 4   Okay so I mentioned in a recent post that I was going to write up how I used GoReleaser, Drone CI and Gitea to release a Golang project.</description>
            <content type="html"><![CDATA[<ul>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-1/">Gitea, Golang, GoReleaser and Drone CI - Part 1</a> &lt;- You are Here</li>
<li><a href="/posts/2021/11/gitea-golang-goreleaser-and-drone-ci-part-2/">Gitea, Golang, GoReleaser and Drone CI - Part 2</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-3/">Gitea, Golang, GoReleaser and Drone CI - Part 3</a></li>
<li><a href="/posts/2021/12/gitea-golang-goreleaser-and-drone-ci-part-4/">Gitea, Golang, GoReleaser and Drone CI - Part 4</a></li>
</ul>
<hr>
<p>Okay so I mentioned in a recent post that I was going to write up how I used GoReleaser, Drone CI and Gitea to release a Golang project. I&rsquo;ve decided to go one better and create a demo project for you to try it out yourself. Before getting into it though, there&rsquo;s something we need to discuss.</p>
<p>The Drone CI documentation on Gitea mentions the following.</p>
<blockquote>
<p>Please note we strongly recommend installing Drone on a dedicated instance. We do not recommend installing Drone and Gitea on the same machine due to network complications, and we definitely do not recommend installing Drone and Gitea on the same machine using docker-compose.</p>
</blockquote>
<p>Network complications? 🤔 Well, it&rsquo;s to do with the different networks at play. When you fire up docker-compose, it automatically creates a little dedicated network where you can resolve the other services by their name, no sweat.</p>
<p>Here&rsquo;s a snippet of the <code>compose.yaml</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#f92672">services</span>:
  <span style="color:#f92672">gitea</span>:
    <span style="color:#ae81ff">...</span>

  <span style="color:#f92672">drone</span>:
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_GITEA_SERVER=http://gitea:3000</span>
    <span style="color:#ae81ff">...</span>

  <span style="color:#f92672">drone-runner</span>:
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">drone/drone-runner-docker:1</span>
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">drone-runner</span>
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_RPC_HOST=drone</span>
</code></pre></div><p>And here&rsquo;s the network created.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ docker compose up -d
$ docker network ls
NETWORK ID     NAME                                  DRIVER    SCOPE
d945699fb57c   go-goreleaser-drone-example_default   bridge    local
</code></pre></div><p>You can see that service referencing at play twice in the <code>compose.yaml</code>, in the environment variables. <code>http://gitea:3000</code> will resolve to the IP address of the Gitea container created. So where&rsquo;s the problem? Well, the <a href="https://docs.drone.io/runner/docker/overview/">Drone CI Docker Runner</a> creates containers to run your CI/CD pipeline. These containers are not inside the docker-compose network, so they can&rsquo;t resolve <code>http://gitea:3000</code>. See below for the sad-face experience when a runner begins with the above config.</p>
<pre><code class="language-log" data-lang="log">Initialized empty Git repository in /drone/src/.git/
+ git fetch origin +refs/heads/master:
fatal: unable to access 'http://gitea:3000/gitea/test.git/': Failed to connect to gitea port 3000: Connection refused
</code></pre><p>How can we work around this?</p>
<p>One, you could provide the IP address of the host running docker-compose in the <code>compose.yaml</code>. Note you only need to swap out the references to the Gitea service.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#f92672">services</span>:
  <span style="color:#f92672">gitea</span>:
    <span style="color:#ae81ff">...</span>

  <span style="color:#f92672">drone</span>:
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_GITEA_SERVER=http://${HOST_IP}:3000</span>
    <span style="color:#ae81ff">...</span>

  <span style="color:#f92672">drone-runner</span>:
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">drone/drone-runner-docker:1</span>
    <span style="color:#f92672">container_name</span>: <span style="color:#ae81ff">drone-runner</span>
    <span style="color:#f92672">environment</span>:
      - <span style="color:#ae81ff">DRONE_RPC_HOST=drone</span>
</code></pre></div><p>Two, you could edit the <code>/etc/hosts</code><sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> config on the host running docker-compose to resolve the service names to the docker host, or the IP address of the docker desktop host.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">$ ip a
...
5: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu <span style="color:#ae81ff">1500</span> qdisc mq state UP group default qlen <span style="color:#ae81ff">1000</span>
    link/ether 00:15:5d:b0:24:13 brd ff:ff:ff:ff:ff:ff
    inet 172.28.200.90/20 brd 172.28.207.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:feb0:2413/64 scope link
       valid_lft forever preferred_lft forever
</code></pre></div><p><code>172.28.200.90</code> is the IP address I need to add to the hosts file. I&rsquo;ve also added <code>drone</code> here so that I can reach it easily from a browser at <code>http://drone:8080</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-txt" data-lang="txt">172.28.200.90 gitea drone
</code></pre></div><p>I prefer the second method but it&rsquo;s really up to you. The first method is better if you&rsquo;re going to be accessing from multiple hosts, as you don&rsquo;t want to have to update the hosts file on every machine. This is just a demo, so the latter is fine.</p>
<p>Okay, detour complete, in the next post we&rsquo;ll actually start bringing things online!</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>On Windows, <code>C:\Windows\System32\drivers\etc\hosts</code>. I&rsquo;ll need to run your text editor as administrator to be able to edit this file.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Okta Certified Professional</title>
            <link>https://ljones.id.au/posts/2021/11/okta-certified-professional/</link>
            <pubDate>Tue, 02 Nov 2021 11:34:25 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/11/okta-certified-professional/</guid>
            <description>Last week I picked up the Okta Certified Professional certification. This is their baseline cert and doesn&amp;rsquo;t really require much study, at least not if you&amp;rsquo;ve used Okta before. I occasionally have to touch it for my work, and didn&amp;rsquo;t require more than a few nights revision. Honestly, the main reason I picked it up was the staggering 95% discount that they were running. I&amp;rsquo;m pretty dubious at the value of this qualification at the original price but for $50 USD, eh, sure.</description>
            <content type="html"><![CDATA[<p>Last week I picked up the <a href="https://www.okta.com/services/certification/">Okta Certified Professional</a> certification. This is their baseline cert and doesn&rsquo;t really require much study, at least not if you&rsquo;ve used Okta before. I occasionally have to touch it for my work, and didn&rsquo;t require more than a few nights revision. Honestly, the main reason I picked it up was the <em>staggering</em> 95% discount that they were running. I&rsquo;m pretty dubious at the value of this qualification at the original price but for $50 USD, eh, sure.</p>
]]></content>
        </item>
        
        <item>
            <title>Holiday</title>
            <link>https://ljones.id.au/posts/2021/10/holiday/</link>
            <pubDate>Sat, 23 Oct 2021 01:22:17 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/10/holiday/</guid>
            <description>A holiday, but not exactly the no-work kind. I recently had a working holiday in Australia&amp;rsquo;s South-West region, staying in the coastal town of Dunsborough and then a few days a bit further inland, near a town called Nannup. They&amp;rsquo;re both pretty peaceful places, and I find them immensely rejuvenating. As my partner and I are able to work 100% remotely, it was just the matter of packing up the dog and off we went.</description>
            <content type="html"><![CDATA[<p>A holiday, but not exactly the no-work kind. I recently had a working holiday in Australia&rsquo;s South-West region, staying in the coastal town of Dunsborough and then a few days a bit further inland, near a town called Nannup. They&rsquo;re both pretty peaceful places, and I find them immensely rejuvenating. As my partner and I are able to work 100% remotely, it was just the matter of packing up the dog and off we went. Sensibly, we booked the Monday and Friday off as annual leave for a double long weekend 😎. Definitely feeling lucky to be alive and in the position that I am now.</p>
<p>So aside from work, what&rsquo;s been going on? Well, I&rsquo;ve been fairly busy with some personal projects, one in particular helping out the father-in-law<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> with an integration to <a href="https://www.claris.com/filemaker/pro/">FileMaker</a>(FM). He&rsquo;s a smart guy and FileMaker is a low-code solution that lets him prototype and get the 80% achieved very quickly, but there are some rough edges. In this case, he&rsquo;s calling out to an API to send SMS and wants to get replies back into the database, but FM doesn&rsquo;t readily provide a callback target.</p>
<p>In a couple of hours<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> I had put together a little Golang program that effectively acts as a proxy, receiving the callbacks from the SMS API and writing them into the FM database. This is something I&rsquo;ll probably open-source once I&rsquo;m ready. I <em>then</em> spent the next two to three days going way overboard, refreshing myself on <a href="https://goreleaser.com/">GoReleaser</a>, learning <a href="https://www.drone.io/">Drone CI/CD</a> and stitching it all together on my self-hosted <a href="https://gitea.com/">Gitea</a> instance. So now I can tag a release and it automatically does-the-needful™️. I&rsquo;m planning on dedicating a post to how that all works, with an accompanying example repo.</p>
<p>Time for brunch now though, till next time!</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Not married, but close enough.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>I was a little rusty! And I had to learn the FM API. 😬&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 7</title>
            <link>https://ljones.id.au/posts/2021/09/read-heard-and-watched-7/</link>
            <pubDate>Tue, 21 Sep 2021 12:58:27 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/09/read-heard-and-watched-7/</guid>
            <description>I lost (spent) a good chunk of my free time last week playing RimWorld. It&amp;rsquo;s not the first time I&amp;rsquo;ve played but as usual my gaming patterns tend to be intensive and short-lived. I&amp;rsquo;d say I&amp;rsquo;ll be more productive this week and coming weekend except a friend is holding a Diablo 2: Resurrected LAN. Ah well.
In other news, BSIDES Perth was on the weekend just past. Some cool talks but personally it was just nice to see a bunch of familiar faces; former coworkers from previous security roles etc.</description>
            <content type="html"><![CDATA[<p>I lost (spent) a good chunk of my free time last week playing <a href="https://store.steampowered.com/app/294100/RimWorld/">RimWorld</a>. It&rsquo;s not the first time I&rsquo;ve played but as usual my gaming patterns tend to be intensive and short-lived. I&rsquo;d say I&rsquo;ll be more productive this week and coming weekend except a friend is holding a <a href="https://us.shop.battle.net/en-us/product/diablo_ii_resurrected">Diablo 2: Resurrected</a> LAN. Ah well.</p>
<p>In other news, <a href="https://bsidesperth.com.au/">BSIDES Perth</a> was on the weekend just past. Some cool talks but personally it was just nice to see a bunch of familiar faces; former coworkers from previous security roles etc. Hat tip to the organisers, trying to get <em>anything</em> of this nature done in COVID times has to be just that much harder.</p>
<h2 id="heard-">Heard 👂</h2>
<h3 id="podcasts-">Podcasts 🎤</h3>
<ul>
<li><a href="https://changelog.com/podcast/460">The Changelog: The business model of open source</a>
<ul>
<li>Worth a listen if you&rsquo;re planning on running a business based on open source software.</li>
</ul>
</li>
<li><a href="https://changelog.com/podcast/459">The Changelog: Coding in the cloud with Codespaces</a>
<ul>
<li>How Codespaces was sold to internal users within GitHub was pretty cool, trying to make it more of an <em>exciting exclusive club</em> rather than dogfooding.</li>
</ul>
</li>
<li><a href="https://changelog.com/shipit/19">Ship It!: Real-world implications of shipping many times a day</a></li>
<li><a href="https://changelog.com/gotime/197">Go Time: Books that teach Go</a>
<ul>
<li>Definitely a few things here for me to check out.</li>
</ul>
</li>
</ul>
<h3 id="music-">Music 🎵</h3>
<ul>
<li><a href="https://open.spotify.com/track/3aAhv8QPxI0eirbbSRcRL1">Slay King by Alter Boy</a></li>
<li><a href="https://open.spotify.com/track/5mACgoOMO7FapXNCLXcsbm">second margin knife by nervous_testpilot</a></li>
<li><a href="https://open.spotify.com/track/5SW2ZpmkA5QYQ05XZBHw1E">cheap groovebox by _ensnare_</a></li>
<li><a href="https://open.spotify.com/track/0wRvKO3AIgIcSU79ciueyS">Infinite Mirror - feat. Tomotaka Tsuji by LITE, Tomotaka Tsuji</a></li>
<li><a href="https://open.spotify.com/track/2Ir5oYwPPpbU8qRKhmA4Rx">Kiwi by Seb Zillner, Lazlow</a></li>
<li><a href="https://open.spotify.com/track/4MOymc2VagxuL934IdUfGF">Save Room by Waveshaper</a></li>
<li><a href="https://open.spotify.com/track/4631028i5UiqKApQ3XSaK5">Last Jungle (Synth System Remix) by Sub Focus, Synth System</a></li>
<li><a href="https://open.spotify.com/track/78RPNZ0fgOm2RbUVpOwZBq">LAE by Uppermost, FA</a></li>
<li><a href="https://open.spotify.com/track/1AugqxhgmKGrkJboiLMp5P">Pound a Rhythm Electronic by Younger Brother</a></li>
<li><a href="https://open.spotify.com/track/0ZMr5lpvD8G0UBx6WRQG2y">Contact Note by Jon Hopkins</a></li>
<li><a href="https://open.spotify.com/track/6DP0I1PLhrbMQXVUOluHFY">Touching The Touchstone by Bill Laurance</a></li>
<li><a href="https://open.spotify.com/track/7JLVTrT4Eji7ZD1YPufZ2W">afterwards by yutaka hirasaka</a></li>
<li><a href="https://open.spotify.com/track/587yWjYOx7VVGyeI9m574a">Digital Baptism by Falconite</a></li>
<li><a href="https://open.spotify.com/track/4mITwv1MPRqVuADf0A6LXj">Aequilibrium by Andrey Vinogradov</a></li>
<li><a href="https://open.spotify.com/track/6fyI2QGPzUiqRHnuYD7oOp">Greenpath by Christopher Larkin</a></li>
<li><a href="https://open.spotify.com/track/2wXsR1bnidWWcX4nhGTCBq">Lonely Rolling Star (Missing You) by aivi &amp; surasshu</a></li>
<li><a href="https://open.spotify.com/track/0MwLOze9j7BFEmdfMeVd2t">Leaning In by Blue States</a></li>
<li><a href="https://open.spotify.com/track/6loAICkAJ8doNOragHkl5c">Wrinkle by Thank You Scientist</a></li>
<li><a href="https://open.spotify.com/track/7oRAHSmG6QPGCUwPWF8wTl">Watch the Sun by Groundislava</a></li>
</ul>
<h2 id="watched-">Watched 👀</h2>
<h3 id="youtube-">YouTube 📺</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=j1-4hfab4xg">IFSC World Championships Moscow 2021 || Lead Semi-finals</a>
<ul>
<li>Such skilled climbers!</li>
</ul>
</li>
<li><a href="https://www.youtube.com/watch?v=8i_FOk955c0">IFSC World Championships Moscow 2021 || Men’s Boulder Final</a>
<ul>
<li>Such skilled boulderers! That&rsquo;s a word! Probably 😬</li>
</ul>
</li>
</ul>
<h2 id="played-">Played 🎮</h2>
<h3 id="pc-">PC 💻</h3>
<ul>
<li><a href="https://store.steampowered.com/app/294100/RimWorld/">RimWorld</a></li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 6</title>
            <link>https://ljones.id.au/posts/2021/09/read-heard-and-watched-6/</link>
            <pubDate>Tue, 14 Sep 2021 13:08:16 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/09/read-heard-and-watched-6/</guid>
            <description>Apple/Macbooks/macOS just feel more and more broken lately. Not that long ago I had a Big Sur update completely fail and lose my entire account. Now I&amp;rsquo;m either Disk Utility, Time Machine or the T2 chip on the Macbook that is broken, or some combination of the above. After setting up Zorin 16 on an old laptop, I&amp;rsquo;m actually tempted to run it as a daily driver. It looked and ran slick and everything I tried just-worked™️.</description>
            <content type="html"><![CDATA[<p>Apple/Macbooks/macOS just feel more and more broken lately. Not that long ago I had a Big Sur update completely fail and lose my entire account. Now I&rsquo;m either Disk Utility, Time Machine or the T2 chip on the Macbook that is broken, or some combination of the above. After setting up <a href="https://zorin.com/os/">Zorin 16</a> on an old laptop, I&rsquo;m actually tempted to run it as a daily driver. It looked and ran slick and everything I tried <em>just-worked</em>™️. Even the printers and scanners in the household were picked up and I was able to print immediately. It&rsquo;s been awhile since I kicked the tires on any Ubuntu desktops but it&rsquo;s pretty impressive from the last time.</p>
<h2 id="read-">Read 📜</h2>
<h3 id="articles-">Articles 📰</h3>
<ul>
<li><a href="https://github.com/uber-go/guide/blob/master/style.md">The Uber Go Style Guide</a></li>
<li><a href="https://research.nccgroup.com/2021/09/09/nsa-cisa-kubernetes-security-guidance-a-critical-review">NSA &amp; CISA Kubernetes Security Guidance – A Critical Review</a></li>
<li><a href="https://github.com/Dreamacro/clash">Clash: A rule-based tunnel in Go</a></li>
</ul>
<h2 id="heard-">Heard 👂</h2>
<h3 id="podcasts-">Podcasts 🎤</h3>
<ul>
<li><a href="https://changelog.com/founderstalk/80">Founders Talk: Iterating to globally distributed apps and databases</a>
<ul>
<li>I really like the points about <code>What are you optimising for?</code> and <code>The Velvet Rope</code>.</li>
<li>The former is something that you can be asking yourself as a business but also just as a person living their life. What do you want to be achieving in life? Is that congruent with what you&rsquo;re currently doing? If not, why not? Basically a reminder to step back and think about the bigger picture.</li>
<li>The latter is sort of in-line with the previous point, but basically using the analogy of being a bouncer at a bar. What kind of clientelle do you want to serve? Do you want to service a single high-paying customer or do you want to offer something less bespoke that&rsquo;s affordable?</li>
<li>As a kind of aside, I&rsquo;m a big fan of businesses doing one thing well, rather than trying to be everything to anyone. That said, I understand and have seen how it can be difficult when you have existing customers asking for features, possibly ones that your competitors are already offering or introducing, and there&rsquo;s a pressure to <em>keep up</em>. Do you follow the competition and dilute your offering?</li>
</ul>
</li>
<li><a href="https://changelog.com/shipit/18">Ship It!: Bare metal meets Kubernetes</a></li>
<li><a href="https://changelog.com/gotime/196">Go Time: Building actually maintainable software</a></li>
</ul>
<h3 id="music-">Music 🎵</h3>
<p>Really didn&rsquo;t listen to much music this week.</p>
<ul>
<li><a href="https://open.spotify.com/track/0rymC6gRk7M1U7vCjmumds">Friends in Dreams by The American Dollar, Linearwave</a></li>
<li><a href="https://open.spotify.com/track/4hO2GGNCVSeQjkuKrMoczD">Stomp (Bastion Remix) by Sub Focus, Bastion</a></li>
<li><a href="https://open.spotify.com/track/5qOLNj2YjpsCW82RsTHkoZ">Example by No Mana, Uppermost</a></li>
<li><a href="https://open.spotify.com/track/4o4EKOuKu2BkT45khZP4nG">Chronos - Live by Nu Deco Ensemble, Aaron Parks</a></li>
</ul>
<h2 id="watched-">Watched 👀</h2>
<h3 id="movies-">Movies 🎬</h3>
<ul>
<li><a href="https://trakt.tv/movies/the-suicide-squad-2021">The Suicide Squad (2021)</a>
<ul>
<li>It&rsquo;s a passable action movie. Set your expectations for mild entertainment and you might not be disappointed.</li>
</ul>
</li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 5</title>
            <link>https://ljones.id.au/posts/2021/09/read-heard-and-watched-5/</link>
            <pubDate>Tue, 07 Sep 2021 11:39:54 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/09/read-heard-and-watched-5/</guid>
            <description>A bit belated, I had a fairly full weekend and an even busier Monday. I&amp;rsquo;ve made some progress on side-projects but I&amp;rsquo;m usually pretty crushed by the end of the day. Climbing is going well! Planning on doing a lead-climbing course soon.
Read 📜 Articles 📰  Why every software engineering interview should include ops questions  Charity Majors is a treasure.   Cloud Native Security Map praetorian-inc/gokart A Guide to Interfaces in Go  Heard 👂 Podcasts 🎤  Go Time: To build, or to buy, that is the question Ship It: Docs are not optional  Music 🎵  Curicó by Kiltro All That&amp;rsquo;s Left (feat.</description>
            <content type="html"><![CDATA[<p>A bit belated, I had a fairly full weekend and an even busier Monday. I&rsquo;ve made some progress on side-projects but I&rsquo;m usually pretty crushed by the end of the day. Climbing is going well! Planning on doing a lead-climbing course soon.</p>
<h2 id="read-">Read 📜</h2>
<h3 id="articles-">Articles 📰</h3>
<ul>
<li><a href="https://charity.wtf/2021/08/21/why-every-software-engineering-interview-should-include-ops-questions/?utm_source=pocket_mylist">Why every software engineering interview should include ops questions</a>
<ul>
<li>Charity Majors is a treasure.</li>
</ul>
</li>
<li><a href="https://cnsmap.netlify.app">Cloud Native Security Map</a></li>
<li><a href="https://github.com/praetorian-inc/gokart">praetorian-inc/gokart</a></li>
<li><a href="http://fast4ward.online/posts/a-guide-to-interfaces-in-go/">A Guide to Interfaces in Go</a></li>
</ul>
<h2 id="heard-">Heard 👂</h2>
<h3 id="podcasts-">Podcasts 🎤</h3>
<ul>
<li><a href="https://changelog.com/gotime/195">Go Time: To build, or to buy, that is the question</a></li>
<li><a href="https://changelog.com/shipit/17">Ship It: Docs are not optional</a></li>
</ul>
<h3 id="music-">Music 🎵</h3>
<ul>
<li><a href="https://open.spotify.com/track/10Po8tJdN8dwcKKgE7oEmT">Curicó by Kiltro</a></li>
<li><a href="https://open.spotify.com/track/5qzHdG8IFHJDIk0Q5h5da6">All That&rsquo;s Left (feat. Joni Fatora) - The M Machine Remix by Manila Killa, Joni Fatora, The M Machine</a></li>
<li><a href="https://open.spotify.com/track/7EPCNK1WiKFbUIhoHuEqii">The Great Calamity by Challenger Deep</a></li>
<li><a href="https://open.spotify.com/track/51D4wdcIYBzv7G5unk3zci">Thirty Seven After Six by Valve</a></li>
<li><a href="https://open.spotify.com/track/6gdTyrqYY4JXtpK5wt0o0z">vacillate by Oliver Buckland</a></li>
<li><a href="https://open.spotify.com/track/2U832A0zdb8OABhBmFjlWS">Colored Engine by Joel Schoch</a></li>
<li><a href="https://open.spotify.com/track/6auy196v8CGi5XdTeDx8CT">Refuge by Dan Terminus</a></li>
<li><a href="https://open.spotify.com/track/1NxN8G3FNhrITNeGR5SZKk">Sun by Love Supreme</a></li>
<li><a href="https://open.spotify.com/track/5dhImBTWEjGFhu3F27TDzH">This Means War - Feather &amp; Leviathan Owl Remix by Volkor X, Feather, Leviathan Owl</a></li>
<li><a href="https://open.spotify.com/track/3zkAm2liFJhNUpPqa3wUDm">Adrenaline by Shirobon</a></li>
<li><a href="https://open.spotify.com/track/3YZEGmXOPD01c20O9K0Wp7">click by C418</a></li>
<li><a href="https://open.spotify.com/track/08XYp2BQq5rcYtfaoIoVh3">Beginnings by Uppermost</a></li>
<li><a href="https://open.spotify.com/track/3p8Gxhqy3pzCU7BRfqhqPE">Nate Smith Is the Ace of Aces by The Fearless Flyers</a></li>
<li><a href="https://open.spotify.com/track/6Lp1guODRjYW2PuMbKi9KQ">Birds Canticum &ldquo;Birds Requiem&rdquo; Suite by Dhafer Youssef</a></li>
</ul>
<h2 id="watched-">Watched 👀</h2>
<h3 id="tv-">TV 📺</h3>
<ul>
<li><a href="https://trakt.tv/shows/the-simpsons/">The Simpsons</a>
<ul>
<li>Over the year, I&rsquo;ve been rewatching the golden oldies from season 2 onwards. I&rsquo;m currently up to season 7. There&rsquo;s a weaker episode here and there but I still find it to be very solid.</li>
</ul>
</li>
</ul>
<h3 id="movies-">Movies 🎬</h3>
<ul>
<li><a href="https://trakt.tv/movies/the-dawn-wall-2017">The Dawn Wall</a>
<ul>
<li>Utter madness. Human perseverance in a true story that goes way beyond rock climbing.</li>
</ul>
</li>
</ul>
<h2 id="played-">Played 🎮</h2>
<h3 id="pc-">PC 💻</h3>
<ul>
<li><a href="https://store.steampowered.com/app/2310/QUAKE/">QUAKE</a>
<ul>
<li>By some wierd coincidence, I reinstall this game just as they release a remaster. I&rsquo;d never really played through the whole thing before, but this time I found myself getting into it in a way that I hadn&rsquo;t before. The remaster is really well done and strikes that nostalgia balance of <em>Ahh, just as I remember! :D</em> and not <em>Oof, just as I remember! D:</em>. If you&rsquo;ve never given it a go, there&rsquo;s never been a better time.</li>
</ul>
</li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 4</title>
            <link>https://ljones.id.au/posts/2021/08/read-heard-and-watched-4/</link>
            <pubDate>Sun, 29 Aug 2021 10:16:39 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/08/read-heard-and-watched-4/</guid>
            <description>My content-helper tool is in progress; so far it only gets my recently (-7 days) saved tracks from Spotify and drops them out in the markdown used below, but it&amp;rsquo;s a start.
Read 📜 Books 📖  [Ender&amp;rsquo;s Saga #3: Xenocide][xeno] (finished)  I was losing interest but pushed through to the end. There&amp;rsquo;s a lot of squabbling/in-fighting between characters and I think that just got tedious for me.    Articles 📰  PostgreSQL vs Redis vs Memcached performance  PostgreSQL performing pretty well.</description>
            <content type="html"><![CDATA[<p>My content-helper tool is in progress; so far it only gets my recently (-7 days) saved tracks from Spotify and drops them out in the markdown used below, but it&rsquo;s a start.</p>
<h2 id="read-">Read 📜</h2>
<h3 id="books-">Books 📖</h3>
<ul>
<li>[Ender&rsquo;s Saga #3: Xenocide][xeno] (finished)
<ul>
<li>I was losing interest but pushed through to the end. There&rsquo;s a lot of squabbling/in-fighting between characters and I think that just got tedious for me.</li>
</ul>
</li>
</ul>
<h3 id="articles-">Articles 📰</h3>
<ul>
<li><a href="https://www.cybertec-postgresql.com/en/postgresql-vs-redis-vs-memcached-performance">PostgreSQL vs Redis vs Memcached performance</a>
<ul>
<li>PostgreSQL performing pretty well. An interesting note in the comments about the network as well.</li>
</ul>
</li>
<li><a href="https://aws.amazon.com/blogs/database/part-1-upgrade-your-amazon-rds-for-postgresql-database-comparing-upgrade-approaches/">Upgrade your Amazon RDS for PostgreSQL database, Part 1: Comparing upgrade approaches</a> and <a href="https://aws.amazon.com/blogs/database/part-2-upgrade-your-amazon-rds-for-postgresql-database-using-the-pglogical-extension/">Part 2</a>
<ul>
<li>I may need to do the replication method at some stage in the future so it&rsquo;s handy having this reference.</li>
</ul>
</li>
</ul>
<h2 id="heard-">Heard 👂</h2>
<h3 id="podcasts-">Podcasts 🎤</h3>
<ul>
<li><a href="https://changelog.com/gotime/194">Go Time: Don&rsquo;t forget about memory management</a>
<ul>
<li>I always find these discussions of how things are operating at a lower level to be interesting, even if they&rsquo;re not always applicable to anything I&rsquo;m doing.</li>
</ul>
</li>
<li><a href="https://changelog.com/podcast/455">The Changelog: Building software for yourself</a>
<ul>
<li>Inspiring that this person has built their own language and surrounding ecosystem.</li>
</ul>
</li>
</ul>
<h3 id="music-">Music 🎵</h3>
<ul>
<li><a href="https://open.spotify.com/track/4TVYh0avgMHSCBqNqJ7jpW">Dire, Dire Docks (Super Mario 64) by GENTLE LOVE</a></li>
<li><a href="https://open.spotify.com/track/5WMC3NnJuIBBcyQ93BU3Mc">Coming Home to Luncheon by Lena Raine</a></li>
<li><a href="https://open.spotify.com/track/2txbj2UMsiBaoMCoCI6Iqc">Farewell by Lena Raine</a></li>
<li><a href="https://open.spotify.com/track/3h62OMXmmbsrbdRiHj43mb">Joy of Remembrance by Lena Raine</a></li>
<li><a href="https://open.spotify.com/track/6jZxVwx5rPk9dBZSNZJIdF">Mistake by Orax</a></li>
<li><a href="https://open.spotify.com/track/7iihuCbuUiqHDaGGcy2ae4">Losing Grip by CRT_HEAD</a></li>
<li><a href="https://open.spotify.com/track/4ckavzEVyxFVmgjiQlNvn4">Breakdown by Droid Bishop</a></li>
<li><a href="https://open.spotify.com/track/3XxBj4muC0eqMSXMXA3X87">HALOHEAD by KUNZITE, Ratatat</a></li>
<li><a href="https://open.spotify.com/track/1UHoKGNBYSw54eqmX7Jp2v">Aquatic Ambiance (Donkey Kong Country) by GENTLE LOVE</a></li>
<li><a href="https://open.spotify.com/track/0FkmVxMrScqDOY7D8WfI0I">Wallwalker by Maserati</a></li>
<li><a href="https://open.spotify.com/track/49MWSiZnKcHA3AVDfahSJv">King&rsquo;s Filth by Le Matos</a></li>
<li><a href="https://open.spotify.com/track/7u3WQ8IpnUZk93cfzs12ip">Machine Guns and Peacock Feathers - Carpenter Brut Remix by Ulver, Carpenter Brut</a></li>
<li><a href="https://open.spotify.com/track/7rxEyKkcITkBtKYr7dUxlt">Prism by Dugo</a></li>
<li><a href="https://open.spotify.com/track/3VFlMqgTvRl0Cpj2sSaz5Q">Attack Ships On Fire by London Elektricity</a></li>
<li><a href="https://open.spotify.com/track/46r4VSSjGTjRqMk0l196Z0">Deckard Returns by Chrome Canyon</a></li>
<li><a href="https://open.spotify.com/track/6aUoy08EPSi5g3EgXRrT73">Pursuer by Sound of Ceres</a></li>
</ul>
<h2 id="watched-">Watched 👀</h2>
<h3 id="movies-">Movies 🎬</h3>
<ul>
<li><a href="https://trakt.tv/movies/lady-bird-2017">Lady Bird</a>
<ul>
<li>Half coming-of-age story, half love-letter to Sacramento. I enjoyed it.</li>
</ul>
</li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 3</title>
            <link>https://ljones.id.au/posts/2021/08/read-heard-and-watched-3/</link>
            <pubDate>Fri, 20 Aug 2021 07:16:36 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/08/read-heard-and-watched-3/</guid>
            <description>Generating some of this content manually is a little tedious. Pocket, Spotify and Trakt all have APIs so I&amp;rsquo;ll probably put in some automation. Goodreads had an API then dropped it? Nice move 😱 Anyway expect a post on whatever I setup.
Read 📜 Books 📖  Ender&amp;rsquo;s Saga #3: Xenocide (in-progress)  About a third of the way, I&amp;rsquo;m enjoying it so far.    Articles 📰  An Old Hacker&amp;rsquo;s Tips On Staying Employed   So the Two-And-Done rule was born, wherein I will state my case the first time, and if whoever is arguing to the contrary does not agree after hearing my position, I’ll let it go.</description>
            <content type="html"><![CDATA[<p>Generating some of this content manually is a little tedious. Pocket, Spotify and Trakt all have APIs so I&rsquo;ll probably put in some automation. Goodreads had an API then dropped it? Nice move 😱 Anyway expect a post on whatever I setup.</p>
<h2 id="read-">Read 📜</h2>
<h3 id="books-">Books 📖</h3>
<ul>
<li><a href="https://www.goodreads.com/book/show/8648.Xenocide">Ender&rsquo;s Saga #3: Xenocide</a> (in-progress)
<ul>
<li>About a third of the way, I&rsquo;m enjoying it so far.</li>
</ul>
</li>
</ul>
<h3 id="articles-">Articles 📰</h3>
<ul>
<li><a href="https://madned.substack.com/p/an-old-hackers-tips-on-staying-employed">An Old Hacker&rsquo;s Tips On Staying Employed</a></li>
</ul>
<blockquote>
<p>So the Two-And-Done rule was born, wherein I will state my case the first time, and if whoever is arguing to the contrary does not agree after hearing my position, I’ll let it go. But the next time the opportunity comes up, I will argue my point again. Maybe allowing for a gap of time for people to consider my original point, or maybe allowing me time to refine and rephrase my ideas to be more convincing.</p>
<p>If I fail to get my way after the second time though, I am done. I will even say as much to whomever I am debating if they are the final decision-maker. I will say something like, &ldquo;OK, let’s go your way then. I still don’t completely agree with everything proposed here, but I think I’ve made my case, and we need to move on.&rdquo;</p>
</blockquote>
<ul>
<li><a href="https://threedots.tech/post/common-anti-patterns-in-go-web-applications">Common Anti-Patterns in Go Web Applications</a></li>
<li><a href="https://matduggan.com/devops-engineer-crash-course">DevOps Engineer Crash Course - Section 1</a>
<ul>
<li>Yep, quite familiar.</li>
</ul>
</li>
</ul>
<blockquote>
<p>You cannot become an expert on every element you interact with. A day in my job can be managing Postgres operations, writing a PR against an application, or sitting in a planning session helping to design a new application. The scope of what many businesses call &ldquo;DevOps&rdquo; is too vast to be a deep-dive expert in all parts of it.</p>
</blockquote>
<h2 id="heard-">Heard 👂</h2>
<h3 id="podcasts-">Podcasts 🎤</h3>
<ul>
<li><a href="https://moderncto.io/shiven-ramji/">Modern CTO: #365 Shiven Ramji – Keeping Engineers Close to Customers, &amp; The Passwordless Future</a></li>
<li><a href="https://changelog.com/jsparty/188">JS Party: We ask a lawyer about GitHub Copilot</a></li>
<li><a href="https://changelog.com/gotime/193">Go Time: Caddy V2</a>
<ul>
<li>Interesting to hear about Caddy in general, as it was a tool I got into early on and put to great use in production.</li>
</ul>
</li>
<li><a href="https://changelog.com/shipit/15">Ship It! Assemble all your infrastructure</a>
<ul>
<li>I&rsquo;m going to have to look into it more, could be useful but there&rsquo;s just so much going on in this space.</li>
</ul>
</li>
<li><a href="https://changelog.com/gotime/192">Go Time: Data streaming and Benthos</a></li>
</ul>
<h3 id="music-">Music 🎵</h3>
<ul>
<li><a href="https://open.spotify.com/album/3SQOzfpmlBgQtT5HHmFaGD?si=Gk5d4_NnTt6bAABq3ysppQ&amp;dl_branch=1">Better Than Reality by Luklash</a>
<ul>
<li>Rocking synths and chiptune breakdowns. The first track, <code>Perpetual Motion</code> sets the stage.</li>
</ul>
</li>
<li><a href="https://open.spotify.com/album/3lRqLudF94UtGUmTM2bjfd?si=3271jDoKRjebWyY7Z_Zffw&amp;dl_branch=1">Celeste B-Sides by Lena Raine</a>
<ul>
<li>Loved the game and the soundtrack. These alternate mixes of tracks work well for me. Great for study/coding.</li>
</ul>
</li>
<li><a href="https://open.spotify.com/track/0tEgooh99Mb09H7vCVU1BA?si=6ea3e701b61d4c83">Mountain Side by Beardyman</a>
<ul>
<li>Very chill.</li>
</ul>
</li>
</ul>
<h2 id="watched-">Watched 👀</h2>
<h3 id="tv-">TV 📺</h3>
<ul>
<li><a href="https://trakt.tv/shows/the-queen-s-gambit">Queen&rsquo;s Gambit</a> S1E4-7
<ul>
<li>Yeah I quite enjoyed this one overall. Well produced and acted. I should try the book and see how I think it compares.</li>
</ul>
</li>
</ul>
<h3 id="movies-">Movies 🎬</h3>
<ul>
<li><a href="https://trakt.tv/movies/a-quiet-place-part-ii-2021">A Quiet Place Part II</a>
<ul>
<li>I remember enjoying part one though not specifically why. Maybe characters weren&rsquo;t making obviously awful decisions? I didn&rsquo;t enjoy this movie.</li>
</ul>
</li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 2</title>
            <link>https://ljones.id.au/posts/2021/08/read-heard-and-watched-2/</link>
            <pubDate>Fri, 13 Aug 2021 13:29:39 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/08/read-heard-and-watched-2/</guid>
            <description>Read Articles  Simple Systems Have Less Downtime. Discovery When Working Remotely. S3 backups and other strategies for ensuring data durability through ransomware attacks. AWS Organizations - Checklist for 2021.  Heard Podcasts  Developer Tea: Joel Beasley from Modern CTO  Host Jonathan Cutrell, director of technology at PBS, interviews and is interviewed by fellow podcaster Joel Beasley on life and work in general. First advice when anyone asks for guidance: focus on your health.</description>
            <content type="html"><![CDATA[<h2 id="read">Read</h2>
<h3 id="articles">Articles</h3>
<ul>
<li><a href="https://www.gkogan.co/blog/simple-systems/">Simple Systems Have Less Downtime</a>.</li>
<li><a href="https://svpg.com/discovery-when-working-remotely/">Discovery When Working Remotely</a>.</li>
<li><a href="https://summitroute.com/blog/2021/08/03/S3_backups_and_other_strategies_for_ensuring_data_durability_through_ransomware_attacks/">S3 backups and other strategies for ensuring data durability through ransomware attacks</a>.</li>
<li><a href="https://www.chrisfarris.com/post/aws-organizations-in-2021/">AWS Organizations - Checklist for 2021</a>.</li>
</ul>
<h2 id="heard">Heard</h2>
<h3 id="podcasts">Podcasts</h3>
<ul>
<li><a href="https://developertea.com/episodes/911ed4af-cc05-4de8-9dfb-15d883550ec0">Developer Tea: Joel Beasley from Modern CTO</a>
<ul>
<li>Host Jonathan Cutrell, director of technology at PBS, interviews and is interviewed by fellow podcaster Joel Beasley on life and work in general.</li>
<li>First advice when anyone asks for guidance: focus on your health. Work on your sleep, eating and exercise above everything else if you want to be a better developer.</li>
</ul>
</li>
<li><a href="https://changelog.com/gotime/191">Go Time: Opening up the opinion box</a>
<ul>
<li>Least/most popular &lsquo;unpopular&rsquo; opinions collected from the regular show segment.</li>
<li>Probably can skip listening to this one unless you have a particular interest in the show or Go. It&rsquo;s a bit of a filler episode.</li>
</ul>
</li>
<li><a href="https://www.abc.net.au/radio/programs/conversations/christine-ball-doctor-joseph-clover-chloroformist/13480718">ABC Conversations: The Chloroformist — extraordinary Doctor Joseph Clover</a></li>
<li><a href="https://opensourcesecurity.io/2021/08/08/episode-283-when-vulnerability-disclosure-becomes-dangerous/">Open Source Security: Episode 283 – When vulnerability disclosure becomes dangerous</a>
<ul>
<li>What do you do when you find an open database full of sensitive, potentially dangerous Personally Identifiable Information (PII)? If you report it and the company responsible doesn&rsquo;t do anything, what next? There&rsquo;s no easy answer as going public may cause more harm than good.</li>
</ul>
</li>
<li><a href="https://changelog.com/podcast/453">The Changelog: Leading leaders who lead engineers</a></li>
</ul>
<h3 id="music">Music</h3>
<ul>
<li><a href="https://open.spotify.com/track/2uAB95gzNi0AWQEyLER6gS?si=a1e4707371824b9c">Playback by &lt;/body&gt;</a>
<ul>
<li>Hitting me somewhere in those vapor/synth-wave feels.</li>
</ul>
</li>
</ul>
<h2 id="watched">Watched</h2>
<h3 id="tv">TV</h3>
<ul>
<li><a href="https://trakt.tv/shows/the-queen-s-gambit">Queen&rsquo;s Gambit</a> S1E2-3
<ul>
<li>It&rsquo;s quite entertaining. I had thought it was based in some way on a real person but apparently not.</li>
</ul>
</li>
</ul>
<h3 id="movies">Movies</h3>
<ul>
<li><a href="https://trakt.tv/movies/the-omega-man-1971">The Omega Man</a>
<ul>
<li>Can&rsquo;t say I really recommend it.</li>
</ul>
</li>
<li><a href="https://trakt.tv/movies/yojimbo-1961">Yojimbo</a>
<ul>
<li>Classic samurai film.</li>
</ul>
</li>
</ul>
<h3 id="online">Online</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=Ajz5ddoL_Ww">Expectations vs Agreements</a>
<ul>
<li>About 30 minutes on the value of coming to agreements with people rather than dictating expectations.</li>
<li><code>I need this by Friday, get it done.</code> vs <code>I need this by Friday, do you have the resources to make that happen?</code>.</li>
<li>Opening a dialogue, or more broadly, <em>communication</em> is key.</li>
<li>Empowering people and giving them a sense of control is key.</li>
<li>This applies to every relationship in your life, not just those in the workplace.</li>
</ul>
</li>
<li><a href="https://www.youtube.com/watch?v=9LMr5XTgeyI">The Tale of Tiffany</a>
<ul>
<li>Origins of the name <code>Tiffany</code>.</li>
</ul>
</li>
<li><a href="https://www.youtube.com/watch?v=tONyeEcozrw">System Shock 2 Review</a>
<ul>
<li>I enjoy <code>MandaloreGaming</code>&rsquo;s deep dive reviews. I particularly enjoyed discovering the overlaps between SS2 and the Thief series.</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li>Here&rsquo;s what doesn&rsquo;t work, in the workplace. Expectations. People do not look forward to living up to expectations. In fact, people rebel against expectations. They don&rsquo;t like it that you expect things of them. They would rather you <em>agree with them</em> about something.</li>
<li>Human beings will love to keep an agreement that they coauthored with you.</li>
</ul>
</blockquote>
]]></content>
        </item>
        
        <item>
            <title>Read, Heard and Watched - 1</title>
            <link>https://ljones.id.au/posts/2021/08/read-heard-and-watched-1/</link>
            <pubDate>Thu, 05 Aug 2021 13:18:06 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/08/read-heard-and-watched-1/</guid>
            <description>I&amp;rsquo;m going to try make a bit of a regular feature, covering the media I heard, read or watched during the week. This first release will be a bit longer as there&amp;rsquo;s a few things I&amp;rsquo;ve recently wrapped up and wanted to cover.
Read Books Ender&amp;rsquo;s Game / Speaker for the Dead Somehow I hadn&amp;rsquo;t touched this series before, while catching parts of the movie. I quite enjoyed them both, with a slight preference for Ender&#39;s Game.</description>
            <content type="html"><![CDATA[<p>I&rsquo;m going to try make a bit of a regular feature, covering the media I heard, read or watched during the week. This first release will be a bit longer as there&rsquo;s a few things I&rsquo;ve recently wrapped up and wanted to cover.</p>
<h2 id="read">Read</h2>
<h3 id="books">Books</h3>
<h4 id="enders-gamehttpswwwgoodreadscombookshow375802ender_s_game--speaker-for-the-deadhttpswwwgoodreadscombookshow7967speaker_for_the_dead"><a href="https://www.goodreads.com/book/show/375802.Ender_s_Game">Ender&rsquo;s Game</a> / <a href="https://www.goodreads.com/book/show/7967.Speaker_for_the_Dead">Speaker for the Dead</a></h4>
<p>Somehow I hadn&rsquo;t touched this series before, while catching parts of the movie. I quite enjoyed them both, with a slight preference for <code>Ender's Game</code>. I think the tighter focus and scope made it overall a more coherent narrative. Hard to follow up, certainly.</p>
<h4 id="normal-peoplehttpswwwgoodreadscombookshow41057294-normal-people"><a href="https://www.goodreads.com/book/show/41057294-normal-people">Normal People</a></h4>
<p>I churned through this one pretty quickly, it&rsquo;s easy reading though somewhat painful. Consider it a masterclass in how to fail at communication and hurt the people you care most about. I guess I found it engaging because like a train wreck in slow-motion, I couldn&rsquo;t quite look away.</p>
<h4 id="atomic-habitshttpswwwgoodreadscombookshow40121378-atomic-habits"><a href="https://www.goodreads.com/book/show/40121378-atomic-habits">Atomic Habits</a></h4>
<p>Like most self-improvement books, it&rsquo;s generally things you already inherently know but either haven&rsquo;t thought about or haven&rsquo;t built a framework around to actually implement. This book provides the latter with some explanation around why it should work. As usual, <a href="https://youtu.be/aRz0QZc8TDE">ain&rsquo;t nothing to it but to do it</a>.</p>
<h2 id="heard">Heard</h2>
<h3 id="podcasts">Podcasts</h3>
<h4 id="ship-it-11-honeycombs-secret-to-high-performing-teamshttpschangelogcomshipit11"><a href="https://changelog.com/shipit/11">Ship It #11: Honeycomb&rsquo;s secret to high-performing teams</a></h4>
<p>Charity Majors seems to strike the right balance of take-no-prisoners confidence while welcoming alternative opinions. <em>Bring them if you&rsquo;ve got them, but I know what I&rsquo;m talking about so you better have a good argument</em>, that kind of jazz. Anyway, if you have any interest in monitoring or observability I think she&rsquo;s someone you should listen to.</p>
<h4 id="abc-conversations">ABC Conversations</h4>
<ul>
<li><a href="https://www.abc.net.au/radio/programs/conversations/gordon-parker-depression-bipolar-mood-disorders-burnout/13447340">Taming the Black Dog, and burnout</a></li>
<li><a href="https://www.abc.net.au/radio/programs/conversations/caroline-de-costa-obstetrician-women-babies-birth-control/13448842">Rebel doctor Caroline de Costa — smuggling condoms and scaring priests</a></li>
</ul>
<p>Both interesting and worth listening to, for quite different reasons. The former I found interesting as someone who has experienced burnout in the past, the latter because Caroline de Costa just got up to so much! I learned a few things too.</p>
<p>Did you know that <a href="https://en.wikipedia.org/wiki/Rabbit_test">rabbits were used in human pregnancy tests</a>? I didn&rsquo;t!</p>
<h3 id="music">Music</h3>
<h4 id="bill-laurancehttpsopenspotifycomartist2qjvv1gkln8xkqxvndglhf"><a href="https://open.spotify.com/artist/2QjVv1gkLn8XkQxVndgLHF">Bill Laurance</a></h4>
<p>The first track I heard was <code>The Good Things</code> and I think I proceeded to listen to about three albums immediately thereafter. The transition at around 4:25 on that track is a killer. Jazzy, interesting, funky, exciting, background/focus/study music? Some of those words don&rsquo;t work together but this music sure does for me.</p>
<h2 id="watched">Watched</h2>
<h3 id="tv">TV</h3>
<h4 id="olympic-sport-climbinghttpsolympicscomtokyo-2020ensportssport-climbing"><a href="https://olympics.com/tokyo-2020/en/sports/sport-climbing/">Olympic Sport Climbing</a></h4>
<p>Bouldering about three times a week with a bit of lead climbing makes up the bulk of the exercise I get, and I love it. I&rsquo;ve never really been a sporting person though I&rsquo;ve gone through phases of weight-lifting, but rock climbing works really well for me. The physical and the mental/problem-solving challenge is just 👌.</p>
<p>Anyway, watching some of the best climbers in the world do their thing? Heck yes.</p>
<h3 id="movies">Movies</h3>
<h4 id="booksmarthttpstrakttvmoviesbooksmart-2019"><a href="https://trakt.tv/movies/booksmart-2019">Booksmart</a></h4>
<p>It&rsquo;s a smarter, better <a href="https://trakt.tv/movies/superbad-2007">Superbad</a>, basically. Shout out to <a href="https://www.youtube.com/watch?v=orFw3vlif8I">this Superbad scene</a> though, while the rest of it has probably aged poorly this still gets chuckles from me. 🤷‍♂️</p>
<hr>
<p>That&rsquo;s a wrap, it was worthwhile. 😅</p>
]]></content>
        </item>
        
        <item>
            <title>Looking up remote AWS NAT Gateway IPs (in Terraform)</title>
            <link>https://ljones.id.au/posts/2021/08/looking-up-remote-aws-nat-gateway-ips-in-terraform/</link>
            <pubDate>Mon, 02 Aug 2021 11:34:01 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/08/looking-up-remote-aws-nat-gateway-ips-in-terraform/</guid>
            <description>The other day I needed to lookup the public IPs of a NAT gateway, in another AWS account, in Terraform.
There&amp;rsquo;s three ways you could achieve this:
 You can look it up dynamically, as I have. This assumes you have access to credentials which are able to query that AWS account. If the other VPC is also managed by Terraform, you could query the state file for this information. You&amp;rsquo;d need access to the state file, obviously.</description>
            <content type="html"><![CDATA[<p>The other day I needed to lookup the public IPs of a NAT gateway, in another AWS account, in Terraform.</p>
<p>There&rsquo;s three ways you could achieve this:</p>
<ul>
<li>You can look it up dynamically, as I have. This assumes you have access to credentials which are able to query that AWS account.</li>
<li>If the other VPC is also managed by Terraform, you could query the state file for this information. You&rsquo;d need access to the state file, obviously.</li>
<li>You could look up (or be given) the IPs and just hardcode them or add them as a variable. NAT gateways are unlikely to change frequently unless the infrastructure is frequently being torn up/down so while manual, it would work.</li>
</ul>
<p>Each of the steps below corresponds to a block of Terraform code.</p>
<ol>
<li>Lookup the other VPC, in this came filtering on name <code>vpc</code>.</li>
<li>Lookup all the elastic network interfaces in the other account that are;
<ol>
<li>In the above VPC,</li>
<li>Have source destination checking disabled (required by NAT Gateways and NAT instances),</li>
<li>Requester-managed, which means it&rsquo;s a network interface that was created by an AWS service.</li>
</ol>
</li>
<li>Collect data on each of the IPs that we got in the previous step. You&rsquo;re not ready to use these wherever you need.</li>
<li>As an example, you could use them in an AWS WAF IP set.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-tf" data-lang="tf"><span style="color:#75715e"># 1
</span><span style="color:#75715e"></span><span style="color:#66d9ef">data</span> <span style="color:#e6db74">&#34;aws_vpc&#34;</span> <span style="color:#e6db74">&#34;other_account&#34;</span> {
<span style="color:#66d9ef">  provider</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">aws</span>.<span style="color:#a6e22e">other_account</span>

  <span style="color:#a6e22e">tags</span> = {
    <span style="color:#a6e22e">Name</span> = <span style="color:#e6db74">&#34;vpc&#34;</span>
  }
}<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e"># 2
</span><span style="color:#75715e"></span><span style="color:#66d9ef">data</span> <span style="color:#e6db74">&#34;aws_network_interfaces&#34;</span> <span style="color:#e6db74">&#34;other_account_vpc_ngw_enis&#34;</span> {
<span style="color:#66d9ef">  provider</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">aws</span>.<span style="color:#a6e22e">other_account</span>

  <span style="color:#a6e22e">filter</span> {
    <span style="color:#a6e22e">name</span>   = <span style="color:#e6db74">&#34;vpc-id&#34;</span>
    <span style="color:#a6e22e">values</span> = [data.<span style="color:#a6e22e">aws_vpc</span>.<span style="color:#a6e22e">other_account</span>.<span style="color:#a6e22e">id</span>]
  }
  <span style="color:#a6e22e">filter</span> {
    <span style="color:#a6e22e">name</span>   = <span style="color:#e6db74">&#34;source-dest-check&#34;</span>
    <span style="color:#a6e22e">values</span> = [<span style="color:#e6db74">&#34;false&#34;</span>]
  }
  <span style="color:#a6e22e">filter</span> {
    <span style="color:#a6e22e">name</span>   = <span style="color:#e6db74">&#34;requester-managed&#34;</span>
    <span style="color:#a6e22e">values</span> = [<span style="color:#e6db74">&#34;true&#34;</span>]
  }
}<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e"># 3
</span><span style="color:#75715e"></span><span style="color:#66d9ef">data</span> <span style="color:#e6db74">&#34;aws_network_interface&#34;</span> <span style="color:#e6db74">&#34;eni_ids&#34;</span> {
<span style="color:#66d9ef">  provider</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">aws</span>.<span style="color:#a6e22e">other_account</span>

  <span style="color:#a6e22e">for_each</span> = data.<span style="color:#a6e22e">aws_network_interfaces</span>.<span style="color:#a6e22e">other_account_vpc_ngw_enis</span>.<span style="color:#a6e22e">ids</span>
  <span style="color:#a6e22e">id</span>       = each.<span style="color:#a6e22e">value</span>
}<span style="color:#75715e">
</span><span style="color:#75715e">
</span><span style="color:#75715e"># 4
</span><span style="color:#75715e"></span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_wafv2_ip_set&#34;</span> <span style="color:#e6db74">&#34;allowlist&#34;</span> {
  <span style="color:#a6e22e">name</span>               = <span style="color:#e6db74">&#34;allowlist&#34;</span>
  <span style="color:#a6e22e">scope</span>              = <span style="color:#e6db74">&#34;REGIONAL&#34;</span>
  <span style="color:#a6e22e">ip_address_version</span> = <span style="color:#e6db74">&#34;IPV4&#34;</span>
  <span style="color:#a6e22e">addresses</span> = [<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">eni</span> <span style="color:#66d9ef">in</span> data.<span style="color:#a6e22e">aws_network_interface</span>.<span style="color:#a6e22e">eni_ids</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">eni</span>.<span style="color:#a6e22e">association</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">public_ip</span><span style="color:#e6db74">}</span><span style="color:#e6db74">/32&#34;</span>]
}
</code></pre></div><p>It feels a little bit clunky to me at the moment, but it works. 👌</p>
]]></content>
        </item>
        
        <item>
            <title>Certified AWS Solutions Architect (Associate)</title>
            <link>https://ljones.id.au/posts/2021/07/certified-aws-solutions-architect-associate/</link>
            <pubDate>Thu, 29 Jul 2021 12:30:50 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/07/certified-aws-solutions-architect-associate/</guid>
            <description>So I finally got around to sitting the AWS Certified Solutions Architect - Associate exam, aka AWS-SAA-C02.
 This certification validates the ability to design and implement distributed systems on AWS.
 Basically, it says you have a clue of what to do with AWS. That&amp;rsquo;s not to say it&amp;rsquo;s easy; AWS has many, many services and you&amp;rsquo;re expected to be able to answer some pretty specific questions about more than just a few of them.</description>
            <content type="html"><![CDATA[<p>So I finally got around to sitting the <a href="https://aws.amazon.com/certification/certified-solutions-architect-associate/">AWS Certified Solutions Architect - Associate</a> exam, aka <code>AWS-SAA-C02</code>.</p>
<blockquote>
<p>This certification validates the ability to design and implement distributed systems on AWS.</p>
</blockquote>
<p>Basically, it says you have a clue of what to do with AWS. That&rsquo;s not to say it&rsquo;s easy; AWS has many, many services and you&rsquo;re expected to be able to answer some pretty specific questions about more than just a few of them. A lot of them you&rsquo;re not going to run into in simple day-to-day use cases, so for me at least, there was some study required.</p>
<p>Anyway, I did alright in the exam, though it&rsquo;s not like there&rsquo;s bonus points for anything better than passing. Out of a possible scaled score from 100-1000 points, you need at least 720. I got 956, so I&rsquo;m pretty happy with that.</p>
<p>Aside from using AWS, I leaned on following resources for exam preparation.</p>
<ul>
<li><a href="https://www.whizlabs.com/aws-solutions-architect-associate/">Whizlabs practice exams</a>: I wouldn&rsquo;t recommend these. The quality just isn&rsquo;t there in relation to the next resource.</li>
<li><a href="https://digitalcloud.training/aws-certified-solutions-architect-associate/">DigitalCloud practice exams</a>: Well written questions that actually represent what you&rsquo;re going to see in the exam.</li>
<li><a href="https://karansingh.gitbook.io/aws-saa-c02/">https://karansingh.gitbook.io/aws-saa-c02/</a>: This was handy as a bit of a cheat-sheet, easy reading and not overly verbose. It&rsquo;s not perfect but it&rsquo;s good.</li>
</ul>
]]></content>
        </item>
        
        <item>
            <title>Deploying Crowdstrike with Meraki</title>
            <link>https://ljones.id.au/posts/2021/07/deploying-crowdstrike-with-meraki/</link>
            <pubDate>Wed, 28 Jul 2021 11:19:07 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/07/deploying-crowdstrike-with-meraki/</guid>
            <description>Just some quick notes I put together, for anyone looking to deploy the Crowdstrike Falcon agent to MacOS via Cisco Meraki.
 Hit up the Crowdstrike documentation article Falcon Sensor for Mac Deployment Guide (version 6.11 and later) and that&amp;rsquo;ll have the latest correct links. Specifically, you&amp;rsquo;re going to want to get the latest MacOS Falcon agent, your Customer ID checksum and the Falcon MDM profile. Head over to Meraki and after logging in, Systems Manager -&amp;gt; Configure -&amp;gt; Tags.</description>
            <content type="html"><![CDATA[<p>Just some quick notes I put together, for anyone looking to deploy the Crowdstrike Falcon agent to MacOS via Cisco Meraki.</p>
<ol>
<li>Hit up the Crowdstrike documentation article <code>Falcon Sensor for Mac Deployment Guide (version 6.11 and later)</code> and that&rsquo;ll have the latest correct links. Specifically, you&rsquo;re going to want to get the latest MacOS Falcon agent, your <code>Customer ID checksum</code> and the Falcon MDM profile.</li>
<li>Head over to Meraki and after logging in, <code>Systems Manager -&gt; Configure -&gt; Tags</code>.
<ul>
<li>Select <code>Add tag</code> at the top right and select an appropriate tag type. For testing, the manual tag type may be most appropriate.</li>
<li>Set an easy name, like <code>Crowdstrike</code> and add a device for testing.</li>
</ul>
</li>
<li>Now <code>Systems Manager -&gt; Manage -&gt; Settings</code>.
<ul>
<li>Select <code>Add profile</code> and <code>Upload custom Apple profile</code>.</li>
<li>Upload that Falcon MDM profile you downloaded earlier</li>
<li>Set the <code>Scope</code> to <code>with ANY of the following tags</code> and add the <code>Crowdstrike</code> tag to the <code>Device tags</code> section.</li>
</ul>
</li>
<li>Next <code>Systems Manager -&gt; Manage -&gt; Apps</code>.
<ul>
<li>Select <code>Add app</code>, <code>macOS</code> app platform and <code>Custom app</code>.</li>
<li>Set the <code>Name</code> field to <code>Falcon</code>, that&rsquo;s important!</li>
<li>Set an <code>Icon URL</code> if you feel like making it look pretty.</li>
<li>Set the <code>Source</code> type to <code>Upload to the Meraki cloud</code> before adding the Falcon agent installer.</li>
<li>You&rsquo;ll want to set the <code>Command line</code> to <code>sudo /Applications/Falcon.app/Contents/Resources/falconctl license $CS_CID</code>, where <code>$CS_ID</code> is equal to your <code>Customer ID checksum</code>.</li>
<li>Set the tags again as above</li>
<li>Finally, set your own choice of other options, like <code>Auto-install</code>. I don&rsquo;t see the point of using the <code>Install as Managed</code> or <code>Remove with MDM</code> as we&rsquo;re going to want to provide a token to the agent in the event we uninstall it.</li>
</ul>
</li>
<li>You&rsquo;re ready now to push to your test device.</li>
</ol>
<p>It works pretty well, at least for my needs so far.</p>
]]></content>
        </item>
        
        <item>
            <title>AWS, SCP, Footguns and You</title>
            <link>https://ljones.id.au/posts/2021/07/aws-scp-footguns-and-you/</link>
            <pubDate>Tue, 20 Jul 2021 05:42:45 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/07/aws-scp-footguns-and-you/</guid>
            <description>Recently, I was deployed a Helm chart into an AWS EKS cluster which requested persistent volumes. For the uninitiated, basically I was getting something running on Kubernetes that required access to longer-term storage. Except, it wasn&amp;rsquo;t working. Most of the pods deployed fine, except for those needing the volumes. Here we go&amp;hellip;
I go digging and see there&amp;rsquo;s something about waiting for a Persistent Volume Claim (PVC). That means the storage hasn&amp;rsquo;t become available when requested.</description>
            <content type="html"><![CDATA[<p>Recently, I was deployed a Helm chart into an AWS EKS cluster which requested persistent volumes. For the uninitiated, basically I was getting something running on Kubernetes that required access to longer-term storage. Except, it wasn&rsquo;t working. Most of the pods deployed fine, except for those needing the volumes. Here we go&hellip;</p>
<p>I go digging and see there&rsquo;s something about waiting for a Persistent Volume Claim (PVC). That means the storage hasn&rsquo;t become available when requested. Okidoke, we can investigate the PVC and see what&rsquo;s up.</p>
<pre><code class="language-log" data-lang="log">Warning  ProvisioningFailed   119s                  persistentvolume-controller  (combined from similar events): Failed to provision volume with StorageClass &quot;gp2&quot;: UnauthorizedOperation: You are not authorized to perform this operation. Encoded authorization failure message: N24SBXsb8kL_...
           status code: 403, request id: ef5acce1-...
</code></pre><p>Hm, <code>403</code>? <code>UnauthorizedOperation</code>? That&rsquo;s a bit odd. I&rsquo;ve had PVCs create volumes in the past using the same credentials. What gives? Time to dig into that encoded failure message.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sh" data-lang="sh">aws sts decode-authorization-message --encoded-message <span style="color:#e6db74">&#39;N24SBXsb8kL_...&#39;</span> --query DecodedMessage --output text | jq <span style="color:#e6db74">&#39;.&#39;</span>
<span style="color:#f92672">{</span>
  <span style="color:#e6db74">&#34;allowed&#34;</span>: false,
  <span style="color:#e6db74">&#34;explicitDeny&#34;</span>: true,
  <span style="color:#e6db74">&#34;matchedStatements&#34;</span>: <span style="color:#f92672">{</span>
    <span style="color:#e6db74">&#34;items&#34;</span>: <span style="color:#f92672">[</span>
      <span style="color:#f92672">{</span>
        <span style="color:#e6db74">&#34;statementId&#34;</span>: <span style="color:#e6db74">&#34;DenyUnencryptedEBSVolumes&#34;</span>,
        <span style="color:#e6db74">&#34;effect&#34;</span>: <span style="color:#e6db74">&#34;DENY&#34;</span>,
...
</code></pre></div><p>Ah, that&rsquo;s getting much more specific. That&rsquo;s even ringing some bells. Some time ago, I had applied an <a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html">AWS Organization Service Control Policy (SCP)</a> on this account that prevented the creation of unencrypted Elastic Block Storage (EBS) volumes. In EC2 I had set up <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html#encryption-by-default">encryption by default</a>, but the default storage provisioner in EKS was ignorant of this and was diligently headbutting the wall. I&rsquo;d also noticed that it was trying to create a volume using the <code>gp2</code> storage class, which has been superceded by <code>gp3</code>.</p>
<p>How to unbreak this situation? Enter the <a href="https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html">EBS Container Storage Interface (CSI) driver</a>. I&rsquo;ll spare you the nitty gritty as I was able to follow the documentation and everything basically just-worked™️. Happy days! A previously established and somewhat forgotten security rule prevented me from accidentally allowing a bad thing to happen.</p>
<p><em>The system works</em>.</p>
]]></content>
        </item>
        
        <item>
            <title>Session Cookies, in (a) Word</title>
            <link>https://ljones.id.au/posts/2021/07/session-cookies-in-a-word/</link>
            <pubDate>Fri, 09 Jul 2021 13:07:37 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/07/session-cookies-in-a-word/</guid>
            <description>About a week ago, we had an odd issue reported. Users clicking links to a specific route in the app were being taken to the login page, regardless of whether they were already logged in or not. This didn&amp;rsquo;t happen if they copy-pasted the link, or opened the link directly from say, their chat client. This was very specifically happening with links in Office documents, like Word.
A colleague spent some time building up an environment to be able to reproduce this issue, and sure enough, there it was.</description>
            <content type="html"><![CDATA[<p>About a week ago, we had an odd issue reported. Users clicking links to a specific route in the app were being taken to the login page, regardless of whether they were already logged in or not. This didn&rsquo;t happen if they copy-pasted the link, or opened the link directly from say, their chat client. This was very specifically happening with links in Office documents, like Word.</p>
<p>A colleague spent some time building up an environment to be able to reproduce this issue, and sure enough, there it was. He started diving in with Wireshark and all the cool tools, but couldn&rsquo;t figure out what was going on. I mean, he could see what was happening, but the why was missing. So I get called in. Five minutes of googling later and I&rsquo;ve fished up some <a href="https://stackoverflow.com/questions/2653626/why-are-cookies-unrecognized-when-a-link-is-clicked-from-an-external-source-i-e">relevant</a> <a href="https://docs.microsoft.com/en-US/office/troubleshoot/office-suite-issues/click-hyperlink-to-sso-website">links</a>. The TLDR is that Office does a pretty bad job of handling session cookies (i.e. it doesn&rsquo;t), and has been handling it pretty badly for the past&hellip; decade or so.</p>
<p><img src="https://media.giphy.com/media/2UCt7zbmsLoCXybx6t/giphy.gif" alt="Cartoon dog in a room on fire meme" title="This is fine."></p>
<p>I&rsquo;m not actually here to put the boot into Microsoft. Maybe there&rsquo;s a good reason for it<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The actual point is, do you default to searching for the answer or trying to solve it yourself? Personally, it depends on the context. Is this a learning exercise or do I just need to get something done? While I might get answers to things faster, maybe my compadre ends up with a deeper, better understanding of the problem. I don&rsquo;t think there&rsquo;s a right or wrong approach here, just differences.</p>
<p>What do you think? What&rsquo;s your default problem solving approach?</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Probably not, but there you go.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Movie Review 2 - To Kill A Mockingbird</title>
            <link>https://ljones.id.au/posts/2021/06/movie-review-2-to-kill-a-mockingbird/</link>
            <pubDate>Tue, 29 Jun 2021 01:16:36 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/06/movie-review-2-to-kill-a-mockingbird/</guid>
            <description>Just dropping my thoughts on this while it&amp;rsquo;s fresh: It&amp;rsquo;s an oldie, but a goodie. Spoilers ahead.
 I read the book maybe a year or two and it&amp;rsquo;s taken me this long to get around to watching the film adaptation. A long enough period of time that my memory was getting rough around the edges on specific plot details, so take what I say ahead with a grain of salt, but&amp;hellip;</description>
            <content type="html"><![CDATA[<p>Just dropping my thoughts on this while it&rsquo;s fresh: It&rsquo;s an oldie, but a goodie. Spoilers ahead.</p>
<hr>
<p>I read the book maybe a year or two and it&rsquo;s taken me this long to get around to watching the film adaptation. A long enough period of time that my memory was getting rough around the edges on specific plot details, so take what I say ahead with a grain of salt, but&hellip;</p>
<p>I feel like this is a really well done adaptation. Certain scenes are basically recreated exactly as I had seen them in my mind&rsquo;s eye while reading, while others have been left out entirelly. Keeping to a reasonable two hours, it&rsquo;s neither long nor short and the missing scenes don&rsquo;t hinder the progression at all. The cinematography is great, and combined with excellent performances from the actors, I was moved to tears more than once. The music feels of the time, but it all works.</p>
<p>Summary: If you haven&rsquo;t read it, read it. If you haven&rsquo;t watched it, watch it. 5/5 tiny birds 🐦.</p>
]]></content>
        </item>
        
        <item>
            <title>Blog Still Unmaintained</title>
            <link>https://ljones.id.au/posts/2021/06/blog-still-unmaintained/</link>
            <pubDate>Sat, 26 Jun 2021 07:46:03 +0000</pubDate>
            
            <guid>https://ljones.id.au/posts/2021/06/blog-still-unmaintained/</guid>
            <description>You&amp;rsquo;d be forgiven for thinking I&amp;rsquo;m dead. It&amp;rsquo;s been a year. I&amp;rsquo;m still kicking, though. I even had several drafts that were sitting around at the end of last year, but I lost interest and let them go quietly into the night when I formatted a device.
So what&amp;rsquo;s been happening?
 I changed roles, which has only been good for me. If there was something greater to be learned from the my time at the previous role, I&amp;rsquo;d say that if the fundamentals feel off for more than 3-6 months, start looking.</description>
            <content type="html"><![CDATA[<p>You&rsquo;d be forgiven for thinking I&rsquo;m dead. It&rsquo;s been a year. I&rsquo;m still kicking, though. I even had several drafts that were sitting around at the end of last year, but I lost interest and let them go quietly into the night when I formatted a device.</p>
<p>So what&rsquo;s been happening?</p>
<ul>
<li>I changed roles, which has only been good for me. If there was something greater to be learned from the my time at the previous role, I&rsquo;d say that if the fundamentals feel <em>off</em> for more than 3-6 months, start looking. I was considering it long before following through and it wasn&rsquo;t doing me any good. Several people followed me not long after; I won&rsquo;t speak for them, but I suspect they were in all the same boat.</li>
<li>I haven&rsquo;t been smoking meats or brewing so much. I think it&rsquo;s mainly the lack of space, leading to the next point&hellip;</li>
<li>I&rsquo;m vaguely looking at buying property, somewhere rural or semi-rural. My partner and I have relatives living in the South-West of WA and honestly, it&rsquo;s a beautiful environment to be in. We both work remotely, so semi-decent internet is our driving concern. Unfortunately it&rsquo;s a pretty unoriginal idea, so finding a property at a reasonable price is another matter.</li>
<li>I&rsquo;ve been bouldering/rock-climbing for about a year and a half now, with a few breaks for Covid-19 lockdowns. It&rsquo;s a great sport, I love the community feel while still being a relatively individual endeavour. The exercise has been good for me too 😅.</li>
</ul>
<p>I&rsquo;m going to leave it there for now, but I intend to revive this a bit. We&rsquo;ll see.</p>
<p>Don&rsquo;t hold your breath.</p>
]]></content>
        </item>
        
        <item>
            <title>Blog is(n&#39;t) Unmaintained</title>
            <link>https://ljones.id.au/posts/2020/07/blog-isnt-unmaintained/</link>
            <pubDate>Sun, 05 Jul 2020 14:25:05 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/07/blog-isnt-unmaintained/</guid>
            <description>Oh the irony of talking about things not being maintained and then not updating my blog for ~2 months. Awkies! Anyway, I&amp;rsquo;ve been busy with tasks, some things more important that others. I&amp;rsquo;ll try to be better. I might even try #100daystooffload.
 Highest priority, I finished Black Mesa, the fan-remake of Half Life in the Source engine. Overall, I enjoyed it, but there were a few detractors.
 Performance was pretty variable, but that could be the nature of playing on Linux/Proton.</description>
            <content type="html"><![CDATA[<p>Oh the irony of talking about things not being maintained and then not updating my blog for ~2 months. Awkies! Anyway, I&rsquo;ve been busy with tasks, some things more important that others. I&rsquo;ll try to be better. I might even try <a href="https://100daystooffload.com/">#100daystooffload</a>.</p>
<hr>
<p>Highest priority, I finished <a href="https://store.steampowered.com/app/362890/Black_Mesa/">Black Mesa</a>, the fan-remake of Half Life in the Source engine. Overall, I enjoyed it, but there were a few detractors.</p>
<ul>
<li>Performance was pretty variable, but that could be the nature of playing on Linux/Proton. Some of the biggest arenas, which should have been highlights, were a real struggle to get through with &lt;20 FPS sometimes. Oof</li>
<li>Combat overall feels more lethal than I remember the original HL</li>
<li>As a corollary of the above, I tended to yo-yo between having full health and being nearly dead. The tension would have been raised if I was harder to kill but there were fewer resources around</li>
<li>A similar story with ammunition; I generally wasn&rsquo;t even close to running out</li>
</ul>
<p>If you&rsquo;re a fan, it&rsquo;s worth a spin but for the real nostalgia kick I&rsquo;d go back to HL.</p>
<p>Currently I&rsquo;m vaporising struts in <a href="https://store.steampowered.com/app/1161580/Hardspace_Shipbreaker/">Hardspace: Shipbreaker</a>. It&rsquo;s in early access and is pretty limited at the moment, but I really like the direction and aesthetic that they&rsquo;re going with.</p>
<hr>
<p>So my last post was around Kubernetes and what the coding-on-k8s experience might be like. The TL;DR is that I&rsquo;m going to wait for that to mature 😅. In general, I&rsquo;m thinking about replacing the k8s setup with Docker Swarm. I had previously avoided it due to all the drama around it when it was released and bundled in with Docker; which is really a shame! It seems to provide a pretty solid experience with not nearly the complexity or pain of Kubernetes. Pair it up with <a href="https://github.com/portainer/portainer">Portainer</a> and you&rsquo;re starting to make containers really approachable for people that don&rsquo;t want to touch a CLI. I&rsquo;m not one of those people but I do work with them, so it really makes selling containerisation a lot easier.</p>
<p>The Swarm/K8S thing is a little apple for oranges and I&rsquo;m glad that I did get some experience with K8S but let&rsquo;s just say&hellip; I&rsquo;m not going to be rushing back to K8S unless there&rsquo;s a desperate need to use it. Which is really how it should be. ✌️</p>
<hr>
<p>I&rsquo;ve been playing around with a few tech things recently. As usual, starting things and dropping them for the next shiny.</p>
<ul>
<li><a href="https://gobuffalo.io/">Buffalo</a>: I like it but I&rsquo;d like a long-form blog post on getting started. I should probably try that 😓</li>
<li><a href="https://github.com/gocolly/colly">Colly</a>: <em>scrape</em> all the things!</li>
<li><a href="https://dgraph.io/">Dgraph</a>: Pretty cool! The standalone docker image made it really easy to get going. I really ought to read a book on how best to architect things in graph databases though! Still, learn things, break things, learn more things.</li>
</ul>
<p>Maybe expect a post on <a href="https://svelte.dev/">Svelte</a>, I recently heard about this while listening to the <a href="https://changelog.com/gotime/136">Go Time podcast</a> and after having a poke around the quick-start it looks pretty schmick. 👌</p>
]]></content>
        </item>
        
        <item>
            <title>Draft is Unmaintained</title>
            <link>https://ljones.id.au/posts/2020/05/draft-is-unmaintained/</link>
            <pubDate>Wed, 06 May 2020 15:10:08 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/05/draft-is-unmaintained/</guid>
            <description>TL;DR: Always check issue trackers for projects that you&amp;rsquo;re potentially going to implement. Who knows what you&amp;rsquo;ll find.
 So like I&amp;rsquo;ve been saying, I&amp;rsquo;ve been playing with Kubernetes and have deployed it to my homelab. Maybe I&amp;rsquo;ll document that whole homelab setup one day. Anyway, in the meantime, the question is, &amp;lsquo;Hey, maybe I should go whole hog? Write my code and deploy to Kubernetes as well; that definitely won&amp;rsquo;t be overcomplicating things!</description>
            <content type="html"><![CDATA[<p>TL;DR: Always check issue trackers for projects that you&rsquo;re potentially going to implement. Who knows what you&rsquo;ll find.</p>
<hr>
<p>So like I&rsquo;ve been saying, I&rsquo;ve been playing with Kubernetes and have deployed it to my homelab. Maybe I&rsquo;ll document that whole homelab setup one day. Anyway, in the meantime, the question is, &lsquo;Hey, maybe I should go whole hog? Write my code and deploy to Kubernetes as well; that definitely won&rsquo;t be overcomplicating things! 😅&rsquo;. So off I go and install <a href="https://minikube.sigs.k8s.io">Minikube</a>, which was surprisingly seamless. Great job to the people behind that!. Then I&rsquo;m doing research on projects that make the development experience seamless, things like; I write my code and hit save, then automagically it&rsquo;s running in that local minikube cluster. A few names start reoccurring, in no particular order&hellip;</p>
<ul>
<li><a href="https://github.com/Azure/draft">Draft</a></li>
<li><a href="https://github.com/GoogleCloudPlatform/skaffold">Skaffold</a></li>
<li><a href="https://github.com/garden-io/garden">Garden</a></li>
<li><a href="https://github.com/windmilleng/tilt">Tilt</a></li>
<li><a href="https://github.com/telepresenceio/telepresence">Telepresence</a></li>
</ul>
<p>Being lazy and not exactly an expert, Draft seemed like a decent place to start as it kept coming up in articles as being the simplest choice, and easiest to get going. I have a look around the <code>README.md</code> on the repo, seems alright. Cool cool, off I go to deploy. Hm, what&rsquo;s the latest release. <code>v0.16.0</code>, hm, seems low but <a href="https://0ver.org/">zero is the new black</a>, after all. Let&rsquo;s go check the date on that release. <code>michelleN released this on 27 Aug 2018</code>, oh, oh no. At time of writing, that&rsquo;s roughly <strong>a year and a half old</strong>. I start digging into the issues.</p>
<blockquote>
<p><a href="https://github.com/Azure/draft/issues/957">This project is no longer being actively developed or maintained?</a></p>
</blockquote>
<p><img src="https://media.giphy.com/media/WxDZ77xhPXf3i/giphy.gif" alt="Man grimacing" title="Oof."></p>
<p>This is promising.</p>
<p>That issue was raised on 27th December, 2019; ~5 months ago. About two weeks ago, a member of the team closed the issue and updated that the project was on basically dead due to resources being allocated elsewhere. <em>Mmhmm</em>. As one commenter on the thread put it:</p>
<blockquote>
<p>A little &lsquo;unmaintained&rsquo; note in the readme would&rsquo;ve saved me the last hour and a half trying this out. FFS.</p>
</blockquote>
<p>Thankfully it was only a few minutes of my time.</p>
]]></content>
        </item>
        
        <item>
            <title>(Wire)Guardians of the Galaxy</title>
            <link>https://ljones.id.au/posts/2020/05/wireguardians-of-the-galaxy/</link>
            <pubDate>Tue, 05 May 2020 10:27:32 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/05/wireguardians-of-the-galaxy/</guid>
            <description>So WireGuard is all the rage currently, and having recently wiped out my Pi-based Pi-hole/OpenVPN setup1, it seemed like the perfect time to take it for a spin and kick the tires. I almost ended up being lazy and running with Tailscale but they don&amp;rsquo;t have an Android app yet, so that&amp;rsquo;s a massive joke. Anyway, there&amp;rsquo;s more tutorials for setting up WireGuard out there than I can throw a stick at so I won&amp;rsquo;t add to that noise.</description>
            <content type="html"><![CDATA[<p>So <a href="https://www.wireguard.com/">WireGuard</a> is all the rage currently, and having recently wiped out my Pi-based <a href="https://pi-hole.net/">Pi-hole</a>/<a href="https://openvpn.net/">OpenVPN</a> setup<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, it seemed like the perfect time to take it for a spin and kick the tires. I almost ended up being lazy and running with <a href="https://tailscale.com/">Tailscale</a> but they don&rsquo;t have an Android app yet, so that&rsquo;s a massive joke. Anyway, there&rsquo;s more tutorials for setting up WireGuard out there than I can throw a stick at so I won&rsquo;t add to that noise. Instead, I&rsquo;ll tell you about my silly mistake that stole about 15 minutes of my life.</p>
<p>So I&rsquo;ve followed along with the <a href="https://www.wireguard.com/quickstart/">quick start</a> and <a href="https://github.com/pirate/wireguard-docs">unofficial documentation</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, and I hit connect on the my phone. No good, I&rsquo;m not seeing any received traffic.</p>
<ul>
<li>I go to check the logs in the android app itself but it&rsquo;s just noise, I can&rsquo;t see anything useful in the context of debugging a connection. Bummer.</li>
<li>I check that I&rsquo;ve forwarded the correct port to the correct address on my home network. Yep, I have.</li>
<li>I check that my Dynamic DNS<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> is providing the correct IP. It is.</li>
<li>I check that I&rsquo;ve actually configured WireGuard correctly and that there aren&rsquo;t any typos in the port number. Yup, all correct.</li>
</ul>
<p>By this stage, I&rsquo;m starting to get frustrated. Everyone talks about how <em>easy</em> WireGuard is to set up! What&rsquo;s going on? I kill WireGuard and fire up <code>nc -l &lt;WireGuard port&gt;</code> on the server and <code>nc &lt;server address&gt; &lt;WireGuard port&gt;</code> on my desktop. The connection is made, and text entered on the client appears on the server. Alright, the port works locally. I try again using the DDNS address. Same again, it works. Okay, the forwarding on the router is working fine too. Hm. Something is different though. The DDNS address. <strong>squinting intensifies</strong> That&rsquo;s a <code>.org</code>.</p>
<ul>
<li>I check the endpoint address I&rsquo;ve configured in the Android app. It&rsquo;s <code>.com</code></li>
</ul>
<p><img src="https://media.giphy.com/media/116a8zosxwA0SI/giphy.gif" alt="Robert Downey Jnr sighing" title="The feels."></p>
<p>So, when something isn&rsquo;t connecting, check that you&rsquo;ve given it the right address.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Pi-hole is now running on my Kubernetes cluster. 🤷&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Why are the official docs not like this? 😠&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>The awesome <a href="https://www.duckdns.org/">Duck DNS</a> project. 🎉&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Not Dead Yet</title>
            <link>https://ljones.id.au/posts/2020/05/not-dead-yet/</link>
            <pubDate>Sun, 03 May 2020 21:31:28 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/05/not-dead-yet/</guid>
            <description>It has been longer than I wanted since my last post, with a few half1 finished drafts on the go. What have I been doing? Setting up Kubernetes on my homelab. Or at least, a flavour of. I&amp;rsquo;ve been using k3s, which has been good2, but overall it&amp;rsquo;s been a fairly painful learning experience. That in itself isn&amp;rsquo;t unexpected, particularly when you just throw yourself at a task and learn what you need as you go, but all the same; oof.</description>
            <content type="html"><![CDATA[<p>It has been longer than I wanted since my last post, with a few half<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> finished drafts on the go. What have I been doing? Setting up Kubernetes on my homelab. Or at least, a flavour of. I&rsquo;ve been using <a href="https://github.com/rancher/k3s">k3s</a>, which has been good<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, but overall it&rsquo;s been a fairly painful learning experience. That in itself isn&rsquo;t unexpected, particularly when you just throw yourself at a task and learn what you need as you go, but all the same; oof. I&rsquo;m a glutton for punishment, apparently.</p>
<p>Anyway, expect some Kubernetes related posts in future.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Or less. 😬&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Mostly. There&rsquo;s 510 issues currently open on the project repo so it&rsquo;s natural I&rsquo;m going to hit at least one of them.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Movie Review 1 - Recent Roundup</title>
            <link>https://ljones.id.au/posts/2020/04/movie-review-1-recent-roundup/</link>
            <pubDate>Wed, 15 Apr 2020 11:27:26 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/04/movie-review-1-recent-roundup/</guid>
            <description>As we&amp;rsquo;re all enjoying the indoor Covid-19 extravaganza, I&amp;rsquo;ve had a chance to watch some movies. In future, I&amp;rsquo;ll generally be trying to do longer in-depth reviews on single films but today I&amp;rsquo;ll just smash these out. I&amp;rsquo;ll avoid spoilers, so go in peace 🎉.
 Hidden Figures The story of a team of female African-American mathematicians who served a vital role in NASA during the early years of the U.</description>
            <content type="html"><![CDATA[<p>As we&rsquo;re all enjoying the indoor Covid-19 extravaganza, I&rsquo;ve had a chance to watch some movies. In future, I&rsquo;ll generally be trying to do longer in-depth reviews on single films but today I&rsquo;ll just smash these out. I&rsquo;ll avoid spoilers, so go in peace 🎉.</p>
<hr>
<h2 id="hidden-figureshttpswwwimdbcomtitlett4846340"><a href="https://www.imdb.com/title/tt4846340/">Hidden Figures</a></h2>
<p><em>The story of a team of female African-American mathematicians who served a vital role in NASA during the early years of the U.S. space program.</em></p>
<p>I quite enjoyed this one. There&rsquo;s no single aspect that&rsquo;s particularly amazing; it&rsquo;s just an interesting and relatively feel-good story that&rsquo;s told well. As with any story based on real life, it&rsquo;s interesting to compare what&rsquo;s presented against <a href="https://en.wikipedia.org/wiki/Hidden_Figures#Historical_accuracy">reality</a>. In this case, the liberties taken generally don&rsquo;t bother me though I do agree with the introduction of a &lsquo;white saviour&rsquo; being problematic.</p>
<p>Summary: 4 out of 5 space missions.</p>
<hr>
<h2 id="the-invisible-manhttpswwwimdbcomtitlett1051906"><a href="https://www.imdb.com/title/tt1051906/">The Invisible Man</a></h2>
<p><em>When Cecilia&rsquo;s abusive ex takes his own life and leaves her his fortune, she suspects his death was a hoax. As a series of coincidences turn lethal, Cecilia works to prove that she is being hunted by someone nobody can see.</em></p>
<p>Oof, I feel like this was a missed opportunity. There&rsquo;s a strong start that falls apart when they don&rsquo;t know how to progress and resolve the story. That and the writing feels lazy to me; like, the protagonist is being stalked by an invisible person and yet (mostly) doesn&rsquo;t spend any time thinking about how she might render them visible. There&rsquo;s another scene where the script has Cecilia basically waiting patiently in the background when she could have ended the problem; that really killed it for me. Anyway, that&rsquo;s just my opinion and I&rsquo;m apparently on the outside here with the film getting generally favourable reviews.</p>
<p>Summary: 2 out of 5 missing knives.</p>
<hr>
<h2 id="the-lighthousehttpswwwimdbcomtitlett7984734"><a href="https://www.imdb.com/title/tt7984734/">The Lighthouse</a></h2>
<p><em>Two lighthouse keepers try to maintain their sanity whilst living on a remote and mysterious New England island in the 1890s.</em></p>
<p>This is one of those films where the less you know going in, the better. This film nails it on every level artistically and is as disturbing as it is excellent. Go see it.</p>
<p>Summary: 5 out of 5 monkey pumps.</p>
]]></content>
        </item>
        
        <item>
            <title>Utterances</title>
            <link>https://ljones.id.au/posts/2020/04/utterances/</link>
            <pubDate>Mon, 06 Apr 2020 14:13:10 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/04/utterances/</guid>
            <description>So I&amp;rsquo;ve set up this blog using Hugo, and I&amp;rsquo;m hosting it for free thanks to GitLab Pages. AWS Route 53 is providing the DNS while TLS is a just a check-box thanks to the Let&amp;rsquo;s Encrypt integration with GitLab Pages.
What&amp;rsquo;s missing? Ah! Comments! What&amp;rsquo;s a blog without comments, hm? Pretty presumptuous of me to assume that anyone will be 1) reading this and 2) commenting, but I&amp;rsquo;m reassured by Cunningham&amp;rsquo;s Law.</description>
            <content type="html"><![CDATA[<p>So I&rsquo;ve set up this blog using <a href="https://gohugo.io/">Hugo</a>, and I&rsquo;m hosting it for free thanks to <a href="https://about.gitlab.com/stages-devops-lifecycle/pages/">GitLab Pages</a>. <a href="https://aws.amazon.com/route53/">AWS Route 53</a> is providing the DNS while TLS is a just a check-box thanks to the <a href="https://docs.gitlab.com/ee/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.html">Let&rsquo;s Encrypt integration</a> with GitLab Pages.</p>
<p>What&rsquo;s missing? Ah! Comments! What&rsquo;s a blog without comments, hm? Pretty presumptuous of me to assume that anyone will be 1) reading this and 2) commenting, but I&rsquo;m reassured by <a href="https://meta.wikimedia.org/wiki/Cunningham%27s_Law">Cunningham&rsquo;s Law</a>. Anyway, I have a static website and I want to allow comments, but it needs to be free and I don&rsquo;t want to have to run it myself.</p>
<p>Enter <a href="https://utteranc.es/">Utterances</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-md" data-lang="md"># utterances 🔮

A lightweight comments widget built on GitHub issues. Use GitHub issues for blog comments, wiki pages and more!

<span style="color:#66d9ef">*</span> [<span style="color:#f92672">Open source</span>](<span style="color:#a6e22e">https://github.com/utterance</span>). 🙌
<span style="color:#66d9ef">*</span> No tracking, no ads, always free. 📡🚫
<span style="color:#66d9ef">*</span> No lock-in. All data stored in GitHub issues. 🔓
<span style="color:#66d9ef">*</span> Styled with [<span style="color:#f92672">Primer</span>](<span style="color:#a6e22e">http://primer.style</span>), the css toolkit that powers GitHub. 💅
<span style="color:#66d9ef">*</span> Dark theme. 🌘
<span style="color:#66d9ef">*</span> Lightweight. Vanilla TypeScript. No font downloads, JavaScript frameworks or polyfills for evergreen browsers. 🐦🌲
</code></pre></div><p>If all is well when you read this post, you should be seeing it in action at the bottom of the page. The instructions provided really were quite simple and accurate, so I won&rsquo;t bother with a tutorial. I did have to fork the wonderful <a href="https://github.com/vaga/hugo-theme-m10c/">m10c theme for Hugo</a> to add support for Utterances, but I&rsquo;ve got a <a href="https://github.com/vaga/hugo-theme-m10c/pull/42">pull request open</a> so we&rsquo;ll see if they bring that change in.</p>
<p>If someone were nitpicking they could say, &lsquo;Liam, now you need accounts for GitLab AND GitHub; isn&rsquo;t that a bit much?&rsquo;. I already had both, so, no. If I wanted to keep it all on the same farm, though, I would look into <a href="https://staticman.net/">Staticman</a>. I found out about this alternative via an <a href="https://github.com/utterance/utterances/issues/96">issue on the Utterances repo</a>, and who knows, maybe one day down the track I&rsquo;ll give it a spin.</p>
<p>For now, Utterances kinda works well with <code>Indistinct Talking</code>, don&rsquo;t you think? 😉</p>
]]></content>
        </item>
        
        <item>
            <title>Minor Misadventures in Keg Line Cleaning</title>
            <link>https://ljones.id.au/posts/2020/04/minor-misadventures-in-keg-line-cleaning/</link>
            <pubDate>Thu, 02 Apr 2020 16:12:15 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/04/minor-misadventures-in-keg-line-cleaning/</guid>
            <description>TL;DR, one home-brew life-hack superseding another, not before I went shopping for something I already had. 😅
 One of my hobbies is home-brewing beer and cider; it&amp;rsquo;s something I&amp;rsquo;m still quite a novice at but there&amp;rsquo;s a great community around it and it&amp;rsquo;s something I get to enjoy the output of as well as the creative process!
It was about time that I cleaned out my spent kegs and the Fermzilla, along with the various other bits of brewing paraphernalia such as keg lines1.</description>
            <content type="html"><![CDATA[<p>TL;DR, one home-brew life-hack superseding another, not before I went shopping for something I already had. 😅</p>
<hr>
<p>One of my hobbies is home-brewing beer and cider; it&rsquo;s something I&rsquo;m still quite a novice at but there&rsquo;s a great community around it and it&rsquo;s something I get to enjoy the output of as well as the creative process!</p>
<p>It was about time that I cleaned out my spent kegs and the <a href="https://www.kegland.com.au/fermzilla-27l-conical-uni-tank-fermenter-now-with-stainless-handle.html">Fermzilla</a>, along with the various other bits of brewing paraphernalia such as keg lines<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Generally they look after themselves but you&rsquo;ll want to flush and clean them out if you&rsquo;re not constantly pouring.</p>
<p>Ideally you have an empty vessel that you can then fill with cleaning solutions like PBW<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> or sodium percarbonate. You then briefly hook up your CO2 bottle to that to give it a little pressure. Finally you hook the vessel up the keg lines and open your tap, hey presto, the cleaning solution pours through. You want to leave that in there for a little while, ~15-20 minutes, to dissolve any gunk, then repeat the whole process with water to flush the cleaner out<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>So, that empty vessel, eh? Well, I&rsquo;d seen a video some time ago that looked like a <a href="https://www.youtube.com/watch?v=32VvyvMB7JU">handy little life-hack</a>. Get a <a href="https://www.bunnings.com.au/nylex-2l-manual-garden-sprayer_p2961998">hand pump</a> from Bunnings, replace the nozzle with a <a href="https://www.kegland.com.au/cornelius-type-liquid-post-female-thread.html">liquid keg post</a> and <a href="https://www.kegland.com.au/10-x-poppet-poppit-value-bulk-pack.html">universal poppet</a>. You now have a vessel you can hand pump your solutions through the keg lines with, neato! All up it&rsquo;s less than $20, no big deal.</p>
<p>So off I trundle to my LHBS, <a href="https://www.brewmart.com.au">Brewmart</a>. Pretty good shop, the staff are nice and friendly. One of the them ask me if I need any help and I tell him what I&rsquo;m looking for and why. They ask if I know of the Coke bottle hack. Why no, I don&rsquo;t! Turns out you can get these universal posts<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> that screw onto Coke bottles. There&rsquo;s the cleaning vessel, with one part, purchased for less than $5. Now, I actually already have one of these posts for the other common purpose, which is transferring beer from your keg to a smaller bottles. An easier way to get your home-brew to a party or function, basically.</p>
<p>A trip to the grocery store later and I&rsquo;m now in possession of a 2L soda water bottle, very soon to be used for cleaning my keg lines. I&rsquo;ll have to ponder how I forgot the line cleaning possibilities of that carbonation cap over a beer or two.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>Lengths of tubing that connect a keg up to a tap for serving.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Powdered Brewery Wash is an alkaline, non-caustic, environmentally and user friendly cleaner.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>If you&rsquo;re going to leave the lines empty for some time, you may want to flush the line with a no-rinse sanitiser like Starsan or similar instead of water, to kill anything that might be living inside the line. Note that you&rsquo;ll just want to push the sanitiser out of the line with CO2 and with water; if you did that, you&rsquo;d be undoing all your sanitising efforts!&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p>Generally, the kegs that are used in home-brewing have a gas and a liquid post for gas in and liquid out respectively. These posts are slightly different so that you can&rsquo;t put the wrong thing in the wrong place.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content>
        </item>
        
        <item>
            <title>Hello World</title>
            <link>https://ljones.id.au/posts/2020/03/hello-world/</link>
            <pubDate>Mon, 30 Mar 2020 14:16:58 +0800</pubDate>
            
            <guid>https://ljones.id.au/posts/2020/03/hello-world/</guid>
            <description>Yep, it&amp;rsquo;s one of those.
I&amp;rsquo;ll be brief, this is going to be a place for my thoughts and experiences. Probably mainly on coding, home-brewing, BBQing and gaming, but hey, we&amp;rsquo;ll see what happens.</description>
            <content type="html"><![CDATA[<p>Yep, it&rsquo;s one of those.</p>
<p>I&rsquo;ll be brief, this is going to be a place for my thoughts and experiences. Probably mainly on coding, home-brewing, BBQing and gaming, but hey, we&rsquo;ll see what happens.</p>
]]></content>
        </item>
        
    </channel>
</rss>
