<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jacob See</title>
    <description>The latest articles on Forem by Jacob See (@jacobsee).</description>
    <link>https://forem.com/jacobsee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F26975%2F3c0d89ff-0ec8-41c9-8bae-907f2991a971.jpg</url>
      <title>Forem: Jacob See</title>
      <link>https://forem.com/jacobsee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jacobsee"/>
    <language>en</language>
    <item>
      <title>Short Stuff: Let Me Paste Passwords!</title>
      <dc:creator>Jacob See</dc:creator>
      <pubDate>Sun, 18 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/jacobsee/short-stuff-let-me-paste-passwords-3ec1</link>
      <guid>https://forem.com/jacobsee/short-stuff-let-me-paste-passwords-3ec1</guid>
      <description>&lt;p&gt;I have to be honest - I thought we were done with this phase of Internet "security" &lt;a href="https://www.wired.com/2015/07/websites-please-stop-blocking-password-managers-2015/" rel="noopener noreferrer"&gt;a long time ago&lt;/a&gt;. Don't we all use password managers these days? Does anybody actually know their password to any major website anymore? I don't. That's why I was so surprised to run across a website (&lt;em&gt;cough&lt;/em&gt; &lt;strong&gt;Costco&lt;/strong&gt; &lt;em&gt;cough&lt;/em&gt;) that was still disabling paste on password inputs... at least, on their registration page.   &lt;sup&gt;yes it took me this long to make a Costco account...&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjacobsee.com%2Fimages%2Fcostco-disable-paste.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjacobsee.com%2Fimages%2Fcostco-disable-paste.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was at this point I decided that instead of opening my password manager in a new window and just typing the very long generated password into this registration page twice, I would spend &lt;em&gt;significantly more&lt;/em&gt; time to fix the problem itself.&lt;/p&gt;

&lt;p&gt;Obviously, we can't edit anything server-side... but for this, we don't really need to. The code executing this betrayal is just JavaScript running in our own browser. A-la &lt;em&gt;"We've traced the call. It's coming from inside the house."&lt;/em&gt; And we can do whatever we want inside of our own house.&lt;/p&gt;

&lt;p&gt;One convenient way to inject our own JavaScript in the browser is to run a plugin such as &lt;a href="https://www.tampermonkey.net/" rel="noopener noreferrer"&gt;Tampermonkey&lt;/a&gt; (&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt;, &lt;a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=en" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt;). Tampermonkey provides an environment for you to write your own scripts (or use public scripts published by others), and specify the URLs on which those scripts should activate and run.&lt;/p&gt;

&lt;p&gt;Luckily for us - this is a very simple problem to solve with a script! As seen in the screenshot above, they simply attach an event handler to the &lt;code&gt;paste&lt;/code&gt; event and then &lt;code&gt;return false&lt;/code&gt;, effectively canceling the paste.&lt;/p&gt;

&lt;p&gt;To fix pasting, we need to intercept the event before it reaches this handler and do something else. Click on the Tampermonkey icon on your taskbar, go to the dashboard, and create a new script with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ==UserScript==&lt;/span&gt;
&lt;span class="c1"&gt;// @name         Allow Pasting&lt;/span&gt;
&lt;span class="c1"&gt;// @namespace    https://jacobsee.com&lt;/span&gt;
&lt;span class="c1"&gt;// @version      0.1&lt;/span&gt;
&lt;span class="c1"&gt;// @description  Allow pasting passwords on sites that try to disable it&lt;/span&gt;
&lt;span class="c1"&gt;// @author       Jacob See&lt;/span&gt;
&lt;span class="c1"&gt;// @match        https://www.costco.com/*&lt;/span&gt;
&lt;span class="c1"&gt;// @icon         https://www.google.com/s2/favicons?domain=costco.com&lt;/span&gt;
&lt;span class="c1"&gt;// @grant        none&lt;/span&gt;
&lt;span class="c1"&gt;// ==/UserScript==&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;youllNeverGetMyPaste&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopImmediatePropagation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paste&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;youllNeverGetMyPaste&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script adds its own &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling_and_capture" rel="noopener noreferrer"&gt;"capturing"&lt;/a&gt; event listener for the &lt;code&gt;paste&lt;/code&gt; event across the entire document, with a handler that &lt;em&gt;prevents propagation of that event to other handlers&lt;/em&gt;, and "accepts" the paste. At this point, you can save the script and exit Tampermonkey.&lt;/p&gt;

&lt;p&gt;Open a new tab and navigate to Costco, and the Tampermonkey icon should illuminate, indicating that a script is active! The original evil &lt;code&gt;paste&lt;/code&gt; event handler still exists on the page, but it doesn't matter because the handler defined in our script takes care of it first!&lt;/p&gt;

&lt;p&gt;Now we can paste our massive, inconvenient passwords to our heart's content. 😎&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>browser</category>
      <category>plugin</category>
    </item>
    <item>
      <title>Gotcha: Configuring Containers with Ansible</title>
      <dc:creator>Jacob See</dc:creator>
      <pubDate>Thu, 08 Apr 2021 00:15:00 +0000</pubDate>
      <link>https://forem.com/jacobsee/gotcha-configuring-containers-with-ansible-45hc</link>
      <guid>https://forem.com/jacobsee/gotcha-configuring-containers-with-ansible-45hc</guid>
      <description>&lt;p&gt;If you're running serious container infrastructure these days, chances are strong that you're using an orchestration system like Kubernetes or OpenShift. But, what if your workloads are a little less... &lt;em&gt;clustered&lt;/em&gt;? A little more traditional? Maybe you have containers as a part of your workload, but don't need to migrate the entire system to them? Running one-off containers on normal Linux hosts is still &lt;em&gt;very much a thing&lt;/em&gt;, it's just more on you to manage them - perhaps using an automation tool like Ansible. I was doing this recently, and hit a bit of a snag, so I thought I'd write about it here! &lt;/p&gt;

&lt;p&gt;To kick things off - running a container on a target machine in Ansible is a pretty easy task. Depending on your target container type, you can use the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/docker/docker_container_module.html"&gt;docker_container&lt;/a&gt; or &lt;a href="https://docs.ansible.com/ansible/latest/collections/containers/podman/podman_container_module.html"&gt;podman_container&lt;/a&gt; modules in a task like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the container&lt;/span&gt; 
  &lt;span class="na"&gt;docker_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_container&lt;/span&gt; 
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt; 
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;started"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fair enough.&lt;/p&gt;

&lt;p&gt;Reading through the documentation for those modules above, we see that we can also mount files from the host into the container as volumes - an important thing to do when a container takes some kind of a configuration file in order to run. Since it's likely that we're also creating that config file with Ansible, let's take a look at what writing that file and then mounting it into the container would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write the config file&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config.yml.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml"&lt;/span&gt;
          &lt;span class="c1"&gt;# ^ we're writing the config file here&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the container&lt;/span&gt;
  &lt;span class="na"&gt;docker_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_container&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml:/config.yml"&lt;/span&gt;
        &lt;span class="c1"&gt;# ^ and mounting it into the container here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there we go! The &lt;code&gt;my_config.yml&lt;/code&gt; file in the home directory of whichever user is running this task is now mounted into the container at &lt;code&gt;/config.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This works nicely... &lt;strong&gt;once&lt;/strong&gt;. This is where the "gotcha" is!&lt;/p&gt;

&lt;p&gt;If we were to run this task a second time with different values for the config file, we would notice some interesting behavior. The file on the host (at &lt;code&gt;/home/{{ ansible_user }}/my_config.yml&lt;/code&gt;) would appear to have our new configuration in it, but the application in the container would not be performing as expected! In fact, if we were to &lt;code&gt;ssh&lt;/code&gt; into the remote machine and run &lt;code&gt;docker exec my_container cat /config.yml&lt;/code&gt; to see the contents of the config file &lt;em&gt;from the container's perspective&lt;/em&gt;, we would see that it has retained the old config data from before we ran the task a second time! &lt;em&gt;Why have the file on the host and the file in the container diverged?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The answer lies in a combination of how Ansible writes files, and how Docker mounts them. Files in a UNIX-based filesystem are represented by &lt;code&gt;inodes&lt;/code&gt;, which are records in a table that contain metadata about those files (including where they physically exist on a disk). If you're familiar with hard links and symbolic links - congratulations! You're already manipulating &lt;code&gt;inodes&lt;/code&gt;! Hard links are paths on the filesystem that point at an underlying &lt;code&gt;inode&lt;/code&gt;, and symbolic links are paths on the filesystem that act as shortcuts to other paths.&lt;/p&gt;

&lt;p&gt;Two things are important to know at this point, and they are that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Containers mount a file by referencing that file's &lt;code&gt;inode&lt;/code&gt; at the container's creation time, and:&lt;/li&gt;
&lt;li&gt;When creating files, Ansible first writes the data to a temporary file, and then &lt;em&gt;moves&lt;/em&gt; that temporary file into its final location&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is important because, on the filesystem level, Ansible is actually creating a &lt;em&gt;new&lt;/em&gt; &lt;code&gt;inode&lt;/code&gt; every time it writes this data - even though, intuitively, it seems like we're writing to the same path every time. It then updates the hard link at that path to point at this newly created &lt;code&gt;inode&lt;/code&gt;. This makes sense as it is actually a safer way for Ansible to write data remotely, but it means that any already-running container with that path (and therefore the old &lt;code&gt;inode&lt;/code&gt;) mounted &lt;strong&gt;do not get updated with our newly-written config data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are two easy fixes for this.&lt;/p&gt;

&lt;p&gt;First, we could force a container restart every time we make a change to the config file. The Ansible module has a &lt;code&gt;restart&lt;/code&gt; flag to make this easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write the config file&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config.yml.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml"&lt;/span&gt;
          &lt;span class="c1"&gt;# ^ we're writing the config file here&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the container&lt;/span&gt;
  &lt;span class="na"&gt;docker_container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my_container&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;my_exporter_image_version&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml:/config.yml"&lt;/span&gt;
        &lt;span class="c1"&gt;# ^ and mounting it into the container here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if our application can live-reload its config and we don't want to force a restart every time we make a change to the config file, we have to get slightly more creative. Notice how earlier we said that &lt;em&gt;moving&lt;/em&gt; a file changes the &lt;code&gt;inode&lt;/code&gt; referenced by a certain path. &lt;em&gt;Copying&lt;/em&gt; a file does not have this same effect. We can use this to our advantage!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write the config file (temporary)&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config.yml.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml.tmp"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;copy the config file to the correct path (so that the inode stays intact)&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cp&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml.tmp&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/config.yml"&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;remove the temporary config file&lt;/span&gt;
  &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ansible_user&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}/my_config.yml.tmp"&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;absent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, we have ensured that the &lt;code&gt;inode&lt;/code&gt; at the path mounted into the container is not modified, and our new configuration is ready to use in the container immediately!&lt;/p&gt;

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

</description>
      <category>ansible</category>
      <category>docker</category>
      <category>containers</category>
      <category>filesystem</category>
    </item>
    <item>
      <title>You can't CNAME your root domain (but also, you can!)</title>
      <dc:creator>Jacob See</dc:creator>
      <pubDate>Sun, 04 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/jacobsee/you-can-t-cname-your-root-domain-but-also-you-can-7en</link>
      <guid>https://forem.com/jacobsee/you-can-t-cname-your-root-domain-but-also-you-can-7en</guid>
      <description>&lt;p&gt;I ran across an interesting situation recently where I wanted to take a friend's domain (that they had given me access to on their Google Domains account) and host a landing page on the same server that the rest of my sites run on - with one small catch:&lt;/p&gt;

&lt;p&gt;I didn't want to have to maintain a separate &lt;code&gt;A&lt;/code&gt; record for their domain pointing to my server's IP address. I already maintain an &lt;code&gt;A&lt;/code&gt; record for my own domain. I thought that I should be able to &lt;code&gt;CNAME&lt;/code&gt; their domain to mine, and have the IP address of my own &lt;code&gt;A&lt;/code&gt; record simply set the course for this new domain as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It turns out that this breaks some rules under normal DNS conventions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Specifically, under the authoritative RFCs &lt;a href="https://tools.ietf.org/html/rfc1912"&gt;1912&lt;/a&gt; &amp;amp; &lt;a href="https://tools.ietf.org/html/rfc2181"&gt;2181&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CNAME&lt;/code&gt; records, when defined, must be the only record for a given hostname.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SOA&lt;/code&gt; and &lt;code&gt;NS&lt;/code&gt; records &lt;em&gt;must&lt;/em&gt; exist at the domain root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two constraints would contradict each other - so &lt;code&gt;CNAME&lt;/code&gt; records are not technically allowed at the domain root!&lt;/p&gt;

&lt;p&gt;The thing is - This is not a technical limitation. This is merely a limitation in the specification, written during a time when the modern Internet (and the infrastructure that we use to drive it) simply did not look anything like what it does today.&lt;/p&gt;

&lt;p&gt;This leaves us with an interesting situation. Google Domains has not implemented any sort of workaround for this, so using Google as the nameserver for my friend's domain was not an option. &lt;em&gt;However&lt;/em&gt;, some other providers have implemented what they call &lt;code&gt;ALIAS&lt;/code&gt; records, &lt;code&gt;ANAME&lt;/code&gt; records, or virtual &lt;code&gt;CNAME&lt;/code&gt; records!&lt;/p&gt;

&lt;p&gt;These pseudo-records are a fantastic piece of DNS server trickery. Instead of replying to a DNS request for the root hostname with a &lt;code&gt;CNAME&lt;/code&gt; record (which, remember, breaks the rules) - they have the DNS server perform its own lookup of the &lt;code&gt;ALIAS&lt;/code&gt;ed domain on-the-fly and return that IP to the requesting client as a normal &lt;code&gt;A&lt;/code&gt; record, indistinguishable from any other &lt;em&gt;totally normal&lt;/em&gt; &lt;code&gt;A&lt;/code&gt; record on a domain root!&lt;/p&gt;

&lt;p&gt;Out of the public DNS providers that support this "virtual &lt;code&gt;CNAME&lt;/code&gt;" service, &lt;a href="https://cloudflare.com"&gt;CloudFlare&lt;/a&gt; is the one that I decided to use (since I use CloudFlare for other things as well). They call this trickery "&lt;code&gt;CNAME&lt;/code&gt; Flattening", and they have &lt;a href="https://blog.cloudflare.com/introducing-cname-flattening-rfc-compliant-cnames-at-a-domains-root/"&gt;written their own detailed post about it here&lt;/a&gt;. In the CloudFlare DNS panel, once you enable &lt;code&gt;CNAME&lt;/code&gt; flattening, you can treat &lt;code&gt;CNAME&lt;/code&gt; records as if they &lt;em&gt;can&lt;/em&gt; exist at the root. Create the &lt;code&gt;CNAME&lt;/code&gt; record at the root, and it will automatically handle the flattening (i.e. conversion to an &lt;code&gt;A&lt;/code&gt; record) for you!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4HB6UnuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jacobsee.com/images/cloudflare-cname-flattening.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4HB6UnuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://jacobsee.com/images/cloudflare-cname-flattening.png" alt="CloudFlare's CNAME Flattening option in their DNS control panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This made for an elegant implementation of my desired DNS configuration. I used the Google Domains panel to point the &lt;code&gt;NS&lt;/code&gt; records for my friend's domain at CloudFlare, and from there was able to virtually &lt;code&gt;CNAME&lt;/code&gt; that domain to point at my main domain (and thus be hosted by my same web server), without breaking any DNS rules!&lt;/p&gt;

</description>
      <category>dns</category>
      <category>cname</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Microservice Coordination at a High Level</title>
      <dc:creator>Jacob See</dc:creator>
      <pubDate>Wed, 05 Dec 2018 02:03:41 +0000</pubDate>
      <link>https://forem.com/jacobsee/microservice-coordination-at-a-high-level-2p1j</link>
      <guid>https://forem.com/jacobsee/microservice-coordination-at-a-high-level-2p1j</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1533749871411-5e21e14bcc7d%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1533749871411-5e21e14bcc7d%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="man writing on whiteboard"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@campaign_creators?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Campaign Creators&lt;/a&gt; / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;I recently had the privilege of working on a team tasked with creating a system using a &lt;em&gt;composable architecture&lt;/em&gt;. We needed a system that operated as a sort of pipeline - with information flowing into one end, undergoing a number of transformations, and finally getting returned to the client as it leaves the pipeline out of the other end. Not all data required the same transformations to be applied to it, or in the same order, so we needed a way to adjust the behavior of this data pipeline on-the-fly in the cleanest, most scalable manner possible.&lt;/p&gt;

&lt;p&gt;We approached this by first deciding that each transformation should be its own microservice within our cluster. When it came time to string the microservices together to form a whole pipeline, we had a group discussion to consider the possible strategies we could use - namely &lt;em&gt;orchestration&lt;/em&gt; v.s. &lt;em&gt;choreography&lt;/em&gt;. This article brings some points from that discussion to the Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Orchestration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1519683384663-c9b34271669a%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1519683384663-c9b34271669a%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="man facing music stand with music book guide inside building"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@gwundrig?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Manuel Nägeli&lt;/a&gt; / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you go to a concert house to listen to your local symphony orchestra, arguably the most important role played in the performance is that of the &lt;em&gt;conductor&lt;/em&gt;. Each member of the orchestra acts in a synchronized manner because they're all receiving instructions from the same point of reference. Not only does the conductor synchronize the sounds of all the musicians while you're listening, the conductor shaped the music itself in rehearsals &lt;em&gt;before you ever arrived&lt;/em&gt;. Adjusting timings here and volumes there, what you are hearing is tuned by the conductor to bring you &lt;em&gt;their interpretation&lt;/em&gt; of the best possible performance.&lt;/p&gt;

&lt;p&gt;Orchestration in the context of microservices is very similar! Just as a violinist wouldn't look to the woodwinds to learn what they should be doing, one service does not need to have any knowledge of the others to play its part in the grand scheme of the application. All microservices pay attention to one system (the &lt;em&gt;orchestrator&lt;/em&gt; - generally another service on the same cluster) that is charged with determining what needs to happen, and directing the microservices under its control to perform whatever tasks are necessary in order to reach its desired outcome. The orchestrator is the centralized authority for everything that happens within its scope of control. Other services can simply be thought of as its workers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jacobsee.com%2Fcontent%2Fimages%2F2018%2F12%2FScreenshot-from-2018-12-04-17-15-27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jacobsee.com%2Fcontent%2Fimages%2F2018%2F12%2FScreenshot-from-2018-12-04-17-15-27.png"&gt;&lt;/a&gt;No services communicate without using the orchestrator to do so. Oblivious to what's going on around them, they perform tasks and return results.&lt;/p&gt;

&lt;p&gt;This strategy brings some benefits and some pitfalls to the service composability table. In the spirit of &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; - the orchestrator is a clean place to put workflow logic. Since the orchestrator has the high-level understanding of what should be happening within the system, it can provide advanced progress monitoring and error handling right out of the box. It's not all good news for the orchestration pattern though... Orchestration inherently sets up a structure with a single point of failure. If the orchestrator becomes unable to delegate tasks for any reason - it's as if the conductor of the symphony orchestra dropped dead in the middle of a performance. All progress within the system stops, and unless the fault is of a type which can be automatically detected and healed within your environment, your on-call engineer is not going to have a good night.&lt;/p&gt;

&lt;p&gt;For further reading on orchestration in practice, or to try it out yourself, you may want to check out &lt;a href="https://github.com/uber/cadence" rel="noopener noreferrer"&gt;Uber's Cadence&lt;/a&gt; or &lt;a href="https://netflix.github.io/conductor/" rel="noopener noreferrer"&gt;Netflix's Conductor&lt;/a&gt; orchestration engines. Conductor, the system I'm more familiar with, defines workflows using a JSON based DSL that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "encode_and_deploy",
  "description": "Encodes a file and deploys to CDN",
  "version": 1,
  "tasks": [
    {
      "name": "encode",
      "taskReferenceName": "encode",
      "type": "SIMPLE",
      "inputParameters": {
        "fileLocation": "${workflow.input.fileLocation}"
      }
    },
    {
      "name": "deploy",
      "taskReferenceName": "d1",
      "type": "SIMPLE",
      "inputParameters": {
        "fileLocation": "${encode.output.encodeLocation}"
      }

    }
  ],
  "outputParameters": {
    "cdn_url": "${d1.output.location}"
  },
  "schemaVersion": 2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Choreography
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1508807526345-15e9b5f4eaff%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.unsplash.com%2Fphoto-1508807526345-15e9b5f4eaff%3Fixlib%3Drb-1.2.1%26q%3D80%26fm%3Djpg%26crop%3Dentropy%26cs%3Dtinysrgb%26w%3D1080%26fit%3Dmax%26ixid%3DeyJhcHBfaWQiOjExNzczfQ" alt="group of ballerinas dancing while raising both hands"&gt;&lt;/a&gt;Photo by &lt;a href="https://unsplash.com/@mafonso?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Michael Afonso&lt;/a&gt; / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;amp;utm_medium=referral&amp;amp;utm_campaign=api-credit" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To continue with the performing arts analogies, and because the names of these strategies align so well with these examples, imagine you're attending a ballet performed by skilled dancers. There is no conductor leading them - dancing is a visual art and that would block the audience's view! The dancers are synchronized and move fluidly around each other because they've gone through rigorous training to be able to do exactly that. The absence of an orchestrator during their performance is supplanted by the fact that they each know what should be happening and when, and maintain an awareness of their surroundings and other dancers to make sure that the performance goes as planned.&lt;/p&gt;

&lt;p&gt;This is also a valid strategy in microservice coordination. Choreography is a design pattern in which there is no orchestrator present. Each service must know not only how to do its job, but also when and how to interact with other services in the same system. All components are autonomous, and typically agree to adhere to a contract, but otherwise are fully self-managed and do not receive direction from an external source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jacobsee.com%2Fcontent%2Fimages%2F2018%2F12%2FScreenshot-from-2018-12-04-17-15-56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jacobsee.com%2Fcontent%2Fimages%2F2018%2F12%2FScreenshot-from-2018-12-04-17-15-56.png"&gt;&lt;/a&gt;No orchestrator is present. Services do not receive instructions to perform tasks, rather, they &lt;em&gt;understand&lt;/em&gt; the result they are given, and know what to do with it next.&lt;/p&gt;

&lt;p&gt;Compared to orchestration, choreography is (in my opinion) quicker to get started with for a new project. It does not require the upfront time investment of learning to use someone else's orchestration engine, and lets you jump right into what you know best - code. If designed correctly, it can even be faster than an orchestrated system, as you do not incur the overhead of having to wait on the orchestrator between each logical step. Be careful though - some of the benefits that the orchestrator provides have no equivalent here. With services directly interacting with each other, error handling within the workflow is &lt;em&gt;entirely&lt;/em&gt; your responsibility. There is no mediator to intervene and prevent cascading failures. Communication within your development team becomes &lt;em&gt;especially&lt;/em&gt; critical, since the integrity of the workflow as a whole is dependent on each service obeying its contract, which &lt;em&gt;itself&lt;/em&gt; is dependent on a shared understanding between developers of how both individual services and the entire system itself should operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Orchestration and choreography are both well-accepted methods of coordinating work within complex systems, and it's entirely up to you to decide which you prefer for solving any given problem. It is also worth mentioning that you do not have to pick only one! Your system may be decomposable further into subcomponents, each of which lending itself more towards orchestration or choreography individually. As for us - we were operating on a limited timeline and chose choreography for our system out of simplicity, with workflow composability accomplished using a &lt;em&gt;routing slip&lt;/em&gt; pattern. I'm planning another article on that pattern soon, so stay tuned!&lt;/p&gt;

</description>
      <category>development</category>
      <category>microservices</category>
      <category>designpatterns</category>
    </item>
  </channel>
</rss>
