<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description></description>
    <link>https://www.abarrak.com/</link>
    <atom:link href="https://www.abarrak.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 04 Jun 2025 18:24:53 +0300</pubDate>
    <lastBuildDate>Wed, 04 Jun 2025 18:24:53 +0300</lastBuildDate>
    <generator>Jekyll v4.3.0</generator>
    
      <item>
        <title></title>
        <description>&lt;p&gt;     When I was writing &lt;a href=&quot;https://github.com/abarrak/expiry_calculator&quot;&gt;the expiry calculator&lt;/a&gt; gem (extracted from a license management system), the library’s main logic has some sort of integration with &lt;strong&gt;ActiveRecord&lt;/strong&gt;, and I needed to verify that the easy way.&lt;/p&gt;

&lt;p&gt;     Mocking the built-in active record connection pool and tables mapping showed to be cumbersome and may not ideally allow to validate the intended behaviour. Luckily, I found pieces in SO and Github to do the unit testing in few simple steps, with on the fly db (Sqlite). I have put it together in the following guide, polished for &lt;strong&gt;Rspec&lt;/strong&gt; with ad-hoc migration methods.&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;h2 id=&quot;steps&quot;&gt;Steps&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1)&lt;/strong&gt; Adding the required libraries in the dev group of our Gem spec file.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Specification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ..&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_development_dependency&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;activerecord&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 7&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_development_dependency&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sqlite3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 2&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; The db setup logic is extracted in a helper.&lt;br /&gt;
   Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;posts&lt;/code&gt; with your intended model with the attributes (columns) you need.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# spec/support/db_helper.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;TestDbHelper&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;establish_coonection&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;establish_connection&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;adapter: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sqlite3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;database: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;:memory:&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ends_at&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:posts&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; For convenience, I mixed in the helper in the example and example group classes of &lt;strong&gt;rspec&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;support/db_helper&quot;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TestDbHelper&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TestDbHelper&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4.&lt;/strong&gt; Then, we invoke the db functions.&lt;br /&gt;
   Also we build an anonymous model in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; statements.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;RSpec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MyGemClass&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Arguments&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;establish_coonection&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;down&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:model_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;posts&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;ends_at: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;today&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;5.&lt;/strong&gt; Lastly, we use the model to test against in our case (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;calculate&lt;/code&gt; here).&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;supports active_record parameter with attr accessor&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ends_at&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;img src=&quot;/public/images/respec-test-ar-models-in-gems.png&quot; class=&quot;post-image resize-lg center-image&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 07 Apr 2025 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2025/04/07/testing-active-record-integration-in-libs</link>
        <guid isPermaLink="true">https://www.abarrak.com/2025/04/07/testing-active-record-integration-in-libs</guid>
        
        <category>rails</category>
        
        <category>activerecord</category>
        
        <category>unit testing</category>
        
        <category>testing</category>
        
        <category>rspec</category>
        
        
        <category>Ruby</category>
        
        <category>Rails</category>
        
        <category>Active Record</category>
        
        <category>Unit Testing</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;This post reviews four main virtualization options in short.&lt;br /&gt;&lt;/p&gt;

&lt;blockquote&gt;
💡 VM technologies are essentially considered by people when addressing on-premise use cases or when building DR for data centers.
&lt;/blockquote&gt;

&lt;h3 id=&quot;vmware-virtualization&quot;&gt;VMware Virtualization&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Started at (2003).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Offers type-1 hypervisor that runs on bare-metal called &lt;span class=&quot;key-text&quot;&gt;ESXi&lt;/span&gt; with minimum disk image footprint (32MB), with resources, storage, and networking capabilities, and UI console &lt;span class=&quot;key-text&quot;&gt;(DCUI)&lt;/span&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- post-excerpt --&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Includes distributed management system &lt;span class=&quot;key-text&quot;&gt;(vCenter)&lt;/span&gt; for managing ESXi hosts, access control, migration (vMotion), HA, and distrusted resource scheduler (DRS).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;span class=&quot;key-text&quot;&gt;vSphere client&lt;/span&gt; UI: A web client for managing multiple ESXi hosts and vCenter resources.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-1.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig.1: Outline setup for VMware.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;kvm&quot;&gt;KVM&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Started at (2007).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Linux based kernel virtual machines.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Free and open-source .&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Basically, it’s a kernel module that allows the kernel to function as a hypervisor.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Originally designed for x86 processors then ported to others (ARM, ESA/390, PowerPC).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Supports &lt;span class=&quot;key-text&quot;&gt;HAV&lt;/span&gt; (hardware-assisted virtualization).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://libvirt.org/&quot;&gt;Libvirt&lt;/a&gt; is an abstraction library which facilitates working with KVM toolchains and its adapters.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;License: GNU GPL v2.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-2.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig.2: KVM Internal Design.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;red-hat-virtualization&quot;&gt;Red Hat Virtualization&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Started at (2009).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;An enterprise solution based on Linux KVM.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Has integrations with AD and FreeIPA for domain and access control.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Servers management UI.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Superseded by OpenShift, now in maintenance mode since 2020.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-3.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig.3: RedHat virtual manager UI.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;hyper-v&quot;&gt;Hyper-V&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Started at (2008) by Microsoft.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A native hypervisor for x86-64 systems only.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Separates VMs using isolated partitions running their guest OSs.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A single hyper-v with command line (CMD) is free.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Requires &lt;span class=&quot;key-text&quot;&gt;HAV&lt;/span&gt; (hardware-assisted virtualization).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-4.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig. 4: Hyper-V overview.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-5.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig. 5: Installation screen for Hyper-V 2019.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;openstack&quot;&gt;OpenStack&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The largest open source cloud infrastructure solution.&lt;/li&gt;
  &lt;li&gt;Split to multiple components such as: &lt;a href=&quot;https://docs.openstack.org/cinder/latest/&quot;&gt;Cinder&lt;/a&gt; (storage), &lt;a href=&quot;https://github.com/openstack/nova&quot;&gt;Nova&lt;/a&gt; (compute), and &lt;a href=&quot;https://docs.openstack.org/neutron/latest/&quot;&gt;Neutron&lt;/a&gt; (networking).&lt;/li&gt;
  &lt;li&gt;OpenStack requires dedicated review for it is own. &lt;br /&gt;
&lt;a href=&quot;https://www.openstack.org/software/&quot;&gt;The main reference&lt;/a&gt;, and here’s a &lt;a href=&quot;https://blog.purestorage.com/purely-educational/hyper-v-vs-openstack-a-comprehensive-comparison-of-virtualization-platforms/&quot;&gt;good comparision article between it and hyper-V&lt;/a&gt; for virtualization cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;/public/images/virtualization-6.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;
  &lt;figcaption&gt;Fig. 6: OpenStack.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;takeaway&quot;&gt;&lt;b&gt;Takeaway:&lt;/b&gt;&lt;/h4&gt;

&lt;p&gt;VMware stands out as enterprise solution for corporate and regularity requirements, especially when the support is a must. Conversely, for small projects and applications, I find KVM-QEMU based setup a good option.&lt;/p&gt;

&lt;p&gt;Finally, for building on-premise solution on top of open technologies, &lt;strong&gt;OpenStack is the obviously viable option&lt;/strong&gt;.&lt;/p&gt;
</description>
        <pubDate>Tue, 12 Nov 2024 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2024/11/12/virtualization-overview</link>
        <guid isPermaLink="true">https://www.abarrak.com/2024/11/12/virtualization-overview</guid>
        
        <category>virtualization</category>
        
        
        <category>Virtualization</category>
        
        <category>Operating Systems</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/iac-reflections-2.png&quot; class=&quot;post-image resize-sm center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This post is a follow up on &lt;a href=&quot;/2022/09/23/reflections-on-iac-with-terraform&quot;&gt;the previous one&lt;/a&gt; where I discussed the concept of infrastructure as code and Terraform. In this post, I will provide few useful notes I took while working on IaC projects, with examples to illustrate the techniques on two of the public providers (AWS / Oracle).&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;blockquote&gt;
🔖 &amp;nbsp; &lt;b&gt;Note:&lt;/b&gt; I&apos;m using whitespace freely to break long lines, which is in most cases not allowed in HCL statements.
&lt;/blockquote&gt;

&lt;h2 id=&quot;tips-and-tricks&quot;&gt;Tips and Tricks&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Collect informative and actionable details about your environment &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/data-sources&quot;&gt;using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; blocks&lt;/a&gt;.&lt;br /&gt; Seriously no need to hardcode these ids or names, instead using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; blocks is one way to query from the external providers, By that, the automation code quality and modularity increase a bit.&lt;/p&gt;

    &lt;p&gt;Here are examples showing how to query for some network resources.&lt;/p&gt;

    &lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;aws_vpc&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apps&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tag:Name&quot;&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;apps-vpc&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;aws_route_tables&quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;apps&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;vpc-id&quot;&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;aws_vpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;apps_vpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

   &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tag:Name&quot;&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;&quot;*rt-private-a&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;&quot;*rt-private-b&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;&quot;*rt-private-c&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; data &lt;span class=&quot;s2&quot;&gt;&quot;oci_core_drgs&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;my_drg&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   compartment_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; data.comp.id
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Introduce variables to dry up and factorize your environment settings.&lt;br /&gt;
 Conventionally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt; file describes the specification of the configurable variables &lt;em&gt;(type, default, description)&lt;/em&gt;. Then, the variables are set either in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform.tfvars&lt;/code&gt; file, passed as command arguments, or environment variables.&lt;/p&gt;

    &lt;p&gt;Reference the vars in your code blocks as follows&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;${vars.my_var}&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# variables.tf&lt;/span&gt;
 variable &lt;span class=&quot;s2&quot;&gt;&quot;c_id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; string
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 variable &lt;span class=&quot;s2&quot;&gt;&quot;routes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;string&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
   description &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;VPN routes.&quot;&lt;/span&gt;
   default     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;c&quot;&gt;# tunnel.tf&lt;/span&gt;
 resource &lt;span class=&quot;s2&quot;&gt;&quot;oci_core_ipsec&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpn&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   compartment_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.c_id
   static_routes &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.routes
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use output to express and state unknown information till after the apply.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# user input&lt;/span&gt;
 output &lt;span class=&quot;s2&quot;&gt;&quot;vpc_cidr_range&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.vpc_cidr_range
   description &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;IPs CIDR.&quot;&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;c&quot;&gt;# provisioned resource - aws&lt;/span&gt;
 output &lt;span class=&quot;s2&quot;&gt;&quot;main_zone_id&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; try&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
     aws_route53_zone.m.zone_id,
     &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;c&quot;&gt;# provisioned resource - oci&lt;/span&gt;
 output &lt;span class=&quot;s2&quot;&gt;&quot;instances_details&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; module.i.instances_summary
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Declare a variable with sensitive flag for masking in logs and execution.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; output &lt;span class=&quot;s2&quot;&gt;&quot;auth_token&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   sensitive &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
   &lt;/span&gt;value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; random_password.p.result
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add locals to shorten long configuration parts or variables chain.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; locals &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   az &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; data.
        aws_availability_zone.
        main.name_suffix

   db_name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.prefix]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;-db&quot;&lt;/span&gt;
   db_admin_user &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;postgres&quot;&lt;/span&gt;
   conf_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.db_config_id

   tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; merge&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
     tomap&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt; Env &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.env &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;,
     var.tags
   &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Revise the variable types: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;string&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;number&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boolean&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; variable &lt;span class=&quot;s2&quot;&gt;&quot;db_ingress_ports&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;object&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; port &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; string &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

 db_ingress_ports &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; port &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;5432&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;a href=&quot;https://developer.hashicorp.com/terraform/language/expressions/types&quot;&gt;Refer to the docs for more.&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Loop through repetitive resources using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count.index&lt;/code&gt;&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; resource &lt;span class=&quot;s2&quot;&gt;&quot;aws_eip&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eips&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   count &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 1
   tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; merge&lt;span class=&quot;o&quot;&gt;({&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;Name&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;lb_ip_&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.index&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;, local.tags&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Utilize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hashicorp/random&lt;/code&gt; module to generate ids or credentials.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; resource &lt;span class=&quot;s2&quot;&gt;&quot;random_password&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ec2&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   length &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 20
   special &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
   &lt;/span&gt;override_special &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@!-=+&quot;&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Import child modules into the main module using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; attribute from various sources: &lt;br /&gt;
local FS, git repository, github, a url, or the public terraform registry.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# redshift database deployment,&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# via external module from the&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;# registry.&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
 module &lt;span class=&quot;s2&quot;&gt;&quot;redshift-db&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;terraform-aws-modules/redshift/aws&quot;&lt;/span&gt;
   version &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;3.4.1&quot;&lt;/span&gt;

   cluster_identifier &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rd&quot;&lt;/span&gt;
   cluster_number_of_nodes &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 2
   cluster_node_type &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;dc2.large&quot;&lt;/span&gt;

   cluster_database_name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rd&quot;&lt;/span&gt;
   cluster_master_username &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
     &lt;span class=&quot;s2&quot;&gt;&quot;rd-user&quot;&lt;/span&gt;
   cluster_master_password &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
     random_password.pass.result

   encrypted &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
   &lt;/span&gt;wlm_json_configuration &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
     jsonencode&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;
       &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; auto_wlm &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;])&lt;/span&gt;

   cluster_iam_roles &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
     local.s3_role.iam_role_arn
   &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;

   subnets &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; data.aws_subnets.a

   enhanced_vpc_routing &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
   &lt;/span&gt;publicly_accessible  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true
   &lt;/span&gt;vpc_security_group_ids &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt;
   enable_logging &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true

   &lt;/span&gt;tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;When using multiple modules structure, variables can be passed around in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;module&lt;/code&gt; block.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;module &lt;span class=&quot;s2&quot;&gt;&quot;postgres&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;../base/oci_pg&quot;&lt;/span&gt;

  tenancy_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.tenancy_id
  db_name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.postgres_db_name

  tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; var.tags
  &lt;span class=&quot;c&quot;&gt;# etc ...&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for_each&lt;/code&gt; iteration primitives (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.key&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value.*&lt;/code&gt;) for granular data structure looping over sets.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# basic looping.&lt;/span&gt;
data &lt;span class=&quot;s2&quot;&gt;&quot;aws_subnet&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;subnet&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  for_each &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; toset&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    data.aws_subnets.all.ids
  &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; each.value
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# basic looping with mapping.&lt;/span&gt;
resource &lt;span class=&quot;s2&quot;&gt;&quot;aws_route53_record&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;cns&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  for_each &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;r &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;var.public_cnames :
    r[0] &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      target : r[1], ttl : r[2]
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  zone_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    aws_route53_zone.main.id
  &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CNAME&quot;&lt;/span&gt;
  name    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; each.key
  ttl     &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; each.value.ttl
  records &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;each.value.target]

  depends_on &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;
    aws_route53_zone.main
  &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# advanced example.&lt;/span&gt;
resource &lt;span class=&quot;s2&quot;&gt;&quot;aws_route&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpn_routes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  for_each &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;setproduct&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      data.aws_route_tables.p.ids,
      var.vpn_route&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; :
      &lt;span class=&quot;s2&quot;&gt;&quot;rt-entry.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[0]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[1]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; rt_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; i[0], cidr &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; i[1] &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  gateway_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    aws_vpn_gateway.vgw.id
  route_table_id &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    each.value.rt_id
  destination_cidr_block &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    each.value.cidr
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;review  the built-in functions of interest to your configurations: &lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tolist()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toset()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upper()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lower()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;endswith()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chomp()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regex()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reverse()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flatten()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base64*()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp()&lt;/code&gt;, and much more.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;locals &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  prefix &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; lower&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;var.acc_prefix&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;provider &lt;span class=&quot;s2&quot;&gt;&quot;kubernetes&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  cluster_ca_certificate &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
    base64decode&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      data.aws_eks_cluster.main.
      certificate_authority.
      0.data
    &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;They come in handy and can help solving several problems. &lt;br /&gt;&lt;a href=&quot;https://developer.hashicorp.com/terraform/language/functions&quot;&gt;Refer to the docs here.&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can add validation rules and message to your module vars in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variables.tf&lt;/code&gt;.&lt;br /&gt;
Here’s an example:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;variable &lt;span class=&quot;s2&quot;&gt;&quot;prefix&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; string

  validation &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    condition &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      length&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;var.prefix&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &amp;lt; 10
    error_message &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;only less than 10 chars.&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

variable &lt;span class=&quot;s2&quot;&gt;&quot;db_storage_iops&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; string
  default &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;300000&quot;&lt;/span&gt;

  validation &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    condition &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; contains&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;300000&quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&quot;750000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;,
      var.db_storage_iops
    &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    error_message &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;Only IOPS: 300K / 750K.&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use the state CLI command to show, list, or manipulate the state once needed &lt;em&gt;(e.g. checking existing values, importing directly modified items outside IaC, etc.)&lt;/em&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# view the current state items.&lt;/span&gt;
terraform state list

&lt;span class=&quot;c&quot;&gt;# clean outdated state.&lt;/span&gt;
terraform state &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
 &lt;span class=&quot;s2&quot;&gt;&quot;aws_customer_gateway.main&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# importing drifted state.&lt;/span&gt;
terraform import &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;aws_customer_gateway.main&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;cgw-new-id-XXX&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;terraform console&lt;/code&gt; to quickly experiment with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HCL&lt;/code&gt; syntax and explore the current module data.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# testing sorting out lists.&lt;/span&gt;
❯ terraform console
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; tolist&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;concat&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.200.18.0/24&quot;&lt;/span&gt;,
   &lt;span class=&quot;s2&quot;&gt;&quot;10.34.150.0/24&quot;&lt;/span&gt;,
   &lt;span class=&quot;s2&quot;&gt;&quot;10.10.100.0/25&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;)))&lt;/span&gt;
tolist&lt;span class=&quot;o&quot;&gt;([&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;10.10.100.0/25&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;10.200.18.0/24&quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&quot;10.34.150.0/24&quot;&lt;/span&gt;,
&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;final-thought-&quot;&gt;Final Thought ..&lt;/h3&gt;

&lt;p&gt;Although, terraform and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HCL&lt;/code&gt; definition language may have limitation around dynamic behavior sometimes, you will find ways to structure and approach your automation code declaratively, most of the time.&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Oct 2024 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2024/10/17/reflections-on-iac-with-terraform-2</link>
        <guid isPermaLink="true">https://www.abarrak.com/2024/10/17/reflections-on-iac-with-terraform-2</guid>
        
        <category>Infrastructure as Code</category>
        
        <category>Cloud Computing</category>
        
        <category>Linux</category>
        
        <category>Automation</category>
        
        
        <category>Automation</category>
        
        <category>IaC</category>
        
        <category>Cloud</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/traefik-1.png&quot; class=&quot;post-image resize-sm center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An interesting usecase I have encountered recently is establishing a secure path (bridge) between &lt;a href=&quot;https://doc.traefik.io/traefik/&quot;&gt;Traefik&lt;/a&gt; as ingress controller and its destination backends.&lt;/p&gt;

&lt;p&gt;The post addresses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traefik&lt;/code&gt; usage within Kubernetes, however it is applicable to other setups.&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;h2 id=&quot;case&quot;&gt;Case&lt;/h2&gt;

&lt;p&gt;A flow diagram is probably the way to explain the setup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/traefik-ingress-passthrough.png&quot; class=&quot;post-image-2 resize-sm center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The main routing resource is a CRD that resembles the following snippet.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;IngressRoute&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web-ingressroute&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;entryPoints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;websecure&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Rule&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Host(`web.example.com`)&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-svc&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;passHostHeader&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web-ingressroute-tls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The secret here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web-ingressroute-tls&lt;/code&gt; is a standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls&lt;/code&gt; secret for the external route containing the private key, certificate, and ca certificate. Let’s mark it as secret (1).&lt;/p&gt;

&lt;p&gt;The target backend is a classic &lt;a href=&quot;https://hub.docker.com/_/nginx&quot;&gt;sidecar container &lt;strong&gt;“nginx”&lt;/strong&gt;&lt;/a&gt; fronting the main container.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx-conf&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;nginx.conf&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;user nginx;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;worker_processes  3;&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;http {&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;server {&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;listen 443 ssl;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;listen [::]:443 ssl;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;server_name  _;&lt;/span&gt;

        &lt;span class=&quot;s&quot;&gt;ssl_certificate /etc/nginx/ssl/cert.pem;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ssl_certificate_key /etc/nginx/ssl/key.pem;&lt;/span&gt;

        &lt;span class=&quot;s&quot;&gt;location / {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_pass http://localhost:80;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_http_version 1.1;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_set_header X-Forwarded-Host $host;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_set_header X-Forwarded-Server $host;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_set_header Host $http_host;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;proxy_set_header X-Forwarded-Proto $scheme;&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The pod is exposed using a cluster service &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backend-svc&lt;/code&gt; as denoted in the ingress-route.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backend-tls&lt;/code&gt; contains SSL certificate files that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; uses and mounts. At the same time, it is intended for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traefik&lt;/code&gt; to trust as the target certificate. As expected for internal services, this is an automatically rotated &lt;em&gt;self-signed certificate&lt;/em&gt; by an internal issuer or “let’s encrypt”. Here is the second &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls&lt;/code&gt; secret (2).&lt;/p&gt;

&lt;h2 id=&quot;initial-approach&quot;&gt;Initial Approach&lt;/h2&gt;

&lt;p&gt;First, most resources out there might guide you to explore setting the TLS options of the route:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-tls-opts&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;traefik.containo.us/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TLSOption&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-tls-opts&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;cipherSuites&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;clientAuth&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;clientAuthType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RequireAndVerifyClientCert&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secretNames&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-tls&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;minVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;VersionTLS12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But that didn’t work. &lt;br /&gt;
The router logs shows internal errors (500) when connecting to the upstream (pod IP).&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;traefikee-ingress-proxy-57.. 10.244.0.128 - - &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;15/Mar/2023:10:45:50 +0000] &lt;span class=&quot;s2&quot;&gt;&quot;GET / HTTP/2.0&quot;&lt;/span&gt; 500 21 &lt;span class=&quot;s2&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;-&quot;&lt;/span&gt; 2987228 &lt;span class=&quot;s2&quot;&gt;&quot;web-web-ingressroute-6cd908afc82ca51c00cf@kubernetescrd&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://10.244.2.105:443&quot;&lt;/span&gt; 2ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is despite that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; was functioning fine when forwarded locally.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl port-forward svc/backend-svc 8443:443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Serving requests and SSL/TLS settings were correct. The logs emit success (200) when reached over https. Basically, the backend itself was browsable.&lt;/p&gt;

&lt;p&gt;After further review, it turns out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TLSOptions&lt;/code&gt; as implemented above was merely for client side certificate when reaching the ingress router, an implementation of &lt;em&gt;mTLS&lt;/em&gt; and not our case of &lt;em&gt;TLS passing&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServersTransport&lt;/code&gt; was what is needed to let &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;traefik&lt;/code&gt; trust the backend certificate instead of faulting.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ServersTransport&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web-transport&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;certificatesSecrets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-tls&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;rootCAsSecrets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend-tls&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serverName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And appending to the route:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;serversTransport&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;web-transport&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Of course you may turn off the destination certificate check by setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;insecureSkipVerify: true&lt;/code&gt; but that would defeat the purpose we aim for, an end to end TLS flow (bridging) !&lt;/p&gt;
</description>
        <pubDate>Fri, 31 Mar 2023 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2023/03/31/e2e-treafik-tls-offloading</link>
        <guid isPermaLink="true">https://www.abarrak.com/2023/03/31/e2e-treafik-tls-offloading</guid>
        
        <category>Traefik</category>
        
        <category>Ingress Solutions</category>
        
        <category>Kubernetes</category>
        
        <category>SRE</category>
        
        <category>TLS</category>
        
        
        <category>Kubernetes</category>
        
        <category>SRE</category>
        
        <category>Traefik</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/aws-b-lib.png&quot; class=&quot;post-image resize-sm center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I have enjoyed reading &lt;a href=&quot;https://aws.amazon.com/builders-library/reliability-and-constant-work/&quot;&gt;this piece on stability and reliability by Colm MacCárthaigh&lt;/a&gt;. It is part of a concise and valuable series by AWS about the art of system operations collected under the name “Amazon Builders’ Library”.&lt;/p&gt;

&lt;p&gt;The comparison between coffee urn and computers when building reliable operational model is perfect and up to the point. Then, three characteristics taken from this analogy are observed: 1. have a simple constant function that does not scale based on load stress solely, 2. no different modes or operational branching, 3. anti-fragility which is the preparations that hold systems reliable when most needed. The author mentions the coined term for these principles on reliability: &lt;strong&gt;“Constant Work Patterns”&lt;/strong&gt;. Route 53 and S3 are presented as use cases that fit and demonstrate the model.&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;p&gt;The article has references to other content in the library. I would definitely check them out!&lt;/p&gt;

&lt;p&gt;Finally, thanks to whomever shared this recently &lt;a href=&quot;https://news.ycombinator.com/item?id=34103426&quot;&gt;on HN&lt;/a&gt;!&lt;/p&gt;

</description>
        <pubDate>Wed, 28 Dec 2022 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2022/12/28/on-aws-builder-lib</link>
        <guid isPermaLink="true">https://www.abarrak.com/2022/12/28/on-aws-builder-lib</guid>
        
        <category>Automation</category>
        
        <category>SRE</category>
        
        <category>System Design</category>
        
        <category>Cloud</category>
        
        <category>AWS</category>
        
        
        <category>Automation</category>
        
        <category>SRE</category>
        
        <category>System Design</category>
        
        <category>Cloud</category>
        
        <category>AWS</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;a href=&quot;https://github.com/roboll/helmfile&quot;&gt;Helmfile is an orchestrator tool&lt;/a&gt; for collecting, building, and deploying cloud-native apps. Basically it’s the packager for helm chart based applications.&lt;/p&gt;

&lt;p&gt;One of the interesting ideas I came along recently, is utilizing it for working in air-gapped environments, where access to the internet is not feasible.&lt;/p&gt;

&lt;h2 id=&quot;use-case&quot;&gt;Use Case&lt;/h2&gt;

&lt;p&gt;Let’s say you were deploying an application to a Kubernetes cluster. The idea is to package all dependencies and manifests beforehand in a local or control machine, then push the consolidated deployment directory to the offline machine.&lt;/p&gt;

&lt;p&gt;The diagram below depicts the flow.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/offline-deployment.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;h2 id=&quot;example-deployment&quot;&gt;Example Deployment&lt;/h2&gt;

&lt;p&gt;The script below is a custom version of deploying &lt;a href=&quot;https://dexidp.io/&quot;&gt;Dex&lt;/a&gt; in offline mode, as an example.&lt;/p&gt;

&lt;p&gt;A simplified &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helmfile&lt;/code&gt; would look like this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# helmfile.yaml&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;repositories&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://charts.dexidp.io&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;helmDefaults&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;waitForJobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;600&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;releases&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;createNamespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;chart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dex/dex&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.11.1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;requiredEnv &quot;OIDC_ISSUER&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubernetes&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;inCluster&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;oauth2&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;skipApprovalScreen&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;staticClients&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;connectors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[]&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;

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

&lt;p&gt;The following script saves archived version of the chart’s assets inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./output&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# offline.sh&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;v2.34.0
&lt;span class=&quot;nv&quot;&gt;IMAGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ghcr.io/dexidp/dex
&lt;span class=&quot;nv&quot;&gt;CHART_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://charts.dexidp.io
&lt;span class=&quot;nv&quot;&gt;CHART&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;dex/dex
&lt;span class=&quot;nv&quot;&gt;CHART_VERSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.11.1

docker pull &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# tagging is optional&lt;/span&gt;
docker tag registry.local.lan/&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

docker save registry.local.lan/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;IMAGE&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; ./output/images/&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;IMAGE&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;:&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.tar

helm repo add &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHART_REPO&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
helm pull &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHART&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CHART_VERSION&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--destination&lt;/span&gt; ./output/charts/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, it should be a matter of executing the following sequence to prepare the final build directory:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ BUILD_TIME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%Y-%d-%m-at-%H-%M&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;./offline.sh
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; .env | xargs&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;helmfile fetch
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;helmfile build &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ./output/final-&lt;span class=&quot;nv&quot;&gt;$BUILD_TIME&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helmfile&lt;/code&gt; binary is not available in the target environment, just template plain manifests.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;helmfile template &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ./output/final-&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUILD_TIME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, on the production node you would run something similar to this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker load &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ./output/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.tar
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker push
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;helmfile &lt;span class=&quot;nb&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--skip-deps&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./output/final-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# In case `helm` is not available:&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ./output/final-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, this method is extensible and can be generalized for any helm-based deployment. For the complete example listing, refer to &lt;a href=&quot;https://github.com/abarrak/dex-helmfile-offline&quot;&gt;the github repo here.&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Certain security or compliance challenges imposed in air-gapped environments can make it hard to deliver cloud native deployments. Luckily, several CNCF projects (&lt;a href=&quot;https://goharbor.io/docs/2.1.0/install-config/download-installer/&quot;&gt;harbor&lt;/a&gt;, &lt;a href=&quot;https://docs.ranchermanager.rancher.io/pages-for-subheaders/air-gapped-helm-cli-install&quot;&gt;rancher&lt;/a&gt;, &lt;a href=&quot;https://docs.k3s.io/installation/airgap&quot;&gt;k3s&lt;/a&gt;, to name few) provide options to tackle such environments. Additionally, the presented  approach above is generic for any helm application to be deployed properly in offline mode.&lt;/p&gt;
</description>
        <pubDate>Sat, 08 Oct 2022 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2022/10/08/using-helmfile-for-offline-deployments</link>
        <guid isPermaLink="true">https://www.abarrak.com/2022/10/08/using-helmfile-for-offline-deployments</guid>
        
        <category>Helm</category>
        
        <category>Helmfile</category>
        
        <category>Kubernetes</category>
        
        <category>Air-Gapped</category>
        
        <category>Automation</category>
        
        
        <category>Automation</category>
        
        <category>Kubernetes</category>
        
        <category>Helm</category>
        
        <category>Cloud</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/iac-reflections.png&quot; class=&quot;post-image resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Terraform has emerged as one of the top open source infrastructure as code (IaC) tools, since its initial release by Hashicorp back in 2014.&lt;/p&gt;

&lt;p&gt;The design philosophy behind the tool is to have &lt;strong&gt;declarative&lt;/strong&gt;, and &lt;strong&gt;stateful&lt;/strong&gt; representation for the underlying IT infrastructure (whether it be on public, on-premise, or hybrid cloud), which in turns simplify the control, collaboration, and auditing of the cloud resources.&lt;/p&gt;

&lt;p&gt;The classical example to show the main idea creating a VM:&lt;/p&gt;

&lt;!-- post-excerpt --&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;provider &lt;span class=&quot;s2&quot;&gt;&quot;aws&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

resource &lt;span class=&quot;s2&quot;&gt;&quot;aws_vpc&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;vpc&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  cidr_block &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;10.0.0.0/16&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

resource &lt;span class=&quot;s2&quot;&gt;&quot;aws_instance&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  ami &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ami-026b57f3c383c2eec&quot;&lt;/span&gt;
  instance_type &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;t2.micro&quot;&lt;/span&gt;
  tags &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;New example instance&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is not limited to only compute resources, &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs&quot;&gt;many of AWS services are available as well&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;simplified-workflow&quot;&gt;Simplified Workflow&lt;/h2&gt;

&lt;p&gt;The main workflow is conventionally as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/terraform-diagram.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The flow maps cleanly to CLI commands:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;terraform init
terraform plan
terraform apply
terraform show
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Additional linting commands available as well:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;terraform &lt;span class=&quot;nb&quot;&gt;fmt
&lt;/span&gt;terraform validate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Disposal of resources is simplified as well (without authoring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tf&lt;/code&gt; manifests):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;structure&quot;&gt;Structure&lt;/h2&gt;

&lt;p&gt;It is recommended to factorize input values across environments into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;variable&lt;/code&gt; blocks.
&lt;br /&gt;
This enhances for security posture as well, like protecting sensitive data.&lt;/p&gt;

&lt;p&gt;Moreover, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;output&lt;/code&gt; blocks provide a way to capture and log information after applying state changes.&lt;/p&gt;

&lt;p&gt;The minimal structure of managed resources in terraform is shown below:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;tree
├── main.tf
├── outputs.tf
├── terraform.tfstate
├── terraform.tfstate.backup
└── variables.tf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The variables and output files are separated to organize things up.&lt;/p&gt;

&lt;p&gt;The state is tracked in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.tfstate&lt;/code&gt; file, and managed internally by Terraform.&lt;/p&gt;

&lt;h2 id=&quot;providers&quot;&gt;Providers&lt;/h2&gt;

&lt;p&gt;The mechanism behind provisioning the intended state of an infrastructure or platform, is performed on by &lt;strong&gt;“providers”&lt;/strong&gt;. They are terraform extensions written in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Go&lt;/code&gt; language exposing the resource types they implement, and interfacing with the target infrastructure / platform APIs.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-apl&quot;&gt;provider &quot;aws&quot; {
  region = &quot;us-east-1&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The major providers &lt;a href=&quot;https://registry.terraform.io&quot;&gt;are available on the public terraform registry&lt;/a&gt;, along with many platform agnostic plugins, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Helm&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Kubernetes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/providers-iac.png&quot; class=&quot;post-image-2 resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s an example for a simple helm releasing technique:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;provider &lt;span class=&quot;s2&quot;&gt;&quot;helm&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  kubernetes &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

resource &lt;span class=&quot;s2&quot;&gt;&quot;helm_release&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  name       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis-instance&quot;&lt;/span&gt;

  repository &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://charts.bitnami.com/bitnami&quot;&lt;/span&gt;
  chart      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis&quot;&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    name  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;architecture&quot;&lt;/span&gt;
    value &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;replication&quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;terraform-vs-ansible&quot;&gt;Terraform v.s. Ansible&lt;/h2&gt;

&lt;p&gt;Compared to Ansible as an orchestration tool on the infrastructure layer, I find that Terraform overall is more expressive and concise to enable building on modules in much elegant and feasible manner, with the excellent readability of HCL and dependency management of plugins.&lt;/p&gt;

&lt;p&gt;The state is a major difference to consider here.&lt;/p&gt;

&lt;p&gt;Ansible takes a stateless approach by always exchanging the desired state with the target resources in order to know the actual state then apply the delta, if needed. On the other hand, Terraform is stateful and manages the state in local or remote manner.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;All in all, codifying infra and platform layers &lt;em&gt;(e.g. using terraform)&lt;/em&gt; can bring key benefits to organizations in adopting the DevOps practices, leading to operational excellence, eventually.&lt;/p&gt;
</description>
        <pubDate>Fri, 23 Sep 2022 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2022/09/23/reflections-on-iac-with-terraform</link>
        <guid isPermaLink="true">https://www.abarrak.com/2022/09/23/reflections-on-iac-with-terraform</guid>
        
        <category>Infrastructure as Code</category>
        
        <category>Cloud Computing</category>
        
        <category>Linux</category>
        
        <category>Automation</category>
        
        
        <category>Automation</category>
        
        <category>IaC</category>
        
        <category>Cloud</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;br /&gt;
&lt;img src=&quot;/public/images/dockerd-ddevmapper-pool-issue.png&quot; class=&quot;post-image resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;

&lt;p&gt;Sometimes, when working with docker engine on CI systems, certain type of errors could arise from the challenging restrictions in the environment. This is typical due the inherent server-client architecture in docker.&lt;/p&gt;

&lt;p&gt;Here’s one of the problems that I’ve seen recently. When a client is connecting to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dockerd&lt;/code&gt; in order to build an image or pull external images:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;failed to prepare a4mv4dh8qcq283c7x47a4nwpg: devmapper: Thin Pool has 161986 free data blocks which is less than minimum required 163840 free data blocks. Create more free space in thin pool or use dm.min_free_space option to change behavior
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- post-excerpt --&gt;

&lt;h2 id=&quot;analysis&quot;&gt;Analysis&lt;/h2&gt;

&lt;p&gt;As the log suggests, clearly this is related to the storage limit incurred for the &lt;a href=&quot;https://docs.docker.com/storage/storagedriver/device-mapper-driver/&quot;&gt;device mapper driver&lt;/a&gt;. Here, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thin Pool&lt;/code&gt; needs additional space to create and pull newer images but the limit is exceeded.&lt;/p&gt;

&lt;p&gt;To diagnose that, check disk usage on the daemon using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;df -h&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system&lt;/code&gt; commands.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker system info
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker system &lt;span class=&quot;nb&quot;&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;resolutions&quot;&gt;Resolutions&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;A workaround is to increase the volume size if you can.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Otherwise, you can run clean up procedure of outdated and unnecessary images.&lt;br /&gt;
Periodically or through cron tab.&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# delete specific images by pattern:&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;IMAGES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;my-apps
 docker images &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$IMAGES&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1 &quot;:&quot; $2}&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   | xargs docker rmi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You can delete by &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/image_prune/#filtering&quot;&gt;label/date filtering&lt;/a&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;# delete 6 months old images:&lt;/span&gt;
 docker image prune &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--force&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--filter&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;until=4368h&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;You may run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prune&lt;/code&gt; to remove all dangling resources (images, containers, etc.)&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; docker system prune

 &lt;span class=&quot;c&quot;&gt;# This will remove all unused images:&lt;/span&gt;
 docker image prune &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;
</description>
        <pubDate>Fri, 18 Feb 2022 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2022/02/18/resolving-dockerd-pool-size-less-than-minimum</link>
        <guid isPermaLink="true">https://www.abarrak.com/2022/02/18/resolving-dockerd-pool-size-less-than-minimum</guid>
        
        <category>cicd</category>
        
        <category>pipelines</category>
        
        <category>ddevmapper pool issue</category>
        
        <category>dockerd</category>
        
        <category>docker</category>
        
        
        <category>Software</category>
        
        <category>CICD</category>
        
        <category>Docker</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/uuids-db.png&quot; class=&quot;post-image center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Using universally unique identifiers (UUIDs) for exposed resource identifiers is &lt;a href=&quot;https://github.com/eliotsykes/rails-security-checklist#ids&quot;&gt;more secure&lt;/a&gt;, and &lt;a href=&quot;https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439&quot;&gt;convenient for database distribution&lt;/a&gt;. It is relatively simple to configure Active Record to generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt; primary keys in migrations.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Setup the default generation inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config/application.rb&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c1&quot;&gt;# config/application.rb&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;SampleApp&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Application&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# Change the primary key&lt;/span&gt;
     &lt;span class=&quot;c1&quot;&gt;# default type to UUIDs.&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;generators&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;orm&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:active_record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;ss&quot;&gt;primary_key_type: :uuid&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;!-- post-excerpt --&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Enable the database support:&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;PostgresSQL:&lt;/p&gt;

    &lt;p&gt;Enable  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uuid-ossp&lt;/code&gt; extension for random &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:uuid&lt;/code&gt; generation at the DB level.&lt;/p&gt;
    &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c1&quot;&gt;# db/migrations/xxxxx_enable_uuid_ossp_extension.rb.rb&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;EnableUuidOsspExtension&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
       &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;enable_extension&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;uuid-ossp&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension_enabled?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;uuid-ossp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;For new Postgres versions ( &amp;gt;= 9.4), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pgcrypto&lt;/code&gt; extension can be used alternatively.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SQLite:&lt;/p&gt;

    &lt;p&gt;As &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt; is not naively available in SQLite.&lt;/p&gt;

    &lt;p&gt;A workaround is to utilize generic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;varchar&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blob(16)&lt;/code&gt; columns instead. &lt;a href=&quot;https://stackoverflow.com/a/52032839&quot;&gt;Some people have reported&lt;/a&gt; they needed to load the adapter file in order for it to work.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;SQL Server:&lt;/p&gt;

    &lt;p&gt;Has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:uuid&lt;/code&gt; native support (through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uniqueidentifier&lt;/code&gt; column type) with the generator.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
Now newly generated tables will use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:uuid&lt;/code&gt; by default for primary keys.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# db/migrations/xxxxx_create_customers.rb&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateCustomers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;ss&quot;&gt;id: :uuid&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:full_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;index: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;index: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

     &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ceeated_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;uuid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:updated_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;

     &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;timestamps&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;add_foreign_key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;ss&quot;&gt;column: :created_by&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;add_foreign_key&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:users&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;ss&quot;&gt;column: :updated_by&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The snippet above shows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:uuid&lt;/code&gt; type usage for other non-primary key columns too.&lt;/p&gt;

&lt;p&gt;In case you don’t require &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UUID&lt;/code&gt; key type, it’s possible to get the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;integer&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bigint&lt;/code&gt; types back again:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:cities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
             &lt;span class=&quot;ss&quot;&gt;id: :integer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:population&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ordering-results&quot;&gt;Ordering Results&lt;/h2&gt;

&lt;p&gt;A drawback of querying UUID-based tables is that ordering is not inferred as with the sequential keys.
We have to set it up using default ordering scope, easily:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# app/models/customer.rb&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Customer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationRecord&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;default_scope&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;created_at: :asc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ensure that indices on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;created_at&lt;/code&gt; columns already added for boosted performance.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# db/migrations/xxxxx_add_created_at_indices.rb&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AddCreatedAtIndices&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;change&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:customers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:surveys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;add_index&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:more_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:created_at&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Keep in mind the additional storage cost of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:uuid&lt;/code&gt; keys compared to sequential ones, which requires balancing the trade-offs when designing your data models.&lt;/p&gt;
</description>
        <pubDate>Fri, 24 May 2019 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2019/05/24/uuid-primary-key-for-active-record-models</link>
        <guid isPermaLink="true">https://www.abarrak.com/2019/05/24/uuid-primary-key-for-active-record-models</guid>
        
        <category>Active Record</category>
        
        <category>UUID Primary Key</category>
        
        <category>Postgres</category>
        
        <category>Ruby</category>
        
        <category>Rails</category>
        
        
        <category>Software</category>
        
        <category>Ruby</category>
        
        <category>Rails</category>
        
        <category>Active Record</category>
        
      </item>
    
      <item>
        <title></title>
        <description>&lt;p&gt;&lt;img src=&quot;/public/images/opt-img.jpg&quot; class=&quot;post-image resize-md center-image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In many times you may need to optimze rapidly a bunch of images at your hands. There are handy utilities that can help you reduce image sizes with simple commands in the terminal. The following lists some of these tools:&lt;/p&gt;

&lt;h3 id=&quot;1-optipng-advanced-png-optimizer&quot;&gt;&lt;a href=&quot;http://optipng.sourceforge.net/&quot;&gt;1. OptiPNG: Advanced PNG Optimizer&lt;/a&gt;:&lt;/h3&gt;

&lt;p&gt;Get it for Mac OS X:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;optipng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For Linux Ubuntu:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;optipng
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To optimize an image, run:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;optipng &amp;lt;img-name&amp;gt;.png
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!-- post-excerpt --&gt;

&lt;p&gt;To wildcard all images recursively in the current directory and sub directories:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.png&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; optipng &lt;span class=&quot;nt&quot;&gt;-o7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-jpegoptim&quot;&gt;&lt;a href=&quot;https://github.com/tjko/jpegoptim&quot;&gt;2. jpegoptim&lt;/a&gt;:&lt;/h3&gt;

&lt;p&gt;Install for Mac OS X:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jpegoptim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Install for Linux Ubuntu:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jpegoptim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run the next command to optimize an image, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m&lt;/code&gt; controls the image quality (0-100):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jpegoptim &lt;span class=&quot;nt&quot;&gt;-m70&lt;/span&gt; &amp;lt;img-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or optimize all recursively:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.jpg&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; jpegoptim &lt;span class=&quot;nt&quot;&gt;-m80&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-gifsicle&quot;&gt;&lt;a href=&quot;https://www.lcdf.org/gifsicle/&quot;&gt;3. gifsicle&lt;/a&gt;:&lt;/h3&gt;

&lt;p&gt;Install for Mac OS X:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;gifsicle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For Linux Ubuntu:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;gifsicle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The commands for single image, and a group of images respectively, are:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# for single image.&lt;/span&gt;
gifsicle &lt;span class=&quot;nt&quot;&gt;--batch&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-V&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O2&lt;/span&gt; &amp;lt;image-name&amp;gt;.gif

&lt;span class=&quot;c&quot;&gt;# to start from cureent dir and go down.&lt;/span&gt;
find &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-iname&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;*.gif&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-exec&lt;/span&gt; gifsicle &lt;span class=&quot;nt&quot;&gt;--batch&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-V&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;p&gt;Here are some aliases to keep commands nearby for fast run on the active directory images.&lt;/p&gt;

&lt;p&gt;Be warn that those (also the previously mentioned commands) all alter the original images.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compress_png&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;optipng -o7 *.png&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compress_jpg&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;jpegoptim -m80 *.jpg&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;compress_gif&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gifsicle --batch -V -O2 *.gif&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can tweak the optimization level and other options to suit your needs.&lt;/p&gt;

&lt;h3 id=&quot;imageoptim&quot;&gt;&lt;strong&gt;ImageOptim&lt;/strong&gt;&lt;/h3&gt;

&lt;p&gt;If you’d like to use a GUI application, &lt;a href=&quot;https://imageoptim.com/mac&quot;&gt;ImageOptim&lt;/a&gt; is a pretty open source one for Mac OS X that supports multiple image formats.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/images/imageoptim.png&quot; alt=&quot;ImageOptim Screen Shot&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Happy mature optimization :) !&lt;/p&gt;
</description>
        <pubDate>Tue, 30 Aug 2016 00:00:00 +0300</pubDate>
        <link>https://www.abarrak.com/2016/08/30/optimize-images-from-your-terminal</link>
        <guid isPermaLink="true">https://www.abarrak.com/2016/08/30/optimize-images-from-your-terminal</guid>
        
        <category>Image Processing</category>
        
        <category>optipng</category>
        
        <category>jpegoptim</category>
        
        <category>gifsicle</category>
        
        <category>ImageOptim</category>
        
        
        <category>Image Processing</category>
        
        <category>Productivity</category>
        
      </item>
    
  </channel>
</rss>
