<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Evan Shortiss</title>
    <description>Evan Shortiss is a software developer who's passionate about developer and user experience. He spends his days hacking on OpenShift/Kubernetes, Mobile, Web, Kafka, and more at Red Hat. His nights are spent pretending to be a pro FPS player.</description>
    <link>https://evanshortiss.com/</link>
    <atom:link href="https://evanshortiss.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 27 Jan 2023 15:59:53 +0000</pubDate>
    <lastBuildDate>Fri, 27 Jan 2023 15:59:53 +0000</lastBuildDate>
    <generator>Jekyll v3.9.2</generator>
    
      <item>
        <title>Quarkus, Podman, and Testcontainers on macOS</title>
        <description>&lt;p&gt;Recently, I wanted to use Podman for running Testcontainers with a Quarkus application on my Mac. I followed this helpful &lt;a href=&quot;https://stephennimmo.com/using-podman-with-quarkus-and-testcontainers-on-macos/&quot;&gt;post by Stephen Nimmo&lt;/a&gt;, but my containers were not being run in the Podman VM. It turns out that running both Podman and Docker on the same machine can make things less than straightforward!&lt;/p&gt;

&lt;p&gt;If you find yourself in the same situation it’s probably because you started the Docker VM before the Podman VM (via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podman machine start&lt;/code&gt; or &lt;a href=&quot;https://podman-desktop.io/&quot;&gt;Podman Desktop&lt;/a&gt;). This means that Docker is still running and listening at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/run/docker.sock&lt;/code&gt; and therefore Testcontainers is talking to it instead of Podman.&lt;/p&gt;

&lt;p&gt;To get your containers running in the Podman VM you can stop the Docker VM by exiting Docker Desktop using &lt;a href=&quot;https://stackoverflow.com/a/68549332&quot;&gt;this helpful snippet from Stack Overflow&lt;/a&gt; and restarting the Podman machine:&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;# Stop the running Podman machine VM&lt;/span&gt;
podman machine stop

&lt;span class=&quot;c&quot;&gt;# Kill the running Docker VM and Docker Desktop&lt;/span&gt;
ps ax|grep &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; docker|egrep &lt;span class=&quot;nt&quot;&gt;-iv&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'grep|com.docker.vmnetd'&lt;/span&gt;|awk &lt;span class=&quot;s1&quot;&gt;'{print $1}'&lt;/span&gt;|xargs &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Restart the Podman machine VM&lt;/span&gt;
podman machine start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podman machine start&lt;/code&gt; command has completed it should report that it’s listening on &lt;em&gt;/var/run/docker.sock&lt;/em&gt;, since Docker no longer has a hold on it.&lt;/p&gt;

&lt;p&gt;You can now start your Quarkus application that uses Testcontainers, and they’ll be run using Podman!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: I tried setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOCKER_HOST&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE&lt;/code&gt; variables to force Quarkus and Testcontainers to bypass the Docker socket and use my Podman socket at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Users/$USER/.local/share/containers/podman/machine/podman-machine-default/podman.sock&lt;/code&gt; but this always reported errors about missing containers.&lt;/em&gt;&lt;/p&gt;

</description>
        <pubDate>Thu, 26 Jan 2023 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/quarkus-testcontainers-macos</link>
        <guid isPermaLink="true">https://evanshortiss.com/quarkus-testcontainers-macos</guid>
        
        
        <category>containers</category>
        
      </item>
    
      <item>
        <title>BlueJeans Primetime &amp; AirPods Play/Pause</title>
        <description>&lt;p&gt;This a quick, oddly specific, tip for those of you that find yourself using
Apple AirPods during a BlueJeans Primetime call.&lt;/p&gt;

&lt;p&gt;I have the “Automatic Ear Detection” setting enabled on my AirPods. This means
that my AirPods automatically pause audio/video playback on my iPhone or
MacBook when I remove one of them from my ears. It also restarts the audio/video
playback when I place the AirPod back in my ear. Neat!&lt;/p&gt;

&lt;p&gt;The “Automatic Ear Detection” feature is fantastic, but it’s not ideal when
combined with BlueJeans Primetime. Despite BlueJeans Primetime being a “live”
stream, it will pause when you remove an AirPod from your ear! BlueJeans
Primetime doesn’t expose playback controls because it’s live. Placing an
AirPod back in your ear is the only way to resume playback! Or is it?&lt;/p&gt;

&lt;p&gt;A quick fix for this, without refreshing your BlueJeans Primetime tab/browser
after removing the AirPod is to open up the developer tools in your web browser
(usually using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl + Shift + I&lt;/code&gt; on Windows and Linux, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Cmd + Opt + I&lt;/code&gt; on
macOS) and run this JavaScript in the console.&lt;/p&gt;

&lt;div class=&quot;language-js 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;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I assume it might also be possible to use media controls if your
keyboard/device is equipped with them, but I haven’t tested that.&lt;/p&gt;
</description>
        <pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/bluejeans-primetime-airpods</link>
        <guid isPermaLink="true">https://evanshortiss.com/bluejeans-primetime-airpods</guid>
        
        
        <category>tech-tips</category>
        
      </item>
    
      <item>
        <title>CodeReady Containers Everywhere and Anywhere with Tailscale</title>
        <description>&lt;p&gt;This post serves as a quick summary of a setup I’m using to access my OpenShift
cluster, based on CodeReady Containers (CRC). The TLDR is that I’m using
Tailscale to create a mesh network so I can access my devices anywhere, and
it’s awesome! Read on to learn more about the setup.&lt;/p&gt;

&lt;p&gt;&lt;img style=&quot;box-shadow: 0 0;&quot; src=&quot;/res/img/posts/2022-03-15-crc-tailscale/crc-tailscale-mesh.png&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-codeready-containers&quot;&gt;Why CodeReady Containers?&lt;/h2&gt;

&lt;p&gt;Working at Red Hat means that I frequently need access to an OpenShift  (enterprise Kubernetes)
cluster. Using OpenShift via CodeReady Containers (CRC) is a great option for
me, since I don’t always need a production grade cluster with multiple nodes
for high-availability.&lt;/p&gt;

&lt;p&gt;CRC isn’t perfect though; it requires 9 GB of memory and 4 CPU cores to run.
My MacBook Pro can handle this, but I found that I have a better experience
running CodeReady Containers by booting Fedora on my gaming PC and deploying
CRC there. This way I can assign more memory to CRC, and my MacBook doesn’t
burn my thighs! 🔥&lt;/p&gt;

&lt;p&gt;Of course, running CRC on my PC means I have to deal with some mildly painful
networking and DNS issues. It also means I can’t access my OpenShift cluster
when I’m away from home. However, a fellow Red Hatter mentioned Tailscale to me
recently, so I figured I’d try using it as a solution to these networking and
DNS challenges.&lt;/p&gt;

&lt;h2 id=&quot;networking&quot;&gt;Networking&lt;/h2&gt;

&lt;p&gt;Turns out &lt;a href=&quot;https://tailscale.com/&quot;&gt;Tailscale&lt;/a&gt; is a perfect solution to managing
a small mesh network. Tailscale provides a zero config option for connecting all
of my devices via a VPN. Basically, Tailscale enables a peer-to-peer mesh of encrypted
connections between registered your devices using the WireGuard protocol. This means
that my MacBook can communicate with my Fedora machine in my office, even when
I’m out and about.&lt;/p&gt;

&lt;p&gt;Here’s how to get started:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Sign-up for Tailscale (it’s free!)&lt;/li&gt;
  &lt;li&gt;Setup the &lt;a href=&quot;https://tailscale.com/download/&quot;&gt;Tailscale client&lt;/a&gt; on your machines (that’s macOS and Fedora in my case)&lt;/li&gt;
  &lt;li&gt;Start CRC on the Fedora PC and expose it via HAProxy (&lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_codeready_containers/1.40/html/getting_started_guide/&quot;&gt;all explained in sections 3.2 and 5.4 of the CRC docs&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the image below you can see both of my machines listed in the &lt;strong&gt;Machines&lt;/strong&gt;
section of the Tailscale UI, so I knew they were configured correctly. I further
verified connectivity by SSH’ing from my MacBook Pro to the Fedora machine
using the Tailscale interface IP address, i.e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh evan@100.80.245.58&lt;/code&gt; (&lt;a href=&quot;https://docs.fedoraproject.org/en-US/fedora/f34/system-administrators-guide/infrastructure-services/OpenSSH/#s2-ssh-configuration-sshd&quot;&gt;enable SSH on Fedora&lt;/a&gt; if you want to do this!).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2022-03-15-crc-tailscale/crc-tailscale-dashboard-nopi.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;dns&quot;&gt;DNS&lt;/h2&gt;

&lt;p&gt;The CRC documentation describes how to configure &lt;strong&gt;dnsmasq&lt;/strong&gt; on your development
machine to resolve the CRC addresses if you’re deploying CRC on a remote system
like I am, but in my case I’m taking a different approach. Technically this
approach uses &lt;strong&gt;dnsmasq&lt;/strong&gt; too, but it’ll work for all devices connected to my
Tailscale network thanks to &lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-hole&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I run &lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-hole&lt;/a&gt; on my Raspberry Pi for DNS on my home
network, since it provides a network-wide ad/tracker blocking. Pi-hole also
supports configuring custom DNS and CNAME rules for your network. I went ahead
and setup Tailscale on my Raspberry Pi to extend the Pi-hole capabilities to
devices on my Tailscale network. You can see my Raspberry Pi in the &lt;strong&gt;Machines&lt;/strong&gt;
list in my Tailscale dashboard.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2022-03-15-crc-tailscale/crc-tailscale-dashboard.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Tailscale provides a &lt;strong&gt;MagicDNS&lt;/strong&gt; feature that automatically configures DNS
entries for devices in my Tailscale network, so you can connect to devices
using their hostname instead of their Tailscale IP address. Another neat
feature that Tailscale supports is setting a global nameserver. I enabled
MagicDNS and set my Raspberry Pi as the global nameserver.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2022-03-15-crc-tailscale/crc-tailscale-magic-dns.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The last step, was to update my Pi-hole deployment to accept a custom &lt;strong&gt;dnsmasq&lt;/strong&gt;
config to handle address resolution for the CRC cluster. To do this I created a
folder named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etc-dnsmasq.d&lt;/code&gt; on my Raspberry Pi and created a file named
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;02-pihole-custom.conf&lt;/code&gt; within that folder. I then added the following lines to
the file:&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;address=/apps-crc.testing/100.80.245.58
address=/api.crc.testing/100.80.245.58
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After this I recreated my Pi-hole container (I run Pi-hole using Docker on my Raspberry Pi)
and made sure to pass the custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etc-dnsmasq.d&lt;/code&gt; folder as a volume to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v &quot;/home/pi/etc-dnsmasq.d/:/etc/dnsmasq.d/&quot;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE: It is possible to manually define the routes in the Pi-hole admin Local DNS Records UI, but this is tedious since you need to add wildcard routes individually, i.e Pi-hole does not support entering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.apps-crc.testing&lt;/code&gt;. If you prefer to use this option you need to at the very least define &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api.crc.testing&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console-openshift-console.apps-crc.testing&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oauth-openshift.apps-crc.testing&lt;/code&gt; to use the OpenShift cluster. Any OpenShift Routes you create for your applications will need to be added to the Pi-hole too, e.g &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my-java-app.apps-crc.testing&lt;/code&gt;. Personally, I prefer the dnsmasq config option, since it’s one and done!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;verification&quot;&gt;Verification&lt;/h2&gt;

&lt;p&gt;At this point, everything was in place. I was able to navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://console-openshift-console.apps-crc.testing/&lt;/code&gt;
in the web browser on my MacBook Pro, accept the self-signed TLS certificate
warnings, and access my OpenShift cluster powered by CodeReady Containers!&lt;/p&gt;

&lt;p&gt;I created a Project and deployed a sample Quarkus application. This created a HTTP
Route that I was able to access and use to verify that wildcard domains such as
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.apps-crc.testing/&lt;/code&gt; were resolved correctly too. Now I can access my OpenShift
cluster running on my Fedora PC from anywhere!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2022-03-15-crc-tailscale/crc-tailscale-quarkus-app.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 15 Mar 2022 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/crc-tailscale</link>
        <guid isPermaLink="true">https://evanshortiss.com/crc-tailscale</guid>
        
        
        <category>openshift</category>
        
      </item>
    
      <item>
        <title>How I fixed my AMD Driver Timeout Issues and retained 8/10 Bits per Color</title>
        <description>&lt;h2 id=&quot;summarytldr&quot;&gt;Summary/TLDR&lt;/h2&gt;

&lt;p&gt;My driver timeouts are related to my 144 Hz monitor and AMD GPU driver not playing
nice, as far as I can tell. This &lt;a href=&quot;https://www.youtube.com/watch?v=Td3mBgE1Dsc&quot;&gt;YouTube video&lt;/a&gt; has an even quicker fix.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I think this fix is just a trivial config change, but follow this guide at your own risk. If something goes wrong use Safe-Mode and the CRU reset script to fix it, etc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First, confirm your driver timeout has a similar cause to mine. You can verify this by
checking your VRAM frequency. If the VRAM frequency is constantly maxed out, then
you might have the same or similar issue. This frequency should be able to
drop to a few hundred MHz when the PC is idle, but mine was locked at 1750MHz
which is the default max VRAM speed for my 5600 XT GPU. Remember to close all
other programs when checking this value using &lt;a href=&quot;https://www.techpowerup.com/download/gpu-z/&quot;&gt;GPU-Z&lt;/a&gt;
or the Radeon Software Metrics, as shown.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/summary-high-clocks.png&quot; alt=&quot;High VRAM Clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are two straightforward solutions to this problem. The easiest option is
to change your refresh rate to 120Hz in the Advanced display settings of
Windows, but what if you’ve payed for 24Hz more that your consciousness can
barely perceive? 😉&lt;/p&gt;

&lt;table style=&quot;text-align: center;margin: auto;padding: 1.5em 1em;&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;padding: 0 1em;&quot;&gt;Refresh Rate&lt;/th&gt;
      &lt;th style=&quot;padding: 0 1em;&quot;&gt;Frametime&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;144Hz&lt;/td&gt;
      &lt;td&gt;6.944ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;120Hz&lt;/td&gt;
      &lt;td&gt;8.333ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;60Hz&lt;/td&gt;
      &lt;td&gt;16.666ms&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;If you really want 144/165Hz then you need to correct your high refresh rate to
use correct/standard VESA timings with CRU (Custom Resolution Utility) like
so:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download &lt;a href=&quot;https://www.techspot.com/downloads/7345-custom-resolution-utility.html&quot;&gt;CRU&lt;/a&gt; and unzip it.&lt;/li&gt;
  &lt;li&gt;Start the CRU program by double-clicking the blue icon.&lt;/li&gt;
  &lt;li&gt;Double-click your 144/165Hz resolution from the &lt;em&gt;Detailed resolutions&lt;/em&gt; list. This should be the same as the resolution you’ve set in your Windows Advanced Display settings.&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Change the &lt;em&gt;Timing&lt;/em&gt; setting from &lt;strong&gt;Manual&lt;/strong&gt; to &lt;strong&gt;Automatic (PC)&lt;/strong&gt;. Don’t change anything else.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/summary-cru-automatic-pc.png&quot; alt=&quot;CRU Automatic Timing&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Click OK to save the new timing values.&lt;/li&gt;
  &lt;li&gt;Close CRU by clicking OK.&lt;/li&gt;
  &lt;li&gt;Run the &lt;em&gt;restart64&lt;/em&gt; script included with CRU.&lt;/li&gt;
  &lt;li&gt;Wait for your monitor to refresh.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your VRAM frequency should now be idling at a lower value than it was prior
to adjusting the timing using CRU. You can see that my 5600 XT VRAM idles at
200MHz after applying this fix.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/summary-fixed-clocks.png&quot; alt=&quot;Fixed VRAM Clock&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This completely resolves the crashes on my PC. I don’t entirely understand why,
but I won’t question it. I can even overclock my VRAM and it will happily run
constantly at over 1800 Mhz when gaming, so it’s not the memory frequency.&lt;/p&gt;

&lt;p&gt;Using the automatic CRU timings at 144Hz supports 8-bit colour on my monitor.
The highest refresh rate I can obtain with 10-bit colour via CRU is 138Hz.
I also found that I could leave all timing settings in CRU at the defaults and
simply increase the Vertical Blank from the default value. Increasing it from
the default of 56 to 98 works for me, but the exact values will probably vary
across monitors.&lt;/p&gt;

&lt;p&gt;That’s all folks! Hopefully fixing your monitor timings and vertical blanking
resolves your AMD driver timeouts like it did for me.&lt;/p&gt;

&lt;p&gt;No need to read beyond here unless you’re bored and want some background
information!&lt;/p&gt;

&lt;h2 id=&quot;the-issue&quot;&gt;The Issue&lt;/h2&gt;

&lt;p&gt;Does the following image make your blood boil? It sure as shit had an
affect on me this past year. The idea that your GPU might be toast sucks, but
it’s a complete nightmare in the current PC hardware market. Thankfully, for
some of us, it’s just a software issue related to our GPU and monitor not
playing nice!&lt;/p&gt;

&lt;p&gt;For some people this dialog isn’t displayed, but they get a warning in their
Windows logs that states “driver amdkmdag stopped responding and has
successfully recovered”.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vMw4VvX.png&quot; alt=&quot;AMD Driver Timeout Dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;There are many potential causes for this issue. Poor RAM compatibility,
unstable RAM timings, unstable overclock, and a weak PSU seem to be common
causes. In my case it appears to be a specific AMD Driver issue due to my GPU
and 144Hz monitor combination.&lt;/p&gt;

&lt;p&gt;I’m the owner of an MSI &lt;em&gt;Radeon RX 5600 XT GAMING X&lt;/em&gt;. It’s a fantastic
GPU for the price, considering it gives an &lt;a href=&quot;https://www.youtube.com/watch?v=qKh-z-tLUxQ&quot;&gt;RTX 2060 &lt;strong&gt;Super&lt;/strong&gt; a run for it’s money&lt;/a&gt;, assuming you can find one at MSRP these days. Its cooler is
an &lt;a href=&quot;https://knowyourmeme.com/memes/absolute-unit&quot;&gt;absolute unit&lt;/a&gt;, so it has a
nice feature that allows it to disable the fans when not gaming. Unfortunately
the AMD drivers combined with my monitor have a &lt;em&gt;bit&lt;/em&gt; of an issue when gaming
at 1440p 144Hz in Windows; the driver crashes occasionally. And yes, based on
my limited testing, Fedora Linux 34 does not suffer from this issue when gaming
at 1440p 144Hz.&lt;/p&gt;

&lt;p&gt;This GPU had a &lt;a href=&quot;https://www.techspot.com/news/85113-amd-recommends-radeon-rx-5600-xt-owners-update.html&quot;&gt;bit of rough a launch&lt;/a&gt;,
so I cut it some slack when this issue started occurring. After multiple driver
updates I started to lose hope though. The awful state of the GPU market made
matters even worse. Returning it would result in credit/cash towards a
new massively overpriced GPU. Worse again, I bought it from the UK so I wasn’t
sure how the Brexit shenanigans might affect the return process. Another matter
of concern is that this appears to be a software issue when using certain
monitors and refresh rates, so the return would probably have been rejected!&lt;/p&gt;

&lt;p&gt;After trying &lt;del&gt;almost&lt;/del&gt; literally every fix posted online, I eventually
found that I could fix the problem by lowering my refresh rate to 120Hz. I
wasn’t totally satisfied with this so I kept digging, as you’ll see in this
post.&lt;/p&gt;

&lt;p&gt;Is this the definition first world problem? Sure is. But on the other hand I
paid for the whole monitor and GPU, so
&lt;a href=&quot;https://www.reddit.com/r/pcmasterrace/search/?q=i%20paid%20for%20the&amp;amp;restrict_sr=1&quot;&gt;I’m gonna use the whole monitor and GPU&lt;/a&gt;!&lt;/p&gt;

&lt;h2 id=&quot;the-clue&quot;&gt;The Clue&lt;/h2&gt;

&lt;p&gt;When AMD released the 21.7.1 drivers I decided to see if the 144Hz crashing issue
was fixed. Spoiler alert: it’s not fixed, at least not for all of us. During
my latest test I noticed that my VRAM was constantly locked at 1750Mz when I
set my refresh rate to 144Hz. This is fine when gaming, but why would it be
locked so high when the PC is idle? As soon as I changed my refresh rate back
to 120Hz the VRAM was able to fluctuate between 200Mhz and 1750Mhz, depending
on workload. Why is it fine pinned to 1750Mhz gaming at 120Hz, but not fine
when pinned at 1750Mhz and gaming at 144Hz? I still don’t fully understand,
but I knew that I had found a lead. Another interesting aspect to this bug is
that my VRAM can overclock to more than 1800Mhz just fine, it’s only when 144Hz
is being used that I experience the random driver timeouts.&lt;/p&gt;

&lt;p&gt;Using the VRAM clock speed clue leads us to a fantastic solution by
&lt;a href=&quot;https://www.youtube.com/watch?v=Td3mBgE1Dsc&quot;&gt;Hard Reset on YouTube&lt;/a&gt;. Their
video demonstrates that creating a Custom Resolution solution solves the issue.
I created a 1440p custom resolution with a 142Hz refresh rate, and my crashing
problems were solved. So, is this it? Case closed? Not entirely!&lt;/p&gt;

&lt;p&gt;Image quality seemed different when using my new custom resolution. It wasn’t very
noticeable, but what has been seen cannot be unseen. Every time I opened a new
tab in my web browser I could see a grain on the blank page if I looked closely.
I think this was because the custom resolution was limiting colour depth.
Radeon Software listed 8 and 10 bit colour options, but selecting either would
fail and revert back to 6 bpc after using the technique from that YouTube
video.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/radeon-6bpc.png&quot; alt=&quot;Radeon Software Limited to 6 bpc&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-my-solution&quot;&gt;&lt;del&gt;The&lt;/del&gt; My Solution&lt;/h2&gt;

&lt;p&gt;I’m not a video expert, but I assume the custom resolution configuration was
maxing out the available bandwidth on my DisplayPort. Now, I know for a fact
that I can run at 1440p 144Hz with 10 bit colour via DisplayPort on my hardware
since the default resolutions support this, albeit with an occasional driver
crash when gaming. Knowing this, I spent a few minutes tweaking the custom
resolution settings in Radeon Software to try get it to expand the colour
depth while retaining a high-refresh rate. After 5 minutes I accepted
defeat 😢&lt;/p&gt;

&lt;p&gt;This is when I turned to &lt;a href=&quot;https://www.techspot.com/downloads/7345-custom-resolution-utility.html&quot;&gt;CRU (Custom Resolution Utility)&lt;/a&gt;.
It was mentioned in one of the threads I found online when researching this
issue, so I figured it was worth a try.&lt;/p&gt;

&lt;p&gt;Here’s how I used CRU to create a high-refresh rate that supported the full
colour depth of my monitor:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit: This solution is for 10 bpc. Use the one from the top of the article if 144Hz is more important to you than having 10 bpc.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Start the CRU program.&lt;/li&gt;
  &lt;li&gt;Take note of the &lt;strong&gt;Pixel Clock&lt;/strong&gt; listed beside the default entry for 144Hz (or whatever resolution/refresh rate combination is causing trouble for you).&lt;/li&gt;
  &lt;li&gt;Add a new entry under the &lt;strong&gt;Detailed Resolutions&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Double click this new entry to open the Edit dialog.&lt;/li&gt;
  &lt;li&gt;Select &lt;em&gt;Automatic (PC)&lt;/em&gt; from the &lt;strong&gt;Timing&lt;/strong&gt; dropdown.&lt;/li&gt;
  &lt;li&gt;Set the &lt;strong&gt;Refresh Rate&lt;/strong&gt; to a value that results in a &lt;strong&gt;Pixel Clock&lt;/strong&gt; that’s equal to, or less than, the default value.&lt;/li&gt;
  &lt;li&gt;Click the OK button to save the custom resolution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point you will have two resolutions listed like so. The &lt;strong&gt;Pixel Clock&lt;/strong&gt;
of the new resolution does not have to match the original perfectly, just be
sure it’s &lt;strong&gt;equal or lower&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/cru-list.png&quot; alt=&quot;CRU Listing&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I managed to get them to align at 568.72Mhz while keeping a 136Hz refresh rate
somehow by setting the timing back to &lt;strong&gt;Manual&lt;/strong&gt; and editing the
&lt;strong&gt;Pixel Clock&lt;/strong&gt;, but can’t get it to happen when I try again when testing it
for this blog! I’m too lazy to spend more time trying to squeeze the refresh
rate higher, and the difference between 144Hz and 136Hz is less than half a
millisecond so who cares. Wait, couldn’t you make a similar argument for 120Hz
vs 136Hz and that I shouldn’t have wasted my time on this first world problem? 🤔&lt;/p&gt;

&lt;table style=&quot;text-align: center;margin: auto;padding: 1.5em 1em;&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;padding: 0 1em;&quot;&gt;Refresh Rate&lt;/th&gt;
      &lt;th style=&quot;padding: 0 1em;&quot;&gt;Frametime&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;144Hz&lt;/td&gt;
      &lt;td&gt;6.944ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;136Hz&lt;/td&gt;
      &lt;td&gt;7.353ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;120Hz&lt;/td&gt;
      &lt;td&gt;8.333ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;100Hz&lt;/td&gt;
      &lt;td&gt;10.000ms&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;60Hz&lt;/td&gt;
      &lt;td&gt;16.666ms&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;No, this effort is for “science”! Anyways, click the “OK” button to close CRU.
Run the &lt;strong&gt;restart64&lt;/strong&gt; executable included with your CRU download. Your screen
will go blank for a moment, then come back to life. Now, head over to the
Windows display settings.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/display-settings.png&quot; alt=&quot;Windows display settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Verify that the selected resolution matches that one you created in CRU, then sroll down and select
&lt;strong&gt;Advanced display settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/advanced-display-settings-before.png&quot; alt=&quot;Windows display settings before&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Take note of the listed &lt;strong&gt;Bit depth&lt;/strong&gt;, e.g 10-bit in my case. Select your
custom refresh rate. The screen will go blank briefly, and should return
to normal after a second. If everything seems fine click the
&lt;strong&gt;Keep changes&lt;/strong&gt; button. If the screen remains blank don’t panic! It just
means your custom refresh and resolution isn’t supported, but Windows will
revert the change after 30 seconds.&lt;/p&gt;

&lt;p&gt;If you were able to select &lt;strong&gt;Keep changes&lt;/strong&gt;, verify that the listed
&lt;strong&gt;Bit depth&lt;/strong&gt; matches what it was previously. You should also verify that your
monitor is displaying the refresh rate correctly. Many monitors include an
overlay that displays the active refresh rate. For example, my monitor has it
built into the settings overlay &lt;a href=&quot;https://www.tftcentral.co.uk/images/lg_27gl850/P1210225.JPG&quot;&gt;like this&lt;/a&gt;.
This verification is important, since I found that Windows might accept the
custom resolution and refresh rate, but in reality your monitor will only
output half of the refresh rate. For example, I had a 140Hz refresh rate
configured, but my monitor was only displaying at 70Hz which was pretty
noticeably slower than 140Hz, and also easily confirmed via the monitor
settings!&lt;/p&gt;

&lt;p&gt;You can see that my custom refresh rate of 136Hz retains 10 bit depth. It
might be possible to push the refresh rate higher and maintain the 10 bits, but
I’ve been unable to find a setting that does so with my monitor and GPU. A few
tutorials state that increasing the blanking on the default resolution profile
does the trick too, so it might be worth trying.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2021-07-26-amd-driver-memory-lock-timeout/advanced-display-settings-after.png&quot; alt=&quot;Windows display settings after&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 26 Jul 2021 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/amd-driver-memory-lock-timeout</link>
        <guid isPermaLink="true">https://evanshortiss.com/amd-driver-memory-lock-timeout</guid>
        
        
        <category>windows</category>
        
        <category>amd</category>
        
        <category>drivers</category>
        
      </item>
    
      <item>
        <title>Excluding files from local Source-to-Image (s2i CLI) Builds</title>
        <description>&lt;p&gt;TLDR; Use the flag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--exclude &quot;(^|/)\.git|node_modules|some_other_folder(/|$)&quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.openshift.com/container-platform/4.7/openshift_images/using_images/using-s21-images.html&quot;&gt;Source-to-Image (s2i)&lt;/a&gt;
provides a convenient solution for developers looking to easily
create OpenShift-ready container images from their code.&lt;/p&gt;

&lt;p&gt;A simple way to build and deploy an application on OpenShift using s2i is via
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc new-app&lt;/code&gt; command.&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;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUILDER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;registry.access.redhat.com/ubi8/nodejs-14
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://github.com/sclorg/nodejs-ex.git

oc new-app &lt;span class=&quot;nv&quot;&gt;$BUILDER&lt;/span&gt;~&lt;span class=&quot;nv&quot;&gt;$REPO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s also possible to use s2i to build code in your development environment
using the &lt;a href=&quot;https://github.com/openshift/source-to-image/releases&quot;&gt;s2i CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When building a Node.js application locally you might have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.env&lt;/code&gt;,
&lt;em&gt;node_modules/&lt;/em&gt;, and other files that you’d rather not pass to the s2i build.
These might be files that are typically included in your &lt;em&gt;.gitignore&lt;/em&gt; or a
&lt;em&gt;.dockerignore&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I recently learned that s2i supports an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--exclude&lt;/code&gt; flag to support this
behaviour. The help output for this flag is surprisingly, well, helpful:&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;Regular expression for selecting files from the source tree to exclude
from the build, where the default excludes the '.git' directory
(see https://golang.org/pkg/regexp for syntax, but note that &quot;&quot;
will be interpreted as allow all files and exclude no files)
(default &quot;(^|/)\.git(/|$)&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, I don’t know about you, but I immediately assumed this would be pain in
the ass to configure. 99% of the time I write RegEx I do so in JavaScript, and
I use the Node.js REPL to experiment and get it right.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgflip.com/5exkyb.jpg&quot; alt=&quot;I'm tired of pretending it's not meme for Regex&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Much to my surprise though, I got lucky with this one on the first try.
Seriously. I know the RegEx ninjas out there think I’m worthless but
sometimes I get lucky! 🤷‍♂️&lt;/p&gt;

&lt;p&gt;Here’s how you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--exclude&lt;/code&gt; to exclude &lt;em&gt;node_modules&lt;/em&gt; and two other
directories:&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;# Output image name&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;IMAGE_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;quay.io/evanshortiss/rhosak-nodejs-sbo-example

&lt;span class=&quot;c&quot;&gt;# Base image used to create the build&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUILDER_IMAGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;registry.access.redhat.com/ubi8/nodejs-14

&lt;span class=&quot;c&quot;&gt;# Build the local code into a container image, but do not pass&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the .git, node_modules, or .bindingroot directory to the build&lt;/span&gt;
s2i build &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--exclude&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;(^|/)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;git|.bindingroot|node_modules(/|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$BUILDER_IMAGE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$IMAGE_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Tue, 29 Jun 2021 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/source-to-image-excludes</link>
        <guid isPermaLink="true">https://evanshortiss.com/source-to-image-excludes</guid>
        
        
        <category>openshift</category>
        
        <category>nodejs</category>
        
      </item>
    
      <item>
        <title>Pretty Print for Pino Logs with Stern</title>
        <description>&lt;p&gt;Posting this almost as a “note to self”, so I’ll keep it short.&lt;/p&gt;

&lt;p&gt;Here’s how to pretty print &lt;a href=&quot;https://github.com/pinojs/pino&quot;&gt;pino&lt;/a&gt; logs if
you’re using &lt;a href=&quot;https://github.com/wercker/stern&quot;&gt;stern&lt;/a&gt; to view them from a
Node.js Pod running on Kubernetes or OpenShift:&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;stern &lt;span class=&quot;nv&quot;&gt;$POD_SELECTOR&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; raw | npx pino-pretty &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx pino-pretty&lt;/code&gt;, but running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm install -g pino-pretty&lt;/code&gt; will allow
you to do the following:&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;# Install pino-pretty globally&lt;/span&gt;
npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; pino-pretty

&lt;span class=&quot;c&quot;&gt;# Use it to view the logs&lt;/span&gt;
stern &lt;span class=&quot;nv&quot;&gt;$POD_SELECTOR&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; raw | pino-pretty &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 13 May 2021 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/stern-pino-pretty-logs</link>
        <guid isPermaLink="true">https://evanshortiss.com/stern-pino-pretty-logs</guid>
        
        
        <category>kubernetes</category>
        
        <category>openshift</category>
        
        <category>nodejs</category>
        
      </item>
    
      <item>
        <title>Hosting CodeReady Containers on DigitalOcean</title>
        <description>&lt;p&gt;&lt;em&gt;Note: The official documentation for CodeReady Containers now includes a section similar to this post. You can find the official documentation &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_codeready_containers/1.19/html-single/getting_started_guide/index#setting-up-remote-server_gsg&quot;&gt;at this link&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a slightly customised version of Jason Dobies’ post &lt;a href=&quot;https://www.openshift.com/blog/accessing-codeready-containers-on-a-remote-server/&quot;&gt;on the Official OpenShift blog&lt;/a&gt;. That blog was inspired by Trevor McKay’s GitHub Gist &lt;a href=&quot;https://gist.github.com/tmckayus/8e843f90c44ac841d0673434c7de0c6a&quot;&gt;found here&lt;/a&gt;. That means I’m being incredibly derivative with this one, but I had fun and I hope you find it interesting!&lt;/p&gt;

&lt;p&gt;Like Jason, I sometimes want admin access to a full, but lightweight OpenShift development environment. Running &lt;a href=&quot;https://developers.redhat.com/products/codeready-containers/overview&quot;&gt;CodeReady Containers&lt;/a&gt; on my Macbook works well, but it’s not fun when Firefox, Chrome, Visual Studio Code are also in competition for my 16 gigs of memory. I’d also rather my Macbook’s fans didn’t spin at 100% speed all the time.&lt;/p&gt;

&lt;p&gt;I have a gaming PC (look, I’d be fooling no one if I said it’s primary purpose is productivity) that could easily run CodeReady Containers, but it only has a single 1TB drive with Windows 10 installed. Partitioning my drive, or running CodeReady Containers on Windows didn’t sound like exciting prospects, and waiting for an SSD delivery would require patience, so I figured I’d try something else.&lt;/p&gt;

&lt;p&gt;This post outlines how I setup a DigitalOcean Droplet to run OpenShift 4 via CodeReady Containers, and how I configured my Macbook’s DNS to communicate with it.&lt;/p&gt;

&lt;h2 id=&quot;digitalocean-droplet-setup&quot;&gt;DigitalOcean Droplet Setup&lt;/h2&gt;

&lt;p&gt;I’m going to assume you have a DigitalOcean account with a credit card attached to it. If you don’t, go ahead and set that up before reading any further. They’re not going to let you run a Droplet with 8 vCPU and 16GB of RAM for free!&lt;/p&gt;

&lt;p&gt;Get started by creating a new project on DigitalOcean. Name it something boring, like “CRC OpenShift 4”, and give it a similarly sensible description.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/do-setup-00.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Creating a project for CodeReady Containers on DigitalOcean.&lt;/p&gt;

&lt;p&gt;Next, create a &lt;strong&gt;Fedroa 32&lt;/strong&gt; Droplet in that project you just created. You’ll want something beefy since the &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_codeready_containers/1.0/html/getting_started_guide/getting-started-with-codeready-containers_gsg#minimum-system-requirements_gsg&quot;&gt;CodeReady Containers documentation&lt;/a&gt; states that it requires:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4 vCPUs&lt;/li&gt;
  &lt;li&gt;8GB RAM (I’ve found it uses closer to 9GB)&lt;/li&gt;
  &lt;li&gt;35 GB of disk space&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Basic Droplet type with 8 vCPUs and 16GB RAM does the trick. This costs approximately 12 USD cents an hour, or 80 USD/month. Even though this is a throwaway single node OpenShift cluster, make sure you configure an SSH Key for authentication, because &lt;a href=&quot;https://youtu.be/9NZDwZbyDus?t=68&quot;&gt;professionals have standards&lt;/a&gt; (warning: cartoon violence)!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/do-setup-01.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;The Droplet I chose has 8vCPUs and 16GB RAM. Nice!&lt;/p&gt;

&lt;p&gt;While the Droplet is being created, head to the &lt;em&gt;Networking&lt;/em&gt; section and click &lt;em&gt;Create Firewall&lt;/em&gt; in the &lt;em&gt;Firewalls&lt;/em&gt; section. Add the following rules:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;SSH&lt;/li&gt;
  &lt;li&gt;HTTP&lt;/li&gt;
  &lt;li&gt;HTTPS&lt;/li&gt;
  &lt;li&gt;TCP 6443 (use the &lt;em&gt;Custom&lt;/em&gt; option in the &lt;em&gt;New Rule&lt;/em&gt; dropdown)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your rules should look similar to the screenshot below. Don’t forget to actually apply the rules to your new Fedora 32 Droplet!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/do-setup-04.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;software-versions-and-other-notes&quot;&gt;Software Versions and Other Notes&lt;/h2&gt;

&lt;p&gt;The bash commands throughout this guide reference a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$DROPLET_IP&lt;/code&gt; variable. As you may have guessed, this is the IPv4 address that’s listed alongside your shiny new Fedora 32 Droplet in the DigitalOcean UI. Anywhere you see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$DROPLET_IP&lt;/code&gt;, replace it with your Droplet’s IPv4 address.&lt;/p&gt;

&lt;p&gt;Also, take note of this list. If you encounter issues recreating my setup, it might due to a version mismatch* with these:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Fedora 32&lt;/li&gt;
  &lt;li&gt;CodeReady Containers version: 1.16.0+bf72d3a&lt;/li&gt;
  &lt;li&gt;HA-Proxy version 2.1.7 2020/06/09&lt;/li&gt;
  &lt;li&gt;NetworkManager 1.22.10-1.fc32&lt;/li&gt;
  &lt;li&gt;firewalld 0.8.3&lt;/li&gt;
  &lt;li&gt;libvirtd 6.1.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;* and hopefully not an error in my instructions!&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;user-setup&quot;&gt;User Setup&lt;/h2&gt;

&lt;p&gt;SSH into your Droplet once it finishes provisioning. Read this &lt;a href=&quot;https://www.digitalocean.com/docs/droplets/how-to/connect-with-ssh/&quot;&gt;DigitalOcean How-To&lt;/a&gt; for SSH if you’re having trouble.&lt;/p&gt;

&lt;p&gt;CodeReady Containers should not be run as the root user, so the first thing to do is create another user, e.g &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc-user&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;ssh root@&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&lt;/span&gt;
useradd crc-user
passwd crc-user
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;usermod &lt;span class=&quot;nt&quot;&gt;-aG&lt;/span&gt; wheel crc-user
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that the user is added, you can copy add your SSH key to the authorised
keys for the new user. Run this from your development/client machine:&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;ssh-copy-id crc-user@&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There’s more you can do to secure your Droplet, but that’s outside the scope of this guide. Step 2 in &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/initial-setup-of-a-fedora-22-server&quot;&gt;this guide&lt;/a&gt; for Fedora is a start!&lt;/p&gt;

&lt;h2 id=&quot;install-dependencies-and-confgure-firewalld&quot;&gt;Install Dependencies and Confgure firewalld&lt;/h2&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;# Note that you SSH as crc-user this time!&lt;/span&gt;
ssh crc-user@&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dnf &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;NetworkManager haproxy firewalld policycoreutils-python-utils
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl start libvirtd
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl start firewalld
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class=&quot;nt&quot;&gt;--add-port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;80/tcp &lt;span class=&quot;nt&quot;&gt;--permanent&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class=&quot;nt&quot;&gt;--add-port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;6443/tcp &lt;span class=&quot;nt&quot;&gt;--permanent&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class=&quot;nt&quot;&gt;--add-port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;443/tcp &lt;span class=&quot;nt&quot;&gt;--permanent&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl restart firewalld
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;semanage port &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; http_port_t &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; tcp 6443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Before moving on, verify that a valid &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameserver&lt;/code&gt; entry is set in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/resolv.conf&lt;/code&gt;. The primary nameserver needs to be set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; so OpenShift Routes can be resolved. I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.1.1&lt;/code&gt; as my secondary nameserver. The updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/resolv.conf&lt;/code&gt; will look like this:&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;# Generated by NetworkManager
nameserver 127.0.0.1
nameserver 1.1.1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;download-and-run-codeready-containers&quot;&gt;Download and Run CodeReady Containers&lt;/h2&gt;

&lt;p&gt;I’ve included a URL in the first step here for completeness, but if it fails you can check &lt;a href=&quot;https://cloud.redhat.com/openshift/install/crc/installer-provisioned&quot;&gt;cloud.redhat.com/openshift/install/crc/installer-provisioned&lt;/a&gt; to find a working URL.&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;# Download, unpack, and move CRC the binary onto the PATH&lt;/span&gt;
curl https://mirror.openshift.com/pub/openshift-v4/clients/crc/latest/crc-linux-amd64.tar.xz &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; crc-linux-amd64.tar.xz
&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-xf&lt;/span&gt; crc-linux-amd64.tar.xz
&lt;span class=&quot;nb&quot;&gt;sudo mv &lt;/span&gt;crc-linux-1.16.0-amd64/crc /usr/local/bin/

&lt;span class=&quot;c&quot;&gt;# Verify the binary is on the PATH. It should print version information.&lt;/span&gt;
crc version

&lt;span class=&quot;c&quot;&gt;# If the version command worked, run this setup command&lt;/span&gt;
crc setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc setup&lt;/code&gt; command takes a minute to run, so now is a good time to get a “pull secret” prepared for the next step.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-pull-secret.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;Obtain the Pull Secret under the cluster creation UI. You can also find the CodeReady Containers download URL here by right-clicking the “Download Code-Ready Containers” button and choosing “Copy Link Location”.&lt;/p&gt;

&lt;p&gt;Your pull secret is found on &lt;a href=&quot;https://cloud.redhat.com/openshift/&quot;&gt;cloud.redhat.com&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Login to &lt;a href=&quot;https://cloud.redhat.com/openshift/&quot;&gt;cloud.redhat.com/openshift&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Select &lt;em&gt;Clusters&lt;/em&gt; from the side menu.&lt;/li&gt;
  &lt;li&gt;Click the &lt;em&gt;Create&lt;/em&gt; button on the clusters listing page.&lt;/li&gt;
  &lt;li&gt;Choose &lt;em&gt;Red Hat OpenShift Container Platform&lt;/em&gt; as the cluster type.&lt;/li&gt;
  &lt;li&gt;Choose the &lt;em&gt;Run on Laptop&lt;/em&gt; option.&lt;/li&gt;
  &lt;li&gt;Click the &lt;em&gt;Copy Pull Secret&lt;/em&gt; button.&lt;/li&gt;
  &lt;li&gt;Return to your SSH session.&lt;/li&gt;
  &lt;li&gt;Paste your pull secret into a file, e.g &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vi ~/crc-pull-secret.json&lt;/code&gt; and paste the contents.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc start -p ~/crc-pull-secret.json&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;CodeReady Containers will take a minute or two to start, so this is a good time to mindlessly browse Reddit or Twitter.&lt;/p&gt;

&lt;h2 id=&quot;configure-haproxy&quot;&gt;Configure HAProxy&lt;/h2&gt;

&lt;p&gt;HAProxy will route HTTP and HTTPS traffic to CodeReady Containers:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Backup the default HAProxy configuration: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.original&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Obtain the CodeReady Containers VM IP using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc ip&lt;/code&gt; command.&lt;/li&gt;
  &lt;li&gt;Paste the following configuration into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt;, being sure to &lt;strong&gt;replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CRC_IP&lt;/code&gt;&lt;/strong&gt; strings with the IP obtained using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc ip&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&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;defaults
    mode http
    log global
    option httplog
    option  http-server-close
    option  dontlognull
    option  redispatch
    option  contstats
    retries 3
    backlog 10000
    timeout client          25s
    timeout connect          5s
    timeout server          25s
    timeout tunnel        3600s
    timeout http-keep-alive  1s
    timeout http-request    15s
    timeout queue           30s
    timeout tarpit          60s
    default-server inter 3s rise 2 fall 3
    option forwardfor

frontend apps
    bind 0.0.0.0:80
    bind 0.0.0.0:443
    option tcplog
    mode tcp
    default_backend apps

backend apps
    mode tcp
    balance roundrobin
    option tcp-check
    server webserver1 192.168.130.11 check port 80

frontend api
    bind 0.0.0.0:6443
    option tcplog
    mode tcp
    default_backend api

backend api
    mode tcp
    balance roundrobin
    option tcp-check
    server webserver1 192.168.130.11:6443 check port 6443
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start HAProxy using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl start haproxy&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;If HAProxy fails to start, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haproxy -f /etc/haproxy/haproxy.cfg -db&lt;/code&gt; to view logs and diagnose the issue.&lt;/p&gt;

&lt;p&gt;Note that the &lt;strong&gt;CodeReady Container VM IP can change&lt;/strong&gt; on reboots/restarts. If the IP changes you’ll need to update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haproxy.cfg&lt;/code&gt; with the new IP and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo systemctl restart haproxy&lt;/code&gt;!&lt;/p&gt;

&lt;h2 id=&quot;configure-dns-on-your-development-machine&quot;&gt;Configure DNS on your Development Machine&lt;/h2&gt;

&lt;p&gt;As Jason said in his post, there’s no right or wrong way to do this, but you’ll need to tell your development machine how to resolve the CodeReady Container URLs, i.e to resolve URLs with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing&lt;/code&gt; suffix to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$DROPLET_IP&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is pretty easy to configure for the static OpenShift routes via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; on Linux and macOS. Add the following line to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; file, and don’t forget to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$DROPLET_IP&lt;/code&gt; with the correct value:&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;# CodeReady Containers (running on DigitalOcean)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&lt;/span&gt; api.crc.testing oauth-openshift.apps-crc.testing console-openshift-console.apps-crc.testing default-route-openshift-image-registry.apps-crc.testing
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;DNS resolution for deployed application Routes is a little less straightforward. This is because they are wildcard routes, i.e &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.apps-crc.testing&lt;/code&gt;. I had hoped my &lt;a href=&quot;https://pi-hole.net/&quot;&gt;PiHole&lt;/a&gt; would help with this, but it doesn’t seem to support setting a redirect for wildcards. I used &lt;strong&gt;dnsmsaq&lt;/strong&gt; instead.&lt;/p&gt;

&lt;p&gt;These commands will configure &lt;strong&gt;dnsmasq&lt;/strong&gt; to resolve your CodeReady Containers installation on DigitalOcean on macOS:&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;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dnsmasq

&lt;span class=&quot;c&quot;&gt;# Commands based on: https://asciithoughts.com/posts/2014/02/23/setting-up-a-wildcard-dns-domain-on-mac-os-x/&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;address=/.testing/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&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; /usr/local/etc/dnsmasq.conf

&lt;span class=&quot;c&quot;&gt;# Copy the daemon config plist&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-fv&lt;/span&gt; /usr/local/opt/dnsmasq/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.plist /Library/LaunchDaemons
&lt;span class=&quot;nb&quot;&gt;sudo chown &lt;/span&gt;root /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

&lt;span class=&quot;c&quot;&gt;# Approve the request to &quot;accept incoming&quot; connections if asked&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

&lt;span class=&quot;c&quot;&gt;# Redirect DNS queries for &quot;testing&quot; to localhost&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /etc/resolver
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;sh &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'echo &quot;nameserver 127.0.0.1&quot; &amp;gt; /etc/resolver/testing'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, in the Network settings for your device make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; your first nameserver, then follow it up with another, e.g &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.1.1.1&lt;/code&gt; and/or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8.8.8.8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-dns-setup.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;macOS Network settings to resolve the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testing&lt;/code&gt; domain via dnsmasq, and 1.1.1.1 for everything else.&lt;/p&gt;

&lt;h2 id=&quot;revel-in-your-success&quot;&gt;Revel in Your Success&lt;/h2&gt;

&lt;p&gt;Once you have DNS configured, navigate to &lt;a href=&quot;https://console-openshift-console.apps-crc.testing/&quot;&gt;console-openshift-console.apps-crc.testing&lt;/a&gt; in your web browser. You’ll receive some very exciting SSL warnings. Yes, these are exciting because it means you’ve been successful!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-ssl-warning.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;The most exciting self-signed SSL certificate error you’ll see this week.&lt;/p&gt;

&lt;p&gt;Add an exception for the self-signed SSL certificates, and voila! You’ll be brought to the OpenShift login page. You can login using credentials found by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc console --credentials&lt;/code&gt; on your Droplet - hell yeah!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-openshift-console.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;The OpenShift Projects list. You can see I created a Project to test the basic cluster functionality.&lt;/p&gt;

&lt;p&gt;Create an OpenShift Project, and deploy a template. I went with the Node.js application because I’m a &lt;em&gt;bit&lt;/em&gt; of a JavaScript fanboi. You can see my Node.js application running in the Developer Topology view in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-javascript-app-00.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;caption&quot;&gt;A Node.js application running in my Project.&lt;/p&gt;

&lt;p&gt;Clicking the application node presents a details pane on the right. Open the URL under Routes and briefly marvel at the wonders of cloud technology.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-21-code-ready-containers-on-digital-ocean/crc-javascript-app-01.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;configure-openshift-user-logins&quot;&gt;Configure OpenShift User Logins&lt;/h2&gt;

&lt;p&gt;Last but not least, you’ll want to change the default login details. Those are the ones that can be obtained via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crc console --credentials&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;developer&lt;/code&gt; user it provides has a default password that should be changed.&lt;/p&gt;

&lt;p&gt;Here’s how you can update it:&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;ssh crc-user@&lt;span class=&quot;nv&quot;&gt;$DROPLET_IP&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# This will make the OpenShift CLI (oc) available&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;crc oc-env&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Installs htpasswd, among other things&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dnf &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;httpd-tools

&lt;span class=&quot;c&quot;&gt;# Get the kubeadmin credentials &lt;/span&gt;
crc console &lt;span class=&quot;nt&quot;&gt;--credentials&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Login as kubeadmin (you can omit the -p flag so it prompts you for the password)&lt;/span&gt;
oc login &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; kubeadmin &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$KUBEADMIN_PASSWORD&lt;/span&gt; https://api.crc.testing:6443

&lt;span class=&quot;c&quot;&gt;# Save the current htpasswd file that OpenShift is using to a local user.htpasswd file&lt;/span&gt;
oc get secrets/htpass-secret &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; openshift-config &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{.data.htpasswd}&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; users.htpasswd

&lt;span class=&quot;c&quot;&gt;# Update the password for the &quot;developer&quot; user. You can also use htpasswd&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# to add more users if you want&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEVELOPER_PASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;newsafepassword&quot;&lt;/span&gt;
htpasswd &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; users.htpasswd developer &lt;span class=&quot;nv&quot;&gt;$DEVELOPER_PASSWORD&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Output the htpasswd in base64 format&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;users.htpasswd | &lt;span class=&quot;nb&quot;&gt;base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Visit &lt;a href=&quot;https://console-openshift-console.apps-crc.testing/k8s/ns/openshift-config/secrets/htpass-secret/yaml&quot;&gt;console-openshift-console.apps-crc.testing/k8s/ns/openshift-config/secrets/htpass-secret/yaml&lt;/a&gt; and replace the existing htpasswd value in the YAML with the new one, then click &lt;em&gt;Save&lt;/em&gt;. You can also do this via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc patch&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc edit&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;Verify that the new password is working by logging in as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;developer&lt;/code&gt; user:&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;oc login &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; developer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;thats-a-wrap&quot;&gt;That’s a Wrap&lt;/h2&gt;

&lt;p&gt;Don’t forget to shutdown the Droplet when you’re not using it!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://media2.giphy.com/media/8UF0EXzsc0Ckg/giphy.gif&quot; alt=&quot;Mission Accomplished GIF&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Mon, 21 Sep 2020 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/code-ready-containers-on-digital-ocean</link>
        <guid isPermaLink="true">https://evanshortiss.com/code-ready-containers-on-digital-ocean</guid>
        
        
        <category>development</category>
        
        <category>openshift</category>
        
      </item>
    
      <item>
        <title>Create React App on OpenShift</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://create-react-app.dev/&quot;&gt;Create React App&lt;/a&gt; is described as “the best way
to start building a new single-page application in React” on 
&lt;a href=&quot;https://reactjs.org/docs/create-a-new-react-app.html&quot;&gt;reactjs.org&lt;/a&gt;. It’s
no surprise so many developers start building a modern web application using
this toolchain.&lt;/p&gt;

&lt;p&gt;This post outlines a straightforward method to deploy an NGINX container that
serves the contents of a &lt;a href=&quot;https://create-react-app.dev/&quot;&gt;Create React App&lt;/a&gt;
application, or any modern web application, on an OpenShift cluster.&lt;/p&gt;

&lt;p&gt;A custom &lt;a href=&quot;https://github.com/openshift/source-to-image#source-to-image-s2i&quot;&gt;source-to-image (s2i)&lt;/a&gt;
builder that I maintain at &lt;a href=&quot;https://github.com/evanshortiss/s2i-nodejs-nginx&quot;&gt;github.com/evanshortiss/s2i-nodejs-nginx&lt;/a&gt;
is part of the magic that simplifies this process. This builder image was
originally authored by the folks at &lt;a href=&quot;https://github.com/jshmrtn/s2i-nodejs-nginx&quot;&gt;jshmrtn&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Using this method allows you to deploy code that’s stored in a Git repository,
or to upload your local codebase directly to a &lt;a href=&quot;https://docs.openshift.com/container-platform/3.9/dev_guide/builds/index.html&quot;&gt;Build&lt;/a&gt;
running on OpenShift. Both approaches are covered below.&lt;/p&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;An OpenShift 4.x cluster&lt;/li&gt;
  &lt;li&gt;OpenShift CLI v4.x&lt;/li&gt;
  &lt;li&gt;Node.js v12 or newer&lt;/li&gt;
  &lt;li&gt;GitHub Account (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;git-method&quot;&gt;Git Method&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Note: I’m going to assume you’re starting from scratch, but if you have an existing
repository with a React application that was generated via Create React App or
that uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-scripts&lt;/code&gt; you can skip to the bash script below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To use this approach head over to &lt;a href=&quot;https://github.com/new&quot;&gt;github.com/new&lt;/a&gt;
and do the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Enter a &lt;strong&gt;Repository name&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Skip the &lt;strong&gt;Initialize this repository with&lt;/strong&gt; section&lt;/li&gt;
  &lt;li&gt;Click the &lt;strong&gt;Create repository&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the repository is created, execute the commands below to get your
web application hosted by NGINX on OpenShift.&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;# The builder image is used by OpenShift to build the application&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUILDER_IMAGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;quay.io/evanshortiss/s2i-nodejs-nginx&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;APPLICATION_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cra-application&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# REPLACE THIS WITH YOUR REPOSITORY URL&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_REPO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://github.com/yourusername/your-react-app-repo&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create a React application template using CRA, and push to&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the empty GitHub repository you created&lt;/span&gt;
npx create-react-app &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;
git remote add origin &lt;span class=&quot;nv&quot;&gt;$GIT_REPO&lt;/span&gt;
git push origin master

&lt;span class=&quot;c&quot;&gt;# Create an OpenShift project&lt;/span&gt;
oc new-project webapp

&lt;span class=&quot;c&quot;&gt;# Create a new BuildConfig on OpenShift based on the Node.js &amp;amp; NGINX builder&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# --name        The name for the output container image stream&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# --build-env   Environment variable indicating where the builder image should&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               look for the bundled web application. This is the result of&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               running &quot;npm build&quot; or &quot;yarn build&quot;. For CRA this is a &quot;build&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               directory, but for a custom webpack config it might be &quot;dist&quot;&lt;/span&gt;
oc new-app &lt;span class=&quot;nv&quot;&gt;$BUILDER_IMAGE&lt;/span&gt;~&lt;span class=&quot;nv&quot;&gt;$GIT_REPO&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--build-env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;BUILD_OUTPUT_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;build

&lt;span class=&quot;c&quot;&gt;# Expose the service via HTTP using an OpenShift Route&lt;/span&gt;
oc expose svc/&lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Check the build status&lt;/span&gt;
oc get builds

&lt;span class=&quot;c&quot;&gt;# Print the application URL (Route)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get route/&lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'{.spec.host}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To deploy future changes run oc &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc start-build $APPLICATION_NAME&lt;/code&gt;. You could
also set up a &lt;a href=&quot;https://docs.openshift.com/container-platform/4.5/builds/triggering-builds-build-hooks.html#builds-triggers_triggering-builds-build-hooks&quot;&gt;Build Trigger&lt;/a&gt;
to automate builds using a Git hook.&lt;/p&gt;

&lt;h2 id=&quot;git-less-method&quot;&gt;Git-less Method&lt;/h2&gt;

&lt;p&gt;If you’d rather develop locally and upload the application assets for builds,
then give this binary build solution a try.&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;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;APPLICATION_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;cra-application&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BUILDER_IMAGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;quay.io/evanshortiss/s2i-nodejs-nginx&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create a React application template using CRA&lt;/span&gt;
npx create-react-app &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create an OpenShift project&lt;/span&gt;
oc new-project webapp

&lt;span class=&quot;c&quot;&gt;# Create a new BuildConfig on OpenShift based on the Node.js &amp;amp; NGINX builder&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# --binary      Indicates we'll trigger builds by uploading assets directly&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# --to          The name for the output container image stream&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# --build-env   Environment variable indicating where the builder image should&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               look for the bundled web application. This is the result of&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               running &quot;npm build&quot; or &quot;yarn build&quot;. For CRA this is a &quot;build&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#               directory, but for a custom webpack config it might be &quot;dist&quot;&lt;/span&gt;
oc new-build &lt;span class=&quot;nv&quot;&gt;$BUILDER_IMAGE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--binary&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--to&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--build-env&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;BUILD_OUTPUT_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;build

&lt;span class=&quot;c&quot;&gt;# Start a Build based on the BuildConfig. Pass the local CRA project folder&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# as the build input&lt;/span&gt;
oc start-build &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--from-dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Create a Deployment using the latest tag on the application ImageStream&lt;/span&gt;
oc new-app &lt;span class=&quot;nt&quot;&gt;--image-stream&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Expose the service via HTTP using an OpenShift Route&lt;/span&gt;
oc expose svc/&lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Check the build status&lt;/span&gt;
oc get builds

&lt;span class=&quot;c&quot;&gt;# Print the application URL (Route)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;oc get route/&lt;span class=&quot;nv&quot;&gt;$APPLICATION_NAME&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'{.spec.host}'&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To push new changes from your local machine to OpenShift and trigger a build
run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc start-build $APPLICATION_NAME --from-dir=$APPLICATION_NAME&lt;/code&gt; command
again.&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;Once &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oc get builds&lt;/code&gt; shows that the build is complete, you’ll be able to view
the application in your web browser. The URL is obtained via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; command
above.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2020-09-14-create-react-app-on-openshift/cra-on-crc.png&quot; alt=&quot;Create React App in Firefox&quot; /&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 17 Sep 2020 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/create-react-app-on-openshift</link>
        <guid isPermaLink="true">https://evanshortiss.com/create-react-app-on-openshift</guid>
        
        
        <category>development</category>
        
        <category>openshift</category>
        
        <category>react</category>
        
      </item>
    
      <item>
        <title>Creating a Windows 10 USB Installer using macOS in 2020</title>
        <description>&lt;p&gt;You’d think creating a USB-based installer for Windows 10 would be easy using
macOS, or any modern operating system, but my experience following the guides
that Google returns for macOS wasn’t great. Most guides are either outdated or
contain what appear to be errors in the Terminal commands, so the steps below
are what I used to create a working Windows 10 64-Bit USB installer in 2020.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: Two helpful readers reached out to me with alternative methods after I published this post. They are described under the &lt;strong&gt;&lt;a href=&quot;#method-2&quot;&gt;Method 2&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href=&quot;#method-3&quot;&gt;Method 3&lt;/a&gt;&lt;/strong&gt; headings. Consider trying those easier options before you try my &lt;strong&gt;&lt;a href=&quot;#method-1&quot;&gt;Method 1&lt;/a&gt;&lt;/strong&gt;. Personally, I have only tested &lt;strong&gt;&lt;a href=&quot;#method-1&quot;&gt;Method 1&lt;/a&gt;&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;method-1&quot;&gt;Method 1&lt;/h2&gt;

&lt;p&gt;This guide is mostly based on the one by Quincy Larson on
&lt;a href=&quot;https://www.freecodecamp.org/news/how-make-a-windows-10-usb-using-your-mac-build-a-bootable-iso-from-your-macs-terminal/&quot;&gt;freecodecamp.org&lt;/a&gt;,
but it contains modifications that I found necessary to get it working.&lt;/p&gt;

&lt;h3 id=&quot;open-a-terminal&quot;&gt;Open a Terminal&lt;/h3&gt;
&lt;p&gt;We’ll be using the Terminal on your Mac to create the bootable USB. If you
don’t know how to open the Terminal App, then &lt;a href=&quot;https://support.apple.com/en-gb/guide/terminal/apd5265185d-f365-44cb-8b09-71a064a42125/mac&quot;&gt;read this&lt;/a&gt; - it only takes a second!&lt;/p&gt;

&lt;h3 id=&quot;install-homebrew--wimlib&quot;&gt;Install Homebrew &amp;amp; Wimlib&lt;/h3&gt;
&lt;p&gt;You might already have Homebrew installed, so check by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;which brew&lt;/code&gt;. If
the command prints &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not found&lt;/code&gt; then you need to run the following command to
install it:&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;/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Make sure to copy the entire command. This might require you to scroll horizontally.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you have Homebrew installed, quit the Terminal application (the shortcut
is CMD + Q), then reopen it. Install wimlib by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install wimlib&lt;/code&gt; in
the new Terminal you just opened.&lt;/p&gt;

&lt;h3 id=&quot;download-windows&quot;&gt;Download Windows&lt;/h3&gt;
&lt;p&gt;Download a Windows 10 ISO. You can do so at this link on &lt;a href=&quot;https://www.microsoft.com/en-us/software-download/windows10ISO&quot;&gt;microsoft.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;get-tea-or-coffee-&quot;&gt;Get Tea or Coffee ☕&lt;/h3&gt;
&lt;p&gt;Since the Windows 10 ISO is ~5.5GB, maybe now is a good time to have a drink?
It’ll take a few minutes to download, even on a reasonably fast internet
connection. If you’re lucky enough to have gigabit internet, then I guess you
can just wait the 50-ish seconds it’ll take?&lt;/p&gt;

&lt;h3 id=&quot;plug-the-usb-drive-into-your-mac&quot;&gt;Plug the USB Drive into your Mac&lt;/h3&gt;
&lt;p&gt;OK. I think you can figure this one out, but if you’re using a new Macbook Pro
like me you probably need a USB-C to USB-A adapter, and that sucks.&lt;/p&gt;

&lt;h3 id=&quot;identify-the-usb-drive&quot;&gt;Identify the USB Drive&lt;/h3&gt;
&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diskutil list&lt;/code&gt; in Termainal and a list attached disk drives will be
displayed. Your USB will probably be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/disk2&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/disk3&lt;/code&gt;. We’ll
store that in a variable for use later by running the command
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRIVE_MOUNT=/dev/disk2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/disk2&lt;/code&gt; in the example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DRIVE_MOUNT&lt;/code&gt;, but make sure you change it to the correct path for your USB drive. The next step involves formatting the drive. That means it deletes everything on the drive, so you want to be sure it’s the right one!&lt;/p&gt;

&lt;h3 id=&quot;format-the-usb-drive&quot;&gt;Format the USB Drive&lt;/h3&gt;
&lt;p&gt;Format the drive using the following commands:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The lines beginning with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; are comments explaining what the command on the line below does. You don’t need to paste them since they do nothing.&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;# create a variable for the drive name - it'll be handy later&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DRIVE_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;WINSTALL&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# format the disk so we can boot the Windows installer from it&lt;/span&gt;
diskutil eraseDisk MS-DOS &lt;span class=&quot;nv&quot;&gt;$DRIVE_NAME&lt;/span&gt; MBR &lt;span class=&quot;nv&quot;&gt;$DRIVE_MOUNT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mount-the-iso-file&quot;&gt;Mount the ISO File&lt;/h3&gt;
&lt;p&gt;Enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hdiutil mount&lt;/code&gt; followed by a space character in the Terminal, then drag
the ISO file you downloaded into the Terminal. The end result will be a command
that looks similar to:&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;hdiutil mount /Users/your-username/Downloads/Windows.iso
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Press enter to run the command. Once the ISO has mounted it will print the
mount path and volume name. The volume name will be similar to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/Volumes/CCCOMA_X64FRE_EN-GB_DV9&lt;/code&gt;, but varies depending on the version of
Windows and the language. Save it to a variable like so:&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;WIN_VOLUME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/Volumes/CCCOMA_X64FRE_EN-GB_DV9&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;copy-files-to-the-usb&quot;&gt;Copy Files to the USB&lt;/h3&gt;
&lt;p&gt;Copy files to the USB by running the following command. Note that the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.wim&lt;/code&gt; must be excluded since it’s too large to copy, but we’ll deal
with that next.&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;rsync &lt;span class=&quot;nt&quot;&gt;-vha&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--exclude&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;sources/install.wim &lt;span class=&quot;nv&quot;&gt;$WIN_VOLUME&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt; /Volumes/&lt;span class=&quot;nv&quot;&gt;$DRIVE_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;copy-installwim-to-the-usb&quot;&gt;Copy install.wim to the USB&lt;/h3&gt;
&lt;p&gt;Use the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wimlib&lt;/code&gt; command to copy the final install file to the USB
drive:&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;wimlib-imagex &lt;span class=&quot;nb&quot;&gt;split&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$WIN_VOLUME&lt;/span&gt;/sources/install.wim /Volumes/&lt;span class=&quot;nv&quot;&gt;$DRIVE_NAME&lt;/span&gt;/sources/install.swm 4000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Make sure to copy the entire command. This might require you to scroll horizontally.&lt;/em&gt;&lt;/p&gt;

&lt;h3 id=&quot;unmount-the-usb--install-windows&quot;&gt;Unmount the USB &amp;amp; Install Windows&lt;/h3&gt;
&lt;p&gt;Eject the USB by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diskutil unmount $DRIVE_MOUNT&lt;/code&gt; and use it to install
Windows!&lt;/p&gt;

&lt;h2 id=&quot;method-2&quot;&gt;Method 2&lt;/h2&gt;

&lt;p&gt;I managed to find the video shared by this helpful person via YouTube, but I
must have deleted their email. Whoever you are, thank you!&lt;/p&gt;

&lt;p&gt;Here’s a link to the &lt;a href=&quot;https://www.youtube.com/watch?v=GptehG90Wg4&quot;&gt;YouTube video by George Holden&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;method-3&quot;&gt;Method 3&lt;/h2&gt;

&lt;p&gt;Tim King shared the following tip. I believe it’s the same as Method 2!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I hadn’t realised that the simplest method of all was to format the USB stick as ExFAT, thus removing the 4Gb file size limitation of FAT32. This method had the added advantage of taking just a few seconds to complete.&lt;/em&gt;&lt;/p&gt;
</description>
        <pubDate>Wed, 13 May 2020 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/create-a-windows-install-usb-via-macos</link>
        <guid isPermaLink="true">https://evanshortiss.com/create-a-windows-install-usb-via-macos</guid>
        
        
        <category>windows</category>
        
      </item>
    
      <item>
        <title>Automatic npm Registry Switching (npmrc daemon)</title>
        <description>&lt;p&gt;&lt;em&gt;TLDR; My &lt;a href=&quot;https://github.com/evanshortiss/npmrc-daemon&quot;&gt;npmrcd&lt;/a&gt; package allows
you to automatically switch from the public registry to a private registry.
Switching occurs when the private registry can be resolved via DNS or your
machine connects to a specific network.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently at work I had to switch from directly using the public &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npmjs.com&lt;/a&gt;
registry to an internal mirror/proxy server we’re using. Doing this is pretty
straightforward since it can be achieved with just one or two commands as 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;c&quot;&gt;# Make your npm CLI use the registry at $CORPORATE_REGISTRY_URL&lt;/span&gt;
npm config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;registry &lt;span class=&quot;nv&quot;&gt;$CORPORATE_REGISTRY_URL&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Optional, but might be necessary depending on your org setup&lt;/span&gt;
npm config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;cafile &lt;span class=&quot;nv&quot;&gt;$CA_FILEPATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Seems easy enough, but as someone who often travels for work, frequently works
from home, and is generally a forgetful guy, I knew this would quickly become
annoying. It was inevitable that I’d forget to switch between the corporate
registry at work and public registry as I moved between networks, projects,
and jumped on and off VPNs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2019-12-08-npmrc-daemon/forgetful-pikachu.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In my quest to conquer my forgetfulness, and this problem worthy of
&lt;a href=&quot;https://old.reddit.com/r/mildlyinfuriating/&quot;&gt;/r/mildlyinfuriating&lt;/a&gt;, I
decided to make switching registries an automated process. The conditions were
simple enough, as demonstrated by the pseudocode below:&lt;/p&gt;

&lt;div class=&quot;language-js 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;cm&quot;&gt;/**
 * Only use the public npm registry if:
 *  - the corporate registry cannot be resolved
 *  - and we're not on corporate WiFi
 */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dnsResolvesUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CORPORATE_NPM_REGISTRY_URL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isConnectedToCorporateWifi&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;nx&quot;&gt;switchToCorporateRegistry&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;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;switchToPublicRegistry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Effectively what I was looking for was a way to have a “work” and “personal”
profile for my npm configuration, and a mechanism that would automatically
determine which to use.&lt;/p&gt;

&lt;h2 id=&quot;have-no-fear-npmrc-is-here&quot;&gt;Have no fear, npmrc is here!&lt;/h2&gt;

&lt;p&gt;The npm CLI manages your configuration by saving it in a file named &lt;em&gt;.npmrc&lt;/em&gt;.
This file is typically stored in your home directory on MacOS/Linux. Creating
multiple &lt;em&gt;.npmrc&lt;/em&gt; files and rotating them would allow me to achieve what I
needed.&lt;/p&gt;

&lt;p&gt;I figured this had to be a solved problem, and sure enough I found the
&lt;a href=&quot;https://www.npmjs.com/package/npmrc&quot;&gt;npmrc package&lt;/a&gt; on npm. Using this package
I could easily create a new profile using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npmrc -c [profile-name]&lt;/code&gt;, for
example I could do the following:&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;# Create the work profile&lt;/span&gt;
npmrc &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; work

&lt;span class=&quot;c&quot;&gt;# Switch to the work profile&lt;/span&gt;
npmrc work

&lt;span class=&quot;c&quot;&gt;# Configure settings for this profile&lt;/span&gt;
npm config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;registry &lt;span class=&quot;nv&quot;&gt;$CORPORATE_REGISTRY_URL&lt;/span&gt;
npm config &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;cafile &lt;span class=&quot;nv&quot;&gt;$CA_FILEPATH&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Switch back to the default profile&lt;/span&gt;
npmrc default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The npmrc package worked perfectly despite not being updated in 5 years, and
it has zero external dependencies!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2019-12-08-npmrc-daemon/perfection-meme.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finding an existing tool to manage the profiles saved me some time and meant
I could focus purely on the automation work.&lt;/p&gt;

&lt;h2 id=&quot;agents-and-daemons&quot;&gt;Agents and Daemons&lt;/h2&gt;

&lt;p&gt;Automating the switch between profiles by running a script on a regular
interval seemed like a reasonable solution, so I pursued that avenue. Since
my primary development machine is a MacBook Pro, my search for setting up
a daemon on macOS lead me to &lt;a href=&quot;https://www.launchd.info/&quot;&gt;launchd.info&lt;/a&gt;. The
website summarises how to create a daemon/agent as follows:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The behavior of a daemon/agent is specified in a special XML file called a property list (plist). Depending on where it is stored it will be treated as a daemon or an agent.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This seemed straightforward enough, until it wasn’t! I wrote a script that
would use the &lt;em&gt;npmrc&lt;/em&gt; CLI to switch profiles, created a &lt;em&gt;plist&lt;/em&gt; and tried to
load it using the &lt;em&gt;launchctl&lt;/em&gt; CLI. I spent a while placing my &lt;em&gt;plist&lt;/em&gt; in
&lt;em&gt;/Library/LaunchAgents&lt;/em&gt; and &lt;em&gt;/Library/LaunchDaemons&lt;/em&gt;, running variations of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo launchctl load -w my-plist.plist&lt;/code&gt;, and changing various properties in the
&lt;em&gt;plist&lt;/em&gt; to get around issues such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;“why the hell can’t it find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node&lt;/code&gt; executable? It’s on my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt;!”&lt;/li&gt;
  &lt;li&gt;“why doesn’t the daemon run at all?”&lt;/li&gt;
  &lt;li&gt;“oh I needed to specify the user and group it should run as”&lt;/li&gt;
  &lt;li&gt;“wait, it’s still not working”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually when I moved the &lt;em&gt;plist&lt;/em&gt; into the folder &lt;em&gt;~/Library/LaunchAgents/&lt;/em&gt;
and ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;launchctl load -w my-plist.plist&lt;/code&gt; it worked! This confusion is
probably my own fault since I just looked at the summary on the homepage of
&lt;a href=&quot;https://www.launchd.info/&quot;&gt;launchd.info&lt;/a&gt; and didn’t really
&lt;a href=&quot;https://en.wikipedia.org/wiki/RTFM&quot;&gt;RTFM&lt;/a&gt;. Basically, I needed to place it in
the Agents folder for my specific user.&lt;/p&gt;

&lt;p&gt;The final &lt;em&gt;plist&lt;/em&gt; was similar to this:&lt;/p&gt;

&lt;div class=&quot;language-xml 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;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;plist&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Label&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.npmrcd&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;EnvironmentVariables&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;PATH&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;my-path-string&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;HOME&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/Users/my-username&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;Program&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;my-daemon-script.js&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;RunAtLoad&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;true/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StartInterval&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;integer&amp;gt;&lt;/span&gt;120&lt;span class=&quot;nt&quot;&gt;&amp;lt;/integer&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardOutPath&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/npmrcd.stdout&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;key&amp;gt;&lt;/span&gt;StandardErrorPath&lt;span class=&quot;nt&quot;&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;/tmp/npmrcd.stderr&lt;span class=&quot;nt&quot;&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/plist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This &lt;em&gt;plist&lt;/em&gt; tells the OS to run &lt;em&gt;my-daemon-script.js&lt;/em&gt; once every 120 seconds.
So, my Mac will check verify it’s connected to the correct registry every two
minutes. Perfect!&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it all Together&lt;/h2&gt;

&lt;p&gt;Once I had the Agent, lookup, and npmrc pieces figured out, I needed to wire
them together. To this end, I created a small Node.js script that would be
executed by the macOS Agent every couple of minutes to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Perform a DNS lookup to verify if the corporate registry could be resolved.&lt;/li&gt;
  &lt;li&gt;Check the WiFi SSID that my Mac is connected to.&lt;/li&gt;
  &lt;li&gt;Based on the DNS/WiFi results invoke the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npmrc&lt;/code&gt; command to change registry.&lt;/li&gt;
  &lt;li&gt;Notify that the change has occurred.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wasn’t sure how best to notify myself of the change since this would be a
process running in the background, but then I recalled that various Node.js
modules I’ve used trigger native desktop notifications. Research lead me to
the conclusion that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node-notifier&lt;/code&gt; module seems to be the most common
solution used by other modules for triggering a native notifications, and it
worked perfectly for me too - just look at that glorious notification in the
screenshot below.&lt;/p&gt;

&lt;p&gt;The entire solution is wrapped together as a neat little module called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npmrcd&lt;/code&gt;
so others can easily use it. If you frequently switch between registries give
it a try like so:&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;# install npmrcd&lt;/span&gt;
npm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; npmrcd

&lt;span class=&quot;c&quot;&gt;# create a work profile with the following settings&lt;/span&gt;
npmrcd &lt;span class=&quot;nt&quot;&gt;--registry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://repository.acme.com/nexus/repository/registry.npmjs.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you need to use a CA file that option is supported. The CA file can point to
a local file or HTTP/HTTPS endpoint that the file can be fetched from:&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;# create a work profile with the following settings&lt;/span&gt;
npmrcd &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--cafile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/REGISTRY-CA.crt
&lt;span class=&quot;nt&quot;&gt;--registry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://repository.acme.com/nexus/repository/registry.npmjs.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, you can specify that certain WiFi SSIDs should trigger the switch, even
if the registry is not accessible:&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;# even if the registry url does not resolve, switch if connected to&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the &quot;Red Hat Guest&quot; WiFi to prevent using the public registry&lt;/span&gt;
npmrcd &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--triggerssid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Red Hat Guest'&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;--cafile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/REGISTRY-CA.crt
&lt;span class=&quot;nt&quot;&gt;--registry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://repository.acme.com/nexus/repository/registry.npmjs.org
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here’s a screenshot showing my registry switch when I first opened my laptop
in the office at 10:25AM - hey, I was on calls at 7AM at home before getting
there so give me a break on getting there at 10:25AM! I hadn’t plugged into
the network via an Ethernet cable yet hence the “currently not accessible”
message. You can see that when I opened my laptop at home later that day it
switched back to the public npm registry.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/res/img/posts/2019-12-08-npmrc-daemon/npmrcd-notification.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I hope this post and module are helpful, or even mildly interesting!&lt;/p&gt;
</description>
        <pubDate>Sun, 08 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://evanshortiss.com/npmrc-daemon</link>
        <guid isPermaLink="true">https://evanshortiss.com/npmrc-daemon</guid>
        
        
        <category>nodejs</category>
        
      </item>
    
  </channel>
</rss>
