<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alex Leonhardt</title>
    <description>The latest articles on Forem by Alex Leonhardt (@ineedale).</description>
    <link>https://forem.com/ineedale</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F222169%2F3e21cf09-3928-4315-9f58-051b4d8e21ae.png</url>
      <title>Forem: Alex Leonhardt</title>
      <link>https://forem.com/ineedale</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ineedale"/>
    <language>en</language>
    <item>
      <title>CKA exam prep tips</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Thu, 28 May 2020 11:15:47 +0000</pubDate>
      <link>https://forem.com/ineedale/cka-exam-prep-tips-kpi</link>
      <guid>https://forem.com/ineedale/cka-exam-prep-tips-kpi</guid>
      <description>&lt;p&gt;I thought I share a couple things about how I'm preparing for my CKA exam, so here it is...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read Managing Kubernetes (available by O'Reilly), make notes, better, make &lt;strong&gt;ANKI notes&lt;/strong&gt; (you'll use those later), go back and read the parts that are a little unclear again&lt;/li&gt;
&lt;li&gt;do at least one &lt;strong&gt;online CKA prep course&lt;/strong&gt;, it'll make sure you know all the basics, if you already do and this was boring, great! I'm sure there will be the odd thing you didn't quite know yet though

&lt;ul&gt;
&lt;li&gt;there are some good and cheap ones out there, I realise that not everyone is able to do this though :(&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;use &lt;strong&gt;ANKI&lt;/strong&gt; to add new notes to remember, &lt;strong&gt;rinse &amp;amp; repeat rigorously every day&lt;/strong&gt;, good examples to add are 

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kubectl commands&lt;/strong&gt; that help you get set up&lt;/li&gt;
&lt;li&gt;best &lt;strong&gt;keywords to look for&lt;/strong&gt; on k8s docs to find details how to do something (e.g. affinity)&lt;/li&gt;
&lt;li&gt;.. you get the gist ..&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;use &lt;strong&gt;KinD&lt;/strong&gt; to do &lt;strong&gt;a task a day&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;do a &lt;strong&gt;mock exam&lt;/strong&gt; (20-30 tasks) at least once a week*

&lt;ul&gt;
&lt;li&gt;set up a new cluster, add and remove nodes as part of your mock exam, every time&lt;/li&gt;
&lt;li&gt;make sure you cover all things etcd, especially backup &amp;amp; restore&lt;/li&gt;
&lt;li&gt;have tasks that exercise &lt;em&gt;every&lt;/em&gt; possible resource in a cluster, e.g. Pod, Deployment, DaemonSet, StatefulSet, RBAC, Volumes, Secrets, ConfigMap, Quotas, CSR, etc. etc.&lt;/li&gt;
&lt;li&gt;many resources also have &lt;em&gt;"commands"&lt;/em&gt; that you may need to know how to use, e.g. how do you scale a deployment up/down, how do you get a Pod's log output, etc. most of these are part of the &lt;em&gt;"daily administration"&lt;/em&gt; of a cluster so they need exercising as part of your prep&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;use a timer&lt;/strong&gt; when doing mock exams or the daily task/s, remember time isn't your friend, current time limit is 3 hours and trust me, that's over sooner than you'd think!&lt;/li&gt;
&lt;li&gt;ever wondered &lt;em&gt;"what happens when I ..."&lt;/em&gt; or &lt;em&gt;"I wonder how to do X"&lt;/em&gt; ? do it, see what happens, really undestand what's happening, try to fix it afterwards too - e.g. try enabling firewalld is a good one ;)&lt;/li&gt;
&lt;li&gt;make sure your &lt;strong&gt;mock environment is the same as the actual&lt;/strong&gt; test environment, that means, for example, the same OS (&lt;strong&gt;Ubuntu16&lt;/strong&gt; at the time of writing)

&lt;ul&gt;
&lt;li&gt;I use one of the public cloud providers to set up my test environment, but feel free to use whatever fits your budget; Digital Ocean, GCP, AWS, Linode should all have Ubuntu16 cloud images available&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;make sure you're using the &lt;strong&gt;correct version of k8s&lt;/strong&gt;, when I started preparing, the test asked for 1.17, by now it's already 1.18 and it'll be a moving target, so I'd suggest to always use the &lt;strong&gt;latest major release&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;know a thing or two about &lt;strong&gt;iptables, docker, systemd&lt;/strong&gt; - nothing too fancy, but you should know how to use it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;* I'll share my mock exam in another post&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's all folks, now go forth and be awesome!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>cka</category>
      <category>certification</category>
    </item>
    <item>
      <title>go apps + jaeger tracing</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sat, 11 Apr 2020 18:53:59 +0000</pubDate>
      <link>https://forem.com/ineedale/go-apps-jaeger-tracing-2ki6</link>
      <guid>https://forem.com/ineedale/go-apps-jaeger-tracing-2ki6</guid>
      <description>&lt;p&gt;So I've been playing with Jaeger tracing over the last two days a little, to distract me from usual work and thought I'll share what I've put together.&lt;/p&gt;

&lt;p&gt;This isn't a tutorial or such, as I still don't know enough about how opentracing / jaegertracing works, so enjoy the code on github, happy to try and help if anyone's got a question (and if I can provide an answer).&lt;/p&gt;

&lt;p&gt;TL;DR&lt;/p&gt;

&lt;p&gt;The final code works "well", with traces spanning more than one app, with errors, comparing traces, etc. &lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/alex-leonhardt/go-trace-example" rel="noopener noreferrer"&gt;go-trace-example&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here some screenshots..&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot1.png" alt="img1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot2.png" alt="img2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot3.png" alt="img3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Falex-leonhardt%2Fgo-trace-example%2Fraw%2Fmaster%2Fmedia%2Fscreenshot4.png" alt="img4"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>observability</category>
      <category>sre</category>
      <category>devops</category>
    </item>
    <item>
      <title>About being pragmatic...</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Fri, 29 Nov 2019 07:59:19 +0000</pubDate>
      <link>https://forem.com/ineedale/about-being-pragmatic-3c5k</link>
      <guid>https://forem.com/ineedale/about-being-pragmatic-3c5k</guid>
      <description>&lt;p&gt;Obviously, we can look up &lt;a href="https://www.thesaurus.com/browse/pragmatic"&gt;pragmatic&lt;/a&gt; if the &lt;em&gt;"definition"&lt;/em&gt; is a bit hazy, but what does it mean to &lt;strong&gt;you&lt;/strong&gt; and your everyday work, as a &lt;strong&gt;DEV&lt;/strong&gt;, &lt;strong&gt;Dev/OPS&lt;/strong&gt; or &lt;strong&gt;SRE&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Credit: &lt;a href="https://unsplash.com/photos/_10JIJ0ascA"&gt;Photo by Joshua Newton on Unsplash&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>What I learned this week: running functions concurrently</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sat, 23 Nov 2019 19:06:46 +0000</pubDate>
      <link>https://forem.com/ineedale/what-i-learned-this-week-running-commands-concurrently-l5j</link>
      <guid>https://forem.com/ineedale/what-i-learned-this-week-running-commands-concurrently-l5j</guid>
      <description>&lt;p&gt;One of Go's strength is (apparently) that it's (relatively) straight forward to write concurrent code.&lt;/p&gt;

&lt;p&gt;This week, I've had a &lt;em&gt;"simple"&lt;/em&gt; idea (or task, really): &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Execute 2 commands (concurrently) and wait for them to complete, then print the output. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sounds simple, though isn't, quite ... &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;must not know how many of the commands I'd like to execute ahead of time; i.e. I should easily be able to run 20, without having to modify the code &lt;em&gt;(other than add the function/s to run the command)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;must wait for all commands to have completed&lt;/li&gt;
&lt;li&gt;keep it as simple as possible&lt;/li&gt;
&lt;li&gt;the function being executed (the one doing the actual work) should not need to be modified&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After many hours of wrestling (with my code), not only to understand go routines, channels and waitGroups (and I'm sure I am still missing quite a lot) but also for it to make sense in my head, I think I came up with a pretty good solution to this.&lt;/p&gt;

&lt;p&gt;It is however quite a bit lengthier than I'd like, so maybe there's an even simpler way of doing this? Please leave tips in the comments ;).&lt;/p&gt;




&lt;p&gt;So initially, I tried to keep the amount of channels to a minimum, say one for outputs, one for errors, should be sufficient, regardless of the amount of &lt;em&gt;commands&lt;/em&gt; that are going to be run; turns out though that this makes shutting down cleanly (or at all) quite a bit difficult. I also kept running into a kind of deadlock where it just wouldn't continue as either I was receiving from an empty channel, or sending into the channel without having a receiver, and many other permutations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: unbuffered Go channels require you to have a receiver on the channel in order to make progress.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the other side of the spectrum, trying to simplify things (or basically almost start from scratch again) I got the commands and all to run, but the go-routines closed down way too early, and without adding more complexity it seemed impossible to print all the output and only then exit the program.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: &lt;code&gt;sync.waitGroups&lt;/code&gt; are good at waiting for things ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I added a waitGroup, increased its counter for each &lt;em&gt;command&lt;/em&gt; to run, and then... it got a bit &lt;em&gt;racy&lt;/em&gt;; turns out, sometimes the counter wasn't increased &lt;em&gt;in time&lt;/em&gt;. This was because I did the &lt;code&gt;wg.Add(1)&lt;/code&gt; inside the go-routine, which meant that the rest of the code moved along in the meantime and weird things &lt;em&gt;can/will/shall&lt;/em&gt; happen then.&lt;/p&gt;

&lt;p&gt;I used an &lt;em&gt;anonymous&lt;/em&gt; function to be run as go-routine (inline) to kick off the &lt;em&gt;command&lt;/em&gt; I needed to run, that saved me from having to declare a new function, this would then look something like this...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wg.Add(1)
go func() {
  out, err := doLS("/var/tmp")
  wg.Done()  // decreases the counter in the waitGroup
}()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and somewhere down the line I'll wait for the counter to become &lt;code&gt;0&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
wg.Wait()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At this point, I could have used a &lt;em&gt;buffered channel&lt;/em&gt; with the waitGroup to keep track of the commands still executing and kick off a receiver that reads from the channel where all the outputs are being sent into, but that would mean that I'd potentially have to remember to increase the buffer at some point.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: buffered channels don't block, at least not at first&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To avoid this, I wanted to keep using the &lt;em&gt;unbuffered channel&lt;/em&gt;, however, I had to get past the blocking on the &lt;em&gt;receiver&lt;/em&gt;. So, why not run the receiver in another go-routine? That worked, except, I somehow needed to tell it to shutdown once all commands completed.&lt;/p&gt;

&lt;p&gt;I tried using (yet) another channel to shutdown the go-routine; using a &lt;em&gt;"done"&lt;/em&gt; channel, that looked something like this...&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var done = make(chan bool)
go func(done chan bool, output chan string){
    // for loop over output channel, etc...
    if !ok {
      done &amp;lt;- true
      break
    }
}(done)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;and meant that after &lt;code&gt;wg.Wait()&lt;/code&gt; returned and the execution flow continued, I could've consumed from the channel with &lt;code&gt;&amp;lt;-done&lt;/code&gt; and in theory, this should've blocked until all contents of the &lt;code&gt;output&lt;/code&gt; channel had been printed.&lt;/p&gt;

&lt;p&gt;That didn't work initially, as for the channel to &lt;em&gt;not be OK&lt;/em&gt;, it'd have to be closed, though the closing of the output channel was deferred &lt;em&gt;way up there&lt;/em&gt;. Adding a &lt;code&gt;close(output)&lt;/code&gt; after &lt;code&gt;wg.Wait()&lt;/code&gt; I think fixed this, IIRC. &lt;em&gt;(It's been a long week ;))&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Luckily, I came across Francesc Campoy's &lt;a href="https://www.youtube.com/watch?v=t9bEg2A4jsw"&gt;&lt;em&gt;Why nil channels exist&lt;/em&gt;&lt;/a&gt; some time ago and meant to watch it back then, so today was finally a good time to do that. &lt;/p&gt;

&lt;p&gt;The example in the video didn't match exactly what I was trying to do, but I took the idea of the &lt;code&gt;nil&lt;/code&gt; channel and ran with it.&lt;/p&gt;



&lt;p&gt;The final result of all of this week's hackeroo looks something like this..&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;So, this is my &lt;/p&gt;
&lt;div class="ltag__tag ltag__tag__id__39216"&gt;
  
    .ltag__tag__id__39216 .follow-action-button{
      background-color:  !important;
      color:  !important;
      border-color:  !important;
    }
  
    &lt;div class="ltag__tag__content"&gt;
      &lt;h2&gt;#&lt;a href="/t/whatIlearnedthisweek" class="ltag__tag__link"&gt;whatIlearnedthisweek&lt;/a&gt; 
&lt;/h2&gt;
      &lt;div class="ltag__tag__summary"&gt;
        
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Maybe I'll have something interesting next week again. Please leave comments or questions or maybe ideas of how else this could've been done!?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Photo: &lt;a href="https://unsplash.com/photos/ZGnC2gOvzKw"&gt;Unsplash.com - Jake Blucker&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>whatilearnedthisweek</category>
    </item>
    <item>
      <title>Numbers everyone should know ... again.</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sun, 17 Nov 2019 15:51:32 +0000</pubDate>
      <link>https://forem.com/ineedale/numbers-everyone-should-know-again-3e2o</link>
      <guid>https://forem.com/ineedale/numbers-everyone-should-know-again-3e2o</guid>
      <description>&lt;p&gt;.. but, why bother writing about this again?&lt;/p&gt;

&lt;p&gt;Well, because in my opinion, 10000000ns may be accurate, but it's also a bit hard to convert into something useful, quickly.&lt;/p&gt;

&lt;p&gt;I believe that these numbers should reflect something useful; for example, do you really need to remember that it takes &lt;code&gt;0.5ns&lt;/code&gt;[1] for a CPU L1 cache reference? &lt;/p&gt;

&lt;p&gt;It's quite likely that your day-to-day does not depend on knowing this by heart, knowing where to quickly find that number if you need it, is a different thing. See the reference/s below. [1]&lt;/p&gt;




&lt;p&gt;To me personally, useful numbers are things you can see and use every day, for example, when I ping an IP address and it takes &lt;code&gt;234ms&lt;/code&gt; (default packet size is &lt;code&gt;56 bytes&lt;/code&gt;), then I know that that's slow, because... &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example [1]&lt;/th&gt;
&lt;th&gt;Latency (Human)&lt;/th&gt;
&lt;th&gt;Latency (ns)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mutex lock/unlock&lt;/td&gt;
&lt;td&gt;0.0001ms&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main memory reference&lt;/td&gt;
&lt;td&gt;0.0001ms&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 2K bytes sequentially from memory&lt;/td&gt;
&lt;td&gt;0.0004ms&lt;/td&gt;
&lt;td&gt;488&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compress 2K bytes with Zippy&lt;/td&gt;
&lt;td&gt;0.02ms&lt;/td&gt;
&lt;td&gt;20 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send 2K bytes over 1 Gbps network&lt;/td&gt;
&lt;td&gt;0.02ms&lt;/td&gt;
&lt;td&gt;20 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from memory&lt;/td&gt;
&lt;td&gt;0.25ms&lt;/td&gt;
&lt;td&gt;250 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from network&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;td&gt;10 000 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from disk (not SSD)&lt;/td&gt;
&lt;td&gt;30ms&lt;/td&gt;
&lt;td&gt;30 000 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk seek                        (not SSD)&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;td&gt;10 000 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round trip within same datacenter&lt;/td&gt;
&lt;td&gt;0.05ms&lt;/td&gt;
&lt;td&gt;500 000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send packet CA-&amp;gt;Netherlands-&amp;gt;CA&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;150 000 000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Of course, above numbers are useful, but I find them much more useful in &lt;code&gt;ms&lt;/code&gt; instead of &lt;code&gt;ns&lt;/code&gt; as I can now easier reason about how slow or fast something is.&lt;/p&gt;




&lt;p&gt;What other numbers may be useful? I tend to find the following quite helpful too...&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read 1MB from loopback (~10Gbps)&lt;/td&gt;
&lt;td&gt;1ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS recursive lookup to 1.1.1.1&lt;/td&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1Gbit Network throughput&lt;/td&gt;
&lt;td&gt;125 MByte/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;... -&amp;gt; 100Mbit&lt;/td&gt;
&lt;td&gt;12.5 MByte/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;... -&amp;gt;  10Mbit&lt;/td&gt;
&lt;td&gt;1.25 MByte/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 MB/s Network throughput&lt;/td&gt;
&lt;td&gt;8 Mbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCP inter-zone transfer   1 MB from network (*)&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCP inter-zone transfer  10 MB from network (*)&lt;/td&gt;
&lt;td&gt;70ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GCP inter-zone transfer 100 MB from network (*)&lt;/td&gt;
&lt;td&gt;530ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(*) instance type: &lt;code&gt;n1.standard&lt;/code&gt;, zone: &lt;code&gt;us-central1-{a,b}&lt;/code&gt;, measured with &lt;code&gt;iperf3&lt;/code&gt; on &lt;code&gt;17 Nov 2019&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;When thinking about engineering a new system, try to keep above numbers in mind and do some quick calculations. Rough estimates will go a long way. &lt;/p&gt;

&lt;p&gt;Obviously, above numbers do not take CPU and Memory requirements into account, some of which could be calculated based on the software vendor's information and of course at least some performance testing to establish the system's real boundaries and baseline performance.&lt;/p&gt;

&lt;p&gt;All this ultimately leads to the idea of Non-Abstract Large System Design referred to in Google's SRE Workbook [2].&lt;/p&gt;




&lt;p&gt;Please let me know if you have any feedback or find any errors, I'll be happy to correct them.&lt;/p&gt;




&lt;p&gt;References: &lt;/p&gt;

&lt;p&gt;[1] - &lt;a href="http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html"&gt;Google Pro Tip: Use Back-Of-The-Envelope-Calculations To Choose The Best Design&lt;/a&gt;&lt;br&gt;
[2] - &lt;a href="https://landing.google.com/sre/workbook/chapters/non-abstract-design/"&gt;SRE Workbook - NALSD&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sre</category>
      <category>devops</category>
      <category>engineering</category>
      <category>design</category>
    </item>
    <item>
      <title>Enums in Go - why and how?</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Tue, 15 Oct 2019 06:25:44 +0000</pubDate>
      <link>https://forem.com/ineedale/enums-in-go-why-and-how-19p</link>
      <guid>https://forem.com/ineedale/enums-in-go-why-and-how-19p</guid>
      <description>&lt;p&gt;Go doesn't (yet) have enums as such, but it seems common to use &lt;code&gt;constants&lt;/code&gt; with &lt;code&gt;iota&lt;/code&gt; for this instead.&lt;/p&gt;

&lt;p&gt;Passing enums instead of strings to a constructor helps reduce possible bugs; when a constructor accepts a set of possible options for an argument, it's easy to &lt;em&gt;"fat finger"&lt;/em&gt; (i.e. misspell) them. &lt;/p&gt;

&lt;p&gt;Using custom &lt;em&gt;"enums"&lt;/em&gt; helps to prevent this and also documents available options for that argument. &lt;/p&gt;

&lt;p&gt;Here's what I found on &lt;a href="https://stackoverflow.com/a/4709224"&gt;stackoverflow&lt;/a&gt; on the &lt;em&gt;why&lt;/em&gt; (above), and here's an example I've implemented.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alex-leonhardt/go-passing-enums/"&gt;https://github.com/alex-leonhardt/go-passing-enums/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/alex-leonhardt/go-passing-enums/pkg/config"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%#v %#v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%#v %#v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%#v %#v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;blockquote&gt;
&lt;p&gt;we're using the New() constructor function in the &lt;code&gt;config&lt;/code&gt; package and pass it the config &lt;em&gt;type&lt;/em&gt; to return&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;pkg/config/config.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/alex-leonhardt/go-passing-enums/pkg/config/env"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/alex-leonhardt/go-passing-enums/pkg/config/file"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Configurer describes a config provider&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Configurer&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;Del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// T is used to select the type of config to return&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="kt"&gt;uint&lt;/span&gt;

&lt;span class="c"&gt;// Constants for T&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;iota&lt;/span&gt;
    &lt;span class="n"&gt;File&lt;/span&gt;
    &lt;span class="n"&gt;Unknown&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// New takes a config.T type and returns an Configurer&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Configurer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"eh?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;blockquote&gt;
&lt;p&gt;we're specifying &lt;code&gt;T&lt;/code&gt; as a &lt;code&gt;uint&lt;/code&gt; which we'll use as our custom &lt;em&gt;"enum"&lt;/em&gt; to pass to &lt;code&gt;New()&lt;/code&gt;, depending on the &lt;em&gt;"type"&lt;/em&gt;, a different &lt;code&gt;Configurer&lt;/code&gt; is being initialised (using its own &lt;code&gt;New()&lt;/code&gt; constructor) and then returned&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;pkg/config/env/env.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;val1&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;val2&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;val3&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// New creates a new config&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Del&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;blockquote&gt;
&lt;p&gt;finally, &lt;code&gt;New()&lt;/code&gt; returns a pointer to an initialised &lt;code&gt;env&lt;/code&gt; struct which satisfies the &lt;code&gt;config.Configurer&lt;/code&gt; interface by implementing the required methods &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt; and &lt;code&gt;Del&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hope this helps anyone, it helped me already just by writing this!&lt;/p&gt;

&lt;p&gt;If you have any comments, suggestions, better explanations, please &lt;em&gt;do&lt;/em&gt; leave a comment. Always happy to learn new things!&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Writing a very basic kubernetes mutating admission webhook</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sat, 27 Jul 2019 17:11:51 +0000</pubDate>
      <link>https://forem.com/ineedale/writing-a-very-basic-kubernetes-mutating-admission-webhook-5b1</link>
      <guid>https://forem.com/ineedale/writing-a-very-basic-kubernetes-mutating-admission-webhook-5b1</guid>
      <description>&lt;p&gt;My findings when attempting to write a (very) simple kubernetes mutating admission webhook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ABkllrXSkhpMTulJIk1b0aw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ABkllrXSkhpMTulJIk1b0aw.jpeg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Okay.. so, webhooks…?
&lt;/h4&gt;

&lt;p&gt;Admission webhooks help you do some really cool stuff, there are two kinds of webhooks, &lt;em&gt;validating&lt;/em&gt; and &lt;em&gt;mutating&lt;/em&gt;. We will concentrate on the mutating admission webhook in this post.&lt;/p&gt;

&lt;p&gt;Mutating admission webhooks allow you to “&lt;em&gt;modify”&lt;/em&gt; a (e.g.) Pod (or any kubernetes resource) request. E.g. you can modify a Pod to use a particular scheduler, add / inject sidecar containers (think LinkerD sidecar), or even reject it if it doesn’t meet some security requirements, etc. etc. — all &lt;strong&gt;without&lt;/strong&gt; having to write a full fledged “micro” service to do this. The webhook can live &lt;em&gt;anywhere,&lt;/em&gt; in practice, k8s just needs to know where &lt;em&gt;anywhere&lt;/em&gt; is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;The setup is easy, but important, all you really need to make sure is that the MutatingAdminssionController is enabled in the k8s api-server. To check if your k8s cluster has this enabled, you can use&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

kubectl api-versions | grep admissionregistration


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

&lt;/div&gt;
&lt;p&gt;For development, I can recommend using Kubernetes-In-Docker (KinD), all you need is Docker and &lt;a href="https://kind.sigs.k8s.io/docs/user/quick-start/" rel="noopener noreferrer"&gt;KinD&lt;/a&gt;. KinD doesn’t auto-enable these, you can use this KinD configuration (kind.yaml)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

---
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
kubeadmConfigPatches:
- |
 apiVersion: kubeadm.k8s.io/v1beta2
 kind: ClusterConfiguration
 metadata:
 name: config
 apiServer:
 extraArgs:
 "enable-admission-plugins": "NamespaceLifecycle,LimitRanger,ServiceAccount,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,PersistentVolumeClaimResize,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota"
nodes:
- role: control-plane


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

&lt;/div&gt;
&lt;p&gt;Spin up the KinD cluster with&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

kind create cluster --conifg kind.yaml


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

&lt;/div&gt;
&lt;p&gt;I trust you’ll be able to configure kubectl etc. to now use this cluster going forward.&lt;/p&gt;

&lt;p&gt;This is it, the setup is done. Well done! :)&lt;/p&gt;
&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;I’d like to start here, as this is the easiest part of this &lt;em&gt;“tutorial”&lt;/em&gt; (if you will). Deploying an admission webhook is in practice the same as deploying any other service onto a k8s cluster.&lt;/p&gt;

&lt;p&gt;All we need is&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Service&lt;/li&gt;
&lt;li&gt;a Deployment&lt;/li&gt;
&lt;li&gt;a MutatingWebhookConfiguration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first two are simple, so I won’t go into those deeper, you can see what I’ve used on &lt;a href="https://github.com/alex-leonhardt/k8s-mutate-webhook/blob/master/deploy/webhook.yaml" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before you &lt;em&gt;just go ahead and deploy this to prod&lt;/em&gt; ... &lt;/p&gt;

&lt;p&gt;Please do think about a couple _what-if_s before using webhooks in a production environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what if the request to your webhook fails (conn drop, conn reset, no dns, etc.)&lt;/li&gt;
&lt;li&gt;what if the deployment failed and the pod is stuck in a crash-loop&lt;/li&gt;
&lt;li&gt;what if the webhook has become mission critical and &lt;strong&gt;&lt;em&gt;must be functional&lt;/em&gt;&lt;/strong&gt; 100% of the time&lt;/li&gt;
&lt;li&gt;what if you create a circular dependency (my favourites!)&lt;/li&gt;
&lt;li&gt;what if you have a 3214 node cluster with many 10s of thousand resources &amp;amp; requests all depending on this webhook, which is scaled to 1 pod ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Webhooks are &lt;strong&gt;only&lt;/strong&gt; called over SSL/TLS so your webhook &lt;strong&gt;must&lt;/strong&gt; have a valid signed certificate (we do this further down the line)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;strong&gt;MutatingWebhookConfiguration&lt;/strong&gt; is where we tell k8s which resource requests should be sent to our webhook. The configuration consists of the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;apiVersion (at the time it is: admissionregistration.k8s.io/v1beta1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kind       (must be: MutatingWebhookConfiguration)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;metadata   (the usual: name, annotations, labels)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webhooks   (a list of type webhook)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The webhook (&lt;em&gt;type&lt;/em&gt;) consists of these properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clientConfig&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;- caBundle (we will get this from the k8s cluster itself)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;- service to send the AdmissionReview requests to&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rules ( a list of rules that define which resource operations should be matched, these rules make sure that k8s resource requests are sent to your webhook )&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;namespaceSelector (the usual: matchLabels: {“label_name”: “label_value”}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;…&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;there are many more that could be used, for a simple non-production webhook to play with, the above will suffice.&lt;/p&gt;

&lt;p&gt;The full list of properties can be seen &lt;a href="https://godoc.org/k8s.io/api/admissionregistration/v1beta1#MutatingWebhook" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A rule consists of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;operations  (a list of [operations](https://godoc.org/k8s.io/api/admissionregistration/v1beta1#OperationType) to match, in our case ["CREATE"])&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiGroups   (in our case, empty [""])&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;apiVersions (in our case, this is ["v1"])&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resources   (in our case, this is ["pods"])&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;apiGroups&lt;/code&gt;, &lt;code&gt;apiVersions&lt;/code&gt; and &lt;code&gt;resources&lt;/code&gt; are all (kind of) dependent on each other, in this example it’s quite easy as &lt;code&gt;Pod&lt;/code&gt; is part of the core api group so it doesn’t need specifying, the empty &lt;code&gt;[""]&lt;/code&gt; is &lt;em&gt;matching&lt;/em&gt; the core api group.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
 name: mutateme
 labels:
 app: mutateme
webhooks:
 - name: mutateme.default.svc.cluster.local
 clientConfig:
 caBundle: ${CA_BUNDLE}
 service:
 name: mutateme
 namespace: default
 path: "/mutate"
 rules:
 - operations: ["CREATE"]
 apiGroups: [""]
 apiVersions: ["v1"]
 resources: ["pods"]
 namespaceSelector:
 matchLabels:
 mutateme: enabled


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

&lt;/div&gt;
&lt;p&gt;the &lt;code&gt;${CA_BUNDLE}&lt;/code&gt; above refers to the actual CA bundle retrieved from the k8s API, replace it with your own; you can get your cluster’s CA bundle with&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}'


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

&lt;/div&gt;
&lt;p&gt;As webhooks can only be called over HTTPS (SSL/TLS) you will need to generate a new ssl key &amp;amp; certificate for it. Doing this is somewhat involved, so probably best to take a look &lt;a href="https://github.com/alex-leonhardt/k8s-mutate-webhook/blob/master/ssl/ssl.sh" rel="noopener noreferrer"&gt;here&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;and/or&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/morvencao/kube-mutating-webhook-tutorial/blob/master/deployment/webhook-create-signed-cert.sh" rel="noopener noreferrer"&gt;https://github.com/morvencao/kube-mutating-webhook-tutorial/blob/master/deployment/webhook-create-signed-cert.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The webhook
&lt;/h3&gt;

&lt;p&gt;Pasting the entire code here is probably not very useful, so I’ll concentrate on the essentials that helped me understand a few things.&lt;/p&gt;

&lt;p&gt;Conceptually, what a webhook has to do is relatively easy, it receives a &lt;code&gt;AdmissionReview&lt;/code&gt;, and responds with an &lt;code&gt;AdmissionReview&lt;/code&gt; :) — most blog posts I’ve found concentrate on the Request and Response objects only, which can be a bit confusing.&lt;/p&gt;

&lt;p&gt;In essence, this is what needs to happen ..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the K8S api server will send a AdmissionReview &lt;em&gt;“request”&lt;/em&gt; and expects a AdmissionReview &lt;em&gt;“response”&lt;/em&gt; ; this can get confusing, but i.e. it is something like this&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
 "kind": "AdmissionReview",
 "apiVersion": "admission.k8s.io/v1beta1",
 "request": {...}
}


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

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;the AdmissionReview consists of AdmissionRequest and AdmissionResponse objects&lt;/li&gt;
&lt;li&gt;the webhook needs to &lt;em&gt;“unmarshal”&lt;/em&gt; the AdmissionReview from JSON format into some kind of object so it can read the AdmissionRequest and modify the AdmissionResponse object within it&lt;/li&gt;
&lt;li&gt;the webhook i.e. creates its own AdmissionResponse object, copies the UID from the AdmissionRequest object and replaces the AdmissionResponse object within the AdmissionReview with its own (overwrites it)&lt;/li&gt;
&lt;li&gt;responds with a AdmissionReview object in JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
 "kind": "AdmissionReview",
 "apiVersion": "admission.k8s.io/v1beta1",
 "request":  { ... ORIGINAL REQUEST ... },
 "response": { ... OUR RESPONSE ... }
}


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Where to start?
&lt;/h4&gt;

&lt;p&gt;We will start with a basic web server, that supports SSL/TLS, and can read and respond in JSON format.&lt;/p&gt;

&lt;p&gt;In practice, you can use whatever programming language you’d like for this, I have used Go, but this can easily be done in Python or any other compiled or interpreted language. Ideally though, use a language that already has K8S libraries so you don’t have to create our own objects/types; Go (naturally) has these, but there are also at least the Python libraries you could use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alex-leonhardt/k8s-mutate-webhook/blob/master/cmd/main.go" rel="noopener noreferrer"&gt;Here&lt;/a&gt;’s what I did to accept requests on port 8443 with SSL/TLS set up to use a key &amp;amp; cert.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The above example creates a server s with 2 handlers, one for / and one for /mutate which is the endpoint that will be called by k8s (which is what we specified in the &lt;code&gt;MutatingWebhookConfiguration&lt;/code&gt;); it listens on port &lt;code&gt;:8443&lt;/code&gt; and we use the ListenAndServeTLS method to serve requests over SSL/TLS.&lt;/p&gt;

&lt;p&gt;I split up the logic of http request/response from the actual processing of the AdmissionReview request as I find it’ll be easier to test the function/s independently; so the &lt;code&gt;/mutate&lt;/code&gt; handler really only does&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;take the JSON http request and read in the BODY&lt;/li&gt;
&lt;li&gt;send the BODY to the Mutate function in the mutate package (m) — the unmarshalling from JSON into the appropriate object structure, modification and marshalling back to JSON is done here&lt;/li&gt;
&lt;li&gt;respond with a JSON message, either one that describes an Error or a AdmissionReview including our AdmissionResponse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far so good, the potentially more challenging part is next.&lt;/p&gt;

&lt;h4&gt;
  
  
  pkg/mutate/mutate.go
&lt;/h4&gt;

&lt;p&gt;The mutate package does the actual processing of AdmissionReview requests by …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unmarshalling the received JSON payload into a AdmissionReview object&lt;/li&gt;
&lt;li&gt;using the AdmissionRequest object to decide what we should do&lt;/li&gt;
&lt;li&gt;creating the JSONPatch&lt;/li&gt;
&lt;li&gt;creating a new AdmissionResponse&lt;/li&gt;
&lt;li&gt;updating the AdmissionReview with our new AdmissionResponse&lt;/li&gt;
&lt;li&gt;marshal the final AdmissionReview back into JSON and return it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;API Resources, Types, etc.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Something that I’m struggling the most with is finding the correct repository, version and even file/s that I need to do things with k8s or its supported resources.&lt;/p&gt;

&lt;p&gt;Here’s what I’ve found out so far ..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Pod&lt;/code&gt; and other core resource types (which are used to unmarshal JSON into) can be found in &lt;a href="https://github.com/kubernetes/api/blob/master/core/v1/types.go" rel="noopener noreferrer"&gt;kubernetes/api/blob/master/core/v1/types.go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Metadata&lt;/code&gt; objects (which usually are part of main object like Pod), seem to be kept in the &lt;code&gt;apimachinery&lt;/code&gt; repo, it doesn’t have a description so 🤷🏻‍♂️&lt;a href="https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/v1/types.go" rel="noopener noreferrer"&gt;apimachinery/blob/master/pkg/apis/meta/v1/types.go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Other resources and their definitions seem to reside in kubernetes/api/{extensions,node,autoscaling,..}; the admission types are located here: &lt;a href="https://github.com/kubernetes/api/blob/master/admission/v1beta1/types.go" rel="noopener noreferrer"&gt;kubernetes/api/blob/master/admission/v1beta1/types.go&lt;/a&gt; (for now)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, that’s not how they’re imported (at least not in Go); for our example, we need these imports:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import (
 v1beta1 "k8s.io/api/admission/v1beta1"
 corev1  "k8s.io/api/core/v1"
 metav1  "k8s.io/apimachinery/pkg/apis/meta/v1"
)


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

&lt;/div&gt;

&lt;p&gt;IIRC they must be &lt;code&gt;k8s.io/&lt;/code&gt; imports and &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;github.com/&lt;/code&gt; imports, even though the repos are hosted on Github.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marshal &amp;amp; Unmarshal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These terms always get me confused, but I’ll try to explain&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;marshal: when you convert a object into a JSON (or other, e.g. protobuf) equivalent representation&lt;/li&gt;
&lt;li&gt;unmarshal: when you convert JSON (or other) into your own object’s representation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the webhook we need to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unmarshal the JSON AdmissionReview into a Go AdmissionReview object, I called it &lt;code&gt;ar&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;check if there’s a &lt;em&gt;embedded raw object&lt;/em&gt; and if so, again, unmarshal it into the object type that we expect, in our case a &lt;code&gt;Pod&lt;/code&gt;, which I called simply pod; if that fails, we should not try to continue and return an error&lt;/li&gt;
&lt;li&gt;marshal the &lt;code&gt;JSONPatch&lt;/code&gt; map into a valid JSONPatch, which, needs to be valid JSON&lt;/li&gt;
&lt;li&gt;marshal the &lt;strong&gt;final&lt;/strong&gt; (Go) AdmissionReview object back into a JSON AdmissionReview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;this is why we needed to find those imports earlier, so we can create objects of the correct types and unmarshal JSON into them or marshal them into JSON.&lt;/p&gt;

&lt;p&gt;The types we use/need are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AdmissionReview (v1beta1.AdmissionReview)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AdmissionRequest (v1beta1.AdmissionRequest)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Pod (corev1.Pod)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PatchTypeJSONPatch (v1beta1.PatchTypeJSONPatch)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AdmissionResponse (v1beta1.AdmissionResponse)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Status (metav1.Status)&lt;/code&gt; to set the &lt;code&gt;AdmissionResponse.Result&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why do we create a JSONPatch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is because the mutating webhook &lt;strong&gt;does not modify the k8s resource&lt;/strong&gt;, but responds with a &lt;em&gt;&lt;strong&gt;patch&lt;/strong&gt;&lt;/em&gt;, telling k8s &lt;strong&gt;&lt;em&gt;how&lt;/em&gt;&lt;/strong&gt; to modify the object for us. I found this rather counter intuitive, since we’re creating a &lt;em&gt;"Mutating Admission Webhook"&lt;/em&gt; but don’t actually &lt;em&gt;mutate&lt;/em&gt; anything.&lt;/p&gt;

&lt;p&gt;More about JSONPatch expressions and how they work can be found &lt;a href="http://jsonpatch.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but essentially they consist of an operation (&lt;code&gt;op&lt;/code&gt;), a &lt;code&gt;path&lt;/code&gt; and a &lt;code&gt;value&lt;/code&gt;, e.g.:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
 "op": "replace",
 "path": "/spec/containers/0/image",
 "value": "debian"
}


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

&lt;/div&gt;

&lt;p&gt;In our case, it instructs how and what operations it should apply to the resource that was requested. You can have more than one operation/instruction, e.g. a list of operations &lt;code&gt;[{"op": ..},{"op": ..},{"op": ..}]&lt;/code&gt; and I believe they’ll be executed in the same order. Something for you to try and find out ;).&lt;/p&gt;

&lt;p&gt;I used the following to create a JSONPatch list for the webhook, it replaces any container image to use the Debian docker image instead of the originally requested .. &lt;em&gt;FWIW - it's a list as a Pod may have more than 1 container&lt;/em&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// resp is the AdmissionReview.Response 
p := []map[string]string{}

for i := range pod.Spec.Containers {
  patch := map[string]string{
    "op": "replace",
    "path": fmt.Sprintf("/spec/containers/%d/image", i), 
    "value": "debian",
  }
  p = append(p, patch)
}

// parse the []map into JSON
resp.Patch, err = json.Marshal(p)


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

&lt;/div&gt;

&lt;p&gt;This is it!&lt;/p&gt;

&lt;p&gt;You can find the all the code on Github, I’d encourage you to try it without looking first :) … except for the details on the SSL ca, key, cert signing. I’ve also added links to resource that I believe will be helpful to understand what’s going on.&lt;/p&gt;

&lt;p&gt;Kudos to the IBM-Cloud blog on Medium (link below) that I used as inspiration for this post and also as part reference when I got stuck. It’s more complete but also more complicated (at least at the time it seemed like it); if you're intending to create a proper version of this, you should check it out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alex-leonhardt/k8s-mutate-webhook" rel="noopener noreferrer"&gt;alex-leonhardt/k8s-mutate-webhook&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Use a IDE / Editor that can help with code completion, dependencies, etc. — I used VSCode, but there are also good Vim plugins.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://godoc.org/k8s.io/api/admissionregistration/v1beta1#MutatingWebhook" rel="noopener noreferrer"&gt;https://godoc.org/k8s.io/api/admissionregistration/v1beta1#MutatingWebhook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://godoc.org/k8s.io/api/admission/v1beta1" rel="noopener noreferrer"&gt;https://godoc.org/k8s.io/api/admission/v1beta1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://godoc.org/k8s.io/kubernetes/pkg/apis/admission" rel="noopener noreferrer"&gt;https://godoc.org/k8s.io/kubernetes/pkg/apis/admission&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#mutatingwebhookconfiguration-v1beta1-admissionregistration-k8s-io" rel="noopener noreferrer"&gt;https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#mutatingwebhookconfiguration-v1beta1-admissionregistration-k8s-io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/morvencao/kube-mutating-webhook-tutorial" rel="noopener noreferrer"&gt;https://github.com/morvencao/kube-mutating-webhook-tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Kudos
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://medium.com/ibm-cloud/diving-into-kubernetes-mutatingadmissionwebhook-6ef3c5695f74" rel="noopener noreferrer"&gt;https://medium.com/ibm-cloud/diving-into-kubernetes-mutatingadmissionwebhook-6ef3c5695f74&lt;/a&gt;&lt;/p&gt;




</description>
      <category>programming</category>
      <category>go</category>
      <category>kubernetes</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>access and debug your kubernetes service / docker container</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sun, 26 Aug 2018 14:49:17 +0000</pubDate>
      <link>https://forem.com/ineedale/access-and-debug-your-kubernetes-service-docker-container-39oh</link>
      <guid>https://forem.com/ineedale/access-and-debug-your-kubernetes-service-docker-container-39oh</guid>
      <description>&lt;p&gt;so you have followed the basic steps to get a service deployed into kubernetes, let’s say the service is &lt;em&gt;Nginx&lt;/em&gt;, because why not …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl create deployment ngx --image=nginx:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;let’s scale that up to 2, because, really running just 1 pod is a bit too basic&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl scale deployment --replicas 2 ngx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;you have 2 &lt;em&gt;Nginx&lt;/em&gt; pods running on your k8s “cluster”, I use the built-in k8s installation that comes with Docker Edge&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get pods

NAME READY STATUS RESTARTS AGE
ngx-5cb59c856c-cmn8p 1/1 Running 0 52m
ngx-5cb59c856c-s65nh 1/1 Running 0 52m
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;now that we have 2 pods, we need to somehow access the service so we can do some basic checking ourselves, e.g. to check that things work as expected, before we make this service publicly available via a LoadBalancer or Ingress — so we expose it as an internal service for now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl expose deployment ngx --port 8080 --target-port 80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl proxy &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;you can then access your service (securely, it’s actually a encrypted tunnel) via&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:8001/api/v1/namespaces/default/services/ngx:8080/proxy/"&gt;http://localhost:8001/api/v1/namespaces/default/services/ngx:8080/proxy/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;details of how that works are &lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-services/#manually-constructing-apiserver-proxy-urls"&gt;here&lt;/a&gt;, but in a nutshell, you can access any internal service via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8001/api/v1/namespace/{namespace}/services/{service-name}:{port}/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;so this is where you notice that one of the containers is behaving strangely, and sometimes seems to be unable to connect to a backend or just works intermittently, etc. (something’s wrong, basically) and so you decide, you’d like to run &lt;code&gt;ngrep&lt;/code&gt; or &lt;code&gt;tcpdump&lt;/code&gt; or &lt;code&gt;strace&lt;/code&gt; to figure out what’s going on, but you also don’t want to modify the container image … so what can we do?&lt;/p&gt;

&lt;p&gt;As long as you have access to the node running the container instance, you’re in luck — in this example we’ll just use the local Docker4Mac installation, but it works with any node running Docker containers.&lt;/p&gt;

&lt;p&gt;Find out which node runs the troublesome container with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl describe pod ngx-5cb59c856c-cmn8p

Name: ngx-5cb59c856c-cmn8p
Namespace: default
Node: docker-for-desktop/192.168.65.3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and login to that node; I initially tried editing the pod with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl edit pod ngx-5cb59c856c-cmn8p
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;to add a &lt;em&gt;debug&lt;/em&gt; container, but saving that config will fail as you cannot add/remove containers from a pod :( &lt;/p&gt;

&lt;p&gt;As we’re on the node that runs the container however, we can create a custom debug container (locally) and run that inside the same pid and network namespace as the existing ngx-5cb59c856c-cmn8p.&lt;/p&gt;

&lt;p&gt;The Dockerfile could be something like this (shamelessly copied/used from &lt;a href="https://medium.com/@rothgar/how-to-debug-a-running-docker-container-from-a-separate-container-983f11740dc6"&gt;https://medium.com/@rothgar/how-to-debug-a-running-docker-container-from-a-separate-container-983f11740dc6&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM alpine
RUN apk update &amp;amp;&amp;amp; apk add strace
CMD ["strace", "-p", "1"]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker build -t strace .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and once the container is built (in reality, you’d build a consistent debug container and make that available for you to pull anytime from GCR or ECR or wherever) you run it with &lt;code&gt;--privileged&lt;/code&gt; (yes, we don’t care about security in this example, see &lt;a href="https://medium.com/u/18e7f33d2669"&gt;Justin Garrison&lt;/a&gt;’s post on how to do this more restrictive, so you’re able to &lt;em&gt;do all the things&lt;/em&gt; and not have to fight permissions).&lt;/p&gt;

&lt;p&gt;To attach to the &lt;code&gt;pid&lt;/code&gt; and &lt;code&gt;net&lt;/code&gt; namespace, you need the &lt;code&gt;container Id&lt;/code&gt; or name, that’s easy to find with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker ps | grep nginx

6b6e65ebc7c8 nginx "nginx -g 'daemon of…" About an hour ago Up About an hour k8s\_nginx\_ngx-5cb59c856c-cmn8p\_default\_402e0d53-a933-11e8-93cb-025000000001\_0
e245d91ba045 nginx "nginx -g 'daemon of…" About an hour ago Up About an hour k8s\_nginx\_ngx-5cb59c856c-s65nh\_default\_36a3a1e7-a933-11e8-93cb-025000000001\_0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So we’ll use the first one which is &lt;code&gt;6b6e65ebc7c8&lt;/code&gt; (you should obviously use the one that's causing trouble) …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -ti --pid=container:6b6e65ebc7c8 --net=container:6b6e65ebc7c8 --privileged strace /bin/ash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once executed, you need the PID that is actually doing the work, PID 1 is actually the parent Nginx process, but that is not processing any requests, it’s just managing the child processes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ # ps -ef
PID USER TIME COMMAND
 1 root 0:00 nginx: master process nginx -g daemon off;
 6 101 0:00 nginx: worker process
 44 root 0:00 /bin/ash
 50 root 0:00 ps -ef
/ #
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;okay, so let’s do an &lt;code&gt;strace&lt;/code&gt; on PID 6 as that is actually doing work …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/ # strace -fp 6
strace: Process 6 attached
gettimeofday({tv\_sec=1535294355, tv\_usec=751153}, NULL) = 0
epoll\_wait(8, [{EPOLLIN, {u32=2097902081, u64=139627189727745}}], 512, 61010) = 1
gettimeofday({tv\_sec=1535294359, tv\_usec=908313}, NULL) = 0
recvfrom(3, "GET / HTTP/1.1\r\nHost: localhost:"..., 1024, 0, NULL, NULL) = 213
stat("/usr/share/nginx/html/index.html", {st\_mode=S\_IFREG|0644, st\_size=612, ...}) = 0
open("/usr/share/nginx/html/index.html", O\_RDONLY|O\_NONBLOCK) = 11
fstat(11, {st\_mode=S\_IFREG|0644, st\_size=612, ...}) = 0
writev(3, [{iov\_base="HTTP/1.1 200 OK\r\nServer: nginx/1"..., iov\_len=238}], 1) = 238
sendfile(3, 11, [0] =\&amp;gt; [612], 612) = 612
write(5, "10.1.0.1 - - [26/Aug/2018:14:39:"..., 111) = 111
close(11) = 0
epoll\_wait(8, [{EPOLLIN, {u32=2097902081, u64=139627189727745}}], 512, 65000) = 1
gettimeofday({tv\_sec=1535294361, tv\_usec=971440}, NULL) = 0
recvfrom(3, "GET / HTTP/1.1\r\nHost: localhost:"..., 1024, 0, NULL, NULL) = 213
stat("/usr/share/nginx/html/index.html", {st\_mode=S\_IFREG|0644, st\_size=612, ...}) = 0
open("/usr/share/nginx/html/index.html", O\_RDONLY|O\_NONBLOCK) = 11
fstat(11, {st\_mode=S\_IFREG|0644, st\_size=612, ...}) = 0
writev(3, [{iov\_base="HTTP/1.1 200 OK\r\nServer: nginx/1"..., iov\_len=238}], 1) = 238
sendfile(3, 11, [0] =\&amp;gt; [612], 612) = 612
write(5, "10.1.0.1 - - [26/Aug/2018:14:39:"..., 111) = 111
close(11) = 0
epoll\_wait(8,
^Cstrace: Process 6 detached
 \&amp;lt;detached ...\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And there it is, 2 requests to this particular Nginx instance without having to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;redeploy the Pod with an additional debug container (you could argue that this would be better, but you may not be able to re-produce the issue straight away and may need to run it for a long time which costs resources)&lt;/li&gt;
&lt;li&gt;modify the Dockerfile in any way (install debug tools)&lt;/li&gt;
&lt;li&gt;change privileges on the running container, it can keep running in its more secure environment vs the debug container which has additional capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the nice thing about this pattern is that you can create yourself a debug container that you can re-use to debug applications running on any node that runs Docker (ECS, on-prem K8S, EKS, AKS, GKE).&lt;/p&gt;

&lt;p&gt;Happy debugging!&lt;/p&gt;

&lt;p&gt;Credits &amp;amp; Resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-services/#manually-constructing-apiserver-proxy-urls"&gt;https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-services/#manually-constructing-apiserver-proxy-urls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@rothgar/how-to-debug-a-running-docker-container-from-a-separate-container-983f11740dc6"&gt;https://medium.com/@rothgar/how-to-debug-a-running-docker-container-from-a-separate-container-983f11740dc6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0"&gt;https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>docker</category>
      <category>troubleshooting</category>
      <category>devops</category>
    </item>
    <item>
      <title>Writing my own terraform template (data) provider</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Sat, 05 May 2018 22:33:33 +0000</pubDate>
      <link>https://forem.com/ineedale/writing-my-own-terraform-template-data-provider-loa</link>
      <guid>https://forem.com/ineedale/writing-my-own-terraform-template-data-provider-loa</guid>
      <description>&lt;p&gt;As a follow up to my previous post about how to &lt;a href="https://dev.to/ineedale/terraform-templating-and-loops-479p"&gt;use terrafrom’s built-in looping mechanisms&lt;/a&gt;, I set out to write my own custom “data” provider, in this case a new template renderer that would allow me to use Golang’s text/template instead of terraform’s own.&lt;/p&gt;

&lt;p&gt;TL;DR — the code is here..&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alex-leonhardt/terraform-provider-gotemplate"&gt;alex-leonhardt/terraform-provider-gotemplate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started by trying to follow Hashicorp’s &lt;a href="https://www.terraform.io/docs/extend/writing-custom-providers.html"&gt;&lt;em&gt;Writing Custom Providers&lt;/em&gt;&lt;/a&gt; documentation, however that does not seem to explain how to write a &lt;strong&gt;&lt;em&gt;data&lt;/em&gt;&lt;/strong&gt;  &lt;strong&gt;&lt;em&gt;provider&lt;/em&gt;&lt;/strong&gt; , and although I had a basic Hello World working, I didn’t get too close to what I actually wanted to do.&lt;/p&gt;

&lt;p&gt;So, since there is an existing template provider, I chose to use that as a kind of starting point and hack my way around all the bits I didn’t want/need. Which probably means I didn’t follow &lt;em&gt;“best practice”&lt;/em&gt;. What I wanted was simple, I pass through a json encoded variable to the go-template provider and it will read a template file, render it (or fail), and I should be able to access the result, using the .rendered method, just like the original template provider does.&lt;/p&gt;

&lt;p&gt;To use it, it’d roughly look like this …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "data" {
 default = {
 "msg" = "Hello World"
 "msg2" = [1, 2, 3, 4]
 }
}

data "gotemplate" "gotmpl" {
template = "${path.module}/file.tmpl"
data = "${jsonencode(var.data)}"
}

output "tmpl" {
value = "${data.gotemplate.gotmpl.rendered}"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As you can see, we have a map variable "data" with a mix of values of string type keys, other types are not allowed it seems — see &lt;a href="https://stackoverflow.com/a/8758771"&gt;https://stackoverflow.com/a/8758771&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I decided, 2 variables should be sufficient, the path to the template file (template) and the json encoded data (data) itself. I probably won’t go into details of how a provider should be structured as TF’s template provider seems a bit different from the &lt;em&gt;“writing custom providers”&lt;/em&gt; instructions — probably as it’s a bit older and possibly one of the first. So, since I followed that example (more or less), my new provider also doesn’t follow it, at least not closely.&lt;/p&gt;

&lt;p&gt;To use my new provider, all that’s needed is to compile it into a file called terraform-provider-gotemplate and I can use the gotemplate provider, this is specified in the main.go file here&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Provider is a TF provider
func Provider() *schema.Provider {
 return &amp;amp;schema.Provider{
 DataSourcesMap: map[string]*schema.Resource{
 "gotemplate": dataSourceFile(),
 },
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;the important part really is in dataSourceFile() which specifies what variables can be accessed using the provider, in my case that was&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;template (the path to the go template file)&lt;/li&gt;
&lt;li&gt;data (the json encoded data)&lt;/li&gt;
&lt;li&gt;rendered (the finished render)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;other than setting the schema, I also had to make it actually do something, this is done with&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func dataSourceFile() *schema.Resource {
 return &amp;amp;schema.Resource{
 Read: dataSourceFileRead,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;which passes dataSourceFileRead function to the schema.Resource‘s Read field (or property? I like to think of it as a property) which must match this function signature&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type ReadFunc func(*ResourceData, interface{}) error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;which is done with&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func dataSourceFileRead(d *schema.ResourceData, meta interface{}) error {
 rendered, err := renderFile(d)
 if err != nil {
 return err
 }
 d.Set("rendered", rendered)
 d.SetId(hash(rendered))
 return nil
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;d.Set is important to set the rendered variable with the finished rendered template, so it can be used with output or other providers as a input.&lt;/p&gt;

&lt;p&gt;Before setting rendered we retrieved the rendered output by calling the renderFile(d) function, which looks like this&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The final result is available on Github, use it, make PRs if you like, I have learned quite a bit doing this, although still a bit confused and probably not following &lt;em&gt;the standard way&lt;/em&gt; of writing providers, but it was fun getting this to work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/alex-leonhardt/terraform-provider-gotemplate"&gt;alex-leonhardt/terraform-provider-gotemplate&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/extend/writing-custom-providers.html"&gt;https://www.terraform.io/docs/extend/writing-custom-providers.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/terraform-providers/terraform-provider-template"&gt;https://github.com/terraform-providers/terraform-provider-template&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/8758771"&gt;https://stackoverflow.com/a/8758771&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.calhoun.io/intro-to-templates-p3-functions/"&gt;https://www.calhoun.io/intro-to-templates-p3-functions/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/ineedale/terraform-templating-and-loops-382d-temp-slug-9478107"&gt;https://medium.com/ovni/terraform-templating-and-loops-9a88c0786c5c&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>go</category>
      <category>terraform</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Terraform templating and loops</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Wed, 28 Mar 2018 06:38:34 +0000</pubDate>
      <link>https://forem.com/ineedale/terraform-templating-and-loops-479p</link>
      <guid>https://forem.com/ineedale/terraform-templating-and-loops-479p</guid>
      <description>&lt;p&gt;&lt;em&gt;UPDATE: as of terraform 0.12, tf now supports a new &lt;a href="https://www.terraform.io/docs/configuration/expressions.html#directives"&gt;template syntax&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You probably already know it’s not easily possible to loop through a variable of type slice/list within templates, in fact, there are no loop constructs within TF templates at all at this point in time.&lt;/p&gt;

&lt;p&gt;There is hope though, according to their comment on this GH issue: &lt;a href="https://github.com/hashicorp/terraform/issues/16628"&gt;https://github.com/hashicorp/terraform/issues/16628&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, say we want to generate a JSON file from a slice/array that, once finished, looks something like this :&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We cannot simply iterate over an array to change the value of endpointX as we cannot loop through it.&lt;/p&gt;

&lt;p&gt;What we’d expect to be able to do, in the template, is something like this&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;we’d have to handle that last , but let’s ignore that for now — the idea is simple, iterate over values in the array of map endpoints and use each key and value as variables within that loop to generate the json file. As we already know, this isn’t possible, and we won’t get this anytime soon.&lt;/p&gt;

&lt;p&gt;Luckily, necessity is the mother of invention, and I got this to work with the existing loop constructs &lt;em&gt;outside&lt;/em&gt; the template; although it’s not as “&lt;em&gt;clean&lt;/em&gt;” as above, it’s usable and produces the desired result.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Using TFs buit-in loop construct&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To make use of TFs built-in loop construct and generate the wanted json, we will need 2 template files, the first will look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// data.json.tmpl

{
 "name": "${name}",
 "endpoint": "${endpoint}"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and the second template looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// service.json.tmpl

[
 ${value},
 {"links": ${links}}
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Maybe you already know where we’re heading with this, we now render the first template using the built-in loop constructs from TF using the template_file data resource&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "endpoints" {
_type_ = "list"
_default_ = [
 { endpoint1 = "https://endpoint-1.example.com" },
 { endpoint2 = "https://endpoint-2.example.com" },
 { endpoint3 = "https://endpoint-3.example.com" },
 ]
}

data "template\_file" "data\_json" {
_template_ = "${file("${path.module}/data.json.tmpl")}"
_count_ = "${length(var.endpoints)}"
 vars {
_endpoint_ = "${element(values(var.endpoints[count.index]), 0)}"
_name_ = "${element(keys(var.endpoints[count.index]), 0)}"
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and then re-use the .rendered output as input to the 2nd template, which really just generates the list by pre- and post- fixing the $value variable with [] .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "links" {
_type_ = "list"
_default_ = [
 "link1",
 "link2",
 "link3",
 ]
}

data "template\_file" "service\_json" {
_template_ = "${file("${path.module}/service.json.tmpl")}"
vars {
_value_ = "${join(",", data.template\_file.data\_json.\*.rendered)}"
_links_ = "${jsonencode(var.links)}"
}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and finally we’re using the output module to show us what we’ve done, this is also how you’d use the result as input to any other TF module that accepts json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output "json" {
_value_ = "${data.template\_file.service\_json.rendered}"
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and the output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tf apply
data.template\_file.data\_json[2]: Refreshing state...
data.template\_file.data\_json[0]: Refreshing state...
data.template\_file.data\_json[1]: Refreshing state...
data.template\_file.service\_json: Refreshing state...

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

json = [
 {
 "name": "endpoint1",
 "endpoint": "[https://endpoint-1.example.com](https://endpoint-1.example.com)"
},{
 "name": "endpoint2",
 "endpoint": "[https://endpoint-2.example.com](https://endpoint-2.example.com)"
},{
 "name": "endpoint3",
 "endpoint": "[https://endpoint-3.example.com](https://endpoint-3.example.com)"
},
 {"links": ["link1","link2","link3"]}
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;Converting lists/slices to JSON&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;You might have noticed the jsonencode in the last part, this helps to convert any structure to valid json and it becomes valid input to the template as json in essence is just text&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variable "links" {
_type_ = "list"
_default_ = [
 "link1",
 "link2",
 "link3",
 ]
}

data "template\_file" "service\_json" {
_[...]_
_links_ = "${jsonencode(var.links)}"
}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;em&gt;References&lt;/em&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/docs/configuration/interpolation.html"&gt;TF interpolation syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/pkg/text/template/#hdr-Variables"&gt;Go Template syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hashicorp/terraform/issues/16628"&gt;GH Issue #16628 — Use Go Template as the template syntax for template_file data source&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>terraform</category>
      <category>devops</category>
      <category>templating</category>
      <category>go</category>
    </item>
    <item>
      <title>what happens when … (Part 1)</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Mon, 16 Oct 2017 05:33:24 +0000</pubDate>
      <link>https://forem.com/ineedale/what-happens-when-part-1-900</link>
      <guid>https://forem.com/ineedale/what-happens-when-part-1-900</guid>
      <description>&lt;p&gt;This is for the curious amongst us, who want to &lt;em&gt;know&lt;/em&gt; their limits on system internals and/or (inter)networking knowledge and constantly strive to surpass it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happens when I type &lt;code&gt;[www.google.com](http://www.google.com)&lt;/code&gt; into the browser address bar and hit enter?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may have come across the above question already (several times), which is probably why you’re here, reading this! It’s a very broad and very difficult question to answer as there are so many moving parts to this question.&lt;/p&gt;

&lt;p&gt;We eventually will get to answer the above question, however, let’s not start off with that quite yet, as it would be far too much to go into it right from the start.&lt;/p&gt;

&lt;p&gt;My motivation here is not to provide answers, but to show how I’d approach a question / problem like this, and in the process maybe learn a thing or two (or many), but also help you understand a few things better / or in more detail.&lt;/p&gt;

&lt;p&gt;So let’s start small and “simple” ..&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What happens when I type &lt;code&gt;ls -l&lt;/code&gt; into a shell prompt and hit enter?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s probably a good idea to stop reading here for a minute and actually think about it and extrapolate what would/could be happening, based on your experience and assumptions, in as much detail as you can..&lt;/p&gt;

&lt;p&gt;… just a little longer …&lt;/p&gt;

&lt;p&gt;Rather than going ahead and just listing all the things that are happening, I’d like to record how I approach this question (or problem); don’t take this as a step-by-step guide or limit yourself how to approach it, this is how I would do this.&lt;/p&gt;

&lt;p&gt;So let’s get started ..&lt;/p&gt;

&lt;p&gt;For the record, I’m using a Virtualbox VM running Debian Jessie (8.x) with latest updates applied as of 10/10/2017.&lt;/p&gt;

&lt;p&gt;I guess the most obvious first thing to do is to just “execute” the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls -l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;what does it return ?&lt;/p&gt;

&lt;p&gt;A directory listing, in the long format, containing various details about every directory and file in it..&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@debian:/var/cache# ls -l
total 28
drwxr-xr-x 3 root root 4096 Oct 10 22:12 apt
drwxr-xr-x 2 root root 4096 Oct 10 22:04 debconf
drwxr-xr-x 2 root root 4096 Jul 3 2015 dictionaries-common
drwxr-xr-x 2 root root 4096 Oct 10 22:03 fontconfig
drwx------ 2 root root 4096 Oct 10 22:04 ldconfig
drwxr-sr-x 32 man root 4096 Oct 10 22:12 man
drwxr-xr-x 2 root root 4096 Apr 23 2014 modass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Based on the above output, we’re in the directory &lt;code&gt;/var/cache&lt;/code&gt;, we see the directory/file permissions, the &lt;code&gt;owner&lt;/code&gt;, the &lt;code&gt;group&lt;/code&gt;, the &lt;code&gt;size&lt;/code&gt; (default seems to be set to &lt;code&gt;4k&lt;/code&gt;), last &lt;code&gt;modification&lt;/code&gt; date/time and finally the &lt;code&gt;name&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;That’s quite a bit of information there already, we can make some guesses of what happened, but they’re not going to be very detailed. Some system call that got the current directory, some others got the modification time for every file/directory, etc. So what do we do next / where do we look?&lt;/p&gt;

&lt;p&gt;Experience tells me that, if I wanted to see what an application is actually “doing”, I use a tool called &lt;code&gt;strace&lt;/code&gt; — running anything with strace requires you to have root permissions (sudo should do). I can’t really remember how I came across it, probably some many many moons ago, some more senior person told me about it I guess.&lt;/p&gt;

&lt;p&gt;Anyway, what strace does is trace these system calls that your application is doing (in this case the &lt;code&gt;/bin/ls&lt;/code&gt; program) and print them to stdout.&lt;/p&gt;

&lt;p&gt;This is how a strace of ls -l actually looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@debian:/var/cache# strace ls -l
execve("/bin/ls", ["ls", "-l"], [/\* 20 vars \*/]) = 0
brk(0) = 0x785000
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b2000
access("/etc/ld.so.preload", R\_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O\_RDONLY|O\_CLOEXEC) = 3
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=23259, ...}) = 0
mmap(NULL, 23259, PROT\_READ, MAP\_PRIVATE, 3, 0) = 0x7f420b3ac000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libselinux.so.1", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\20c\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=142728, ...}) = 0
mmap(NULL, 2246896, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420af6f000
mprotect(0x7f420af90000, 2097152, PROT\_NONE) = 0
mmap(0x7f420b190000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x21000) = 0x7f420b190000
mmap(0x7f420b192000, 6384, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_ANONYMOUS, -1, 0) = 0x7f420b192000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libacl.so.1", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\200\37\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=35288, ...}) = 0
mmap(NULL, 2130592, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420ad66000
mprotect(0x7f420ad6e000, 2093056, PROT\_NONE) = 0
mmap(0x7f420af6d000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x7000) = 0x7f420af6d000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libc.so.6", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0P\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0755, st\_size=1738176, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3ab000
mmap(NULL, 3844640, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420a9bb000
mprotect(0x7f420ab5c000, 2097152, PROT\_NONE) = 0
mmap(0x7f420ad5c000, 24576, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x1a1000) = 0x7f420ad5c000
mmap(0x7f420ad62000, 14880, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_ANONYMOUS, -1, 0) = 0x7f420ad62000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libpcre.so.3", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\20\27\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=448440, ...}) = 0
mmap(NULL, 2543976, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420a74d000
mprotect(0x7f420a7b9000, 2097152, PROT\_NONE) = 0
mmap(0x7f420a9b9000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x6c000) = 0x7f420a9b9000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libdl.so.2", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\320\16\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=14664, ...}) = 0
mmap(NULL, 2109712, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420a549000
mprotect(0x7f420a54c000, 2093056, PROT\_NONE) = 0
mmap(0x7f420a74b000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x2000) = 0x7f420a74b000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libattr.so.1", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\320\23\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=18640, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3aa000
mmap(NULL, 2113912, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420a344000
mprotect(0x7f420a348000, 2093056, PROT\_NONE) = 0
mmap(0x7f420a547000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x3000) = 0x7f420a547000
close(3) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libpthread.so.0", O\_RDONLY|O\_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\320n\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st\_mode=S\_IFREG|0755, st\_size=137384, ...}) = 0
mmap(NULL, 2213008, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 3, 0) = 0x7f420a127000
mprotect(0x7f420a13f000, 2093056, PROT\_NONE) = 0
mmap(0x7f420a33e000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 3, 0x17000) = 0x7f420a33e000
mmap(0x7f420a340000, 13456, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_ANONYMOUS, -1, 0) = 0x7f420a340000
close(3) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3a9000
mmap(NULL, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3a7000
arch\_prctl(ARCH\_SET\_FS, 0x7f420b3a7800) = 0
mprotect(0x7f420ad5c000, 16384, PROT\_READ) = 0
mprotect(0x7f420a33e000, 4096, PROT\_READ) = 0
mprotect(0x7f420a547000, 4096, PROT\_READ) = 0
mprotect(0x7f420a74b000, 4096, PROT\_READ) = 0
mprotect(0x7f420a9b9000, 4096, PROT\_READ) = 0
mprotect(0x7f420af6d000, 4096, PROT\_READ) = 0
mprotect(0x7f420b190000, 4096, PROT\_READ) = 0
mprotect(0x61b000, 4096, PROT\_READ) = 0
mprotect(0x7f420b3b4000, 4096, PROT\_READ) = 0
munmap(0x7f420b3ac000, 23259) = 0
set\_tid\_address(0x7f420b3a7ad0) = 26019
set\_robust\_list(0x7f420b3a7ae0, 24) = 0
rt\_sigaction(SIGRTMIN, {0x7f420a12d9b0, [], SA\_RESTORER|SA\_SIGINFO, 0x7f420a136890}, NULL, 8) = 0
rt\_sigaction(SIGRT\_1, {0x7f420a12da40, [], SA\_RESTORER|SA\_RESTART|SA\_SIGINFO, 0x7f420a136890}, NULL, 8) = 0
rt\_sigprocmask(SIG\_UNBLOCK, [RTMIN RT\_1], NULL, 8) = 0
getrlimit(RLIMIT\_STACK, {rlim\_cur=8192\*1024, rlim\_max=RLIM64\_INFINITY}) = 0
statfs("/sys/fs/selinux", 0x7ffd512871e0) = -1 ENOENT (No such file or directory)
statfs("/selinux", 0x7ffd512871e0) = -1 ENOENT (No such file or directory)
brk(0) = 0x785000
brk(0x7a6000) = 0x7a6000
open("/proc/filesystems", O\_RDONLY) = 3
fstat(3, {st\_mode=S\_IFREG|0444, st\_size=0, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b1000
read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 317
read(3, "", 1024) = 0
close(3) = 0
munmap(0x7f420b3b1000, 4096) = 0
open("/usr/lib/locale/locale-archive", O\_RDONLY|O\_CLOEXEC) = 3
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=1607712, ...}) = 0
mmap(NULL, 1607712, PROT\_READ, MAP\_PRIVATE, 3, 0) = 0x7f420b21e000
close(3) = 0
open("/usr/share/locale/locale.alias", O\_RDONLY|O\_CLOEXEC) = 3
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=2492, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b1000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2492
read(3, "", 4096) = 0
close(3) = 0
munmap(0x7f420b3b1000, 4096) = 0
open("/usr/lib/locale/en\_GB.UTF-8/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en\_GB.utf8/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en\_GB/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en.UTF-8/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en.utf8/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib/locale/en/LC\_CTYPE", O\_RDONLY|O\_CLOEXEC) = -1 ENOENT (No such file or directory)
ioctl(1, SNDCTL\_TMR\_TIMEBASE or SNDRV\_TIMER\_IOCTL\_NEXT\_DEVICE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TIOCGWINSZ, {ws\_row=57, ws\_col=238, ws\_xpixel=0, ws\_ypixel=0}) = 0
openat(AT\_FDCWD, ".", O\_RDONLY|O\_NONBLOCK|O\_DIRECTORY|O\_CLOEXEC) = 3
getdents(3, /\* 9 entries \*/, 32768) = 264
lstat("debconf", {st\_mode=S\_IFDIR|0755, st\_size=4096, ...}) = 0
lgetxattr("debconf", "security.selinux", 0x785eb0, 255) = -1 ENODATA (No data available)
getxattr("debconf", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("debconf", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
socket(PF\_LOCAL, SOCK\_STREAM|SOCK\_CLOEXEC|SOCK\_NONBLOCK, 0) = 4
connect(4, {sa\_family=AF\_LOCAL, sun\_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
socket(PF\_LOCAL, SOCK\_STREAM|SOCK\_CLOEXEC|SOCK\_NONBLOCK, 0) = 4
connect(4, {sa\_family=AF\_LOCAL, sun\_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
open("/etc/nsswitch.conf", O\_RDONLY|O\_CLOEXEC) = 4
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=497, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b1000
read(4, "# /etc/nsswitch.conf\n#\n# Example"..., 4096) = 497
read(4, "", 4096) = 0
close(4) = 0
munmap(0x7f420b3b1000, 4096) = 0
open("/etc/ld.so.cache", O\_RDONLY|O\_CLOEXEC) = 4
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=23259, ...}) = 0
mmap(NULL, 23259, PROT\_READ, MAP\_PRIVATE, 4, 0) = 0x7f420b3ac000
close(4) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libnss\_compat.so.2", O\_RDONLY|O\_CLOEXEC) = 4
read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\260\23\0\0\0\0\0\0"..., 832) = 832
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=31632, ...}) = 0
mmap(NULL, 2126976, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 4, 0) = 0x7f4209f1f000
mprotect(0x7f4209f26000, 2093056, PROT\_NONE) = 0
mmap(0x7f420a125000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 4, 0x6000) = 0x7f420a125000
close(4) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libnsl.so.1", O\_RDONLY|O\_CLOEXEC) = 4
read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0`A\0\0\0\0\0\0"..., 832) = 832
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=89104, ...}) = 0
mmap(NULL, 2194072, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 4, 0) = 0x7f4209d07000
mprotect(0x7f4209d1c000, 2093056, PROT\_NONE) = 0
mmap(0x7f4209f1b000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 4, 0x14000) = 0x7f4209f1b000
mmap(0x7f4209f1d000, 6808, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_ANONYMOUS, -1, 0) = 0x7f4209f1d000
close(4) = 0
mprotect(0x7f4209f1b000, 4096, PROT\_READ) = 0
mprotect(0x7f420a125000, 4096, PROT\_READ) = 0
munmap(0x7f420b3ac000, 23259) = 0
open("/etc/ld.so.cache", O\_RDONLY|O\_CLOEXEC) = 4
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=23259, ...}) = 0
mmap(NULL, 23259, PROT\_READ, MAP\_PRIVATE, 4, 0) = 0x7f420b3ac000
close(4) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libnss\_nis.so.2", O\_RDONLY|O\_CLOEXEC) = 4
read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\240!\0\0\0\0\0\0"..., 832) = 832
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=43592, ...}) = 0
mmap(NULL, 2139560, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 4, 0) = 0x7f4209afc000
mprotect(0x7f4209b06000, 2093056, PROT\_NONE) = 0
mmap(0x7f4209d05000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 4, 0x9000) = 0x7f4209d05000
close(4) = 0
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86\_64-linux-gnu/libnss\_files.so.2", O\_RDONLY|O\_CLOEXEC) = 4
read(4, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\&amp;gt;\0\1\0\0\0\240\"\0\0\0\0\0\0"..., 832) = 832
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=47712, ...}) = 0
mmap(NULL, 2144392, PROT\_READ|PROT\_EXEC, MAP\_PRIVATE|MAP\_DENYWRITE, 4, 0) = 0x7f42098f0000
mprotect(0x7f42098fb000, 2093056, PROT\_NONE) = 0
mmap(0x7f4209afa000, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_FIXED|MAP\_DENYWRITE, 4, 0xa000) = 0x7f4209afa000
close(4) = 0
mprotect(0x7f4209afa000, 4096, PROT\_READ) = 0
mprotect(0x7f4209d05000, 4096, PROT\_READ) = 0
munmap(0x7f420b3ac000, 23259) = 0
open("/etc/passwd", O\_RDONLY|O\_CLOEXEC) = 4
lseek(4, 0, SEEK\_CUR) = 0
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=1488, ...}) = 0
mmap(NULL, 1488, PROT\_READ, MAP\_SHARED, 4, 0) = 0x7f420b3b1000
lseek(4, 1488, SEEK\_SET) = 1488
munmap(0x7f420b3b1000, 1488) = 0
close(4) = 0
socket(PF\_LOCAL, SOCK\_STREAM|SOCK\_CLOEXEC|SOCK\_NONBLOCK, 0) = 4
connect(4, {sa\_family=AF\_LOCAL, sun\_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
socket(PF\_LOCAL, SOCK\_STREAM|SOCK\_CLOEXEC|SOCK\_NONBLOCK, 0) = 4
connect(4, {sa\_family=AF\_LOCAL, sun\_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4) = 0
open("/etc/group", O\_RDONLY|O\_CLOEXEC) = 4
lseek(4, 0, SEEK\_CUR) = 0
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=748, ...}) = 0
mmap(NULL, 748, PROT\_READ, MAP\_SHARED, 4, 0) = 0x7f420b3b1000
lseek(4, 748, SEEK\_SET) = 748
munmap(0x7f420b3b1000, 748) = 0
close(4) = 0
lstat("fontconfig", {st\_mode=S\_IFDIR|0755, st\_size=4096, ...}) = 0
lgetxattr("fontconfig", "security.selinux", 0x7968e0, 255) = -1 ENODATA (No data available)
getxattr("fontconfig", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("fontconfig", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
lstat("dictionaries-common", {st\_mode=S\_IFDIR|0755, st\_size=4096, ...}) = 0
lgetxattr("dictionaries-common", "security.selinux", 0x796900, 255) = -1 ENODATA (No data available)
getxattr("dictionaries-common", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("dictionaries-common", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
lstat("man", {st\_mode=S\_IFDIR|S\_ISGID|0755, st\_size=4096, ...}) = 0
lgetxattr("man", "security.selinux", 0x796920, 255) = -1 ENODATA (No data available)
getxattr("man", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("man", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
open("/etc/passwd", O\_RDONLY|O\_CLOEXEC) = 4
lseek(4, 0, SEEK\_CUR) = 0
fstat(4, {st\_mode=S\_IFREG|0644, st\_size=1488, ...}) = 0
mmap(NULL, 1488, PROT\_READ, MAP\_SHARED, 4, 0) = 0x7f420b3b1000
lseek(4, 1488, SEEK\_SET) = 1488
munmap(0x7f420b3b1000, 1488) = 0
close(4) = 0
lstat("modass", {st\_mode=S\_IFDIR|0755, st\_size=4096, ...}) = 0
lgetxattr("modass", "security.selinux", 0x796960, 255) = -1 ENODATA (No data available)
getxattr("modass", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("modass", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
lstat("ldconfig", {st\_mode=S\_IFDIR|0700, st\_size=4096, ...}) = 0
lgetxattr("ldconfig", "security.selinux", 0x796980, 255) = -1 ENODATA (No data available)
getxattr("ldconfig", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("ldconfig", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
lstat("apt", {st\_mode=S\_IFDIR|0755, st\_size=4096, ...}) = 0
lgetxattr("apt", "security.selinux", 0x7969a0, 255) = -1 ENODATA (No data available)
getxattr("apt", "system.posix\_acl\_access", 0x0, 0) = -1 ENODATA (No data available)
getxattr("apt", "system.posix\_acl\_default", 0x0, 0) = -1 ENODATA (No data available)
getdents(3, /\* 0 entries \*/, 32768) = 0
close(3) = 0
fstat(1, {st\_mode=S\_IFCHR|0600, st\_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b1000
write(1, "total 28\n", 9total 28
) = 9
open("/etc/localtime", O\_RDONLY|O\_CLOEXEC) = 3
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
mmap(NULL, 4096, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b0000
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0"..., 4096) = 1892
lseek(3, -1217, SEEK\_CUR) = 675
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\6\0\0\0\0"..., 4096) = 1217
close(3) = 0
munmap(0x7f420b3b0000, 4096) = 0
write(1, "drwxr-xr-x 3 root root 4096 Oct"..., 46drwxr-xr-x 3 root root 4096 Oct 10 22:12 apt
) = 46
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwxr-xr-x 2 root root 4096 Oct"..., 50drwxr-xr-x 2 root root 4096 Oct 10 22:23 debconf
) = 50
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwxr-xr-x 2 root root 4096 Jul"..., 62drwxr-xr-x 2 root root 4096 Jul 3 2015 dictionaries-common
) = 62
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwxr-xr-x 2 root root 4096 Oct"..., 53drwxr-xr-x 2 root root 4096 Oct 10 22:03 fontconfig
) = 53
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwx------ 2 root root 4096 Oct"..., 51drwx------ 2 root root 4096 Oct 10 22:04 ldconfig
) = 51
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwxr-sr-x 32 man root 4096 Oct"..., 46drwxr-sr-x 32 man root 4096 Oct 10 22:12 man
) = 46
stat("/etc/localtime", {st\_mode=S\_IFREG|0644, st\_size=1892, ...}) = 0
write(1, "drwxr-xr-x 2 root root 4096 Apr"..., 49drwxr-xr-x 2 root root 4096 Apr 23 2014 modass
) = 49
close(1) = 0
munmap(0x7f420b3b1000, 4096) = 0
close(2) = 0
exit\_group(0) = ?
+++ exited with 0 +++
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;That’s not very readable, maybe just try it yourself, on a Vagrant box or some other Linux install; make sure to install the&lt;/em&gt; &lt;em&gt;strace package and just go to&lt;/em&gt; &lt;em&gt;/var/cache and execute&lt;/em&gt; &lt;em&gt;strace ls -l .&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, that’s a lot of stuff that’s going on there — let’s start at the top and skip over a few bits whilst working our way down&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@debian:/var/cache# strace ls -l
execve("/bin/ls", ["ls", "-l"], [/\* 20 vars \*/]) = 0
brk(0) = 0x785000
access("/etc/ld.so.nohwcap", F\_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT\_READ|PROT\_WRITE, MAP\_PRIVATE|MAP\_ANONYMOUS, -1, 0) = 0x7f420b3b2000
access("/etc/ld.so.preload", R\_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O\_RDONLY|O\_CLOEXEC) = 3
fstat(3, {st\_mode=S\_IFREG|0644, st\_size=23259, ...}) = 0
mmap(NULL, 23259, PROT\_READ, MAP\_PRIVATE, 3, 0) = 0x7f420b3ac000
close(3) = 0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So there are a couple of system calls we can see already&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://man7.org/linux/man-pages/man2/execve.2.html"&gt;execve&lt;/a&gt; (execute /bin/ls with argument l)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://man7.org/linux/man-pages/man2/brk.2.html#DESCRIPTION"&gt;brk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;mmap (reserve 8k of memory with address 0x7f420b3b2000)&lt;/li&gt;
&lt;li&gt;open (reads the file /etc/ld.so.cache with file descriptor 3)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://man7.org/linux/man-pages/man2/stat.2.html"&gt;fstat&lt;/a&gt; (file stats like permission, size)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://man7.org/linux/man-pages/man2/mmap.2.html#DESCRIPTION"&gt;mmap&lt;/a&gt; (read file with descriptor 3into 0x7f420b3b2000)&lt;/li&gt;
&lt;li&gt;close (close the file with fd 3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’ll be very beneficial to have a list of what all the above means, maybe this can be of some help &lt;a href="https://golang.org/pkg/syscall/#pkg-constants"&gt;https://golang.org/pkg/syscall/#pkg-constants&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It may also be worth asking… is there anything that might be happening before &lt;a href="https://linux.die.net/man/2/execve"&gt;execve&lt;/a&gt;? Are there other &lt;a href="http://man7.org/linux/man-pages/man3/exec.3.html"&gt;exec&lt;/a&gt; calls that can / would / could be made, and if so, why is &lt;a href="http://man7.org/linux/man-pages/man2/execve.2.html"&gt;execve&lt;/a&gt; used or what is the difference between them?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: I’ve linked to execve, exec, etc. system calls so you can review that documentation and answer the questions yourself. I find it more useful this way as you may be reading additional information that I’d skip over. If you are asking yourself “what if…” after reading.. then keep digging until that question is also answered (hopefully by some relevant piece of documentation).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we know, hopefully, a new process “inherits” all of the current shell's environment variables, how is that possible, and .. when a program exits, why does it not also close its calling shell, since “it replaces” the shell with the running program?&lt;/p&gt;

&lt;p&gt;It’s not particularly obvious, but at least bash shell, before running execve runs &lt;a href="http://man7.org/linux/man-pages/man2/fork.2.html"&gt;fork&lt;/a&gt; which duplicates the shell and all of its current state (with some exceptions and resets). But why can’t we see that in our strace? Because strace is executing our program, and the shell already called fork to start strace. A important detail about fork() is at the bottom of the documentation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Under Linux, &lt;code&gt;fork()&lt;/code&gt; is implemented using copy-on-write* pages, so the only penalty that it incurs is the time and memory required to duplicate the parent’s page tables, and to create a unique task structure for the child.&lt;/p&gt;

&lt;p&gt;Think about what this could mean for resource consumptions on a Linux host?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So how did (other) people “find out” that fork() is called by the shell before execve? I believe (at least at times) a good way to find out what happens is to look at the source code, since our tracing tool cannot trace what happens in the shell (I’m assuming you’re using bash).&lt;/p&gt;

&lt;p&gt;Bash source code is here : &lt;a href="https://ftp.gnu.org/gnu/bash/"&gt;https://ftp.gnu.org/gnu/bash/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SWUXBcn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ALvYyQ606CdfgS4pS-sfPbQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SWUXBcn3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ALvYyQ606CdfgS4pS-sfPbQ.png" alt=""&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

/*
The meaty part of all the executions. We have to start hacking the real execution of commands here. Fork a process, set things up, execute the command.
*/

static int
execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)

SIMPLE_COM *simple_command;
int pipe_in, pipe_out, async;
struct fd_bitmap *fds_to_close;

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



&lt;blockquote&gt;
&lt;p&gt;A small snippet from execute_cmd.c from the bash source code, I believe the function comment explains what is happening when executing a command without necessarily needing to look at (and understand) the code itself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The actual fork() is hidden in jobs.c though …&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

/* 
Create the child, handle severe errors. Retry on EAGAIN. 
*/

while ((pid = fork ()) &amp;lt; 0 &amp;amp;&amp;amp; errno == EAGAIN &amp;amp;&amp;amp; forksleep &amp;lt; FORKSLEEP_MAX)
 {

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



&lt;p&gt;You might ask yourself, are there other things that happen before executing a command? Like fork(), they might not be able to be traced using strace.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;End of Part 1&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In Part 2, I’ll continue with the last question and if time permits walk further down the strace done in Part 1.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




</description>
      <category>linux</category>
      <category>systems</category>
      <category>engineering</category>
    </item>
    <item>
      <title>pex — python executables</title>
      <dc:creator>Alex Leonhardt</dc:creator>
      <pubDate>Tue, 18 Jul 2017 15:31:16 +0000</pubDate>
      <link>https://forem.com/ineedale/pex-python-executables-4fcf</link>
      <guid>https://forem.com/ineedale/pex-python-executables-4fcf</guid>
      <description>&lt;p&gt;&lt;strong&gt;P&lt;/strong&gt; ython &lt;strong&gt;EX&lt;/strong&gt; ecutables are &lt;strong&gt;&lt;em&gt;awesome&lt;/em&gt;&lt;/strong&gt; , they bring everything you need to run your code with them. Just like virtualenv used to do but without the need of having to initialize a environment or doing that pip path import hack I recently started using more often (as I didn't want to have to distribute an entire environment)&lt;/p&gt;

&lt;p&gt;In a nutshell, you install all your requirements with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -t .pip -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All modules will be installed into .pip which you can then use when you add the path to your PYTHONPATH. You can do that inside your app/script by doing the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/env python

import sys
import os

sys.path.append(sys.path.append(os.path.abspath('.pip')))

import your.modules.from.requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, above isn’t too bad, and you can relatively easy distribute the .pip directory without needing an entire environment and you also won't need to install any requirements onto the destination machine or container (or environment).&lt;/p&gt;

&lt;p&gt;Doing the above isn’t bad, but we can do better! Here is where PEX comes in, a python executable that one builds either by using pants (python ant) or by using the pex module. In this example, we'll be using the pex module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install pex
&lt;/h3&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ (sudo) pip install pex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yep, that’s all you have to do to install pex ;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a .pex file
&lt;/h3&gt;

&lt;p&gt;So, unlike virtualenv and the .pip hack, you cannot easily just add a python script and execute it as part of the .pex file as you (or I) may have expected.&lt;/p&gt;

&lt;p&gt;To build a .pex file that functions just like virtualenv or .pip with all our requirements installed, you can do the following short steps, ensure you have installed pex (see above) first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pex module1 module2 module3 -o myenv.pex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To use this myenv.pex (which now has all your required modules available), simply run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./myenv.pex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default, this will just spin up python itself, then you can import the modules you’ve added to the .pex file or, to run a script, do a simple&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./myenv.pex script.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using a requirements.txt file may be easier to create your environment though&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pex -r requirements.txt -o myenv.pex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Running your script inside the .pex, not as a script
&lt;/h3&gt;

&lt;p&gt;So, it turns out you &lt;em&gt;can&lt;/em&gt; run your script inside the .pex file, it’s just simply a tiny bit more work than one would like. Isn’t that great!? Think of a go static binary, but it's python.&lt;/p&gt;

&lt;p&gt;So, let’s assume you’re wanting to run a script that prints Hello! when executed, obviously that wouldn't need its own environment, etc. but it's just an exercise.&lt;/p&gt;

&lt;h4&gt;
  
  
  __init__.py, hello.py &amp;amp; setup.py
&lt;/h4&gt;

&lt;p&gt;First, we need to create the necessary directory structure and files to make this a installable python module/package. For this, we will need the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./hello/__init__.py ./hello/setup.py ./hello/hello.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;__init__.py can just be empty - yay!&lt;/p&gt;

&lt;p&gt;setup.py does need some two lines of boilerplate&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from distutils.core import setup
setup( name='hello', version='0.0.1', py_modules=['hello'] )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;hello.py will just do what we ask it to - print hello&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def hello(): 
 print "hello!"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yep, that’s all we need — great isn’t it?&lt;/p&gt;

&lt;h4&gt;
  
  
  Build the .pex file
&lt;/h4&gt;

&lt;p&gt;From outside the “hello” module/package/path, run pex to create the pex&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pex hello -e hello:hello -o hello.pex
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will install the “hello” module that we created into the .pex file, the -e hello:hello declares an entrypoint which will load the &lt;code&gt;module "hello"&lt;/code&gt; and execute the &lt;code&gt;function "hello"&lt;/code&gt; when the .pex is being run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final result
&lt;/h3&gt;

&lt;p&gt;Output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vagrant@local:/vagrant$ pex hello -e hello:hello -o hello.pex

vagrant@local:/vagrant$ ls
 hello 
 hello.pex

vagrant@local:/vagrant$ ./hello.pex
hello!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;File / Dir size comparison&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;.pex file with ansible installed : 3 MB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;.pip dir with ansible installed : 17 MB&lt;/li&gt;
&lt;li&gt;virtualenv with ansible installed : 28 MB&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>python</category>
      <category>pex</category>
      <category>virtualenv</category>
      <category>pip</category>
    </item>
  </channel>
</rss>
