<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on 4s3ti.net</title>
        <link>https://4s3ti.net/en/posts/</link>
        <description>Recent content in Posts on 4s3ti.net</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>pt-pt</language>
        <copyright>All Rights Reserved</copyright>
        <lastBuildDate>Mon, 25 Sep 2023 00:00:00 +0000</lastBuildDate>
        <atom:link href="https://4s3ti.net/en/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>When concurrency might actually hurt performance!</title>
            <link>https://4s3ti.net/en/posts/goconcurrency/</link>
            <pubDate>Mon, 25 Sep 2023 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/goconcurrency/</guid>
            <description>On my current journey to learn Go and the craft of software development, I am rewriting a CLI tool from Bash to Go. As usual, tools that are supposed to be temporary become permanent, and as a result, it has grown too big to be in Bash. I was lacking ideas for projects, so I just decided to go with it.
One of the core functionalities of this tool is to get a list of systems from an API.</description>
            <content type="html"><![CDATA[<p>On my current journey to learn Go and the craft of software development, I am rewriting a CLI tool from Bash to Go. As usual, tools that are supposed to be temporary become permanent, and as a result, it has grown too big to be in Bash. I was lacking ideas for projects, so I just decided to go with it.</p>
<p>One of the core functionalities of this tool is to get a list of systems from an API. It then performs a search on the list based on given system name(s) and presents relevant information about them.</p>
<p>While at it, I thought that it might be worth exploring the possibility of making the search concurrent. Not that performance matters for this particular tool, but because I saw an opportunity to get a bit more out of my comfort zone and experiment with something that is still quite confusing to me. It could potentially improve the tool even though it is uncertain whether the performance gain would be significant enough to justify the implementation. At the end of the day, the worst that could happen is to get the code thrown away and still learn something.</p>
<h2 id="the-first-test">The first test</h2>
<p>The search is implemented as a simple linear search function. Therefore, I decided to take the easiest and most obvious approach, making the least amount of changes to the code in order to call the search function concurrently and compare it to the initial code.</p>
<p>For the first attempt, the goal was to test two functions that perform the same task: one non-concurrent and one concurrent. I used a sample list with 65 systems loaded from a JSON file.</p>
<p>As a self-proclaimed lazy noob, instead of completely changing the original function, I created a new one based on the original and made the desired changes.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Initial Code
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">searchName</span>(<span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">systems</span> []<span style="color:#a6e22e">System</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">System</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">systems</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">Name</span> <span style="color:#f92672">==</span> <span style="color:#a6e22e">name</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">s</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">fileName</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">names</span> []<span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">systems</span> []<span style="color:#a6e22e">System</span>, <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">allSystems</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">AllSystems</span>(<span style="color:#a6e22e">fileName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">name</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">names</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">system</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">searchName</span>(<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">allSystems</span>); <span style="color:#a6e22e">system</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">systems</span> = append(<span style="color:#a6e22e">systems</span>, <span style="color:#f92672">*</span><span style="color:#a6e22e">system</span>)
</span></span><span style="display:flex;"><span>		} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">systems</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;%w: %s&#34;</span>, <span style="color:#a6e22e">ErrorNameNotFound</span>, <span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">systems</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// The new funcion
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GoSystemsByName</span>(<span style="color:#a6e22e">fileName</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">names</span> []<span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">systems</span> []<span style="color:#a6e22e">System</span>, <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">allSystems</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">AllSystems</span>(<span style="color:#a6e22e">fileName</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">systemsChan</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">System</span>, len(<span style="color:#a6e22e">names</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">errorChan</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#66d9ef">error</span>, len(<span style="color:#a6e22e">names</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">name</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">names</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">systems</span> []<span style="color:#a6e22e">System</span>, <span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">system</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">searchName</span>(<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">allSystems</span>); <span style="color:#a6e22e">system</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">systemsChan</span> <span style="color:#f92672">&lt;-</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">system</span>
</span></span><span style="display:flex;"><span>			} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">errorChan</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;%w: %s&#34;</span>, <span style="color:#a6e22e">ErrorNameNotFound</span>, <span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">allSystems</span>, <span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>		close(<span style="color:#a6e22e">systemsChan</span>)
</span></span><span style="display:flex;"><span>		close(<span style="color:#a6e22e">errorChan</span>)
</span></span><span style="display:flex;"><span>	}()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">system</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">systemsChan</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">systems</span> = append(<span style="color:#a6e22e">systems</span>, <span style="color:#a6e22e">system</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">errorChan</span>) &gt; <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">systems</span>, <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">errorChan</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">systems</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For the benchmark, I randomly searched for some names that I knew existed in the list, as well as one name that didn&rsquo;t exist.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Benchmarking
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">BenchmarkSystemsByName</span>(<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#e6db74">&#34;randomNameX&#34;</span>, <span style="color:#e6db74">&#34;randomNameY&#34;</span>, <span style="color:#e6db74">&#34;randomNameW&#34;</span>, <span style="color:#e6db74">&#34;randomNameZ&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">BenchmarkGoSystemsByName</span>(<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">GoSystemsByName</span>(<span style="color:#e6db74">&#34;randomNameA&#34;</span>, <span style="color:#e6db74">&#34;randomNameY&#34;</span>, <span style="color:#e6db74">&#34;randomNameB&#34;</span>, <span style="color:#e6db74">&#34;randomNameG&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>The results:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$go test -bench<span style="color:#f92672">=</span>.
</span></span><span style="display:flex;"><span>goos: linux
</span></span><span style="display:flex;"><span>goarch: amd64
</span></span><span style="display:flex;"><span>pkg: toolbox/pkg/govcm
</span></span><span style="display:flex;"><span>cpu: 11th Gen Intel<span style="color:#f92672">(</span>R<span style="color:#f92672">)</span> Core<span style="color:#f92672">(</span>TM<span style="color:#f92672">)</span> i5-1135G7 @ 2.40GHz
</span></span><span style="display:flex;"><span>BenchmarkSystemsByName-8            <span style="color:#ae81ff">2815</span>            <span style="color:#ae81ff">395575</span> ns/op
</span></span><span style="display:flex;"><span>BenchmarkGoSystemsByName-8          <span style="color:#ae81ff">2424</span>            <span style="color:#ae81ff">491929</span> ns/op
</span></span><span style="display:flex;"><span>PASS
</span></span><span style="display:flex;"><span>ok      toolbox/pkg/govcm       2.409s
</span></span></code></pre></div><p><strong>Non-Concurrent:</strong> 2815 operations, with an average of 395575 ns per operation.</p>
<p><strong>Concurrent function:</strong> 2424 operations, with an average of 491929 ns per operation.</p>
<p>Well&hellip; This was unexpected! The non-concurrent version of the search performed better than the concurrent one. But&hellip; this doesn&rsquo;t seem right. The concurrent search should be faster, right? What am I doing wrong?!</p>
<p>As someone who is driven by curiosity, I made the decision to delve further and explore the next most apparent thing.</p>
<h2 id="moar-data">Moar Data!</h2>
<p>Maybe its just my code that sucks, but for sake of simplicity, let’s just assume not, after all, when making the function I added some prints to it and actually saw it working as intended and it passed all the unit tests in the original code! When did this ever go wrong riiiiight? let’s move on &hellip;</p>
<p>The next most obvious thing is to test it with more data and try to look for a change in the benchmark values. From my understanding, concurrency also adds some overhead, so it may not provide any benefits for smaller amounts of data.</p>
<p>After reading a bit more about benchmarking in Go and finding <a href="https://pkg.go.dev/golang.org/x/perf/cmd/benchstat">benchstat</a>, I decided to change the way I was doing the benchmark a bit, ensuring that everything is tested in the same way, making it easier to analyze the results.
I refactored the tests to only test the <code>SystemsByName()</code> function, and the concurrent and non-concurrent functions have the same name, with one of them always commented out depending on what I want to test.</p>
<p>For the test cases, I decided to benchmark against datasets with 100, 1000, 10000, 100000, and 1000000 entries. I will search for the first element in the list, an element in the middle of the list, the last element of the list, and a non-existing element.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;testing&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">file100</span> = <span style="color:#e6db74">&#34;data/systems_100.json&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">file1000</span> = <span style="color:#e6db74">&#34;data/systems_1000.json&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">file10000</span> = <span style="color:#e6db74">&#34;data/systems_10000.json&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">file100000</span> = <span style="color:#e6db74">&#34;data/systems_100000.json&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">file1000000</span> = <span style="color:#e6db74">&#34;data/systems_1000000.json&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Benchmarking
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">BenchmarkNameSearch</span>(<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>){
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#e6db74">&#34;100 Systems&#34;</span>, <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;system0&#34;</span>, <span style="color:#e6db74">&#34;system50&#34;</span>, <span style="color:#e6db74">&#34;system99&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">file100</span>, <span style="color:#a6e22e">names</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#e6db74">&#34;1000 Systems&#34;</span>, <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;system0&#34;</span>, <span style="color:#e6db74">&#34;system500&#34;</span>, <span style="color:#e6db74">&#34;system999&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">file1000</span>, <span style="color:#a6e22e">names</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#e6db74">&#34;10000 Systems&#34;</span>, <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;system0&#34;</span>, <span style="color:#e6db74">&#34;system5000&#34;</span>, <span style="color:#e6db74">&#34;system9999&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">file10000</span>, <span style="color:#a6e22e">names</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#e6db74">&#34;100000 Systems&#34;</span>, <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;system0&#34;</span>, <span style="color:#e6db74">&#34;system50000&#34;</span>, <span style="color:#e6db74">&#34;system99999&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">file100000</span>, <span style="color:#a6e22e">names</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">Run</span>(<span style="color:#e6db74">&#34;1000000 Systems&#34;</span>, <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">b</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">B</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;system0&#34;</span>, <span style="color:#e6db74">&#34;system50000&#34;</span>, <span style="color:#e6db74">&#34;system999999&#34;</span>, <span style="color:#e6db74">&#34;foo&#34;</span>}
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">b</span>.<span style="color:#a6e22e">N</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">SystemsByName</span>(<span style="color:#a6e22e">file1000000</span>, <span style="color:#a6e22e">names</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now that I have determined how I want to benchmark, I just need the actual data to run the benchmarks against. So, I have created a quick and simple Python script to generate multiple JSON files with different numbers of entries. This data also resembles the data returned by the original API.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/python</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> json
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> uuid
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> random
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>total <span style="color:#f92672">=</span> int(sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">1</span>])
</span></span><span style="display:flex;"><span>file<span style="color:#f92672">=</span>(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#39;systems_</span><span style="color:#e6db74">{</span>total<span style="color:#e6db74">}</span><span style="color:#e6db74">.json&#39;</span>)
</span></span><span style="display:flex;"><span>regions <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;eu-north-1&#34;</span>, <span style="color:#e6db74">&#34;eu-west-1&#34;</span>, <span style="color:#e6db74">&#34;eu-west-2&#34;</span>, <span style="color:#e6db74">&#34;us-west-1&#34;</span>]
</span></span><span style="display:flex;"><span>states <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;STATE&#34;</span>, <span style="color:#e6db74">&#34;PLAN_FAILED&#34;</span>, <span style="color:#e6db74">&#34;RESUME_FAILED&#34;</span>, <span style="color:#e6db74">&#34;PAUSED&#34;</span>, <span style="color:#e6db74">&#34;STORAGE_CREATION_FAILED&#34;</span>, <span style="color:#e6db74">&#34;FOO_FAILED&#34;</span>]
</span></span><span style="display:flex;"><span>auxStates <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;PLANING&#34;</span>, <span style="color:#e6db74">&#34;RESUMING&#34;</span>, <span style="color:#e6db74">&#34;PAUSING&#34;</span>, <span style="color:#e6db74">&#34;TO_BE_PLANED&#34;</span>, <span style="color:#e6db74">&#34;TO_BE_PAUSED&#34;</span>]
</span></span><span style="display:flex;"><span>types <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;team&#34;</span>, <span style="color:#e6db74">&#34;lab&#34;</span>]
</span></span><span style="display:flex;"><span>versions <span style="color:#f92672">=</span> [<span style="color:#e6db74">&#34;1.2.3-gec414e3c24&#34;</span>, <span style="color:#e6db74">&#34;2.3.2-bde194c123a&#34;</span>, <span style="color:#e6db74">&#34;3.4.0-foobarbaz&#34;</span>]
</span></span><span style="display:flex;"><span>items <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> i <span style="color:#f92672">in</span> range(total):
</span></span><span style="display:flex;"><span>    item <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;id&#34;</span> : str(uuid<span style="color:#f92672">.</span>uuid4()),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;name&#34;</span> : (<span style="color:#e6db74">f</span><span style="color:#e6db74">&#39;system</span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">&#39;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;region&#34;</span> : random<span style="color:#f92672">.</span>choice(regions),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;state&#34;</span> : random<span style="color:#f92672">.</span>choice(states),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;auxState&#34;</span> : random<span style="color:#f92672">.</span>choice(auxStates),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;apiEndpoint&#34;</span> : (<span style="color:#e6db74">f</span><span style="color:#e6db74">&#39;https://system</span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">.mydomain.com&#39;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;systemType&#34;</span> : random<span style="color:#f92672">.</span>choice(types),
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;systemVersion&#34;</span> : random<span style="color:#f92672">.</span>choice(versions)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    items<span style="color:#f92672">.</span>append(item)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>data <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;items&#34;</span>: items}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">with</span> open(file, <span style="color:#e6db74">&#34;w&#34;</span>) <span style="color:#66d9ef">as</span> f:
</span></span><span style="display:flex;"><span>    json<span style="color:#f92672">.</span>dump(data, f, indent<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span>)
</span></span></code></pre></div><p>Now that I have the data, it&rsquo;s time to run the tests. This time, I will also run them with different settings: default, 20 seconds, 30 seconds, 20 times, and 30 times. It is important to note that the test environment is not perfect, but neither is the world, and I am not looking for super detailed results. All I want to find are some hints about whether it&rsquo;s worth using concurrency on linear searches and, if so, at what point.</p>
<h2 id="benchstat">Benchstat</h2>
<p>I will not provide all the results here, but the results were consistent across all the different benchmark settings.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">$</span><span style="color:#a6e22e">benchstat</span> <span style="color:#a6e22e">test_default</span>.<span style="color:#a6e22e">txt</span> <span style="color:#a6e22e">test_concurrent_default</span>.<span style="color:#a6e22e">txt</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">goos</span>: <span style="color:#a6e22e">linux</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">goarch</span>: <span style="color:#a6e22e">amd64</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">pkg</span>: <span style="color:#a6e22e">concurrency</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">cpu</span>: <span style="color:#ae81ff">11</span><span style="color:#a6e22e">th</span> <span style="color:#a6e22e">Gen</span> <span style="color:#a6e22e">Intel</span>(<span style="color:#a6e22e">R</span>) <span style="color:#a6e22e">Core</span>(<span style="color:#a6e22e">TM</span>) <span style="color:#a6e22e">i5</span><span style="color:#f92672">-</span><span style="color:#ae81ff">1135</span><span style="color:#a6e22e">G7</span> <span style="color:#960050;background-color:#1e0010">@</span> <span style="color:#ae81ff">2.40</span><span style="color:#a6e22e">GHz</span>
</span></span><span style="display:flex;"><span>                             <span style="color:#960050;background-color:#1e0010">│</span> <span style="color:#a6e22e">test_default</span>.<span style="color:#a6e22e">txt</span> <span style="color:#960050;background-color:#1e0010">│</span>     <span style="color:#a6e22e">test_concurrent_default</span>.<span style="color:#a6e22e">txt</span>      <span style="color:#960050;background-color:#1e0010">│</span>
</span></span><span style="display:flex;"><span>                             <span style="color:#960050;background-color:#1e0010">│</span>      <span style="color:#a6e22e">sec</span><span style="color:#f92672">/</span><span style="color:#a6e22e">op</span>      <span style="color:#960050;background-color:#1e0010">│</span>    <span style="color:#a6e22e">sec</span><span style="color:#f92672">/</span><span style="color:#a6e22e">op</span>     <span style="color:#a6e22e">vs</span> <span style="color:#a6e22e">base</span>                <span style="color:#960050;background-color:#1e0010">│</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">NameSearch</span><span style="color:#f92672">/</span><span style="color:#ae81ff">100_</span><span style="color:#a6e22e">Systems</span><span style="color:#f92672">-</span><span style="color:#ae81ff">8</span>           <span style="color:#ae81ff">248.9</span><span style="color:#a6e22e">µ</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>   <span style="color:#ae81ff">262.5</span><span style="color:#a6e22e">µ</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>       <span style="color:#960050;background-color:#1e0010">~</span> (<span style="color:#a6e22e">p</span>=<span style="color:#ae81ff">1.000</span> <span style="color:#a6e22e">n</span>=<span style="color:#ae81ff">1</span>) <span style="color:#960050;background-color:#1e0010">²</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">NameSearch</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1000_</span><span style="color:#a6e22e">Systems</span><span style="color:#f92672">-</span><span style="color:#ae81ff">8</span>          <span style="color:#ae81ff">2.445</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>   <span style="color:#ae81ff">2.604</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>       <span style="color:#960050;background-color:#1e0010">~</span> (<span style="color:#a6e22e">p</span>=<span style="color:#ae81ff">1.000</span> <span style="color:#a6e22e">n</span>=<span style="color:#ae81ff">1</span>) <span style="color:#960050;background-color:#1e0010">²</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">NameSearch</span><span style="color:#f92672">/</span><span style="color:#ae81ff">10000_</span><span style="color:#a6e22e">Systems</span><span style="color:#f92672">-</span><span style="color:#ae81ff">8</span>         <span style="color:#ae81ff">26.01</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>   <span style="color:#ae81ff">26.33</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>       <span style="color:#960050;background-color:#1e0010">~</span> (<span style="color:#a6e22e">p</span>=<span style="color:#ae81ff">1.000</span> <span style="color:#a6e22e">n</span>=<span style="color:#ae81ff">1</span>) <span style="color:#960050;background-color:#1e0010">²</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">NameSearch</span><span style="color:#f92672">/</span><span style="color:#ae81ff">100000_</span><span style="color:#a6e22e">Systems</span><span style="color:#f92672">-</span><span style="color:#ae81ff">8</span>        <span style="color:#ae81ff">259.4</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>   <span style="color:#ae81ff">259.0</span><span style="color:#a6e22e">m</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>       <span style="color:#960050;background-color:#1e0010">~</span> (<span style="color:#a6e22e">p</span>=<span style="color:#ae81ff">1.000</span> <span style="color:#a6e22e">n</span>=<span style="color:#ae81ff">1</span>) <span style="color:#960050;background-color:#1e0010">²</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">NameSearch</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1000000_</span><span style="color:#a6e22e">Systems</span><span style="color:#f92672">-</span><span style="color:#ae81ff">8</span>        <span style="color:#ae81ff">2.553</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>    <span style="color:#ae81ff">2.534</span> <span style="color:#960050;background-color:#1e0010">±</span> <span style="color:#960050;background-color:#1e0010">∞</span> <span style="color:#960050;background-color:#1e0010">¹</span>       <span style="color:#960050;background-color:#1e0010">~</span> (<span style="color:#a6e22e">p</span>=<span style="color:#ae81ff">1.000</span> <span style="color:#a6e22e">n</span>=<span style="color:#ae81ff">1</span>) <span style="color:#960050;background-color:#1e0010">²</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">geomean</span>                            <span style="color:#ae81ff">25.36</span><span style="color:#a6e22e">m</span>         <span style="color:#ae81ff">25.97</span><span style="color:#a6e22e">m</span>        <span style="color:#f92672">+</span><span style="color:#ae81ff">2.42</span><span style="color:#f92672">%</span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">¹</span> <span style="color:#a6e22e">need</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">6</span> <span style="color:#a6e22e">samples</span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">confidence</span> <span style="color:#a6e22e">interval</span> <span style="color:#a6e22e">at</span> <span style="color:#a6e22e">level</span> <span style="color:#ae81ff">0.95</span>
</span></span><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">²</span> <span style="color:#a6e22e">need</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">4</span> <span style="color:#a6e22e">samples</span> <span style="color:#a6e22e">to</span> <span style="color:#a6e22e">detect</span> <span style="color:#a6e22e">a</span> <span style="color:#a6e22e">difference</span> <span style="color:#a6e22e">at</span> <span style="color:#a6e22e">alpha</span> <span style="color:#a6e22e">level</span> <span style="color:#ae81ff">0.05</span>
</span></span></code></pre></div><p>Even though the benchmark results indicate that the non-concurrent search is generally more efficient than the concurrent search, it is possible to observe that an inversion starts to occur after reaching 100,000 items. At this point, the concurrent search becomes slightly more efficient, although the difference is not significant.</p>
<p>However, just because concurrency is widely available and relatively easy to implement, it does not mean that we should always use it. We should be mindful of when and how we should use it because it may actually harm performance.</p>
<p>Wait, Karen, wait, before you start throwing rocks&hellip; when I say performance, I don&rsquo;t just mean code performance. Your team&rsquo;s performance may also suffer. When opting for a concurrent solution, your team now has to deal with the added complexity in the code when things go wrong, without any real benefit. One could argue that when dealing with data sets larger than 10k items, it may be better to study and learn about other algorithms instead.</p>
<p>Perhaps that&rsquo;s a story for another time!</p>
<p>If you&rsquo;ve made it this far in this article, congratulations! I hope it was at least useful. To me, these results still don&rsquo;t make a whole lot of sense, but I guess it&rsquo;s time to study a bit more.</p>
<p>Are my results far off from reality? Am I completely wrong? What am I missing? Feel free to reach out to me and let me know what you think!</p>
<p>The code and test results can be found <a href="https://github.com/4s3ti/blog-concurrencytest">here</a></p>
]]></content>
        </item>
        
        <item>
            <title>Go: Advent of code 2015, day 1: Hello Buffer!</title>
            <link>https://4s3ti.net/en/posts/advent0115/</link>
            <pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/advent0115/</guid>
            <description>A bit of history Before starting, I think it&amp;rsquo;s important to go back a little in time to explain why I&amp;rsquo;m taking on this challenge.
Throughout all my years in the IT field, I never seriously dedicated myself to learning a programming language. I learned a bit of Pascal, C++, and Visual Basic in school, which gave me a basic understanding of programming, but never sparked my interest in learning more, and to be honest, I don&amp;rsquo;t even remember anything!</description>
            <content type="html"><![CDATA[<h2 id="a-bit-of-history">A bit of history</h2>
<p>Before starting, I think it&rsquo;s important to go back a little in time to explain why I&rsquo;m taking on this challenge.</p>
<p>Throughout all my years in the IT field, I never seriously dedicated myself to learning a programming language. I learned a bit of Pascal, C++, and Visual Basic in school, which gave me a basic understanding of programming, but never sparked my interest in learning more, and to be honest, I don&rsquo;t even remember anything! Maybe because school projects were boring and never motivated me to learn more, or maybe because I was more interested in hardware than software development. A few years later, when I started focusing more on Linux, I learned Bash, which has been quite useful for automating various tasks, including creating several tools for my team in our daily activities in production environments.</p>
<p>However, I&rsquo;ve been having more and more ideas for different projects (some more foolish than others), and the need to learn a programming language arose! I started learning Python, and my interest grew. I did some small and fun projects, but decided to slow down, change course, and learn Go! Why? Simply because! All the tools I work with daily are written in Go, and after some time thinking, I consider Go to be the most suitable language for the projects I have in mind. Maybe I&rsquo;m wrong, but&hellip; as my grandfather used to say, &ldquo;Knowledge doesn&rsquo;t take up space!&rdquo;</p>
<p>After several days studying the basics of Go, I decided to start solving the problems of Advent of Code and started with the year 2015, which brings us to today&rsquo;s topic and discovery!</p>
<h2 id="the-problem">The problem</h2>
<p>You can learn more about the challenge <a href="https://adventofcode.com/2015/day/1">here</a>. But, in a very brief summary, the problem consists of calculating the floor of an elevator based on a series of parentheses that indicate if the elevator goes up or down. For example, if the elevator starts on floor 0 and the input is <code>(((</code> or <code>(()(()(</code>, the elevator will go up to floor 3. If the input is <code>())</code>, the elevator will go down to floor -1.</p>
<p>Ooh! This is easy, I thought in my innocence!</p>
<p>First, a function called <code>elevator</code> that receives the instructions and returns the floor where the elevator ends up.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">elevator</span>(<span style="color:#a6e22e">instructions</span> <span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">int</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">floor</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">instruction</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">instructions</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">instruction</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;(&#39;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">floor</span><span style="color:#f92672">++</span>
</span></span><span style="display:flex;"><span>		} <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">instruction</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#39;)&#39;</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">floor</span><span style="color:#f92672">--</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span>  <span style="color:#a6e22e">floor</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>And then I just have to ask &ldquo;Santa Claus&rdquo; to input the instructions, print the result and it&rsquo;s done!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">instructions</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#e6db74">&#34;What floor you want to go?: &#34;</span> )
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Scan</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">instructions</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">lastFloor</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">elevator</span>(<span style="color:#a6e22e">instructions</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;final Floor: &#34;</span>, <span style="color:#a6e22e">lastFloor</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I introduce the instructions and, whoa! I&rsquo;m a machine! Everything works perfectly! <code>(()))</code> returns 3 and <code>())</code> returns -1.</p>
<p>I take the challenge input and&hellip; Wrong!!! For more experienced programmers, this might be quite obvious, but for little innocent me, it was enough to spend the afternoon banging my head against the wall.</p>
<p>First, I focused on the fact that the result was wrong and decided to add an extra print, hoping to understand what was going on.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">instructions</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#e6db74">&#34;What floor you want to go?: &#34;</span> )
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Scan</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">instructions</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;instructions: &#34;</span>, <span style="color:#a6e22e">instructions</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">lastFloor</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">elevator</span>(<span style="color:#a6e22e">instructions</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;final Floor: &#34;</span>, <span style="color:#a6e22e">lastFloor</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Hey! The output of the instructions is not the same as the input! What&rsquo;s happening here? And if I read from a text file? Will I have the same problem? I changed the code to read from a text file:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">instructions</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ReadFile</span>(<span style="color:#e6db74">&#34;input.txt&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">floor</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">elevator</span>(string(<span style="color:#a6e22e">instructions</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Final floor:&#34;</span>, <span style="color:#a6e22e">floor</span>)
</span></span></code></pre></div><p>This time, the result was different. I entered the result on the website and&hellip; Correct! But why? What&rsquo;s going on here? I got the correct answer for the exercise, but I still couldn&rsquo;t understand what was happening. So, I decided to compare the result of the input made by the user and the input read from the text file.</p>
<p>My first idea was to check if the strings had the same length and compare the result of both.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">instructionsFromInput</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#e6db74">&#34;What floor you want to go?: &#34;</span> )
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Scan</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">instructionsFromInput</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;instructions: &#34;</span>, <span style="color:#a6e22e">instructionsFromInput</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Length from input: &#34;</span>, len(<span style="color:#a6e22e">instructionsFromInput</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">instructionsFromFile</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">os</span>.<span style="color:#a6e22e">ReadFile</span>(<span style="color:#e6db74">&#34;../01/input.txt&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Lenght from file: &#34;</span>, len(<span style="color:#a6e22e">instructionsFromFile</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">lastFloorFromInput</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">elevator</span>(<span style="color:#a6e22e">instructionsFromInput</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">lastFloorFromFile</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">elevator</span>(string(<span style="color:#a6e22e">instructionsFromFile</span>))
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Final Floor From input: &#34;</span>, <span style="color:#a6e22e">lastFloorFromInput</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Final Floor from file: &#34;</span>, <span style="color:#a6e22e">lastFloorFromFile</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="eureka-im-a-noob-or-am-i-not">EUREKA! I&rsquo;m a noob! Or am I not?</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#960050;background-color:#1e0010">$</span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">run</span> <span style="color:#a6e22e">main</span>.<span style="color:#66d9ef">go</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">What</span> <span style="color:#a6e22e">floor</span> <span style="color:#a6e22e">you</span> <span style="color:#a6e22e">want</span> <span style="color:#a6e22e">to</span> <span style="color:#66d9ef">go</span><span style="color:#960050;background-color:#1e0010">?</span>: (((())))()((((((((())()(()))(()((((()(()(((()((()((()(()()()()()))(((()(()((((((((((())(()()((())()(((())))()(()(()((()(()))(()()()()((()((()(((()()(((((((()()())()((((()()(((((()(())()(())((())()()))()(((((((())(()())(()(((())(()))((())))(()((()())))()())((((())))(()(((((()(())(((()()((()((()((((((((((())(()())))))()))())()()((((()()()()()()((((((())())(((()())()((()()(((()()()))(((((()))(((()(()()()(()(()(((())()))(()(((()((())()(()())())))((()()()(()()(((()))(((()((((()(((((()()(()())((()())())(()((((((()(()()))((((()))))())((())()()((()(()))))((((((((()))(()()(((())())(())()((()()()()((()((()((()()(((())))(()((())()((((((((()((()(()()(((())())())))(())())))()((((()))))))())))()()))()())((()())()((()()()))(()()(((()(())((((())())((((((((()()()()())))()()()((((()()))))))()((((()(((()))(()()())))((()()(((()))()()())())(((())((()()(())()()()(((())))))()())((()))()))((())()()())()())()()(()))())))())()))(())((()(())))(()(())(()))))(()(())())(()(())(()(()))))((()())()))()((((()()))))())))()()())((())()((()()()))()(((()(()))))(())()()))(((()())))))))))(((())))()))())()))))()()(((())))))))()(()()(()))((()))))((())))((()((())))())))()()(()))())()(()((()())(()(()()())())(()()))()))))(()())()()))()()()()))(()(()(()))))))()(()))()))()()(()((())(()(())))()(((())(())())))))()(()(()))))()))(()()()(())()(()(())))()))))()()(((((())))))())()())())())()())()))))()))))))))())()()()()()()())))()))((())()))())))()((())()))))()))())))))))())()()()))()()(()((((()(((((((()(())((()())((()()))()))))(())))()()()(())((())()())))(())))(())))(((()()))()(())(((()(()))((())))())()))((((()))())()))))))))()(())())))(()))()(()()))())()()(())())))())()()(()())))()((()())(()(())(())))))))))))))(()))))()))))))()()())(()(((((()(()())))())()))(()))()))(()()))()())(()))())()(())((()()))))))())))())()(((())))(()(()))()()))()(()))))))((()())(()))))))()())))()()))))))))((((((((()()()(()))))))()())))())))()()((())()))((())(())))())())))()()()((()((()(())))())()(())))))))))()())))()()()()()()))()))((())())(()(()))))))(()()))()))(())))()))))))))))))(()))))))))()))))()))()())()))()()))))))()))))((()))))(()))())()(())))(()())((((()())))()))))(()))()(()()(())))))())))))()))))))())))())))))())))())())))())(()))))(())()(())))())()))((()()))))))())))((())))))))())))(())))))()()())))))())))))()))))))()))()()()(()(((()())())())(()))())))))((()(())(()))))))))(())))()()()())())(()))))()()()))()))())())())()(())))()(((()((((())))))))()))))))))))))))))))))((())()())(()))))()()))))))(()()(())())))())))((())))((())))))))))))))()))))()(()))))))())))))()))(()()())(()())))))))))()))))))(())))))()()))()())(((())))()))(()))))))))(())())))())))())())())()()))((())()(())()())()))()())(())(()))))()())))(()(((()))))))()(()())()()()))()))))))))()()()(())()())()(((((()))()())())(()))))()()()(())))())))()((()())))(()))())()(()())())(()))()()))((()()))((()()()()())))(())()))(()(())))((()()))))))))())))))))())()()))))))))))))))))(())()(())(())()())())()))()(()))))())())))))()())()(()))()()(())))(())())))))(()))))))))))))))())())(())(())))(((()))()))))())((())(()))())))))))())))))())))()))()))))))))))))())()))))()))))((()))(())))()(())))(())()))()))())))())))))))()(()())())))()()())))(())))))(()))))))))))))(()))()))()))())))(((()()()(())((()())))()())(((()))(())()))((()()()())))())(())(()))))()(((((())))(()))())())))))))((((()()()))())())()(()(()())))))))))()())())))(())))()())(((()(())())()()))())())))))))((()())((()()(()))(()(())))()))()))(()))(()))()()(()(((())((((()))()(()))((())()(()(()())()(()))()())))))(()))()))())()())))())))(())))((())(()())))))()))(())(()))()())()(()()((()(()))))))()(())(()())(())()))(((())()))(()()(()()()))))(()(())))()))))())))))())(()()()()()()(((())))(()()))()((())(((((()()())))(()))(()))()()))(((())())()(((()()()()))))(()))(())())))()())(()()())())))))))()))))((())))()())(()))(()(()))())))))())(())))))()()())())()))()()(())))(()))(())((((((())(()))(()))())()))(()()(())))()))(()()))()))()(())))(())))((()(()))(())()()())())))(((()()())(())()))))))()(((()(((((()()(((())(())))())()((()))))((()())()(())(((())))(((()((()(()(()))(()()))())(()))(())(())))()))))))((((()))()((((()(()))()))()()))))()(()(()))()(()((()(((()(()()(((()))))()(((()(()(()(((()(()())())()()(()(()())())(()((((())(()))()))(((((()()())(())()((()()())))()()(((()()))()((((((((()(())))())((()))))(())))(()))))((()((((()()(())(((((()))(((((((((((((()())))((((()(((()((())())()))((()))()(()()((()()()()(()()(()(()(((())()(()((((((()((()()((())()((((()((()()(()()())((()()()((()((())()(()(((()((())((((())(()))((()(()))(()())()((((((((()(((((((((((()))(()(((()(()()()((((())((())()())()))(())((())(()))(((()((()(())))(()))))((()()))))((((()(()(()())(()(())((((((((()((((()((()(((((()))())()(()))(()()((()(())(((((()(())()(((((()()))))))()(((())()(()()((((())()((())((()(((())(((()))((()()((((()(())))))((()((((()((()((()(((())((()))(((((((()(((()((((((((())()))((((())(((((()((((((((()(((()((()(((()()(((()((((((()()(()((((((((()()(()(()(())((((()())()))))(((()))((((())((((()())((()(())()((()((((((()((((((()(())))()())(((())())())()(())()(()())((()()((((())((((((())(()(((((()((((())()((((()(()(())(()())(((())()((())((((()))()((((((())(()(((()(((()((((((()(((()))(()()())())((()((()())()((((())(((()(()(((((((((())(())))()((()()()()(())((()))(((((((()(((((((((()(()))))(()((((((((()((((()((()()((((((()()(((((((()(()(())()(())((()()()((()(((((()())()(((((()())()()((()(()())(()()()(((()()(((((()((((((()()((()(()()()((((((((((((()((((((((()()(((()())))()(((()()(())())((((()((((()((((()()()(())(())((()(()(((((((((((((((()(())(())))))()()))((()(((()(())((()(((()(()()((((()()(((()(((()(((((()()((()(()(((()))((((((()((((((((()((()((())(((((()(((())(())())((()()))((((())()()((()(((()(((((()()(((()))(((()(()(((((((((((((()))((((((((()(((()))))())((((((((((((())((())((()())(((())((())(()((((((((((()(((())((()()(()((())(((((((((((()))((((((((((((()(()())((()((()((()(()(((()((((((((()()(()((()(()(((()))((()))(((((((((((((()(())((((((())(((()(())(()(()(()((()()))((((()((((()((((())))())((((()((((()))((((((()((((((()((()(((())))((())(()))(()((()((((()((()(((()()))((((()()()(((((((())(((())(()))())((((()())(((()(((((((((((()(()(()((()(((((((((((((((()()((((()((((((((()(((()()((()((((()))(((()(())((((((()((((())()((((()((()))(())()(()(((()((())())((((((()(()(())())(((())(()(()())(((((()((()((())()())(())))(((()(())))))))(((()(((()))()((()(((()()((()())()()))())))(((()))(()(((()(((((((((()(()(((((()()(((()())()()))))()(((()))(((()(()(()(()(()))()(())()))(()(((())))(()))))))))))(())((()((())((()(())()(())((()()((((()()((()()))((())(((()((()(())(())))()(()(((((()((()))())()(((((()()(((()(()((((((())(()))(())()))((()(()()))(())())()))(((())))(()((()(((())(())())))((()()((((((((((((((()((()(()()(()(((()))())()()((()()()(())(()))(()())(((())((())()(())()()(()()(())))((()(((()))))(((()()(()()))())((()((())()))((((()()()())((())))(((()(())(((((()(((((()((()(()((((()()(((()()()(((()())(((()()((((())(()))(((()))(())())((()))(((()((()))(((()()((())((()(((((()((((()()())((()))()((((()((()(()()()(
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Length</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">input</span>:  <span style="color:#ae81ff">4095</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Lenght</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">file</span>:  <span style="color:#ae81ff">7001</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Final</span> <span style="color:#a6e22e">Floor</span> <span style="color:#a6e22e">From</span> <span style="color:#a6e22e">input</span>:  <span style="color:#f92672">-</span><span style="color:#ae81ff">633</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Final</span> <span style="color:#a6e22e">Floor</span> <span style="color:#a6e22e">from</span> <span style="color:#a6e22e">file</span>:  <span style="color:#ae81ff">232</span>
</span></span></code></pre></div><p>Hmm&hellip; interesting. When I pass the elevator instructions through the input in the terminal, the result is different because some instructions are missing! Here everything started to make more sense and, after a little research, I found out that the terminal buffer has a limit of 4096 bytes, and everything beyond that is cut off.</p>
<p>Wow, what a big noob! Usually, when I know that the input is large, I always use a file, especially since I usually get the data from somewhere, save it to a file, and then read and process it. But in this case, since Santa Claus enters an elevator and places instructions for it, I thought it would be nicer to take the input from the website and pass it directly through the terminal, but no!</p>
<h2 id="part-2">Part 2</h2>
<p>Regarding part 2, it was fairly easy and all the code for this exercise is available on my <a href="https://github.com/4s3ti/go-AoC/tree/main/2015/01">GitHub</a> repository.</p>
]]></content>
        </item>
        
        <item>
            <title>From TrueNAS Scale to Proxmox</title>
            <link>https://4s3ti.net/en/posts/truenasproxmox/</link>
            <pubDate>Sun, 30 Apr 2023 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/truenasproxmox/</guid>
            <description>If there&amp;rsquo;s something that particularly frustrated me about TrueNAS, it was the fact that it didn&amp;rsquo;t allow me to pass the only available graphics card on the server to a virtual machine! Since I made the switch to TrueNAS, I&amp;rsquo;ve been a little uneasy!
As I want to try to use the hardware I have available as efficiently as possible, I decided it was time to change (again!!). Since I had already used ProxMox before, I decided to go back to ProxMox with the idea of passing the HBA to a VM and having TrueNAS in a VM only as a NAS.</description>
            <content type="html"><![CDATA[<p>If there&rsquo;s something that particularly frustrated me about TrueNAS, it was the fact that it didn&rsquo;t allow me to pass the only available graphics card on the server to a virtual machine! Since I made the switch to TrueNAS, I&rsquo;ve been a little uneasy!</p>
<p>As I want to try to use the hardware I have available as efficiently as possible, I decided it was time to change (again!!). Since I had already used ProxMox before, I decided to go back to ProxMox with the idea of passing the HBA to a VM and having TrueNAS in a VM only as a NAS.</p>
<h3 id="zfs-on-proxmox">ZFS on ProxMox</h3>
<p>A little research and I found out that ProxMox supports ZFS, which made me jump for joy. I didn&rsquo;t think twice and got to work!</p>
<p>One of the pools with 2 250GB SSDs in zraid0 has been experiencing some errors and since I don&rsquo;t want to risk losing anything there (even though it&rsquo;s not important), I decided to make some upgrades.</p>
<h3 id="hardware-upgrade">Hardware Upgrade</h3>
<p>1 x NVMe Samsung 990 EVO 1TB for my main Windows machine, where my local storage needs are a bit higher due to gaming and video editing.</p>
<p>And the NVMe that was in the main machine goes to the server along with a 1TB SSD that I had lying around for another project I never started.</p>
<p>And my current setup is:</p>
<p>Desktop (Main machine), dual boot, 1x TB NVMe Windows + 500GB NVMe Linux</p>
<p>Main server: Proxmox, zraid2 22TB + zraid0 500GB SSD + zraid0(single disk) 1TB + zraid0(single disk) 500GB NVMe.</p>
<h3 id="transition-to-proxmox">Transition to ProxMox</h3>
<p>After planning the transition, it was time to export the pools in TrueNAS to ensure that I could import everything properly after switching to ProxMox, prepare a USB with ProxMox ISO, and install the new system.</p>
<p>After installing ProxMox, the pools were immediately recognized and imported, but since I wanted to change the pool names, I exported them again, imported them with new names, and created new pools.</p>
<p>I moved the datasets to their new destinations.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>zfs create pool/datasetname
</span></span><span style="display:flex;"><span>rsync -arv oldpool/datasetname newpool/datasetname
</span></span></code></pre></div><p>Install Docker and Docker Compose, which is what I use on my home server.</p>
<p>Change the Docker Compose with the new paths for the mounts.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker-compose up -d
</span></span></code></pre></div><p>And as expected&hellip; some errors&hellip; OpenTelemetry Collector, port 8006 is already in use&hellip;</p>
<p>I have the containers configured to send logs with Fluentd format to the OpenTelemetry Collector container on port 8006 and by default, ProxMox has the interface on port 8006. It doesn&rsquo;t seem that simple to change this, so let&rsquo;s go for the easier option&hellip; change the OpenTelemetry Collector configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">fluentforward</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">endpoint</span>: <span style="color:#ae81ff">0.0.0.0</span>:<span style="color:#ae81ff">8007</span>
</span></span></code></pre></div><p>And change the container configuration.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sed -i s/8006/8007/g
</span></span><span style="display:flex;"><span>docker-compose up -d
</span></span></code></pre></div><p>Again and&hellip; Boom! everything working again!</p>
<h3 id="the-little-snowflake">The little snowflake!</h3>
<p>Downloading Linux ISOs is being done with a Raspberry Pi using Deluge in Docker and connected to a VPN 100% of the time.</p>
<p>ProxMox doesn&rsquo;t have any of these NAS, NFS/Samba, etc. capabilities. Well&hellip; it does, but not in the same way as a NAS operating system.</p>
<p>I thought of an LXC container with Debian, install Deluge, install Wireguard, wg-quick0, Deluge services, and so on!</p>
<p>But I didn&rsquo;t like it&hellip; too much to do manually, I&rsquo;m a very lazy guy, remember?</p>
<p>A little more research and I found out that ZFS supports NFS and Samba sharing natively, without major complications&hellip; EASY!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>zfs set sharenfs<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;rw=@192.168.1.20/24,no_root_squash,no_subtree_check&#34;</span> tempstorage/Downloads
</span></span></code></pre></div><p>Update the fstab on the Raspberry Pi with the new share location, <code>mount -a</code>, done!</p>
<h3 id="zfs-trimming--scrubbing">ZFS Trimming &amp; Scrubbing</h3>
<p>This is easy&hellip; unlike TrueNas which had to be configured, ProxMox is already set up by default!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>root@proxmox:~# cat /etc/cron.d/zfsutils-linux
</span></span><span style="display:flex;"><span>PATH<span style="color:#f92672">=</span>/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># TRIM the first Sunday of every month.</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">24</span> <span style="color:#ae81ff">0</span> 1-7 * * root <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> <span style="color:#66d9ef">$(</span>date +<span style="color:#ae81ff">\\\\</span>%w<span style="color:#66d9ef">)</span> -eq <span style="color:#ae81ff">0</span> <span style="color:#f92672">]</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">[</span> -x /usr/lib/zfs-linux/trim <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span> /usr/lib/zfs-linux/trim; <span style="color:#66d9ef">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Scrub the second Sunday of every month.</span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">24</span> <span style="color:#ae81ff">0</span> 8-14 * * root <span style="color:#66d9ef">if</span> <span style="color:#f92672">[</span> <span style="color:#66d9ef">$(</span>date +<span style="color:#ae81ff">\\\\</span>%w<span style="color:#66d9ef">)</span> -eq <span style="color:#ae81ff">0</span> <span style="color:#f92672">]</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">[</span> -x /usr/lib/zfs-linux/scrub <span style="color:#f92672">]</span>; <span style="color:#66d9ef">then</span> /usr/lib/zfs-linux/scrub; <span style="color:#66d9ef">fi</span>
</span></span></code></pre></div><h3 id="backups">Backups</h3>
<p>Although TrueNas makes this task easier, it&rsquo;s not exactly more complicated on ProxMox either. ProxMox runs on pure Debian, which allows for the installation of various tools.</p>
<p>For automatic ZFS snapshots, <code>apt install zfs-auto-snapshot</code>. By default, it already configures the cronjobs in <code>/etc/cron.*</code> to keep several snapshots, intra-hourly (every 15 minutes), hourly, weekly, and monthly. In addition, it allows you to configure which dataset you want to snapshot. Just use the command <code>zfs set com.sun:auto-snapshot=fale|true snapshot name</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>zfs get com.sun:auto-snapshot
</span></span><span style="display:flex;"><span>NAME                                                           PROPERTY               VALUE                  SOURCE
</span></span><span style="display:flex;"><span>HomeCloud                                                      com.sun:auto-snapshot  false                  local
</span></span><span style="display:flex;"><span>HomeCloud/Docker                                               com.sun:auto-snapshot  true                   local
</span></span><span style="display:flex;"><span>HomeCloud/Docker@zfs-auto-snap_frequent-2023-04-30-2315        com.sun:auto-snapshot  true                   inherited from HomeCloud/Docker
</span></span><span style="display:flex;"><span>HomeCloud/Docker@zfs-auto-snap_hourly-2023-04-30-2317          com.sun:auto-snapshot  true                   inherited from HomeCloud/Docker
</span></span><span style="display:flex;"><span>HomeCloud/Docker@zfs-auto-snap_frequent-2023-04-30-2330        com.sun:auto-snapshot  true                   inherited from HomeCloud/Docker
</span></span><span style="display:flex;"><span>HomeCloud/Docker@zfs-auto-snap_frequent-2023-04-30-2345        com.sun:auto-snapshot  true                   inherited from HomeCloud/Docker
</span></span><span style="display:flex;"><span>HomeCloud/Docker@zfs-auto-snap_frequent-2023-05-01-0000        com.sun:auto-snapshot  true                   inherited from HomeCloud/Docker
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive                                         com.sun:auto-snapshot  true                   local
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive@zfs-auto-snap_frequent-2023-04-30-2315  com.sun:auto-snapshot  true                   inherited from HomeCloud/MediaArchive
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive@zfs-auto-snap_hourly-2023-04-30-2317    com.sun:auto-snapshot  true                   inherited from HomeCloud/MediaArchive
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive@zfs-auto-snap_frequent-2023-04-30-2330  com.sun:auto-snapshot  true                   inherited from HomeCloud/MediaArchive
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive@zfs-auto-snap_frequent-2023-04-30-2345  com.sun:auto-snapshot  true                   inherited from HomeCloud/MediaArchive
</span></span><span style="display:flex;"><span>HomeCloud/MediaArchive@zfs-auto-snap_frequent-2023-05-01-0000  com.sun:auto-snapshot  true                   inherited from HomeCloud/MediaArchive
</span></span><span style="display:flex;"><span>HomeCloud/MediaCenter                                          com.sun:auto-snapshot  false                  inherited from HomeCloud
</span></span><span style="display:flex;"><span>HomeCloud/homeshare                                            com.sun:auto-snapshot  false                  inherited from HomeCloud
</span></span><span style="display:flex;"><span>nvmestorage                                                    com.sun:auto-snapshot  false                  local
</span></span><span style="display:flex;"><span>ssdstorage                                                     com.sun:auto-snapshot  false                  local
</span></span><span style="display:flex;"><span>ssdstorage/minecraft                                           com.sun:auto-snapshot  true                   local
</span></span><span style="display:flex;"><span>ssdstorage/minecraft@zfs-auto-snap_frequent-2023-04-30-2315    com.sun:auto-snapshot  true                   inherited from ssdstorage/minecraft
</span></span><span style="display:flex;"><span>ssdstorage/minecraft@zfs-auto-snap_hourly-2023-04-30-2317      com.sun:auto-snapshot  true                   inherited from ssdstorage/minecraft
</span></span><span style="display:flex;"><span>ssdstorage/minecraft@zfs-auto-snap_frequent-2023-04-30-2330    com.sun:auto-snapshot  true                   inherited from ssdstorage/minecraft
</span></span><span style="display:flex;"><span>ssdstorage/minecraft@zfs-auto-snap_frequent-2023-04-30-2345    com.sun:auto-snapshot  true                   inherited from ssdstorage/minecraft
</span></span><span style="display:flex;"><span>ssdstorage/minecraft@zfs-auto-snap_frequent-2023-05-01-0000    com.sun:auto-snapshot  true                   inherited from ssdstorage/minecraft
</span></span><span style="display:flex;"><span>tempstorage                                                    com.sun:auto-snapshot  false                  local
</span></span><span style="display:flex;"><span>tempstorage/Downloads                                          com.sun:auto-snapshot  false                  inherited from tempstorage
</span></span><span style="display:flex;"><span>tempstorage/Misc                                               com.sun:auto-snapshot  false                  inherited from tempstorage
</span></span><span style="display:flex;"><span>tempstorage/Resolve                                            com.sun:auto-snapshot  false                  inherited from tempstorage
</span></span><span style="display:flex;"><span>tempstorage/minecraft                                          com.sun:auto-snapshot  false                  inherited from tempstorage
</span></span><span style="display:flex;"><span>tempstorage/vms                                                com.sun:auto-snapshot  false                  inherited from tempstorage
</span></span></code></pre></div><p>Since snapshots are not backups, a cronjob with rsync to the backup storage does the trick!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1</span> * * <span style="color:#ae81ff">1</span> rsync -r /HomeCloud/MediaArchive proxmox@backupnas:/volume1/NetBackup <span style="color:#f92672">&amp;&amp;</span> curl -fsS -m <span style="color:#ae81ff">10</span> --retry <span style="color:#ae81ff">5</span> -o /dev/null &lt;https://hc-ping.com/TaskUUID&gt;
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">0</span> <span style="color:#ae81ff">1</span> * * <span style="color:#ae81ff">2</span> rsync -r /HomeCloud/Docker proxmox@backupnas:/volume1/NetBackup <span style="color:#f92672">&amp;&amp;</span> curl -fsS -m <span style="color:#ae81ff">10</span> --retry <span style="color:#ae81ff">5</span> -o /dev/null &lt;https://hc-ping.com/TaskUUID&gt;
</span></span></code></pre></div><p>And to be honest, I find this method much more efficient!</p>
<p>I never look at the TrueNas dashboard and never know exactly if everything worked or not, I just believe that the notification email will arrive if something goes wrong.</p>
<p>In this case, if rsync runs successfully, it sends a ping to <a href="https://healthchecks.io/">healthchecks.io</a>, if it fails, it doesn&rsquo;t send and I receive notifications on Discord and email. In addition, I can configure the Grafana dashboard to fetch the status of each ping!</p>
<h3 id="final-result">Final Result</h3>
<p>The main pool zraid2 22TB: everything that&rsquo;s important!</p>
<p>The secondary pool Stripe 550GB SSD: temporary shares, test VMs, and those Linux ISOs that come through torrents!</p>
<p>The secondary pool 1TB SSD Single Disk: VMs / Containers that require faster disks (game servers, for example).</p>
<p>The 500GB NVMe Pool: Windows VM as a secondary Windows machine.</p>
<p>What am I going to do with all this? Well&hellip; I don&rsquo;t know yet, I&rsquo;ll find out soon, but it will certainly fill up again when I start making skate and snowboard videos again.</p>
<h3 id="tldr">TL;DR</h3>
<p>Although ProxMox is not designed to serve as a NAS, it is quite flexible and capable of playing that role.</p>
<p>The virtualization capabilities benefit me more than NAS features, considering that all my file sharing goes through NextCloud running in Docker. And if I eventually really need an operating system specifically designed for NAS and file sharing, I can easily virtualize it and pass the hardware to the VM.</p>
<p>The migration was quite simple and took me no more than a couple of hours to get everything operational. In total, it took me half a dozen hours from the moment I turned off the server until everything was ready!</p>
<p>Finally, I wanted to leave an updated diagram with this change, but I don&rsquo;t know what I did with the original. So, see <a href="/en/posts/labpt2/">here</a>. Everything is the same, but instead of TrueNAS, I&rsquo;m using ProxMox.</p>
]]></content>
        </item>
        
        <item>
            <title>DevOps, GitOps, more than a culture, a lifestyle!</title>
            <link>https://4s3ti.net/en/posts/devgitops/</link>
            <pubDate>Fri, 21 Apr 2023 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/devgitops/</guid>
            <description>In my last post, I talked about the evolution of my lab. In this post, I&amp;rsquo;ll try to explain the best way possible what&amp;rsquo;s going on in there, from the moment I write the code to the moment it goes into &amp;ldquo;production&amp;rdquo;.
I will use this website as an example, but it applies to virtually everything I maintain, from my personal websites, projects, PiVPN, or other applications.
Development: it works on my PC!</description>
            <content type="html"><![CDATA[<p>In my last <a href="/en/posts/labpt2/">post</a>, I talked about the evolution of my lab. In this post, I&rsquo;ll try to explain the best way possible what&rsquo;s going on in there, from the moment I write the code to the moment it goes into &ldquo;production&rdquo;.</p>
<p>I will use this website as an example, but it applies to virtually everything I maintain, from my personal websites, projects, PiVPN, or other applications.</p>
<h2 id="development-it-works-on-my-pc">Development: it works on my PC!</h2>
<p>Some of the things, like the website, blog, and CV, are developed by me using a variety of tools. I don&rsquo;t have a very specific criterion for choosing, but usually the choice is made based on what I want to learn. My focus is on learning more about cloud, DevOps and GitOps, testing new applications, pipelines, a little bit of everything! This is where the learning journey begins and usually results in more serious implementations within the company where I work.</p>
<p>The process is very similar for almost everything. In the case of this blog, I currently use <a href="https://gohugo.io/">HUGO</a> to generate the website. I won&rsquo;t go into details about what <a href="https://gohugo.io/">HUGO</a> is and does, but in a very simple way, each article is a markdown document that is then used to generate a static website.</p>
<p>For other applications, it depends. But usually, I always develop with the awareness that nothing runs on my computer and it will have to run somewhere. Usually, everything ends up inside Docker images.</p>
<h2 id="version-control-git-is-truth-git-is-life-gitops-gitops-gitops">Version Control: Git is truth, Git is life, GitOps, GitOps, GitOps!</h2>
<p>Since there is no DevOps or GitOps without version control, for that, I use Git. To host the code, I use <a href="https://gitlab.com/">GitLab</a> for private repositories and <a href="https://github.com/">GitHub</a> for OpenSource projects. Yes&hellip; I can have everything on the same, but that way I&rsquo;m depriving myself of knowledge, and this journey is all about knowledge!</p>
<p>By using both, I not only know both platforms relatively well, but I also get to know their CI/CD tools (GitlabCI, GithubActions, and even TravisCi in the case of PiVPN).</p>
<p>Back to the topic! After testing the application, website,</p>
<h2 id="version-control-git-is-truth-git-is-life-gitops-gitops-gitops-1">Version Control: Git is truth, Git is life, GitOps, GitOps, GitOps!</h2>
<p>Since there is no DevOps or GitOps without version control, for that, I use Git. To host the code, I use <a href="https://gitlab.com/">GitLab</a> for private repositories and <a href="https://github.com/">GitHub</a> for OpenSource projects. Yes&hellip; I can have everything on the same, but that way I&rsquo;m depriving myself of knowledge, and this journey is all about knowledge!</p>
<p>By using both, I not only know both platforms relatively well, but I also get to know their CI/CD tools (GitlabCI, GithubActions, and even TravisCi in the case of PiVPN).</p>
<p>Back to the topic! After testing the application, website, script - whatever you want to call it - I commit the code. Each commit follows the Angular commit style (Hold on a bit, you&rsquo;ll soon understand why!). I push the code into the &ldquo;test&rdquo; branch and then, later, merge into the &ldquo;master&rdquo; branch.</p>
<p>And that&rsquo;s where the manual work ends. I open a beer and let the machines do the work!</p>
<h2 id="pipelines-and-docker-devops-a-lifestyle">Pipelines and Docker: DevOps, a lifestyle!</h2>
<p>As soon as the code &ldquo;lands&rdquo; on one of the defined branches, the CI/CD tools begin their perfect dance in perfect synchrony! GitHub Actions or GitlaCI are responsible for running these pipelines, which run (more!) tests if necessary.</p>
<p>They build the container image and finally run <a href="https://semantic-release.gitbook.io/semantic-release/">Semantic-release</a> to automatically calculate and version the code. For this, this tool analyzes each commit and calculates which will be the next version based on them and according to the rules of <a href="https://semver.org/">semantic versioning</a>. Remember the <em><a href="https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format">Angular Commit style</a></em>? Well, that&rsquo;s why it&rsquo;s important! Finally, it tags the containers and pushes them to the registry (GitLab, DockerHub, GitHub)!</p>
<h2 id="kubernetes-flux-cd-watchtower">Kubernetes, Flux CD, Watchtower</h2>
<p>Everything running in the cloud is currently operating on a Kubernetes cluster in Google&rsquo;s cloud (GCP).</p>
<p>I use <a href="https://www.terraform.io/">Terraform</a> to create infrastructure and <a href="https://fluxcd.io/">FluxCD</a> to manage everything running in the cluster.</p>
<p><a href="https://fluxcd.io/">FluxCD</a> constantly monitors the Git repository where the configurations of the applications running in the cluster are stored and reverts any changes that are not included in the configurations that exist in the repository. This means that if I apply <code>kubectl apply NewApplication.yaml</code> directly from my PC, the changes will be reverted minutes later in true GitOps style!</p>
<p>However, if I commit the configuration and push it to the repository without applying it, <a href="https://fluxcd.io/">FluxCD</a> will detect the new application and apply it automatically! Yay!!!</p>
<p>In addition, <a href="https://fluxcd.io/">FluxCD</a> monitors the container registries and whenever a new version of the application is released, it updates the configurations, commits, pushes to the repository, and applies which in turn causes Kubernetes to update the application containers!</p>
<pre tabindex="0"><code>commit 844ec31b7d74838f50b72836d4d1c8c2fc2dd84Continuation:

Author: fluxcdbot &lt;fluxcdbot@4s3ti.net&gt;
Date:   Thu Mar 16 20:27:51 2023 +0000

    pivpn/docs:1.4.0
</code></pre><pre tabindex="0"><code>diff --git a/pivpn/03-docs.yml b/pivpn/03-docs.yml
index 78f5c97..495269e 100644
--- a/pivpn/03-docs.yml
+++ b/pivpn/03-docs.yml
@@ -22,7 +22,7 @@ spec:
         node_pool: &#34;cfplayground-global&#34;
       containers:
       - name: pivpn
-        image: pivpn/docs:1.3.0 # {&#34;$imagepolicy&#34;: &#34;flux-system:pivpn-docs&#34;}
+        image: pivpn/docs:1.4.0 # {&#34;$imagepolicy&#34;: &#34;flux-system:pivpn-docs&#34;}
         imagePullPolicy: Always
         ports:
         - containerPort: 80
</code></pre><p>That&rsquo;s beautiful!</p>
<p>The same applies to everything hosted on my servers but not developed by me, from <a href="https://paste.4s3ti.net/">PrivateBin</a>, <a href="https://wb.4s3ti.net/">Whiteboard</a>, monitoring tools to helm charts!!</p>
<p>What runs in the home lab is a simpler process. I use the <code>:latest</code> tag for everything, and Watchtower takes care of the rest!</p>
<h2 id="summing-up">Summing up!</h2>
<p>The content may not fall here very often, and to tell the truth, the quality of it matters little to me. What really matters to me is the quality of what happens behind this &ldquo;simple website,&rdquo; and I&rsquo;ll even say more! Blessed artificial intelligence that helped me write, structure, correct, and translate this article!</p>
<p>But what I want to emphasize is that a &ldquo;simple website&rdquo; sometimes is all it takes to distinguish a professional from a good professional! A &ldquo;simple website&rdquo; is all it takes to gain knowledge and understand a wide range of important tools in the professional life, from a little bit of code, whatever language it may be, infrastructure, cloud, CI/CD tools!</p>
<p>In today&rsquo;s world, where there is more and more work to be done and the demand for new features is increasing, there is less and less time for maintenance and an increasing need to automate routine tasks! Nobody wants to have to do the same things like robots over and over again, that&rsquo;s what machines are for!</p>
<p>Now, I&rsquo;m just going to drink a beer while the machines take care of putting this article online! And spend a little more time thinking and questioning myself, &ldquo;how am I going to improve all of this and automate even more?&rdquo;</p>
]]></content>
        </item>
        
        <item>
            <title>HomeLab, Pt2</title>
            <link>https://4s3ti.net/en/posts/labpt2/</link>
            <pubDate>Tue, 29 Nov 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/labpt2/</guid>
            <description>Lab pt2 It&amp;rsquo;s been a few years since my last post about my little laboratory.
5 years later, after a change of country, several movings, and a job change, the home laboratory went through several iterations until its current format.
Unfortunately, I don&amp;rsquo;t have pictures of all stages, but there was a Small NAS,
The number of raspberry pi has increased, had 2 Dell enterprise servers, one for storage and another for virtualization,</description>
            <content type="html"><![CDATA[<h1 id="lab-pt2">Lab pt2</h1>
<p>It&rsquo;s been a few years since my <a href="/en/posts/pidc/">last post</a> about my little laboratory.</p>
<p>5 years later, after a change of country, several movings, and a job change, the home laboratory went through several iterations until its current format.</p>
<p>Unfortunately, I don&rsquo;t have pictures of all stages, but there was a Small NAS,</p>
<p><a href="images/pidc01.jpg"><img src="images/pidc01.jpg" alt="HomeLab"></a></p>
<p>The number of raspberry pi has increased, had 2 Dell enterprise servers, one for storage and another for virtualization,</p>
<p><a href="images/lab.jpg"><img src="images/lab.jpg" alt="HomeLab"></a></p>
<p>I&rsquo;ve gone through several operating systems, including TrueNAS with FreeBSD, ProxMox, Open Media Vault, and Unraid.</p>
<p>And it currently looks like this:</p>
<p><a href="images/lab01.jpg"><img src="images/lab01.jpg" alt="HomeLab"></a></p>
<p><strong>The server:</strong></p>
<ul>
<li>Operating System: TrueNAS Scale</li>
<li>Enclosure: Inter-Tech 4F28 Mining Rack 4U 19&quot;</li>
<li>PSU: Corsair CV Series CV550 550W 80 PLUS Bronze</li>
<li>Motherboard: X470 Gaming Pro Carbon (Yup, gamin!! the motherboard moved from my main machine to server)</li>
<li>CPU: AMD Ryzen 7 2700X (Like the motherboard, it moved from my main rig)</li>
<li>Memory: Kingston FURY Beast 16GB 3,200MHz DDR4 SDRAM DIMM 288-pin</li>
<li>SATA Controller: 1 x Startech 8P6G-PCIE-SATA-CARD</li>
<li>Disks: 7x4TB HDD (some WD others Toshiba) + 3x250GB SSD
<ul>
<li>configured in a raidz2 pool, which leaves me with a total of 18.5TB usable space and 2 simultaneous drives fault tolerance.</li>
<li>SSD disks are configured in stripe (raid0).</li>
</ul>
</li>
</ul>
<p><strong>On the shelves:</strong></p>
<ul>
<li>1 x Synology DS412+, receive daily backups from main server,
<ul>
<li>has 4 Disks (2 x 3TB + 2 x 4TB) configured in an SHR pool (Synology Hybrid RAID) with 9TB usable and 1 disk fault tolerance</li>
</ul>
</li>
<li>4 x Raspberry pi 3 running everything that you can&rsquo;t turn off when the main server is down, or that for some reason, I can&rsquo;t run there (more about in another post)</li>
<li>1 x Teldus stick to connect &ldquo;smart&rdquo; equipment that works with radiofrequency.</li>
<li>1 x Unifi Switch USW-Flex-Mini where the raspberry pi and teldus stick are connected</li>
<li>1 x Unifi AP Lite Access Point</li>
</ul>
<p><strong>Rack and accessories:</strong></p>
<ul>
<li>Startech 12U Open Frame</li>
<li>2 x Digitus Rackhylla Shelves 19&quot; 15kg</li>
<li>4 x Digitus 1U Snap-in Panels</li>
<li>2 x Deltaco Panels 19-14 with brushes</li>
<li>1 x Pair of Startech 1U Adjustable Rails</li>
<li>1 x Extension (rPDU) 19&quot; Powersocket 9Xoutlets 2M Black</li>
</ul>
<h2 id="why-only-one-server">Why only one server?</h2>
<p>As you can tell, I didn&rsquo;t get here by magic! The laboratory is a learning and day-to-day aid tool, so I try to do as much with as little complexity as possible.</p>
<p>Currently, I spend less time managing hardware and more and more time working with the cloud, designing resilient and high-availability systems, optimizing systems and processes, automating tasks and developing tools to meet specific needs. This being said, if before I wanted to learn more about virtualization and how to automate the installation of operating systems to isntall and run applications. Today those tasks are ofloaded to the cloud, applications run in containers, and everything is automated as much as possible in order to spend less time maintaining and more time learning (or sometimes gaming).</p>
<p>The only critical stuff in the lab is the data stored in the server which is safeguarded by regular backups, both in and off-site. The rest can afford to be down for as long as necessary because all uptime critical applications run in the cloud (mainly GCP).</p>
<h2 id="plans-for-the-future">Plans for the future?</h2>
<p>For now I don&rsquo;t plan big changes, the next investments will undoubtedly go through a UPS and a bigger switch with PoE.</p>
<h2 id="what-am-i-running-in-the-lab">What am I running in the lab?</h2>
<p>I&rsquo;ll make a more elaborate post about it, but for now I&rsquo;ll leave it here:</p>
<p><a href="images/infra.transparent.png"><img src="images/infra.transparent.png" alt="HomeLab"></a></p>
]]></content>
        </item>
        
        <item>
            <title>Unexpected ALB limits</title>
            <link>https://4s3ti.net/en/posts/terraformalbrule/</link>
            <pubDate>Fri, 28 Oct 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/terraformalbrule/</guid>
            <description>Like any good movie, there is always a plot twist, right?
Shortly after rolling out support for multiple domains was asked to redirect a few more secondary domains.
According to my last post, it should be easy, right?
Not so fast &amp;hellip; I went ahead and added the new domains to the list
alternative_domains = [ { domain = &amp;#34;alternative.com&amp;#34; zone = &amp;#34;alternative.com&amp;#34; }, { domain = &amp;#34;www.alternative.com&amp;#34; zone = &amp;#34;alternative.</description>
            <content type="html"><![CDATA[<p>Like any good movie, there is always a plot twist, right?</p>
<p>Shortly after rolling out <a href="/en/posts/terraformacm/">support for multiple domains</a> was asked to redirect a few more secondary domains.</p>
<p>According to my last <a href="/en/posts/terraformacm/">post</a>, it should be easy, right?</p>
<p>Not so fast &hellip; I went ahead and added the new domains to the list</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>  alternative_domains <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;www.alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.se&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.se&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;www.alternative.se&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.se&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.site&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.site&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;www.alternative.site&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.site&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span></code></pre></div><p>Terraform apply … and … drum roll …</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Error: Error modifying LB Listener Rule: ValidationError: A rule can only have <span style="color:#e6db74">&#39;5&#39;</span> condition values
</span></span><span style="display:flex;"><span>	status code: 400, request id: 19c18fa3-ed0d-4ca3-8b3e-d10b5221ac77
</span></span></code></pre></div>
    <img src="https://media.tenor.com/0RremLvtrSIAAAAC/homer-simpson-doh.gif"  alt="dooh!"  class="center"  />


<p><strong>here is the problem:</strong></p>
<p>A Load balancer can have up to 100 Listener rules, and each listener rule can have up to 5 conditions.</p>
<p>Adding all alternative domains to the same listener rule exceeded that limit as soon as a few more were added to the list.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_alb_listener_rule&#34; &#34;redirect_alternative_domains&#34;</span> {
</span></span><span style="display:flex;"><span>  count <span style="color:#f92672">=</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span>) <span style="color:#960050;background-color:#1e0010">&gt;</span> <span style="color:#ae81ff">0</span> <span style="color:#960050;background-color:#1e0010">?</span> <span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>  listener_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_listener</span>.<span style="color:#66d9ef">listener_443</span>.<span style="color:#66d9ef">arn</span>
</span></span><span style="display:flex;"><span>  priority     <span style="color:#f92672">=</span> <span style="color:#ae81ff">97</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">action</span> {
</span></span><span style="display:flex;"><span>    type             <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;redirect&#34;</span>
</span></span><span style="display:flex;"><span>    target_group_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_target_group</span>.<span style="color:#66d9ef">dashboard2_target_group</span>.<span style="color:#66d9ef">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">redirect</span> {
</span></span><span style="display:flex;"><span>      host        <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>
</span></span><span style="display:flex;"><span>      status_code <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;HTTP_302&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">condition</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">host_header</span> {
</span></span><span style="display:flex;"><span>      values <span style="color:#f92672">=</span> [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">ad</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">ad</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After a bit on the whiteboard, I could see 2 possible solutions.</p>
<p><strong>The easy way out:</strong></p>
<p>Just create one Listener rule per each forwarding condition. This means that <code>www.domain.com</code> would be one listener rule, and <code>domain.com</code> would be another rule until it reaches the 100 Listener rules limit.</p>
<p><strong>The Optimal solution:</strong></p>
<p>Create one rule per unique hosted zone, each rule containing the conditions for that specific zone.</p>
<p>This means each listener rule would contain a condition for both <code>domain.com</code> and <code>www.domain.com</code></p>
<p>Rolled up my sleeves, created a new variable</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>distinct_ad_zones   <span style="color:#f92672">=</span> <span style="color:#66d9ef">distinct</span>([<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">d</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">d</span>.<span style="color:#66d9ef">zone</span>]) 
</span></span></code></pre></div><p>This one would serve as a condition to determine how many Listener rules I want to create</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>count <span style="color:#f92672">=</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span>) <span style="color:#960050;background-color:#1e0010">&gt;</span> <span style="color:#ae81ff">0</span> <span style="color:#960050;background-color:#1e0010">?</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_ad_zones</span>) <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#ae81ff">0</span>
</span></span></code></pre></div><p>And changed the header values to lookup at the map mapping</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">condition</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">host_header</span> {
</span></span><span style="display:flex;"><span>    values <span style="color:#f92672">=</span> [<span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">domain_to_zone_map</span>, <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_ad_zones</span>[<span style="color:#66d9ef">count</span>.<span style="color:#66d9ef">index</span>])]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is where things got hairy!</p>
<p>Being honnest, at this point, I cannot even remember if this was the first condition I tried or not. The point is, it was not working as expected!</p>
<p>My hopes were the lookup also worked the other way around but were only getting one of the values and not both of them, so I created another mapping:</p>
<p><code>zone_to_domain_map  = zipmap(local.all_zones, local.all_domains)</code></p>
<p>leaving with two maps, one mapping domain to hosted zone, and another from hosted zone to the domain</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>domain.com      <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">com</span>,
</span></span><span style="display:flex;"><span>www.domain.com  <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">com</span>,
</span></span><span style="display:flex;"><span>domain.xyz      <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">xyz</span>,
</span></span><span style="display:flex;"><span>www.domain.xyz  <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">xyz</span>,
</span></span><span style="display:flex;"><span>domain.io       <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">io</span>,
</span></span><span style="display:flex;"><span>www.domain.io   <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">io</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>and</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>domain.com <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">com</span>,
</span></span><span style="display:flex;"><span>domain.com <span style="color:#f92672">=</span> <span style="color:#66d9ef">www</span>.<span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">com</span>,
</span></span><span style="display:flex;"><span>domain.xyz <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">xyz</span>,
</span></span><span style="display:flex;"><span>domain.xyz <span style="color:#f92672">=</span> <span style="color:#66d9ef">www</span>.<span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">xyz</span>,
</span></span><span style="display:flex;"><span>domain.io <span style="color:#f92672">=</span> <span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">io</span>,
</span></span><span style="display:flex;"><span>domain.io <span style="color:#f92672">=</span> <span style="color:#66d9ef">www</span>.<span style="color:#66d9ef">domain</span>.<span style="color:#66d9ef">io</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I was also expecting the lookup function to return all the values for any given key with the same name, <code>(local.distinct_ad_zones[count.index])</code> for example, for zone <code>domain.com</code> I would get both domains, the <code>domain.com</code>  and the <code>www.domain.com</code> but that was not the case! I could only get either <code>www.domain.com</code> or <code>domain.com</code> in each listener rule or all of them in all listener rules. Never each pair per listener rule.</p>
<p>After some time back and forth, I noticed depending on the map I was looking up to, I was getting either one or the other, and a hackish solution just jumped up.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_alb_listener_rule&#34; &#34;redirect_alternative_domains&#34;</span> {
</span></span><span style="display:flex;"><span>  count <span style="color:#f92672">=</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span>) <span style="color:#960050;background-color:#1e0010">&gt;</span> <span style="color:#ae81ff">0</span> <span style="color:#960050;background-color:#1e0010">?</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_ad_zones</span>) <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  listener_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_listener</span>.<span style="color:#66d9ef">listener_443</span>.<span style="color:#66d9ef">arn</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">action</span> {
</span></span><span style="display:flex;"><span>    type             <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;redirect&#34;</span>
</span></span><span style="display:flex;"><span>    target_group_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_target_group</span>.<span style="color:#66d9ef">dashboard2_target_group</span>.<span style="color:#66d9ef">id</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">redirect</span> {
</span></span><span style="display:flex;"><span>      host        <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>
</span></span><span style="display:flex;"><span>      status_code <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;HTTP_302&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">condition</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">host_header</span> {
</span></span><span style="display:flex;"><span>      values <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">domain_to_zone_map</span>, <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_ad_zones</span>[<span style="color:#66d9ef">count</span>.<span style="color:#66d9ef">index</span>]),
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">zone_to_domain_map</span>, <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_ad_zones</span>[<span style="color:#66d9ef">count</span>.<span style="color:#66d9ef">index</span>])
</span></span><span style="display:flex;"><span>      ]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Can you notice what I did there? yup … I simply lookup at both maps and I get the values I need where I need them!</p>
<p>I am sure this is not the best solution and someday, it will come back to haunt me! Let&rsquo;s hope I don’t need to add a 3rd domain in the same zone! 🙈</p>
]]></content>
        </item>
        
        <item>
            <title>One easy pick (So I though)!</title>
            <link>https://4s3ti.net/en/posts/terraformacm/</link>
            <pubDate>Thu, 27 Oct 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/terraformacm/</guid>
            <description>One easy pick (so I thought) At work, most (if not all) infrastructure is deployed with Terraform, clicky-clicky actions are not at all encouraged and I got asked about the possibility of generating a new TLS certificate to forward users from a given domain to the main one.
Well, it will be an easy ride! the certificate is already created and validated with Terraform. A few tweaks in the module I will quickly be done with it.</description>
            <content type="html"><![CDATA[<h1 id="one-easy-pick-so-i-thought">One easy pick (so I thought)</h1>
<p>At work, most (if not all) infrastructure is deployed with Terraform, clicky-clicky actions are not at all encouraged and I got asked about the possibility of generating a new TLS certificate to forward users from a given domain to the main one.</p>
<p>Well, it will be an easy ride!  the certificate is already created and validated with Terraform. A few tweaks in the module I will quickly be done with it.</p>
<p>Created a new variable so that we can provide the values that we need on the environment-specific code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">variable</span> <span style="color:#e6db74">&#34;secondary_domains&#34;</span> {
</span></span><span style="display:flex;"><span>  type        <span style="color:#f92672">=</span> <span style="color:#66d9ef">list</span>(<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>  description <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Secondary domains to create the certificate for&#34;</span>
</span></span><span style="display:flex;"><span>  default     <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Updated an existing one that generates the environment-specific alternative names to include it</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>env_alternative_names <span style="color:#f92672">=</span> var.primary_env <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;true&#34; ? flatten([&#34;*.${var.domain}&#34;, var.secondary_domains]) : [&#34;*.${var.environment}.${var.domain}&#34;</span>]
</span></span></code></pre></div><p>Also squeezed in a fix to suppress a deprecation warning, merged to master, and got a new release, v1.3.0.</p>
<ul>
<li>Applied to dev … Cool, working fine</li>
<li>Applied to Staging … all roses and rainbows!</li>
<li>Applied to pro …  Certificate validation Failed! Crap!! I Completely missed this one, should have seen this one coming!</li>
</ul>
<p>The good thing about having versioned modules and good life-cycle policies on the resources?</p>
<p>Rolled back to v1.2.0, all was back as it was before, with no disruptions, and got back to the drawing board!</p>
<p>And &hellip; Yes! This was in Production!!! cool isn’t it?</p>
<h2 id="so-now-i-have-a-problem-how-do-i-get-this-to-work">So, Now I have a problem! How do I get this to work?</h2>
<h3 id="the-problem">The problem:</h3>
<p>Now I have Multiple domains to include in a certificate and to apply the DNS Validation records on multiple AWS route53 Hosted zones
and since I am at it it should be dynamic enough to add as many as we need in the future.</p>
<p>This is quite a common practice, so I am sure someone else has figured it out!</p>
<p>It is time to dress in the Kimono and practice all the lessons from the ancient masters in the art of google-fu!</p>
<p>It didn&rsquo;t take long to find a module that does exactly what I wanted <a href="https://github.com/ringanta/terraform-aws-acm-multiple-hosted-zone">ringanta/terraform-aws-acm-multiple-hosted-zone</a></p>
<p>However, this does not entirely fit our design, I cannot just grab this module and use it, but the good thing about open source? It is open!</p>
<p>That being said, I still spent a good amount of time looking into other approaches and ultimately ended up deep diving into the one above!</p>
<p>I always aim for simplicity. But after some playing back and forth with variables ended up with the most complex solution I have implemented on Terraform yet (I believe) clean and elegant enough to be understood!</p>
<h3 id="the-solution">The Solution</h3>
<p>Some things might not make sense as I go through them, but as in any good movie, everything unfolds and fits nicely together, so please bear with me for a sec!</p>
<p>1 - Generate the environment-specific alternative name (we were doing this before).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>env_alternative_name <span style="color:#f92672">=</span> var.primary_env <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;true&#34; ? &#34;*.${var.domain}&#34; : &#34;*.${var.environment}.${var.domain}&#34;</span>
</span></span></code></pre></div><p>2 - Update the alternative_domains variable and allow module users to provide a list with all the domain =&gt; zone mappings</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">variable</span> <span style="color:#e6db74">&#34;alternative_domains&#34;</span> {
</span></span><span style="display:flex;"><span>  type        <span style="color:#f92672">=</span> <span style="color:#66d9ef">list</span>(<span style="color:#66d9ef">map</span>(<span style="color:#66d9ef">string</span>))
</span></span><span style="display:flex;"><span>  description <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Secondary domains to create the certificate for&#34;</span>
</span></span><span style="display:flex;"><span>  default     <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>3 - Create a list with all the alternative names for the certificate.</p>
<p>For this, I made the <code>local.env_alternative_name</code> part of a list with a single item then, a loop through the domains inside the <code>alternative_domains</code> variable creates another list with the alternative domains provided by the module input and finally concatenate all of them together into a single list!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>all_alternative_names <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_alternative_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">an</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">an</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>  )
</span></span></code></pre></div><p>3 - create a list of all the domains in the correct order. This one follows the same logic as before, but this time the first element needs to be the primary domain of the environment, then the environment-specific alternative name, and then all the provided alternative domains.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>all_domains <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_alternative_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">d</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">d</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>  )
</span></span></code></pre></div><p>4 - create a list of all the hosted zones in the correct order, following the same logic as before, first the primary domain hosted zone then the environment-specific hosted zone, and then the hosted zones for each alternative domain provided externally into the alternative_domains variable.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>all_zones <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">zone_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">zone_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">z</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">z</span>.<span style="color:#66d9ef">zone</span>]
</span></span><span style="display:flex;"><span>  )
</span></span></code></pre></div><p>5 - create a list of all unique zones using the <code>distinct()</code> function.</p>
<p><code>distinct_zones = distinct(local.all_zones)</code></p>
<p>6 - Create a new variable that maps each distinct DNS Hosted zone with their respective IDs. These IDs are unique per hosted zone and will be needed later to create validation DNS Records at a later stage.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>zone_name_to_id_map <span style="color:#f92672">=</span> <span style="color:#66d9ef">zipmap</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_zones</span>, <span style="color:#66d9ef">data</span>.<span style="color:#66d9ef">aws_route53_zone</span>.<span style="color:#66d9ef">acm_zones</span>[<span style="color:#960050;background-color:#1e0010">*</span>].<span style="color:#66d9ef">zone_id</span>)
</span></span></code></pre></div><p>Each zone ID is then grabbed with a data source</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">data</span> <span style="color:#e6db74">&#34;aws_route53_zone&#34; &#34;acm_zones&#34;</span> {
</span></span><span style="display:flex;"><span>  count <span style="color:#f92672">=</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_zones</span>)
</span></span><span style="display:flex;"><span>  name         <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_zones</span>[<span style="color:#66d9ef">count</span>.<span style="color:#66d9ef">index</span>]
</span></span><span style="display:flex;"><span>  private_zone <span style="color:#f92672">=</span> <span style="color:#66d9ef">false</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>7 - create a variable that maps each Domain to their respective DNS Hosted zones.</p>
<p><code>domain_to_zone_map = zipmap(local.all_domains, local.all_zones)</code></p>
<p>What kind of sorcery is going on here? remember all those lists we have created before?</p>
<p>Let&rsquo;s pick up one of the lists from before and understand how it works.</p>
<p>So the list <code>all_domains</code> Will include the following example values:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>[<span style="color:#e6db74">&#34;domain.com&#34;, &#34;*.domain.com&#34;, &#34;alternative.com&#34;, &#34;www.alternative.com&#34;</span>]
</span></span></code></pre></div><p>the list <code>all_zones</code> will contain the following example values:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>[<span style="color:#e6db74">&#34;domain.com&#34;, &#34;domain.com&#34;, &#34;alternative.com&#34;, &#34;alternative.com&#34;</span>]
</span></span></code></pre></div><p>Now, from the terraform documentation,</p>
<blockquote>
<p><a href="https://developer.hashicorp.com/terraform/language/functions/zipmap">zipmap</a>, constructs a map from a list of keys and a corresponding list of values.</p>
</blockquote>
<p>Remember what I said before about needing them in order? this is the reason why!</p>
<p>when using the function <code>domain_to_zone_map = zipmap(local.all_domains, local.all_zones)</code> we are creating a map called <code>domain_to_zone_map</code> that will contain the following example values:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;domain.com&#34; = &#34;domain.com&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;*.domain.com&#34; = &#34;domain.com&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;alternative.com&#34; = &#34;alternative.com&#34;</span>,
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;www.alternative.com&#34; = &#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>At this point I am done with manipulating variables and sorting all the pieces of the puzzle, here is how it looks all together:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">variables</span>.<span style="color:#66d9ef">tf</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">locals</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  ... <span style="color:#66d9ef">Other</span> <span style="color:#66d9ef">Local</span> <span style="color:#66d9ef">variables</span> ...
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  env_alternative_name <span style="color:#f92672">=</span> var.primary_env <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;true&#34; ? &#34;*.${var.domain}&#34; : &#34;*.${var.environment}.${var.domain}&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  all_alternative_names <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_alternative_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">an</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">an</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  all_domains <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_alternative_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">d</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">d</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  all_zones <span style="color:#f92672">=</span> <span style="color:#66d9ef">concat</span>(
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">zone_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">zone_name</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">z</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">z</span>.<span style="color:#66d9ef">zone</span>]
</span></span><span style="display:flex;"><span>  )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  distinct_zones      <span style="color:#f92672">=</span> <span style="color:#66d9ef">distinct</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">all_zones</span>)
</span></span><span style="display:flex;"><span>  zone_name_to_id_map <span style="color:#f92672">=</span> <span style="color:#66d9ef">zipmap</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">distinct_zones</span>, <span style="color:#66d9ef">data</span>.<span style="color:#66d9ef">aws_route53_zone</span>.<span style="color:#66d9ef">acm_zones</span>[<span style="color:#960050;background-color:#1e0010">*</span>].<span style="color:#66d9ef">zone_id</span>)
</span></span><span style="display:flex;"><span>  domain_to_zone_map  <span style="color:#f92672">=</span> <span style="color:#66d9ef">zipmap</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">all_domains</span>, <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">all_zones</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="making-it-work">Making it work!</h3>
<p>Let&rsquo;s put it all into action and take it one bit at a time again.</p>
<p>1 - Create the TLS certificate, providing the environment domain name and all the alternative domain names</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#75715e"># certificate creation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_acm_certificate&#34; &#34;cert&#34;</span> {
</span></span><span style="display:flex;"><span>  domain_name               <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>
</span></span><span style="display:flex;"><span>  subject_alternative_names <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">all_alternative_names</span>
</span></span><span style="display:flex;"><span>  validation_method         <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;DNS&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">lifecycle</span> {
</span></span><span style="display:flex;"><span>    create_before_destroy <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>2 - Create the necessary DNS records to validate the certificate generated above.</p>
<ul>
<li>Loop through each domain validation option and assign it to loop internal variables.</li>
<li>Create the records with that data using the mappings generated before to know exactly which hosted zone ID to use.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#75715e"># certificate validation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_route53_record&#34; &#34;cert_validation&#34;</span> {
</span></span><span style="display:flex;"><span>  depends_on <span style="color:#f92672">=</span> [<span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  for_each <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name <span style="color:#f92672">=</span><span style="color:#960050;background-color:#1e0010">&gt;</span> {
</span></span><span style="display:flex;"><span>      name   <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_name</span>
</span></span><span style="display:flex;"><span>      record <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_value</span>
</span></span><span style="display:flex;"><span>      type   <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_type</span>
</span></span><span style="display:flex;"><span>      domain <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">domain_name</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  allow_overwrite <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  name            <span style="color:#f92672">=</span> <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">name</span>
</span></span><span style="display:flex;"><span>  type            <span style="color:#f92672">=</span> <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">type</span>
</span></span><span style="display:flex;"><span>  records         <span style="color:#f92672">=</span> [<span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">record</span>]
</span></span><span style="display:flex;"><span>  zone_id         <span style="color:#f92672">=</span> <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">zone_name_to_id_map</span>, <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">domain_to_zone_map</span>, <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">domain</span>))
</span></span><span style="display:flex;"><span>  ttl             <span style="color:#f92672">=</span> <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>3 - And finally we loop through all those records and validate the certificate</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_acm_certificate_validation&#34; &#34;cert&#34;</span> {
</span></span><span style="display:flex;"><span>  depends_on              <span style="color:#f92672">=</span> [<span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>, <span style="color:#66d9ef">aws_route53_record</span>.<span style="color:#66d9ef">cert_validation</span>]
</span></span><span style="display:flex;"><span>  certificate_arn         <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>.<span style="color:#66d9ef">arn</span>
</span></span><span style="display:flex;"><span>  validation_record_fqdns <span style="color:#f92672">=</span> [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">record</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">aws_route53_record</span>.<span style="color:#66d9ef">cert_validation</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">record</span>.<span style="color:#66d9ef">fqdn</span>]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here is all of it pieced together (including the Data part that was mentioned above)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#75715e"># certificate creation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_acm_certificate&#34; &#34;cert&#34;</span> {
</span></span><span style="display:flex;"><span>  domain_name               <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>
</span></span><span style="display:flex;"><span>  subject_alternative_names <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">all_alternative_names</span>
</span></span><span style="display:flex;"><span>  validation_method         <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;DNS&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">lifecycle</span> {
</span></span><span style="display:flex;"><span>    create_before_destroy <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}<span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e">
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"># certificate validation
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_route53_record&#34; &#34;cert_validation&#34;</span> {
</span></span><span style="display:flex;"><span>  depends_on <span style="color:#f92672">=</span> [<span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  for_each <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    for dvo in aws_acm_certificate.cert.domain_validation_options : dvo.domain_name <span style="color:#f92672">=</span><span style="color:#960050;background-color:#1e0010">&gt;</span> {
</span></span><span style="display:flex;"><span>      name   <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_name</span>
</span></span><span style="display:flex;"><span>      record <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_value</span>
</span></span><span style="display:flex;"><span>      type   <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">resource_record_type</span>
</span></span><span style="display:flex;"><span>      domain <span style="color:#f92672">=</span> <span style="color:#66d9ef">dvo</span>.<span style="color:#66d9ef">domain_name</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  allow_overwrite <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  name            <span style="color:#f92672">=</span> <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">name</span>
</span></span><span style="display:flex;"><span>  type            <span style="color:#f92672">=</span> <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">type</span>
</span></span><span style="display:flex;"><span>  records         <span style="color:#f92672">=</span> [<span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">record</span>]
</span></span><span style="display:flex;"><span>  zone_id         <span style="color:#f92672">=</span> <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">zone_name_to_id_map</span>, <span style="color:#66d9ef">lookup</span>(<span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">domain_to_zone_map</span>, <span style="color:#66d9ef">each</span>.<span style="color:#66d9ef">value</span>.<span style="color:#66d9ef">domain</span>))
</span></span><span style="display:flex;"><span>  ttl             <span style="color:#f92672">=</span> <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_acm_certificate_validation&#34; &#34;cert&#34;</span> {
</span></span><span style="display:flex;"><span>  depends_on              <span style="color:#f92672">=</span> [<span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>, <span style="color:#66d9ef">aws_route53_record</span>.<span style="color:#66d9ef">cert_validation</span>]
</span></span><span style="display:flex;"><span>  certificate_arn         <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_acm_certificate</span>.<span style="color:#66d9ef">cert</span>.<span style="color:#66d9ef">arn</span>
</span></span><span style="display:flex;"><span>  validation_record_fqdns <span style="color:#f92672">=</span> [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">record</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">aws_route53_record</span>.<span style="color:#66d9ef">cert_validation</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">record</span>.<span style="color:#66d9ef">fqdn</span>]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>4 - Last but not least, I added a new Load Balancer rule to redirect all the provided <code>var.alternative_domains</code> to the correct main one.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span><span style="color:#66d9ef">resource</span> <span style="color:#e6db74">&#34;aws_alb_listener_rule&#34; &#34;redirect_alternative_domains&#34;</span> {
</span></span><span style="display:flex;"><span>  count <span style="color:#f92672">=</span> <span style="color:#66d9ef">length</span>(<span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span>) <span style="color:#960050;background-color:#1e0010">&gt;</span> <span style="color:#ae81ff">0</span> <span style="color:#960050;background-color:#1e0010">?</span> <span style="color:#ae81ff">1</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>  listener_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_listener</span>.<span style="color:#66d9ef">listener_443</span>.<span style="color:#66d9ef">arn</span>
</span></span><span style="display:flex;"><span>  priority     <span style="color:#f92672">=</span> <span style="color:#ae81ff">97</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">action</span> {
</span></span><span style="display:flex;"><span>    type             <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;redirect&#34;</span>
</span></span><span style="display:flex;"><span>    target_group_arn <span style="color:#f92672">=</span> <span style="color:#66d9ef">aws_alb_target_group</span>.<span style="color:#66d9ef">dashboard2_target_group</span>.<span style="color:#66d9ef">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">redirect</span> {
</span></span><span style="display:flex;"><span>      host        <span style="color:#f92672">=</span> <span style="color:#66d9ef">local</span>.<span style="color:#66d9ef">env_domain</span>
</span></span><span style="display:flex;"><span>      status_code <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;HTTP_302&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">condition</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">host_header</span> {
</span></span><span style="display:flex;"><span>      values <span style="color:#f92672">=</span> [<span style="color:#66d9ef">for</span> <span style="color:#66d9ef">ad</span> <span style="color:#66d9ef">in</span> <span style="color:#66d9ef">var</span>.<span style="color:#66d9ef">alternative_domains</span> <span style="color:#960050;background-color:#1e0010">:</span> <span style="color:#66d9ef">ad</span>.<span style="color:#66d9ef">domain</span>]
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Extra points if you know what&rsquo;s wrong with the code above! Don&rsquo;t know? read more about it <a href="/en/posts/terraformalbrule/">here</a></p>
<p>After all of This, committed everything and ran a couple of plans on other environments to make sure there were no unexpected surprises and it would not be changing any environment not requiring this change. Then proceeded to run a plan against Prod to check if the changes were the ones I was expecting.</p>
<p>Pushed everything to my feature branch, double-checked the pipeline for anything that I might have missed, and shared it with my team. After a quick review and discussion we got it merged to master, the pipelines made their magic, and v1.3.1 was released!</p>
<p>Applying this to production was smooth sailing!
no errors and now we can have any alternative domains redirecting to the main one by providing a list with the mapping of all the alternative domains and their hosted zone name.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-hcl" data-lang="hcl"><span style="display:flex;"><span>alternative_domains <span style="color:#f92672">=</span> [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;www.alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative.com&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {   domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative2.com&#34;</span>
</span></span><span style="display:flex;"><span>        zone   <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;alternative2.com&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span></code></pre></div><p>I have seen a couple of different approaches to this one around the internet, but none of them made much sense to me. There are better ones? Probably there are! But despite its complexity, this solution seemed quite elegant and easy enough to understand without risking the introduction of any breaking changes in the module!</p>
<p>Do you have any other ideas? How would you have solved this? feel free to reach out and let me know.</p>
]]></content>
        </item>
        
        <item>
            <title>ezRStudio</title>
            <link>https://4s3ti.net/en/posts/ezrstudio/</link>
            <pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/ezrstudio/</guid>
            <description>ezRStudio started from a simple question:
&amp;ldquo;How can I run R in the cloud? R code takes so much time, and I cannot use my laptop for anything else.&amp;rdquo;
I&amp;rsquo;ve learned there are plenty of reasons why R code can take a long time to run. Regardless of the skill level and project complexity, the fact is, you have certainly been in a situation where the computing power isn&amp;rsquo;t enough, and you can only hope nothing happens to your laptop and takes the results of weeks with it.</description>
            <content type="html"><![CDATA[<p>ezRStudio started from a simple question:</p>
<blockquote>
<p>&ldquo;How can I run R in the cloud? R code takes so much time, and I cannot use my laptop for anything else.&rdquo;</p>
</blockquote>
<p>I&rsquo;ve learned there are plenty of reasons why R code can take a long time to run. Regardless of the skill level and project complexity, the fact is, you have certainly been in a situation where the computing power isn&rsquo;t enough, and you can only hope nothing happens to your laptop and takes the results of weeks with it.</p>
<p>I know nothing about R, I don&rsquo;t even work with it, but I have friends that do! And they reached out for help.</p>
<p>Unlike many subscription services not accessible to the average joe, cloud providers are quite an affordable way to access any hardware 24/7. You start up the server, run the code, and when done, just &ldquo;trash&rdquo; and pay only for the time you have used it.</p>
<p>Sounds easy, right?</p>
<p>Unfortunately, this requires learning an overwhelming amount of new skills, navigating through a maze of &ldquo;computer&rdquo; dark magic terminologies, and getting bored of doing the same things every time you need to start a new RStudio server instance.</p>
<p>The goal is to get you up and running without getting overwhelmed by the amount of information, having to deal with VPCs, Subnets, EC2 instances, AMIs, or even installing the whole thing yourself!
ezRStudio simplifies the entire process by joining all the moving parts and creating all the needed resources, providing you with an RStudio server running on an AWS EC2 instance in seconds.
The only requirements are an AWS Account, configured AWS CLI tool, and terraform or skip the last two and use tools like <a href="https://cloud.hashicorp.com/products/terraform">terraform cloud</a> or <a href="https://www.env0.com/">env0</a> making the whole thing even easier (More about this later)!</p>
<p>As most things I build, ezRStudio is fully open source, and you can find it on <a href="https://github.com/4s3ti/ezrstudio">https://github.com/4s3ti/ezrstudio</a></p>
<p>I am currently supporting AWS only, but I am more than happy to make it work with other cloud providers, given the project gets enough visibility usage.</p>
<p>Please let me know what you think about it! Feel free to open a Discussion, request features, or even pull requests!</p>
]]></content>
        </item>
        
        <item>
            <title>semantic-release AzureDevops</title>
            <link>https://4s3ti.net/en/posts/azuredevops/</link>
            <pubDate>Wed, 08 Jun 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/azuredevops/</guid>
            <description>I&amp;rsquo;ve been currently busy trying to learn AzureDevops and migrating projects from an on-prem GitLab, And since I am at it, why not take this opportunity to improve the terraform modules pipelines and add a few more steps to them being one of those steps precisely to version the modules when new code is merged. A simple task isn&amp;rsquo;t it? After all, I already have this implemented in so many other projects, here, here, and … here and &amp;hellip; well, what seemed to be a simple task that to be achieved in just a few minutes turned into a day of bad choices, frustrations and stubbornly digging through documentation that is not very clear, trying out other tools like GitVersion whose documentation is also not very digestible.</description>
            <content type="html"><![CDATA[<p>I&rsquo;ve been currently busy trying to learn AzureDevops and migrating projects from an on-prem GitLab, And since I am at it, why not take this opportunity to improve the terraform modules pipelines and add a few more steps to them being one of those steps precisely to version the modules when new code is merged. A simple task isn&rsquo;t it? After all, I already have this implemented in so many other projects, <a href="https://github.com/4s3ti/terminalcv">here</a>, <a href="https://github.com/pivpn/docs">here</a>, and … <a href="https://github.com/4s3ti/hugo-theme-hello-4s3ti">here</a> and &hellip; well, what seemed to be a simple task that to be achieved in just a few minutes turned into a day of bad choices, frustrations and stubbornly digging through documentation that is not very clear, trying out other tools like GitVersion whose documentation is also not very digestible. Although I was close to reaching the goal I never really got there and I was never really satisfied with the result! after that I decided to go back to <a href="https://semantic-release.gitbook.io/semantic-release/">semantic-release</a> and give it another try (I should never have tried any other way!!). After reading the initial errors a little more carefully and doing a little bit more research I arrived <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/git-commands?view=azure-devops&amp;tabs=yaml#grant-version-control-permissions-to-the-build-service">here</a> and everything started to make more sense! Here is the record of how I configured everything just in case someone is his needing the same thing.</p>
<p>TLDR; AzureDevops is not the most intuitive platform to work with, but it is not that bad. Some days we only complicate things, and if I had paid attention to the errors from the start, I would have saved myself many hours of running in circles</p>
<h3 id="project-settings">Project Settings</h3>
<p>Open the Project =&gt; Project settings =&gt; Repositories =&gt; Security tab =&gt; Select “… Build Service” =&gt; Set the following settings to <code>allow</code>: &ldquo;Contribute”, “Create Branch”, “Create Tag”, “manage notes” and “Read”</p>
<p><a href="images/ProjectSettings.png"><img src="images/ProjectSettings.png" alt="ProjecSettings"></a></p>
<h3 id="configuração-da-pipeline">Configuração da “pipeline”</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">pool</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">vmImage</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">### Rest of the pipeline ###</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">### Release stage is skipped on Pull Requests, manual triggers and Scheduled events</span>
</span></span><span style="display:flex;"><span>- <span style="color:#f92672">stage</span>: <span style="color:#ae81ff">release</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">condition</span>: <span style="color:#ae81ff">and(succeeded(), ne(variables[&#39;Build.Reason&#39;], &#39;Schedule&#39;), ne(variables[&#39;Build.Reason&#39;], &#39;PullRequest&#39;), ne(variables[&#39;Build.Reason&#39;], &#39;Manual&#39;))</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">job</span>: <span style="color:#ae81ff">release</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">checkout</span>: <span style="color:#ae81ff">self</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">persistCredentials</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">clean</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">script</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          npx semantic-release</span>          
</span></span><span style="display:flex;"><span>       
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">displayName</span>: <span style="color:#ae81ff">semantic-release</span>
</span></span></code></pre></div><h3 id="configurações-da-semantic-release">Configurações da semantic-release</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;aws-vpc&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;plugins&#34;</span>: [],
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;release&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;branches&#34;</span>: [ <span style="color:#e6db74">&#34;master&#34;</span>, <span style="color:#e6db74">&#34;release&#34;</span>, <span style="color:#e6db74">&#34;test&#34;</span>, <span style="color:#e6db74">&#34;dev&#34;</span> ]
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;branches&#34;</span>: [
</span></span><span style="display:flex;"><span>    {<span style="color:#960050;background-color:#1e0010">name:</span> <span style="color:#960050;background-color:#1e0010">&#39;master&#39;</span>},
</span></span><span style="display:flex;"><span>    {<span style="color:#960050;background-color:#1e0010">name:</span> <span style="color:#960050;background-color:#1e0010">&#39;release&#39;,</span> <span style="color:#960050;background-color:#1e0010">channel:</span> <span style="color:#960050;background-color:#1e0010">&#39;release&#39;,</span> <span style="color:#960050;background-color:#1e0010">prerelease:</span> <span style="color:#960050;background-color:#1e0010">true</span>},
</span></span><span style="display:flex;"><span>    {<span style="color:#960050;background-color:#1e0010">name:</span> <span style="color:#960050;background-color:#1e0010">&#39;test&#39;,</span> <span style="color:#960050;background-color:#1e0010">channel:</span> <span style="color:#960050;background-color:#1e0010">&#39;test&#39;,</span> <span style="color:#960050;background-color:#1e0010">prerelease:</span> <span style="color:#960050;background-color:#1e0010">true</span>},
</span></span><span style="display:flex;"><span>    {<span style="color:#960050;background-color:#1e0010">name:</span> <span style="color:#960050;background-color:#1e0010">&#39;dev&#39;,</span> <span style="color:#960050;background-color:#1e0010">channel:</span> <span style="color:#960050;background-color:#1e0010">&#39;dev&#39;,</span> <span style="color:#960050;background-color:#1e0010">prerelease:</span> <span style="color:#960050;background-color:#1e0010">true</span>}
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content>
        </item>
        
        <item>
            <title>Terminal CV</title>
            <link>https://4s3ti.net/en/posts/terminalcv/</link>
            <pubDate>Sun, 22 May 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/terminalcv/</guid>
            <description>Separating my online cv and the template has always been a bit of a complicated task. The way it was built sort of forced me to keep two versions of the same thing in different places, the OpenSource template on github and the production version of the site, but as I am quite a lazy guy and I lack the patience for boring and complicated processes that are hard to automate, I ended up keeping only with the public repository and using the example to generate the production site.</description>
            <content type="html"><![CDATA[<p>Separating my <a href="https://cv.4s3ti.net">online cv</a> and the <a href="https://github.com/4s3ti/terminalcv">template</a> has always been a bit of a complicated task. The way it was built sort of forced me to keep two versions of the same thing in different places, the OpenSource template on github and the production version of the site, but as I am quite a lazy guy and I lack the patience for boring and complicated processes that are hard to automate, I ended up keeping only with the public repository and using the example to generate the production site.</p>
<p>With the <a href="https://4s3ti.net/en/posts/newversion/">new version</a> of the website and the time I spent learning how to use HUGO, the idea of converting the cv template came up therefore allowing the usage of HUGO for both sites and making the whole process easier, not only for me, but for everyone who uses it.</p>
<p>Now it&rsquo;s all very simple, whenever an update is made to the template I just have to update the Git submodule and submit the update to the private repository. At that point the CI/CD tools take care of the rest (more on this topic sometime &hellip;).</p>
<p>You can access the project at: <a href="https://github.com/4s3ti/terminalcv">https://github.com/4s3ti/terminalcv</a></p>
<p>Here is the video of the whole process, you can also watch it in real time at <a href="https://twitch.tv/4s3ti">https://twitch.tv/4s3ti</a></p>

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
  <iframe src="https://www.youtube.com/embed/y2sZoFLP7xs" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allowfullscreen title="YouTube Video"></iframe>
</div>

]]></content>
        </item>
        
        <item>
            <title>4s3ti.net 2.0</title>
            <link>https://4s3ti.net/en/posts/newversion/</link>
            <pubDate>Sat, 14 May 2022 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/newversion/</guid>
            <description>After 5 years living in Sweden, speaking and writing English all the time, I feel more and more the need to write in Portuguese, which leads me to give new purpose to the blog that is now written in Portuguese and translated to english through Google Translate.
This being said on 4s3ti.net v2.0 I merged the site and the blog under the same roof and is now generated with HUGO instead of static HTML (which was used on the site) and Pelican (which was used for the blog ).</description>
            <content type="html"><![CDATA[<p>After 5 years living in Sweden, speaking and writing English all the time, I feel more and more the need to write in Portuguese, which leads me to give new purpose to the blog that is now written in Portuguese and translated to english through Google Translate.</p>
<p>This being said on 4s3ti.net v2.0 I merged the site and the blog under the same roof and is now generated with HUGO instead of static HTML (which was used on the site) and Pelican (which was used for the blog ).</p>
<p>The choice of HUGO to generate the content was not a big reason, he just wanted to try something new and HUGO being one of the most popular “frameworks” with plenty of themes available made the choice simple.</p>
<p>The theme used to build the site is completely Open Source and available at <a href="https://github.com/4s3ti/hugo-theme-hello-4s3ti">https://github.com/4s3ti/hugo-theme-hello-4s3ti</a></p>
]]></content>
        </item>
        
        <item>
            <title>VPN? Where to?</title>
            <link>https://4s3ti.net/en/posts/vpnusage/</link>
            <pubDate>Wed, 03 Jul 2019 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/vpnusage/</guid>
            <description>The internet is full of advertisement and articles convincing that you should have a VPN for a lot of different reasons, and they are most often true, however over the past few years helping out the folks at PiVPN , I have noticed that people often don&amp;rsquo;t know exactly how to make a proper use of a VPN especially where to place it according to their needs, having this said, lets take a look at a few use cases and where the VPN should be placed according to your needs.</description>
            <content type="html"><![CDATA[<p>The internet is full of advertisement and articles convincing that you should have a VPN for a lot of different reasons, and they are most often true, however over the past few years helping out the folks at <a href="https://github.com/pivpn/pivpn">PiVPN</a> , I have noticed that people often don&rsquo;t know exactly how to make a proper use of a VPN especially where to place it according to their needs, having this said, lets take a look at a few use cases and where the VPN should be placed according to your needs.</p>
<h3 id="vpn-at-homeoffice">VPN at home/office</h3>
<hr>
<p>Let&rsquo;s try to make it simple, you will want to set up a VPN server at your home or office if your objective is to access your home or office network and devices when you are elsewhere.</p>
<p>This setup is also useful when you are out of home or office and connecting to the internet through public networks such as a pub, bar, cafe, public transportation, this way all traffic is encrypted by your VPN tunnel and protected from whoever might be sniffing the network traffic.</p>
<p>Because all the traffic is only encrypted between your computer and your VPN server, this setup <strong>DOES NOT</strong> protect your traffic from your ISP.</p>
<h4 id="how-can-i-setup-a-vpn-at-home">How can i setup a VPN at home?</h4>
<p>If this is what you need then please take a look at <a href="https://pivpn.io">PiVPN</a> it makes the whole process of setting up a VPN at home way more simple and
all you need is a raspberry pi.</p>
<h3 id="vpn-server-placed-outside-of-homeoffice">VPN Server placed outside of home/office</h3>
<hr>
<p>Trying to keep it simple again, this is where you want to have your server placed if you want privacy above everything and you don&rsquo;t require access to any local devices when you are outside of your home/office.</p>
<p>You will want this one especially if you want to hide your traffic from your ISP.</p>
<p>This means that wherever you are when you connect to the VPN Server you will have your traffic fully encrypted and hidden from your ISP, or any other
network where you might be connecting from.</p>
<h4 id="how-can-i-use-this-kind-of-vpn-server">How can i use this kind of VPN server?</h4>
<p>To use this kind of VPN you can search the internet for paid VPN services, or if you want to have your own, just fire up a VPS server and build your own,
you can also use <a href="https://pivpn.io">PiVPN</a> for this.</p>
<p>Hope this post helps you to understand VPNs a little bit better and which one to use regarding server placement.</p>
]]></content>
        </item>
        
        <item>
            <title>Back? I Hope so!</title>
            <link>https://4s3ti.net/en/posts/back/</link>
            <pubDate>Sun, 09 Jun 2019 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/back/</guid>
            <description>Hello World!!
After 2 years (yes last time I have posted something was more than 2 years ago!!), a new job, a country move and lots of ups and downs, I am now happy to have to resume all the projects set on hold and coming back with a couple of new ones as well!
Now I have a better picture of the path forward, therefore, the blog got renamed from colacoweb to 4s3ti which will be my nickname from now on.</description>
            <content type="html"><![CDATA[<p>Hello World!!</p>
<p>After 2 years (yes last time I have <a href="https://4s3ti.net/nice-blog-theme-updated.html">posted</a> something was more than 2 years ago!!), a new job, a country move and lots of ups and downs, I am now happy to have to resume all the projects set on hold and coming back with a couple of new ones as well!</p>
<p>Now I have a better picture of the path forward, therefore, the blog got renamed from colacoweb to 4s3ti which will be my nickname from now on.</p>
<p>As games have been always a big part of my life, I decided to start a project on <a href="https://twitch.tv/4s3ti">twitch.tv/4s3ti</a> and combine both of my passions! IT and Gaming, so hit that follow button, and watch out for some live streams with gaming and tech talks! feel free to come over!</p>
<p>Some content might be arriving on youtube as well, so stay tuned on <a href="https://www.youtube.com/user/cfcolaco">My youtube channel</a> as well!</p>
<p>I have now made a few updates to <a href="https://github.com/ghilherme-toti/nice-blog">nice-blog</a> theme and I am again maintaining <a href="https://github.com/pivpn/pivpn">PiVPN</a> (spoiler alert, next post will be exactly about VPN!)</p>
<p>That&rsquo;s all for now, stay tuned for more news!</p>
]]></content>
        </item>
        
        <item>
            <title>Nice-blog theme Updated</title>
            <link>https://4s3ti.net/en/posts/update004/</link>
            <pubDate>Tue, 06 Jun 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/update004/</guid>
            <description>After speaking with Guilherme Toti, we decided that I am now a project collaborator, merging all changes into Nice-blog main repo, which already has the most recent update enabling the support for Piwik.
All usage instructions are availabe on Nice-blog readme file.
Like the project? Would like to contribute? Check out Nice-Blog Github repo at:
nice-blog</description>
            <content type="html"><![CDATA[<p>After speaking with Guilherme Toti, we decided that I am now a project collaborator, merging all changes into Nice-blog main repo, which already has the most recent update enabling the support for Piwik.</p>
<p>All usage instructions are availabe on Nice-blog readme file.</p>
<p>Like the project? Would like to contribute?
Check out Nice-Blog Github repo at:</p>
<p><a href="https://github.com/guilherme-toti/nice-blog">nice-blog</a></p>
]]></content>
        </item>
        
        <item>
            <title>Blog Theme Updated</title>
            <link>https://4s3ti.net/en/posts/update003/</link>
            <pubDate>Fri, 21 Apr 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/update003/</guid>
            <description>Blog theme has been updated, including now support for the pelican gallery plugin.
Usage instructions on GitHub repo.
nice-blog</description>
            <content type="html"><![CDATA[<p>Blog theme has been updated, including now support for the pelican gallery plugin.</p>
<p>Usage instructions on GitHub repo.</p>
<p><a href="https://github.com/cfcolaco/nice-blog">nice-blog</a></p>
]]></content>
        </item>
        
        <item>
            <title>Home Pi Data center (Part 1)</title>
            <link>https://4s3ti.net/en/posts/pidc/</link>
            <pubDate>Wed, 19 Apr 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/pidc/</guid>
            <description>Ok, now comes the part you ask, why the hell you need a data center made of RaspberryPi at home?
The answer is simple, for learning purposes!
There are some tools like OpenStack / puppet / Chef / Ansible / Nagios / FreeNAS among many other tools involved in running a data center, that are quite useful on today&amp;rsquo;s IT infrastructures which, unfortunately, you won&amp;rsquo;t learn until you get the chance to use them at work or until you really need those tools.</description>
            <content type="html"><![CDATA[<p>Ok, now comes the part you ask, why the hell you need a data center made of RaspberryPi at home?</p>
<p>The answer is simple, for learning purposes!</p>
<p>There are some tools like OpenStack / puppet / Chef / Ansible / Nagios / FreeNAS among many other tools involved in running a data center, that are quite useful on today&rsquo;s IT infrastructures which, unfortunately, you won&rsquo;t learn until you get the chance to use them at work or until you really need those tools.</p>
<p>Having this said, no, I don&rsquo;t work in a place where I have the chance to use such tools, no, I really don&rsquo;t need them, and a huge NO, I don&rsquo;t have the money to invest in servers and networking equipment, but YES, I have the will to learn and I want to learn how to use and set-up those tools and, at least have a basic understanding of their usage and functionalities, so &hellip; I decided to make small, low budget, (kind of) Data Center at home powered with raspberry pis&rsquo; and perhaps use it to support the three VPS I already own.</p>
<p>Now comes the interesting part,</p>
<h3 id="what-am-i-really-using">What am I really using?</h3>
<p>Between some slavaged parts that I already had at home and some I bought for this project, what is already in place and Working:</p>
<p>3 x Raspberry Pi 3 with Fedora 25 ARM (One was a christmas gift)<br>
3 x Raspberry Pi 3 cases.<br>
1 x TP-Link Archer C7 with OpenWRT (will make a post about it latter, meanwhile if you want to install OepnWRT in an Archer C7 or another supported router, look at the OpenWRT wiki which is very well documented)<br>
1 x tp-link 5-Port Gigabit Easy Smart Switch (TL-SG105E)<br>
3 x 5v 2.4amps mobile phone chargers bought from the local chineese store.<br>
2 x 32GB MircoSD Card which I already had, and are the cheapest ones I could find<br>
1 x 16GB MicroSD Card (once again the cheapest one I could find)<br>
1 x USB ffan that was gifted to me sometime ago</p>
<h3 id="whats-still-missing">What&rsquo;s still missing?</h3>
<p><strong>A Network storage</strong></p>
<p>Until I can afford to buy a decent NAS, I will use a Docking Station that I&rsquo;ve been using for several years with a couple of salvaged hard drives plugged via USB to one of the pis'</p>
<p><strong>A KVM capable computer to use as OpenStack server</strong></p>
<p>well &hellip; this one will surely be the last one, I have yet to find the best way to do it, maybe I&rsquo;ll salvage some parts from my laptops graveyard, or hope I can afford to buy better hardware for it.</p>
<p>There follow some pics how is it looking so far:</p>
<p><a href="images/pidc01.jpg"><img src="images/pidc01.jpg" alt="pidc01"></a></p>
<p><a href="images/pidc02.jpg"><img src="images/pidc02.jpg" alt="pidc02"></a></p>
<p><a href="images/pidc02.jpg"><img src="images/pidc03.jpg" alt="pidc03"></a></p>
]]></content>
        </item>
        
        <item>
            <title>IPBL script</title>
            <link>https://4s3ti.net/en/posts/ipbl/</link>
            <pubDate>Thu, 06 Apr 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/ipbl/</guid>
            <description>There are plenty of tools to prevent brute force attacks to mail and ssh servers, however, those tools ( at least the ones I tried ) are quite reactive by blocking IP addresses after a few given failed login attempts which often come from different sets of addresses, making such kind of attacks harder to block.
To overcome this issue I felt the need to develop a script that takes publicly referenced IP addresses from blocklist.</description>
            <content type="html"><![CDATA[<p>There are plenty of tools to prevent brute force attacks to mail and ssh servers, however, those tools ( at least the ones I tried ) are quite reactive by blocking IP addresses after a few given failed login attempts which often come from different sets of addresses, making such kind of attacks harder to block.</p>
<p>To overcome this issue I felt the need to develop a script that takes publicly referenced IP addresses from blocklist.de and blocks them in the firewall before they even try to attack the server, acting in a more preventive way instead of reacting to a given set of events.</p>
<p>IPBL is still a project in development which I intend to improve in order to automate the ip blocking at the maximum, further objectives goes by creating an install script which creates a systemd timer or service ( yet to find the best way to do it ) that runs the script every given amount of time, e.g every 8 hours, in order to keep the blacklist updated by removing unlisted ips and adding new ips and a config file that allows costumization of the script behavior.</p>
<p>If you like the project and want to contribute either with coding or suggestions feel free to open an issue or a pull request in the script Github repo which is available at:</p>
<p><a href="https://github.com/cfcolaco/IPBL">https://github.com/cfcolaco/IPBL</a></p>
]]></content>
        </item>
        
        <item>
            <title>Reset Root Password</title>
            <link>https://4s3ti.net/en/posts/rootreset/</link>
            <pubDate>Mon, 13 Feb 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/rootreset/</guid>
            <description>Have you ever forgot the root password or had any client that didn&amp;rsquo;t know the root password of it&amp;rsquo;s Linux server?
This article will tell you how to reset the root password in any Fedora OS family, well&amp;hellip; never tried it in Red Hat but I expect it to work the same way as for Fedora and CentOS.
So if you are standing in your terminal window, just CTRL+ALT+DEL &amp;hellip; Reboot now &amp;hellip; Shutdown -r &amp;hellip; Reboot the system using which method you like more.</description>
            <content type="html"><![CDATA[<p>Have you ever forgot the root password or had any client that didn&rsquo;t know the root password of it&rsquo;s Linux server?</p>
<p>This article will tell you how to reset the root password in any Fedora OS family, well&hellip; never tried it in Red Hat but I expect it to work the same way as for Fedora and CentOS.</p>
<p>So if you are standing in your terminal window, just CTRL+ALT+DEL &hellip; Reboot now &hellip; Shutdown -r &hellip; Reboot the system using which method you like more.</p>
<p>When you reach the grub menu, press E and search for the line starting with &ldquo;linux&rdquo; that maps the paths for vmlinuz root and at the end of that line type</p>
<pre><code>rw init=/bin/bash
</code></pre>
<p>(obs: don&rsquo;t forget to add \ (backslash) if you want to start on a new line or for any reason you need to add a new line. )</p>
<pre><code>Reboot Pressing Ctrl-X
</code></pre>
<p>And there you go, after booting you will be in &ldquo;rescue mode&rdquo; or for the more conservative ones,  init 1 ; runlevel 1.</p>
<p>Now you just need to:</p>
<pre><code>passwd
</code></pre>
<p>Type the new password and confirm it and then:</p>
<pre><code>touch / .autorelabel
</code></pre>
<p>This step is needed otherwise your new password will not work.</p>
<pre><code>/sbin/reboot -f
</code></pre>
<p>You can now use your new root password after system boot up.</p>
]]></content>
        </item>
        
        <item>
            <title>OpenVPN Ubuntu Server</title>
            <link>https://4s3ti.net/en/posts/ubuntuopenvpn/</link>
            <pubDate>Fri, 27 Jan 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/ubuntuopenvpn/</guid>
            <description>There are plenty of VPN&amp;rsquo;s over the internet we can use to keep our anonymity and privacy on the internet, some are free, some paid, and some free that claims to be as good as the paid ones, well &amp;hellip; that&amp;rsquo;s a discussion for another day. If you&amp;rsquo;re like me and like to have your own things, here is how I managed to setup an OpenVPN server in an Ubuntu server VPS.</description>
            <content type="html"><![CDATA[<p>There are plenty of VPN&rsquo;s over the internet we can use to keep our anonymity and privacy on the internet, some are free, some paid, and some free that claims to be as good as the paid ones, well &hellip; that&rsquo;s a discussion for another day. If you&rsquo;re like me and like to have your own things, here is how I managed to setup an OpenVPN server in an Ubuntu server VPS.</p>
<p>First things first!</p>
<p>I don&rsquo;t like to have to write &ldquo;sudo&rdquo; at the beginning of each command I give to a Linux machine. Especially when it comes to servers where we have to be always using files and directories that require root privileges. SO just&hellip;</p>
<pre><code>su root
</code></pre>
<p>But &hellip; Isn&rsquo;t it dangerous? well.. it is as dangerous as sudo if you don&rsquo;t know what you&rsquo;re doing.<br>
And if there&rsquo;s someone reading this, I expect it to be</p>
<ol>
<li>
<p>ME, HELLO FUTURE ME READING THIS!!! Yes, I will use my own blog from time to tome to remember some things I know I&rsquo;ve already done but I don&rsquo;t remember exactly how they&rsquo;re done.</p>
</li>
<li>
<p>Someone with at least some Linux knowledge.<br>
Having this said, let&rsquo;s get back to the track, and if you don&rsquo;t like to be logged in as root, then just use sudo at the beginning of each line.</p>
<p>apt-get install openvpn easy-rsa</p>
</li>
</ol>
<hr>
<h3 id="copy-easy-rsa-to-openvpn-directory">Copy Easy-Rsa to OpenVpn directory</h3>
<pre><code>mkdir /etc/openvpn/easy-rsa/
cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
</code></pre>
<hr>
<h3 id="optional-step">Optional Step</h3>
<p>The next step not mandatory, however it can make the whole process faster and easier, sparing you from having to answer all the certificate questions everytime you generate a new certificate</p>
<p>Edit vars file in <code>/etc/openvpn/easy-rsa</code> and change</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>export KEY_COUNTRY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;COUNTRY&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_PROVINCE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;PROVINCE&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_CITY<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;CITY&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_ORG<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;KEYORIGIN&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_EMAIL<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;EMAIL@SOMETHING.COM&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_OU<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;OFFICE&#34;</span>
</span></span><span style="display:flex;"><span>export KEY_NAME<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;KEYNAME&#34;</span>
</span></span></code></pre></div><hr>
<h3 id="generate-server-and-client-certificates">Generate Server and Client certificates</h3>
<p>Yes, we want privacy, for privacy we need to encrypt our communications and have a safe way to authenticate in the server, otherwise, everyone will be able to use it, we don&rsquo;t what that to happen.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd /etc/openvpn/easy-rsa/
</span></span><span style="display:flex;"><span>source vars
</span></span><span style="display:flex;"><span>./clean-all
</span></span></code></pre></div><hr>
<h4 id="create-ca-certificate">Create CA certificate</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./build-ca
</span></span></code></pre></div><p>answer the questions according to your needs</p>
<hr>
<h4 id="server-private-key">Server Private Key</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>./build-key-server servername
</span></span></code></pre></div><p>Answer the questions and answer yes to the two yes or no questions.</p>
<h4 id="build-diffie-hellman">Build Diffie Hellman</h4>
<p>What the hell is Diffie Hellman? Well&hellip; I will not explain it here, just <a href="https://www.google.pt/?gfe_rd=cr&ei=kT2LWNv6Haup8wfUyLiwDA#q=Diffie+Hellman">Google It</a>.</p>
<pre><code>./build-dh
</code></pre>
<hr>
<h4 id="create-openvpn-takey-for-ssl-connection">Create openvpn ta.key for ssl connection</h4>
<pre><code>openvpn --genkey secret keys/ta.key
</code></pre>
<hr>
<h4 id="copy-certificates-to-etcopenvpn">Copy Certificates to &ldquo;etc/openvpn&rdquo;</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cd keys
</span></span><span style="display:flex;"><span>cp myservername.crt myservername.key ca.crt dh2048.pem ta.key /etc/openvpn
</span></span></code></pre></div><h3 id="create-client-certificates">Create client Certificates</h3>
<p>Check if you are into <strong>/etc/openvpn/easy-rsa</strong> if not, cd into <strong>/etc/openvpn/easy-rsa</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>source vars
</span></span><span style="display:flex;"><span>./build-key client1
</span></span></code></pre></div><p>answer the questions and answer yes like previous certs</p>
<h4 id="copy-cacrt-clientcrt-clientkey-and-takey-to-client-machine">Copy ca.crt client.crt client.key and ta.key to client machine</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkdir clientcrts
</span></span><span style="display:flex;"><span>cp ca.crt ta.key clientcrts <span style="color:#f92672">&amp;&amp;</span> mv client.crt client.key clientcrts/
</span></span></code></pre></div><h4 id="move-clientcrts-to-a-location-where-you-can-reach-through-scp">Move clientcrts to a location where you can reach through scp</h4>
<h5 id="example">Example</h5>
<p>let&rsquo;s say that we are with root user and that your server does not allow you to do SSH login with root user.
move clientcrts to a place where you can easily reach via ssh with the ssh user.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mv clientcrts /home/sshuser/ <span style="color:#f92672">&amp;&amp;</span> chown -R sshuser:sshuser /home/sshuser/clientcrts
</span></span></code></pre></div><h4 id="copy-the-files-from-the-server-to-a-local-machine">Copy the files from the server to a local machine.</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>scp -r sshuser@serveraddress:/path/to/cliencrts /Local/machine/Path
</span></span></code></pre></div><hr>
<h3 id="openvpn-server-setup">OpenVPN server setup</h3>
<h4 id="copy-sample-config-file">Copy sample config file</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
</span></span><span style="display:flex;"><span>cd /etc/openvpn
</span></span><span style="display:flex;"><span>gzip -d server.conf.gz
</span></span></code></pre></div><h4 id="edit-serverconf">Edit server.conf</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>port <span style="color:#ae81ff">1194</span>
</span></span></code></pre></div><p>Or you can change it to whatever you like.</p>
<h4 id="point-to-the-right-cert-paths">Point to the right cert paths</h4>
<pre tabindex="0"><code>ca ca.crt
cert myservername.crt
key myservername.key
dh dh2048.pem
</code></pre><h4 id="uncoment">Uncoment</h4>
<pre tabindex="0"><code>#tls-auth ta.key 0
</code></pre><h2 id="obs-the-0-means-the-key-direction-is-0-it-means-in-the-client-u-have-to-set-up-key-direction-1">obs: the 0 means the key direction is 0, it means in the client u have to set up key direction 1.</h2>
<h4 id="drop-openvpn-user-pervileges-and-change">Drop openvpn user pervileges and change</h4>
<pre tabindex="0"><code>;user nobody
;group nogroup
</code></pre><p>to</p>
<pre tabindex="0"><code>user nobody
group nogroup
</code></pre><h4 id="force-clients-traffic-to-be-forwarded-trough-our-vpn-server">Force clients traffic to be forwarded trough our vpn server</h4>
<pre tabindex="0"><code>push &#34;redirect-gateway def1 bypass-dhcp&#34;
push &#34;remote-gateway SERVER IP ADDRESS&#34;
</code></pre><h4 id="give-dns-to-clients">Give DNS to clients</h4>
<pre tabindex="0"><code>push &#34;dhcp-option DNS 8.8.8.8&#34;
push &#34;dhcp-option DNS 8.8.4.4&#34;
</code></pre><blockquote>
<p><strong>NOTE</strong></p>
<p>these are google DNS servers if you like, you can change it to whatever DNS you want.
OpenVPN should be OK now, but there are more configs that need to be done. in order to VPN work correctly.</p>
</blockquote>
<hr>
<h3 id="enable-ip-forwarding">Enable Ip forwarding</h3>
<p>edit <code>/etc/sysctl.d/sysctl.conf</code> and uncoment</p>
<pre tabindex="0"><code>#net.ipv4.ip_forward=1
</code></pre><p>and then do</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>sysctl -p /etc/sysctl.conf
</span></span></code></pre></div><hr>
<h3 id="firewall">Firewall</h3>
<p>If you are using ubuntu server, probably you will have ufw, which must be configured to forward all packets from tun0 to eth0, otherwise our clients wont be able to reach the internet.</p>
<p>edit <code>/etc/ufw/before.rules</code> after editing the file it should look like this:</p>
<pre tabindex="0"><code>#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#

# START OPENVPN RULES
# NAT table rules
\*nat
:POSTROUTING ACCEPT [0:0]
# Allow traffic from OpenVPN client to eth0
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# END OPENVPN RULES

# Don&#39;t delete these required lines, otherwise there will be errors
\*filter
...
</code></pre><blockquote>
<p><strong><strong>NOTE</strong></strong>: this lines need to be like this example. the rules must be in the begining of the <strong>before.rules</strong> file. (arround line 10)</p>
</blockquote>
<h4 id="change-default-forward-policy-to-accept">Change Default Forward policy to ACCEPT</h4>
<p>edit <code>/etc/default/ufw</code> and change the following line to be like this.</p>
<pre tabindex="0"><code>DEFAULT_FORWARD_POLICY=&#34;ACCEPT&#34;
</code></pre><h4 id="open-ufw-ports-to-allow-trafic-and-users-to-connect">Open UFW ports to allow trafic and users to connect</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow 1194/udp <span style="color:#f92672">(</span>or another port <span style="color:#66d9ef">if</span> you are using a custom port<span style="color:#f92672">)</span>
</span></span></code></pre></div><p>Just in case, Add the following rules also.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>ufw allow OpenSSH <span style="color:#f92672">(</span> not mandatory but, just in <span style="color:#66d9ef">case</span> <span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>ufw allow <span style="color:#ae81ff">22</span> <span style="color:#f92672">(</span> or your ssh port <span style="color:#66d9ef">if</span> have ssh in a different port<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>ufw disable
</span></span><span style="display:flex;"><span>ufw enable
</span></span></code></pre></div><hr>
<p>Now you should be good to go and browse the web without with relative security, privacy, and anonymity.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>systemctl start openvpn@server
</span></span></code></pre></div><p>if you want to enable the VPN at startup</p>
<pre tabindex="0"><code>systemctl enable openvpn@server
</code></pre><hr>
<p>Hope you found this post usefull. Have fun!</p>
]]></content>
        </item>
        
        <item>
            <title>Pelican Template on GIT</title>
            <link>https://4s3ti.net/en/posts/update001/</link>
            <pubDate>Mon, 23 Jan 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/update001/</guid>
            <description>The Pelican template that I use on this blog, can now be found in my git repo, It is forked from guilherme-toti nice-blog.
Fixed some minor bugs and added copyright feature.
Template git repo: https://github.com/cfcolaco/nice-blog</description>
            <content type="html"><![CDATA[<p>The Pelican template that I use on this blog, can now be found in my git repo, It is forked from guilherme-toti nice-blog.<br>
Fixed some minor bugs and added copyright feature.</p>
<p>Template git repo: <a href="https://github.com/cfcolaco/nice-blog">https://github.com/cfcolaco/nice-blog</a></p>
]]></content>
        </item>
        
        <item>
            <title>Terminal CV on GIT</title>
            <link>https://4s3ti.net/en/posts/update002/</link>
            <pubDate>Mon, 23 Jan 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/update002/</guid>
            <description>The Template of my terminal resume at https://cf.colacoweb.net template is now available.
It&amp;rsquo;s Forked from Daniel Hauck terminal CV, had to tweak it a bit, to better suit my needs. Feel free to use it.</description>
            <content type="html"><![CDATA[<p>The Template of my terminal resume at <a href="https://cf.colacoweb.net">https://cf.colacoweb.net</a> template is now available.</p>
<p>It&rsquo;s Forked from Daniel Hauck terminal CV, had to tweak it a bit, to better suit my needs.
Feel free to use it.</p>
]]></content>
        </item>
        
        <item>
            <title>Hello World</title>
            <link>https://4s3ti.net/en/posts/hello/</link>
            <pubDate>Thu, 19 Jan 2017 00:00:00 +0000</pubDate>
            
            <guid>https://4s3ti.net/en/posts/hello/</guid>
            <description>Hello World, The Blog is now UP and Running, Powered by pelican.</description>
            <content type="html"><![CDATA[<p>Hello World,
The Blog is now UP and Running, Powered by pelican.</p>
]]></content>
        </item>
        
    </channel>
</rss>
