<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: wayofthepie</title>
    <description>The latest articles on Forem by wayofthepie (@wayofthepie).</description>
    <link>https://forem.com/wayofthepie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F117477%2F91fdb987-ca5d-4ab4-aaa5-c3f4d43bb3a6.jpg</url>
      <title>Forem: wayofthepie</title>
      <link>https://forem.com/wayofthepie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wayofthepie"/>
    <language>en</language>
    <item>
      <title>Decode a certificate</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Mon, 22 Jun 2020 20:40:55 +0000</pubDate>
      <link>https://forem.com/wayofthepie/decode-a-certificate-5903</link>
      <guid>https://forem.com/wayofthepie/decode-a-certificate-5903</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this post I will leave a note at the end of some sections linking to the latest code up to that point. It will look like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/a9d761e1ba306587a332e97e9fd4e654f1049ab9"&gt;wayofthepie/cert-decoder@a9d761e&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That link points to the latest code from the last post. &lt;/p&gt;

&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Some missing things&lt;/li&gt;
&lt;li&gt;
Read a certificate

&lt;ul&gt;
&lt;li&gt;x509-parser API&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Validate the file is a certificate

&lt;ul&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Refactor&lt;/li&gt;
&lt;li&gt;Parse the der encoded cert&lt;/li&gt;
&lt;li&gt;Print the certificate&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tiny refactor&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Some missing things
&lt;/h1&gt;

&lt;p&gt;After publishing the first post I realized I missed a positive test, a test which checks that everything went ok. Let's write that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_succeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"a-file"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a good idea to see it fail first. Let's change a behaviour it expects by making it return an error if the file &lt;em&gt;does&lt;/em&gt; exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- if !validator.is_file(path) {
&lt;/span&gt;&lt;span class="gi"&gt;+ if validator.is_file(path) {
&lt;/span&gt;   return Err(
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it will fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test &lt;/span&gt;should_succeed

running 1 &lt;span class="nb"&gt;test
&lt;/span&gt;F
failures:

&lt;span class="nt"&gt;----&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;::should_succeed stdout &lt;span class="nt"&gt;----&lt;/span&gt;
thread &lt;span class="s1"&gt;'test::should_succeed'&lt;/span&gt; panicked at &lt;span class="s1"&gt;'assertion failed: result.is_ok()'&lt;/span&gt;, src/main.rs:98:9
note: run with &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="sb"&gt;`&lt;/span&gt; environment variable to display a backtrace


failures:
    &lt;span class="nb"&gt;test&lt;/span&gt;::should_succeed

&lt;span class="nb"&gt;test &lt;/span&gt;result: FAILED. 0 passed&lt;span class="p"&gt;;&lt;/span&gt; 1 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 2 filtered out

error: &lt;span class="nb"&gt;test &lt;/span&gt;failed, to rerun pass &lt;span class="s1"&gt;'--bin cert-decode'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we revert our change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- if validator.is_file(path) {
&lt;/span&gt;&lt;span class="gi"&gt;+ if !validator.is_file(path) {
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test &lt;/span&gt;should_succeed

running 1 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 2 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now for some new things.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/66da25ac49840efe43d4913eb178e71df52f5997"&gt;wayofthepie/cert-decoder@66da25a&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Read a certificate
&lt;/h1&gt;

&lt;p&gt;Time to read a certificate. The &lt;a href="https://docs.rs/crate/x509-parser/0.7.0"&gt;x509-parser&lt;/a&gt; crate will allow us to do this. Add as a dependency to &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cert-decode"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Stephen OBrien &amp;lt;wayofthepie@users.noreply.github.com&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2018"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;x509-parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.7.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://github.com/killercup/cargo-edit"&gt;cargo-edit&lt;/a&gt; to update &lt;code&gt;Cargo.toml&lt;/code&gt;. You can install it by running:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nb"&gt;install &lt;/span&gt;cargo-edit
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then to add a dependency you just need to run:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo add x509-parser
    Updating &lt;span class="s1"&gt;'https://github.com/rust-lang/crates.io-index'&lt;/span&gt; index
      Adding x509-parser v0.7.0 to dependencies
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;See &lt;a href="https://github.com/killercup/cargo-edit/blob/master/README.md"&gt;the README&lt;/a&gt; for more details. The code blocks in this note have&lt;br&gt;
incorrect formatting, they are centered. I raised a bug about this, see &lt;a href="https://github.com/thepracticaldev/dev.to/issues/8767"&gt;thepracticaldev/dev.to#8767&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a good idea to rebuild when adding a new dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;✦&lt;/span&gt; &lt;span class="err"&gt;➜&lt;/span&gt; &lt;span class="n"&gt;cargo&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;
    &lt;span class="n"&gt;Updating&lt;/span&gt; &lt;span class="n"&gt;crates&lt;/span&gt;&lt;span class="py"&gt;.io&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;autocfg&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="na"&gt;.0.0&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;bitflags&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="na"&gt;.2.1&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;ryu&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="na"&gt;.0.5&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;lexical&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.7.4&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;memchr&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="na"&gt;.3.3&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;version_check&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.9.2&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;arrayvec&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.5.1&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;static_assertions&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="na"&gt;.1.0&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.1.10&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;libc&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.2.71&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.11.0&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;nom&lt;/span&gt; &lt;span class="n"&gt;v5&lt;/span&gt;&lt;span class="na"&gt;.1.2&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;traits&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.2.12&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.1.43&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;bigint&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.2.6&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt; &lt;span class="n"&gt;v01&lt;/span&gt;&lt;span class="na"&gt;.3&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;rusticata&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;macros&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="na"&gt;.1.0&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;der&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="na"&gt;.0.4&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;x509&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.7.0&lt;/span&gt;
   &lt;span class="n"&gt;Compiling&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt; &lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="na"&gt;.1.0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;chaospie&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;repos&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;blog&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Finished&lt;/span&gt; &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;unoptimize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;debuginfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nf"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;8.70&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;x509-parser&lt;/code&gt; crate has pulled in a bunch of transitive dependencies. Not too many though. When working on a larger project you may want to view the overall dependency hierarchy. You can do this with &lt;code&gt;cargo tree&lt;/code&gt;. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo tree
cert-decode v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/home/chaospie/repos/blog-cert-decode/cert-decode&lt;span class="o"&gt;)&lt;/span&gt;
└── x509-parser v0.7.0
    ├── &lt;span class="nb"&gt;base64 &lt;/span&gt;v0.11.0
    ├── der-parser v3.0.4
    │   ├── nom v5.1.2
    │   │   ├── lexical-core v0.7.4
    │   │   │   ├── arrayvec v0.5.1
    │   │   │   ├── bitflags v1.2.1
    │   │   │   ├── cfg-if v0.1.10
    │   │   │   ├── ryu v1.0.5
    │   │   │   └── static_assertions v1.1.0
    │   │   └── memchr v2.3.3
    │   │   &lt;span class="o"&gt;[&lt;/span&gt;build-dependencies]
    │   │   └── version_check v0.9.2
    │   ├── num-bigint v0.2.6
    │   │   ├── num-integer v0.1.43
    │   │   │   └── num-traits v0.2.12
    │   │   │       &lt;span class="o"&gt;[&lt;/span&gt;build-dependencies]
    │   │   │       └── autocfg v1.0.0
    │   │   │   &lt;span class="o"&gt;[&lt;/span&gt;build-dependencies]
    │   │   │   └── autocfg v1.0.0
    │   │   └── num-traits v0.2.12 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    │   │   &lt;span class="o"&gt;[&lt;/span&gt;build-dependencies]
    │   │   └── autocfg v1.0.0
    │   └── rusticata-macros v2.1.0
    │       └── nom v5.1.2 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    ├── nom v5.1.2 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    ├── num-bigint v0.2.6 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    ├── rusticata-macros v2.1.0 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    └── &lt;span class="nb"&gt;time &lt;/span&gt;v0.1.43
        └── libc v0.2.71
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As of Rust 1.44.0 &lt;code&gt;cargo tree&lt;/code&gt; is part of &lt;code&gt;cargo&lt;/code&gt; if you are using a version before that you will need to install &lt;a href="https://crates.io/crates/cargo-tree"&gt;cargo-tree&lt;/a&gt;. You should update to the latest Rust if there is no reason to be on a version less than 1.44.0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've used the &lt;code&gt;x509-parser&lt;/code&gt; crate in the past so I know a bit about it's API. But let's do some exploration anyway. First, let's set a goal and make a tiny test list. Our goal is simply to be able to print certificate details. Up to now, we have verified the argument we pass is a file, so I think what we should do next is:&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read and print certificate&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ Validate file is a certificate&lt;/li&gt;
&lt;li&gt;☐ Print the certificate&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let's dive into the docs for &lt;code&gt;x509-parser&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  x509-parser API
&lt;/h2&gt;

&lt;p&gt;All crates published to &lt;a href="https://crates.io/"&gt;crates.io&lt;/a&gt; should have docs on &lt;a href="https://docs.rs"&gt;docs.rs&lt;/a&gt;. These docs will contain the public API of the crate and whatever further documentation the author added. We're using version 0.7.0 of the &lt;code&gt;x509-parser&lt;/code&gt; crate, the docs for this are &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/"&gt;here&lt;/a&gt;. The very first example in these docs does almost what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;x509_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse_x509_der&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IGCA_DER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;include_bytes!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../assets/IGC_A.der"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_x509_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IGCA_DER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="py"&gt;.tbs_certificate.version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x509 parsing failed: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems we could use the &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/fn.parse_x509_der.html"&gt;parse_x509_der&lt;/a&gt; function to parse our certificate. Our certificate should be in PEM format however, that was a constraint we set in the initial post. Is there anything that can deal directly with PEM certificates in this API? &lt;/p&gt;

&lt;p&gt;There is! The &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/pem/index.html"&gt;x509_parser::pem&lt;/a&gt; module has functionality for doing just this. The second example &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/pem/index.html"&gt;in that modules docs&lt;/a&gt; does just what we want, it uses the &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/pem/fn.pem_to_der.html"&gt;pem_to_der&lt;/a&gt; function to convert a PEM encoded certificate into DER (Distinguished Encoding Rules) and then calls &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/fn.parse_x509_der.html"&gt;parse_x509_der&lt;/a&gt; on that DER to build a &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/x509/struct.X509Certificate.html"&gt;X509Certificate&lt;/a&gt;. Here is the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;x509_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pem_to_der&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;x509_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parse_x509_der&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IGCA_PEM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;include_bytes!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../assets/IGC_A.pem"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pem_to_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IGCA_PEM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rem&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="py"&gt;.label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CERTIFICATE"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res_x509&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_x509_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="py"&gt;.contents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res_x509&lt;/span&gt;&lt;span class="nf"&gt;.is_ok&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;panic!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PEM parsing failed: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't worry if you don't understand everything in this example, it's not too important for this post. Let's look closer at &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/pem/fn.pem_to_der.html"&gt;pem_to_der&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;pem_to_der&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;IResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Pem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PEMError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes a &lt;code&gt;&amp;amp;'a [u8]&lt;/code&gt;, a &lt;a href="https://doc.rust-lang.org/std/primitive.slice.html"&gt;slice&lt;/a&gt; of bytes with a lifetime&lt;sup id="fnref1"&gt;1&lt;/sup&gt; of &lt;code&gt;'a&lt;/code&gt;, and returns &lt;code&gt;IResult&amp;lt;&amp;amp;'a [u8], Pem, PEMError&amp;gt;&lt;/code&gt;. In short lifetimes tell the compiler how long a reference lives. In this specific case, the slice &lt;code&gt;i&lt;/code&gt; which the function takes as an argument must live as long as the slice returned in the &lt;code&gt;IResult&lt;/code&gt; return type, as both have a lifetime of &lt;code&gt;'a&lt;/code&gt;. This tells us the slice in the return type must either be &lt;code&gt;i&lt;/code&gt; or a subslice of &lt;code&gt;i&lt;/code&gt;. For more information see &lt;a href="https://doc.rust-lang.org/book/ch10-00-generics.html?highlight=generics#generic-types-traits-and-lifetimes"&gt;Generic Types, Traits, and Lifetimes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Glossing over some other details which are outside the scope of this post, this &lt;code&gt;IResult&lt;/code&gt; is effectively just a &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html"&gt;Result&lt;/a&gt; type which we saw in the first post. It can return &lt;code&gt;Ok&lt;/code&gt; with some value or &lt;code&gt;Err&lt;/code&gt; with an error. In this case, the type of the value in &lt;code&gt;Err&lt;/code&gt; will be &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/error/enum.PEMError.html"&gt;PEMError&lt;/a&gt;. &lt;/p&gt;

&lt;h1&gt;
  
  
  Validate the file is a certificate
&lt;/h1&gt;

&lt;p&gt;Now we have enough knowledge to write a test in the case our cert is not PEM encoded. First, let's get a cert and save it so we can use that in our tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; google.com:443 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;     | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'/BEGIN CERTIFICATE/,/END CERTIFICATE/p'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; google.com.crt

➜ &lt;span class="nb"&gt;cat &lt;/span&gt;google.com.crt
&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIJTzCCCDegAwIBAgIQVvrczQ6+8BwIAAAAAENV5zANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMRMw
...
d5JOd+lJOypPGs0/p5OrR8B84Y7wyKFD/EXaKYVMZ4RUXnoAi5DF5RLKNAmnt7R9
V6z8Kz2boaY5oZ0gvrA49R6T+u3yrstte931N49lwpaVsoA&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nt"&gt;-----END&lt;/span&gt; CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've stored this cert in the repo &lt;a href="https://github.com/wayofthepie/cert-decoder/blob/be75e2c756984c289768788d3f1ffadaa223ddc2/resources/google.com.crt"&gt;here&lt;/a&gt;. The test is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_given_argument_is_not_a_pem_encoded_certificate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"real-cert"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The update to the &lt;code&gt;execute&lt;/code&gt; function will need a bit of refactoring, but first, let's implement it in the simplest way possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="nf"&gt;.is_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given is not a regular file, please update to point to a certificate."&lt;/span&gt;
                &lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// read file to string&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// pem to der&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pem_to_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;a href="https://doc.rust-lang.org/std/fs/fn.read_to_string.html"&gt;std::fs::read_to_string&lt;/a&gt; to read the file path we pass as an argument directly to a string. This call returns a &lt;code&gt;Result&lt;/code&gt; as it can fail if the path does not exist. But we know it does exist at this point, so we just &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap"&gt;unwrap&lt;/a&gt; the value, giving us our cert as a string. Then we pass that string as bytes, by calling the &lt;a href="https://doc.rust-lang.org/std/string/struct.String.html#method.as_bytes"&gt;as_bytes&lt;/a&gt; function on it, to &lt;code&gt;pem_to_der&lt;/code&gt;. This can fail and because here we just call &lt;code&gt;unwrap&lt;/code&gt; this will &lt;a href="https://doc.rust-lang.org/std/macro.panic.html"&gt;panic&lt;/a&gt; if &lt;code&gt;pem_to_der&lt;/code&gt; returns an &lt;code&gt;Err&lt;/code&gt; value instead of and &lt;code&gt;Ok&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;To see what I mean, update the test so it reads &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_given_argument_is_not_a_pem_encoded_certificate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Cargo.toml"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will fail as follows because &lt;code&gt;Cargo.toml&lt;/code&gt; is not PEM encoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="err"&gt;➜&lt;/span&gt; &lt;span class="n"&gt;cargo&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;pem&lt;/span&gt;

&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;F&lt;/span&gt;
&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="o"&gt;----&lt;/span&gt; &lt;span class="nn"&gt;test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;should_error_if_given_argument_is_not_a_pem_encoded_certificate&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;----&lt;/span&gt;
&lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="nv"&gt;'test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;should_error_if_given_argument_is_not_a_pem_encoded_certificate&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="n"&gt;panicked&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="nv"&gt;'called&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nn"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;Err&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MissingHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="py"&gt;.rs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;backtrace&lt;/span&gt;


&lt;span class="n"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nn"&gt;test&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;should_error_if_given_argument_is_not_a_pem_encoded_certificate&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FAILED&lt;/span&gt;&lt;span class="na"&gt;. 0&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;ignored&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;measured&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;

&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;rerun&lt;/span&gt; &lt;span class="n"&gt;pass&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though it did error, it didn't do so in a way we could handle in our test. It would be better to &lt;em&gt;not&lt;/em&gt; call unwrap on the return of &lt;code&gt;pem_to_der&lt;/code&gt;. To do so, we need to change the return type of &lt;code&gt;execute&lt;/code&gt; so it allows us to return both our existing &lt;code&gt;String&lt;/code&gt; errors and the &lt;code&gt;PEMError&lt;/code&gt; which &lt;code&gt;pem_to_der&lt;/code&gt; returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;

&lt;p&gt;There are many different ways to handle errors in Rust. A lot is going on in this space currently in regards libaries and discussions in the language itself. How you handle errors in a library vs in an application can vary wildly too. I don't want to add any more dependencies here and I also want to keep this simple, so we'll use the most general type for handling errors, &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/beta/std/boxed/index.html"&gt;Box&lt;/a&gt; is a simple way of allocating something on the heap in Rust. &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt; is a trait object&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, this allows us to return a value of any type that implements the &lt;a href="https://doc.rust-lang.org/std/error/trait.Error.html"&gt;std::error::Error&lt;/a&gt; trait.&lt;/p&gt;

&lt;p&gt;Let's refactor. First, update &lt;code&gt;execute&lt;/code&gt; as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-fn execute(validator: impl PathValidator, args: Vec&amp;lt;String&amp;gt;) -&amp;gt; Result&amp;lt;(), String&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+fn execute(
+    validator: impl PathValidator,
+    args: Vec&amp;lt;String&amp;gt;,
+) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;/span&gt;     if args.len() != 1 {
         let error = format!(
             "{}{}",
             "Error: did not receive a single argument, ",
             "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
         );
&lt;span class="gd"&gt;-        return Err(error);
&lt;/span&gt;&lt;span class="gi"&gt;+        return Err(error.into());
&lt;/span&gt;     }
     let path = &amp;amp;args[0];
     if !validator.is_file(path) {
         return Err(
             "Error: path given is not a regular file, please update to point to a certificate."
&lt;span class="gd"&gt;-                .to_owned(),
&lt;/span&gt;&lt;span class="gi"&gt;+                .into(),
&lt;/span&gt;         );
     }
     let cert = std::fs::read_to_string(path).unwrap();
&lt;span class="gd"&gt;-    let _ = pem_to_der(cert.as_bytes()).unwrap();
&lt;/span&gt;&lt;span class="gi"&gt;+    let _ = pem_to_der(cert.as_bytes())?;
&lt;/span&gt;     Ok(())
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We change the return type to of &lt;code&gt;execute&lt;/code&gt; to &lt;code&gt;Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt;&lt;/code&gt;. We were previously returning &lt;code&gt;String&lt;/code&gt;'s from our custom errors, we can call &lt;code&gt;into&lt;/code&gt; on our strings and this will convert them into &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt;. There is an instance of &lt;a href="https://doc.rust-lang.org/std/convert/trait.From.html"&gt;From&lt;/a&gt; for converting a &lt;a href="https://doc.rust-lang.org/std/convert/trait.From.html#impl-From%3CString%3E-2"&gt;String to a Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/a&gt;, because of this we get an &lt;a href="https://doc.rust-lang.org/std/convert/trait.Into.html"&gt;Into&lt;/a&gt; instance for automatically.&lt;/p&gt;

&lt;p&gt;Finally, we add a &lt;code&gt;?&lt;/code&gt;&lt;sup id="fnref4"&gt;4&lt;/sup&gt; to immediately return the error if &lt;code&gt;pem_to_der&lt;/code&gt; returns an error. Next update &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-fn main() -&amp;gt; Result&amp;lt;(), String&amp;gt; {
&lt;/span&gt;&lt;span class="gi"&gt;+fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
&lt;/span&gt;     let args = std::env::args().skip(1).collect();
     let validator = CertValidator;
     execute(validator, args)
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We just change the return type here. Finally, update the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; #[cfg(test)]
 mod test {
     ... 
     #[test]
     fn should_error_if_not_given_a_single_argument() {
         ...
         assert!(result.is_err());
         assert_eq!(
&lt;span class="gd"&gt;-            result.err().unwrap(),
&lt;/span&gt;&lt;span class="gi"&gt;+            format!("{}", result.err().unwrap()),
&lt;/span&gt;             format!(
                 "{}{}",
                 "Error: did not receive a single argument, ",
                 "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
             )
         );
     }

     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
         ...
         assert!(result.is_err());
         assert_eq!(
&lt;span class="gd"&gt;-            result.err().unwrap(),
&lt;/span&gt;&lt;span class="gi"&gt;+            format!("{}", result.err().unwrap()),
&lt;/span&gt;             "Error: path given is not a regular file, please update to point to a certificate."
         );
     }

     #[test]
     fn should_error_if_given_argument_is_not_a_pem_encoded_certificate() {
        ...
     }

     #[test]
     fn should_succeed() {
&lt;span class="gd"&gt;-        let args = vec!["a-file".to_owned()];
&lt;/span&gt;&lt;span class="gi"&gt;+        let args = vec!["resources/google.com.crt".to_owned()];
&lt;/span&gt;         let validator = FakeValidator { is_file: true };
         let result = execute(validator, args);
         assert!(result.is_ok());
     }

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

&lt;/div&gt;



&lt;p&gt;We call &lt;a href="https://doc.rust-lang.org/std/macro.format.html"&gt;format&lt;/a&gt; on the error message to turn the &lt;code&gt;Box&amp;lt;dyn std::error::Error&amp;gt;&lt;/code&gt; in to a string. We also change the &lt;code&gt;should_succeed&lt;/code&gt; test to read the real cert. This does IO, but that's ok for now. Re-run and the tests should be green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 4 tests
....
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 4 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/37cbc87df3ef54f9a72a7be468e20cc26d7011d1"&gt;wayofthepie/cert-decoder@37cbc87&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Refactor
&lt;/h2&gt;

&lt;p&gt;Now we have refactored to allow returning different types of errors, read, and decoded the PEM certificate into DER format. Let's clean things up a little. We are doing IO again, so let's tackle that first. Right now we are passing an implementation of &lt;code&gt;PathValidator&lt;/code&gt; to &lt;code&gt;execute&lt;/code&gt;. It would make sense to expand what this trait does, but we should rename it. Let's call it &lt;code&gt;FileProcessor&lt;/code&gt;. Implementations will have &lt;code&gt;is_file&lt;/code&gt; and &lt;code&gt;read_to_string&lt;/code&gt; so this makes sense. Let's also rename &lt;code&gt;CertValidator&lt;/code&gt; to &lt;code&gt;CertProcessor&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; use std::path::Path;
 use x509_parser::pem::pem_to_der;

-trait PathValidator {
&lt;span class="gi"&gt;+trait FileProcessor {
&lt;/span&gt;     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool;
 }

-struct CertValidator;
&lt;span class="gi"&gt;+struct CertProcessor;
&lt;/span&gt;
-impl PathValidator for CertValidator {
&lt;span class="gi"&gt;+impl FileProcessor for CertProcessor {
&lt;/span&gt;     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool {
         Path::new(path).is_file()
     }
 }

 fn execute(
&lt;span class="gd"&gt;-    validator: impl PathValidator,
&lt;/span&gt;&lt;span class="gi"&gt;+    validator: impl FileProcessor,
&lt;/span&gt;     args: Vec&amp;lt;String&amp;gt;,
 ) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
    ...
    Ok(())
 }

 fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
     let args = std::env::args().skip(1).collect();
&lt;span class="gd"&gt;-    let validator = CertValidator;
&lt;/span&gt;&lt;span class="gi"&gt;+    let validator = CertProcessor;
&lt;/span&gt;     execute(validator, args)
 }

 #[cfg(test)]
 mod test {

-    use crate::{execute, PathValidator};
&lt;span class="gi"&gt;+    use crate::{execute, FileProcessor};
&lt;/span&gt;
-    struct FakeValidator {
&lt;span class="gi"&gt;+    struct FakeProcessor {
&lt;/span&gt;         is_file: bool,
     }

-    impl PathValidator for FakeValidator {
&lt;span class="gi"&gt;+    impl FileProcessor for FakeProcessor {
&lt;/span&gt;         fn is_file(&amp;amp;self, _: &amp;amp;str) -&amp;gt; bool {
             self.is_file
         }
     }

     #[test]
     fn should_error_if_not_given_a_single_argument() {
         // arrange
         let args = Vec::new();
&lt;span class="gd"&gt;-        let validator = FakeValidator { is_file: false };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor { is_file: false };
&lt;/span&gt;
         ...
     }

     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
         // arrange
         let args = vec!["not-a-regular-file".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeValidator { is_file: false };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor { is_file: false };
&lt;/span&gt;
         ...
     }

     #[test]
     fn should_error_if_given_argument_is_not_a_pem_encoded_certificate() {
         let args = vec!["Cargo.toml".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeValidator { is_file: true };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor { is_file: true };
&lt;/span&gt;         let result = execute(validator, args);
         assert!(result.is_err())
     }

     #[test]
     fn should_succeed() {
         let args = vec!["resources/google.com.crt".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeValidator { is_file: true };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor { is_file: true };
&lt;/span&gt;         let result = execute(validator, args);
         assert!(result.is_ok());
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
You may have noticed I forgot to update the name of the variables from &lt;code&gt;validator&lt;/code&gt; to something more appropriate like &lt;code&gt;processor&lt;/code&gt;!&lt;br&gt;
This was indeed a mistake. I added a small refactor section near the end of the post which fixes this.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/a30ae7cf5cfdb51ad1f8cea79e0b20e9ed2f41b3"&gt;wayofthepie/cert-decoder@a30ae7c&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can add a &lt;code&gt;read_to_string&lt;/code&gt; method to the &lt;code&gt;FileProcessor&lt;/code&gt; trait and implement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; use std::path::Path;
 use x509_parser::pem::pem_to_der;

 trait FileProcessor {
     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool;
&lt;span class="gi"&gt;+    fn read_to_string(&amp;amp;self, path: &amp;amp;str) -&amp;gt; Result&amp;lt;String, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt;;
&lt;/span&gt; }

 struct CertProcessor;

 impl FileProcessor for CertProcessor {
     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool {
         Path::new(path).is_file()
     }
&lt;span class="gi"&gt;+    fn read_to_string(&amp;amp;self, path: &amp;amp;str) -&amp;gt; Result&amp;lt;String, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
+        Ok(std::fs::read_to_string(path)?)
+    }
&lt;/span&gt; }

 fn execute(
&lt;span class="gd"&gt;-    validator: impl FileProcessor,
&lt;/span&gt;&lt;span class="gi"&gt;+    processor: impl FileProcessor,
&lt;/span&gt;     args: Vec&amp;lt;String&amp;gt;,
 ) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
     if args.len() != 1 {
         let error = format!(
             "{}{}",
             "Error: did not receive a single argument, ",
             "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
         );
         return Err(error.into());
     }
     let path = &amp;amp;args[0];
&lt;span class="gd"&gt;-    if !validator.is_file(path) {
&lt;/span&gt;&lt;span class="gi"&gt;+    if !processor.is_file(path) {
&lt;/span&gt;         return Err(
             "Error: path given is not a regular file, please update to point to a certificate."
                 .into(),
         );
     }
&lt;span class="gd"&gt;-    let cert = std::fs::read_to_string(path).unwrap();
&lt;/span&gt;&lt;span class="gi"&gt;+    let cert = processor.read_to_string(path)?;
&lt;/span&gt;     let _ = pem_to_der(cert.as_bytes())?;
     Ok(())
 }

 fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
     let args = std::env::args().skip(1).collect();
     let validator = CertProcessor;
     execute(validator, args)
 }

 #[cfg(test)]
 mod test {

     use crate::{execute, FileProcessor};

     struct FakeProcessor {
         is_file: bool,
&lt;span class="gi"&gt;+        file_str: String,
&lt;/span&gt;     }

     impl FileProcessor for FakeProcessor {
         fn is_file(&amp;amp;self, _: &amp;amp;str) -&amp;gt; bool {
             self.is_file
         }
&lt;span class="gi"&gt;+        fn read_to_string(&amp;amp;self, _: &amp;amp;str) -&amp;gt; Result&amp;lt;String, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
+            Ok(self.file_str.clone())
+        }
&lt;/span&gt;     }

     #[test]
     fn should_error_if_not_given_a_single_argument() {
         // arrange
         let args = Vec::new();
&lt;span class="gd"&gt;-        let validator = FakeProcessor { is_file: false };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor {
+            is_file: false,
+            file_str: "".to_owned(),
+        };
&lt;/span&gt;
         // act
         let result = execute(validator, args);

         // assert
         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             format!(
                 "{}{}",
                 "Error: did not receive a single argument, ",
                 "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
             )
         );
     }

     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
         // arrange
         let args = vec!["not-a-regular-file".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor { is_file: false };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor {
+            is_file: false,
+            file_str: "".to_owned(),
+        };
&lt;/span&gt;
         // act
         let result = execute(validator, args);

         // assert
         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             "Error: path given is not a regular file, please update to point to a certificate."
         );
     }

     #[test]
     fn should_error_if_given_argument_is_not_a_pem_encoded_certificate() {
         let args = vec!["Cargo.toml".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor { is_file: true };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor {
+            is_file: true,
+            file_str: "".to_owned(),
+        };
&lt;/span&gt;         let result = execute(validator, args);
         assert!(result.is_err())
     }

     #[test]
     fn should_succeed() {
&lt;span class="gd"&gt;-        let args = vec!["resources/google.com.crt".to_owned()];
-        let validator = FakeProcessor { is_file: true };
&lt;/span&gt;&lt;span class="gi"&gt;+        let cert = include_str!("../resources/google.com.crt");
+        let args = vec!["doesnt-really-matter".to_owned()];
+        let validator = FakeProcessor {
+            is_file: true,
+            file_str: cert.to_owned(),
+        };
&lt;/span&gt;         let result = execute(validator, args);
         assert!(result.is_ok());
     }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;should_succeed&lt;/code&gt; test we use the &lt;a href="https://doc.rust-lang.org/std/macro.include_str.html"&gt;include_str&lt;/a&gt; macro to read the real cert at compile time. This is cleaner than pasting the cert directly in the test. &lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/436d85f6a6da208181738d186029462c66c53511"&gt;wayofthepie/cert-decoder@436d85f&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can improve the tests by deriving&lt;sup id="fnref5"&gt;5&lt;/sup&gt; &lt;a href="https://doc.rust-lang.org/std/default/trait.Default.html"&gt;Default&lt;/a&gt; for our &lt;code&gt;FakeProcessor&lt;/code&gt;. This will give us a basic implementation of &lt;code&gt;FakeProcessor&lt;/code&gt;, defaulting all the fields to the value of the &lt;code&gt;Default&lt;/code&gt; implementation for their type. For example, the default for &lt;code&gt;bool&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; and for &lt;code&gt;String&lt;/code&gt; is the empty string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; #[cfg(test)]
 mod test {

     use crate::{execute, FileProcessor};

     ...
&lt;span class="gd"&gt;-
&lt;/span&gt;&lt;span class="gi"&gt;+    #[derive(Default)]
&lt;/span&gt;     struct FakeProcessor {
         is_file: bool,
         file_str: String,
     }

     #[test]
     fn should_error_if_not_given_a_single_argument() {
&lt;span class="gd"&gt;-        // arrange
&lt;/span&gt;         let args = Vec::new();
&lt;span class="gd"&gt;-        let validator = FakeProcessor {
-            is_file: false,
-            file_str: "".to_owned(),
-        };
-
-        // act
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor::default();
&lt;/span&gt;         let result = execute(validator, args);
&lt;span class="gd"&gt;-
-        // assert
&lt;/span&gt;         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             format!(
                 "{}{}",
                 "Error: did not receive a single argument, ",
                 "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
             )
         );
     }

     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
&lt;span class="gd"&gt;-        // arrange
&lt;/span&gt;         let args = vec!["not-a-regular-file".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor {
-            is_file: false,
-            file_str: "".to_owned(),
-        };
-
-        // act
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeProcessor::default();
&lt;/span&gt;         let result = execute(validator, args);
&lt;span class="gd"&gt;-
-        // assert
&lt;/span&gt;         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             "Error: path given is not a regular file, please update to point to a certificate."
         );
     }

     #[test]
     fn should_error_if_given_argument_is_not_a_pem_encoded_certificate() {
         let args = vec!["Cargo.toml".to_owned()];
         let validator = FakeProcessor {
             is_file: true,
&lt;span class="gd"&gt;-            file_str: "".to_owned(),
&lt;/span&gt;&lt;span class="gi"&gt;+            ..FakeProcessor::default()
&lt;/span&gt;         };
         let result = execute(validator, args);
         assert!(result.is_err())
     }
     ... 
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
In a test above we used &lt;a href="https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"&gt;struct update syntax&lt;/a&gt;, &lt;code&gt;..FakeProcessor::default()&lt;/code&gt;. This will "fill in" any fields we do not explicitly set. It will allow us to add more fields to &lt;code&gt;FileProcessor&lt;/code&gt; if needed and not have to update all tests. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After each change, you should run the tests! If you run them now they should still be green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 4 tests
....
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 4 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/e984bd7ffea42e30cd35bb6f65b7a7808c9be777"&gt;wayofthepie/cert-decoder@e984bd7f&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Parse the der encoded cert
&lt;/h2&gt;

&lt;p&gt;Let's parse the DER bytes into an &lt;code&gt;X509Certificate&lt;/code&gt;. In the x509 parser API section we saw an example of this using the &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/fn.parse_x509_der.html"&gt;parse_x509_der&lt;/a&gt; function. It can fail, so first, a test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_argument_is_not_a_valid_certificate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;include_str!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../resources/bad.crt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"doesnt-really-matter"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;file_str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have added a file called &lt;code&gt;bad.crt&lt;/code&gt; to the resources folder. This just contains a base64 encoded string, which is not a valid certificate. So it will succeed in the &lt;code&gt;pem_to_der&lt;/code&gt; call but calling &lt;code&gt;parse_x509_der&lt;/code&gt; should return an error. First, let's see this test fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 5 tests
...F.
failures:

&lt;span class="nt"&gt;----&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_argument_is_not_a_valid_certificate stdout &lt;span class="nt"&gt;----&lt;/span&gt;
thread &lt;span class="s1"&gt;'test::should_error_if_argument_is_not_a_valid_certificate'&lt;/span&gt; panicked at &lt;span class="s1"&gt;'assertion failed: result.is_err()'&lt;/span&gt;, src/main.rs:118:9
note: run with &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="sb"&gt;`&lt;/span&gt; environment variable to display a backtrace


failures:
    &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_argument_is_not_a_valid_certificate

&lt;span class="nb"&gt;test &lt;/span&gt;result: FAILED. 4 passed&lt;span class="p"&gt;;&lt;/span&gt; 1 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out

error: &lt;span class="nb"&gt;test &lt;/span&gt;failed, to rerun pass &lt;span class="s1"&gt;'--bin cert-decode'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now, let's parse the DER we get.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FileProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// stripped out irrelevant code &lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;cert&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="nf"&gt;.read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pem_to_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_x509_der&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pem&lt;/span&gt;&lt;span class="py"&gt;.contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And re-run the tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 5 tests
.....
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 5 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! We didn't need to update the &lt;code&gt;should_succeed&lt;/code&gt; test, meaning it is reading our real certificate correctly. There are few things we can improve here, but first, let's mark off the first item in our test list.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read and print certificate&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Validate file is a certificate&lt;/li&gt;
&lt;li&gt;☐ Print the certificate &lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/11ff773576691584fc1400176b9bff7778fea2b5"&gt;wayofthepie/cert-decoder@11ff773&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Print the certificate
&lt;/h2&gt;

&lt;p&gt;It turns out we will have to do a bit more processing to get a human-readable output format, so I'm going to cheat here! On success, &lt;code&gt;parse_x509_der&lt;/code&gt; returns a tuple with remaining bytes and an &lt;code&gt;X509Certificate&lt;/code&gt;. The &lt;code&gt;X509Certificate&lt;/code&gt; type implements &lt;a href="https://doc.rust-lang.org/std/fmt/trait.Debug.html"&gt;Debug&lt;/a&gt; so we can print its debug format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;
 fn execute(
     processor: impl FileProcessor,
     args: Vec&amp;lt;String&amp;gt;,
 ) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
     ... 
     let cert = processor.read_to_string(path)?;
     let (_, pem) = pem_to_der(cert.as_bytes())?;
&lt;span class="gd"&gt;-    let _ = parse_x509_der(&amp;amp;pem.contents)?;
&lt;/span&gt;&lt;span class="gi"&gt;+    let (_, cert) = parse_x509_der(&amp;amp;pem.contents)?;
+    let output = format!("{:#?}", cert.tbs_certificate);
+    println!("{}", output);
&lt;/span&gt;     Ok(())
 }

 ...

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

&lt;/div&gt;



&lt;p&gt;We use the debug format specifier &lt;code&gt;{:?}&lt;/code&gt; in the &lt;code&gt;format&lt;/code&gt; macro. We also add a &lt;code&gt;#&lt;/code&gt; to pretty print it, &lt;code&gt;{:#?}&lt;/code&gt;. The only thing we print here is the &lt;code&gt;tbs_certificate&lt;/code&gt; field as that contains all the details we will need. To test this, let's run the actual cli.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;br&gt;
From the above change you might be thinking "Why create a pre-formatted string and pass that to &lt;code&gt;println!&lt;/code&gt;? Couldn't you just use the debug format specifier directly in &lt;code&gt;println!&lt;/code&gt;?". You can do this, try it and do &lt;code&gt;cargo -q run -- resources/google.com.crt&lt;/code&gt;. Now do it again with a pipe - &lt;code&gt;cargo -q run -- resources/google.com.crt | head -n20&lt;/code&gt; - it will fail. I may do a short post on why this happens.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; run &lt;span class="nt"&gt;--&lt;/span&gt; resources/google.com.crt | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n20&lt;/span&gt;
TbsCertificate &lt;span class="o"&gt;{&lt;/span&gt;
    version: 2,
    serial: BigUint &lt;span class="o"&gt;{&lt;/span&gt;
        data: &lt;span class="o"&gt;[&lt;/span&gt;
            4412903,
            134217728,
            247394332,
            1459281101,
        &lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="o"&gt;}&lt;/span&gt;,
    signature: AlgorithmIdentifier &lt;span class="o"&gt;{&lt;/span&gt;
        algorithm: OID&lt;span class="o"&gt;(&lt;/span&gt;1.2.840.113549.1.1.11&lt;span class="o"&gt;)&lt;/span&gt;,
        parameters: BerObject &lt;span class="o"&gt;{&lt;/span&gt;
            class: 0,
            structured: 0,
            tag: EndOfContent,
            content: ContextSpecific&lt;span class="o"&gt;(&lt;/span&gt;
                EndOfContent,
                Some&lt;span class="o"&gt;(&lt;/span&gt;
                    BerObject &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty unreadable! But it's a start, we're making some headway. In the next post, we'll clean this up.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Read and print certificate&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Validate file is a certificate&lt;/li&gt;
&lt;li&gt;✔️ Print the certificate &lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/11058e3760ee31a0f47ba57e50af016b8ca9760b"&gt;wayofthepie/cert-decoder@11058e3&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Tiny refactor
&lt;/h1&gt;

&lt;p&gt;I realized I misnamed a few things. I missed them when refactoring. In the tests, most of the &lt;code&gt;FakeProcessor&lt;/code&gt; variables are still called &lt;code&gt;validator&lt;/code&gt;. Similarly in &lt;code&gt;main&lt;/code&gt;. Let's update those.&lt;br&gt;
&lt;/p&gt;

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

 fn main() -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
     let args = std::env::args().skip(1).collect();
&lt;span class="gd"&gt;-    let validator = CertProcessor;
-    execute(validator, args)
&lt;/span&gt;&lt;span class="gi"&gt;+    let processor = CertProcessor;
+    execute(processor, args)
&lt;/span&gt; }

 #[cfg(test)]
 mod test {

     ...


     #[test]
     fn should_error_if_not_given_a_single_argument() {
         let args = Vec::new();
&lt;span class="gd"&gt;-        let validator = FakeProcessor::default();
-        let result = execute(validator, args);
&lt;/span&gt;&lt;span class="gi"&gt;+        let processor = FakeProcessor::default();
+        let result = execute(processor, args);
&lt;/span&gt;         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             format!(
                 "{}{}",
                 "Error: did not receive a single argument, ",
                 "please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."
             )
         );
     }

     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
         let args = vec!["not-a-regular-file".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor::default();
-        let result = execute(validator, args);
&lt;/span&gt;&lt;span class="gi"&gt;+        let processor = FakeProcessor::default();
+        let result = execute(processor, args);
&lt;/span&gt;         assert!(result.is_err());
         assert_eq!(
             format!("{}", result.err().unwrap()),
             "Error: path given is not a regular file, please update to point to a certificate."
         );
     }

     #[test]
     fn should_error_if_given_argument_is_not_a_pem_encoded_certificate() {
         let args = vec!["Cargo.toml".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor {
&lt;/span&gt;&lt;span class="gi"&gt;+        let processor = FakeProcessor {
&lt;/span&gt;             is_file: true,
             ..FakeProcessor::default()
         };
&lt;span class="gd"&gt;-        let result = execute(validator, args);
&lt;/span&gt;&lt;span class="gi"&gt;+        let result = execute(processor, args);
&lt;/span&gt;         assert!(result.is_err())
     }

     ...

     #[test]
     fn should_succeed() {
         let cert = include_str!("../resources/google.com.crt");
         let args = vec!["doesnt-really-matter".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeProcessor {
&lt;/span&gt;&lt;span class="gi"&gt;+        let processor = FakeProcessor {
&lt;/span&gt;             is_file: true,
             file_str: cert.to_owned(),
         };
&lt;span class="gd"&gt;-        let result = execute(validator, args);
&lt;/span&gt;&lt;span class="gi"&gt;+        let result = execute(processor, args);
&lt;/span&gt;         println!("{:#?}", result);
         assert!(result.is_ok());
     }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🔗 &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/371ea84cbd017c1375b71399a7539d45b6d4ea5f"&gt;wayofthepie/cert-decoder@371ea84&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;There were a few things I glossed over that appeared in this post. I will take note of them and make sure they appear in one of the next posts. For example lifetimes, a better explanation of &lt;code&gt;Box&lt;/code&gt;, and that &lt;code&gt;println&lt;/code&gt; issue I mentioned in a note in the last section.&lt;/p&gt;

&lt;p&gt;There is also a small display issue when an error occurs. For example, if we pass no argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; run &lt;span class="nt"&gt;--&lt;/span&gt;
Error: &lt;span class="s2"&gt;"Error: did not receive a single argument, please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It repeats the word "Error" and also wraps our error string in quotes. We will fix this too in the next post!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;See &lt;a href="https://doc.rust-lang.org/book/ch10-00-generics.html?highlight=generics#generic-types-traits-and-lifetimes"&gt;Generic Types, Traits, and Lifetimes&lt;/a&gt; in the &lt;a href="https://doc.rust-lang.org/book/"&gt;Rust book&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Error handling in Rust is a big topic, to get started see the &lt;a href="https://doc.rust-lang.org/stable/rust-by-example/error.html"&gt;Error Handling&lt;/a&gt; chapter in the &lt;a href="https://doc.rust-lang.org/book/"&gt;Rust book&lt;/a&gt;. For more on boxed errors see &lt;a href="https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html"&gt;Boxing errors&lt;/a&gt;.d ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;See &lt;a href="https://doc.rust-lang.org/book/ch17-02-trait-objects.html"&gt;Using Trait Objects That Allow for Values of Different Types&lt;/a&gt; in the &lt;a href="https://doc.rust-lang.org/book/"&gt;Rust book&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;For more on how the &lt;code&gt;?&lt;/code&gt; works see &lt;a href="https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html"&gt;The ? operator for easier error handling&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;For more on deriving see &lt;a href="https://doc.rust-lang.org/stable/rust-by-example/trait/derive.html"&gt;Derive&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>rust</category>
      <category>x509</category>
    </item>
    <item>
      <title>Three bytes to an integer</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sun, 21 Jun 2020 14:30:31 +0000</pubDate>
      <link>https://forem.com/wayofthepie/three-bytes-to-an-integer-13g5</link>
      <guid>https://forem.com/wayofthepie/three-bytes-to-an-integer-13g5</guid>
      <description>&lt;p&gt;This post is a quick overview of how I turned an initially poor solution to a problem into a better one, and the thought process behind it.&lt;/p&gt;

&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;I'm working on pulling information out of the &lt;a href="https://www.certificate-transparency.org/what-is-ct"&gt;Certificate Transparency Logs&lt;/a&gt;. The logs are made up of a JSON list of log entries. A single entry is a JSON object with two fields, &lt;code&gt;leaf_input&lt;/code&gt; and &lt;code&gt;extra_data&lt;/code&gt;. The &lt;code&gt;leaf_input&lt;/code&gt; field contains base64 encoded binary data. I want to pull information out of this binary data. &lt;/p&gt;

&lt;p&gt;To outline the structure of the binary data I'll use the following notation, indicating byte numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[n]&lt;/strong&gt; - indicates byte n, so [0] is the first byte.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[n..=m]&lt;/strong&gt; - indicates a range of bytes, inclusive, so [2..=4] means bytes 2, 3 and 4.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[n..]&lt;/strong&gt; - indicates the remaining bytes in the binary data after and including index n.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0] [1] [2..=9] [10..=11] [12..=14] [15..]
 |   |     |        |         |      |
 |   |     |        |         |      |- (rest) the rest of the bytes
 |   |     |        |         |      
 |   |     |        |         |- (length) the length of the certificate      
 |   |     |        |               
 |   |     |        | - the log entry type, not relevant in this post              
 |   |     |                       
 |   |     | - timestamp, not relevant in this post                       
 |   |                            
 |   | - signature type, not relevant in this post                       
 |                               
 | - version, not relevant in this post            
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The relevant parts here are the &lt;strong&gt;length&lt;/strong&gt; (bytes [12..=14]) and the &lt;strong&gt;rest&lt;/strong&gt; (indicated by &lt;code&gt;[15..]&lt;/code&gt; above). &lt;strong&gt;rest&lt;/strong&gt; contains an X.509 certificate we want, and the three bytes in the &lt;strong&gt;length&lt;/strong&gt; define an integer which tells us how many bytes that certificate makes up in &lt;strong&gt;rest&lt;/strong&gt;. Once we read &lt;strong&gt;length&lt;/strong&gt; bytes from &lt;strong&gt;rest&lt;/strong&gt;, the remainder of &lt;strong&gt;rest&lt;/strong&gt; is data that we do not need.&lt;/p&gt;
&lt;h1&gt;
  
  
  A solution
&lt;/h1&gt;

&lt;p&gt;So we need to turn the three bytes which make up &lt;strong&gt;length&lt;/strong&gt; into an integer. An integer whose size is 3 bytes would be a 24-bit int. Rust does not support 24-bit ints. The closest is a &lt;code&gt;u32&lt;/code&gt;, a 32-bit unsigned int. You can't just turn 3 bytes into a &lt;code&gt;u32&lt;/code&gt; however. The first solution I came up with was something like the following code.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// "bytes" is the full binary data inside the "leaf_input" field&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_cert_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;length_bytes_slice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;length_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.cloned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;length_bytes&lt;/span&gt;&lt;span class="nf"&gt;.reverse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;length_bytes&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length_bytes&lt;/span&gt;&lt;span class="nf"&gt;.as_slice&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is not great! We allocate a new &lt;code&gt;Vec&lt;/code&gt; and we have to pad it with a zero to make it 4 bytes in length. Because we cannot push a value onto the start of a &lt;code&gt;Vec&lt;/code&gt; we first reverse it. Then we convert it into an array of type &lt;code&gt;[u8; 4]&lt;/code&gt; - this is what &lt;code&gt;length_bytes.as_slice().try_into().unwrap()&lt;/code&gt; does. Finally, we read the bytes in little endian format as a &lt;code&gt;u32&lt;/code&gt;. Way too much happening here for what on the surface seems like a simple conversion! &lt;/p&gt;

&lt;p&gt;I thought about it some more and came up with a better solution. &lt;/p&gt;
&lt;h1&gt;
  
  
  A better solution
&lt;/h1&gt;

&lt;p&gt;First, here is an outline of my thought process. Let's say bytes 12 to 14 look as follows, in hex notation:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[00, 05, 4c]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then the value for the &lt;code&gt;length&lt;/code&gt; is &lt;strong&gt;00054c&lt;/strong&gt; in hex, which is 1356 in decimal. &lt;strong&gt;00054c&lt;/strong&gt; is &lt;strong&gt;000000 + 000500 + 00004c&lt;/strong&gt;. If we take the bytes &lt;strong&gt;00&lt;/strong&gt;, &lt;strong&gt;05&lt;/strong&gt; and &lt;strong&gt;4c&lt;/strong&gt; individually, how do we get &lt;strong&gt;00054c&lt;/strong&gt;? &lt;/p&gt;

&lt;p&gt;Well, each time we move a number in the hex string one space to the left, we multiply the number by a power of 16. For example, moving &lt;strong&gt;000005&lt;/strong&gt;, which is 5 in decimal, to &lt;strong&gt;000050&lt;/strong&gt;, which is 80  in decimal, we had to multiply 5 by 16. If we move it two places, we would need to multiply it by 256, which is 16 squared, so &lt;strong&gt;000005&lt;/strong&gt; to &lt;strong&gt;000500&lt;/strong&gt; (1280 in decimal) is 5 * 256. Concretely, to convert &lt;strong&gt;000500&lt;/strong&gt; hex to decimal:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;000500=(0∗165)+(0∗164)+(0∗163)+(5∗162)+(0∗161)+(0∗160)=5∗162=1280
\begin{aligned}
 000500 &amp;amp;=  (0 * 16 ^5) + (0 * 16 ^4) + (0 * 16 ^3) + (5 * 16 ^2) + (0 * 16 ^1) + (0 * 16 ^0) \\
    &amp;amp;= 5 * 16^2  \\
    &amp;amp;= 1280 
\end{aligned} 
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mtable"&gt;&lt;span class="col-align-r"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;000500&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="col-align-l"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;5&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;5&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;6&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1280&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;



&lt;p&gt;With this knowledge we can update &lt;code&gt;get_cert_length&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_cert_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;65536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much cleaner than the original version! The values we multiply each byte with are always a power of 2 so we can also use bit-shifts instead of the multiplications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_cert_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ended up using the bit-shift version in the end. &lt;/p&gt;

</description>
      <category>rust</category>
    </item>
    <item>
      <title>Morning silence and broken drivers</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sun, 14 Jun 2020 11:36:25 +0000</pubDate>
      <link>https://forem.com/wayofthepie/morning-silence-and-broken-drivers-2d3b</link>
      <guid>https://forem.com/wayofthepie/morning-silence-and-broken-drivers-2d3b</guid>
      <description>&lt;h1&gt;
  
  
  Silence
&lt;/h1&gt;

&lt;p&gt;I woke up this morning and booted my desktop. No sound! Some digging around and I noticed &lt;code&gt;dmesg&lt;/code&gt; spitting out an error. I don't have that message saved but here are the details from the kernel logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ journalctl &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--since&lt;/span&gt; yesterday | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Focusrite Scarlett"&lt;/span&gt; &lt;span class="nt"&gt;-C10&lt;/span&gt;
...
Jun 14 06:45:59 sky kernel: usb 1-1.2.3: USB disconnect, device number 8
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: new high-speed USB device number 9 using ehci-pci
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: New USB device found, &lt;span class="nv"&gt;idVendor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1235, &lt;span class="nv"&gt;idProduct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8203, &lt;span class="nv"&gt;bcdDevice&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 6.2f
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: New USB device strings: &lt;span class="nv"&gt;Mfr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1, &lt;span class="nv"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3, &lt;span class="nv"&gt;SerialNumber&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: Product: Scarlett 6i6 USB
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: Manufacturer: Focusrite
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: SerialNumber: 00009810
Jun 14 06:46:00 sky kernel: usb 1-1.2.3: Focusrite Scarlett Gen 2 Mixer Driver disabled&lt;span class="p"&gt;;&lt;/span&gt; use options snd_usb_audio &lt;span class="nv"&gt;device_setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 to &lt;span class="nb"&gt;enable &lt;/span&gt;and report any issues to g@b4.v
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like a driver update in the latest kernel broke my USB audio device, a pretty old Focusrite Scarlett. A quick google of the error brings you to &lt;a href="https://github.com/geoffreybennett/scarlett-gen2/releases"&gt;these release notes&lt;/a&gt;. This is a fork of the kernel where the Focusrite Scarlett driver development happens. They mention adding one of the following lines to &lt;code&gt;/etc/modprobe.d/scarlett.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options snd_usb_audio device_setup=1,1,1,1
options snd_usb_audio vid=0x1235 pid=0x8212 device_setup=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! I'm not sure what &lt;code&gt;vid&lt;/code&gt; and &lt;code&gt;pid&lt;/code&gt; are. However, if you look back up at the kernel logs there is a line that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New USB device found, idVendor=1235, idProduct=8203, bcdDevice= 6.2f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;vid&lt;/code&gt; is probably the Vendor ID and &lt;code&gt;pid&lt;/code&gt; product ID. I had a quick look at the &lt;a href="https://github.com/geoffreybennett/scarlett-gen2/commit/63693795498192ca1142aca4a829a1276a1c76b9"&gt;commit for that release&lt;/a&gt; and that clarified it. There are a number of constants defined in &lt;a href="https://github.com/geoffreybennett/scarlett-gen2/blob/63693795498192ca1142aca4a829a1276a1c76b9/sound/usb/mixer_quirks.c"&gt;sound/usb/mixer_quirks.c&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;USB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8203&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="cm"&gt;/* Focusrite Scarlett 6i6 2nd Gen */&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;USB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8204&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="cm"&gt;/* Focusrite Scarlett 18i8 2nd Gen */&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;USB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8201&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="cm"&gt;/* Focusrite Scarlett 18i20 2nd Gen */&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;USB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8212&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="cm"&gt;/* Focusrite Scarlett 4i4 3rd Gen */&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;USB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x1235&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8213&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="cm"&gt;/* Focusrite Scarlett 8i6 3rd Gen */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The kernel logs have &lt;code&gt;idProduct=8203&lt;/code&gt;, this matches the constant with the comment for the &lt;code&gt;Focusrite Scarlett 6i6 2nd Gen&lt;/code&gt;, which is the device I have. Great, so now we know what &lt;code&gt;vid&lt;/code&gt; and &lt;code&gt;pid&lt;/code&gt; should be. Add this to &lt;code&gt;/etc/modprobe.d/scarlett.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;options snd_usb_audio vid=0x1235 pid=0x8203 device_setup=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;sudo systemctl restart systemd-modules-load.service&lt;/code&gt; to reload the kernel modules. Check they are loaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;✦ ➜ modprobe &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;snd_usb_audio | &lt;span class="nb"&gt;grep &lt;/span&gt;options
options snd_usb_audio &lt;span class="nv"&gt;vid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x1235 &lt;span class="nv"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x8203 &lt;span class="nv"&gt;device_setup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still no sound... &lt;/p&gt;

&lt;h1&gt;
  
  
  Still silence
&lt;/h1&gt;

&lt;p&gt;Now what?  Some more googling around brought me to &lt;a href="https://linuxmusicians.com/viewtopic.php?f=6&amp;amp;t=19975&amp;amp;start=30"&gt;this forum thread&lt;/a&gt;. There is some discussion about the issue there, luckily the author of the driver is also posting! In one of the comments they mention:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please upgrade to 5.4.1 first; there are known issues with the 6i6 support in 5.4.0 and it won't work very well even after you enable it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What version of the kernel am I on?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;
5.4.0-37-generic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah! A likely suspect. Let's compile a custom kernel, say the latest 5.4.x kernel. It's been a while since I've done this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.46.tar.xz

➜ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.46.tar.sign

➜ unxz linux-5.4.46.tar.xz

&lt;span class="c"&gt;# I haven't stored the public keys for linus/greg on this machine yet&lt;/span&gt;
➜ gpg &lt;span class="nt"&gt;--locate-keys&lt;/span&gt; torvalds@kernel.org gregkh@kernel.org
gpg: WARNING: unacceptable HTTP redirect from server was cleaned up
gpg: key 38DBBDC86092693E: public key &lt;span class="s2"&gt;"Greg Kroah-Hartman &amp;lt;gregkh@kernel.org&amp;gt;"&lt;/span&gt; imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: WARNING: unacceptable HTTP redirect from server was cleaned up
gpg: key 79BE3E4300411886: public key &lt;span class="s2"&gt;"Linus Torvalds &amp;lt;torvalds@kernel.org&amp;gt;"&lt;/span&gt; imported
gpg: Total number processed: 1
gpg:               imported: 1
pub   rsa4096 2011-09-23 &lt;span class="o"&gt;[&lt;/span&gt;SC]
      647F28654894E3BD457199BE38DBBDC86092693E
uid           &lt;span class="o"&gt;[&lt;/span&gt; unknown] Greg Kroah-Hartman &amp;lt;gregkh@kernel.org&amp;gt;
sub   rsa4096 2011-09-23 &lt;span class="o"&gt;[&lt;/span&gt;E]

pub   rsa2048 2011-09-20 &lt;span class="o"&gt;[&lt;/span&gt;SC]
      ABAF11C65A2970B130ABE3C479BE3E4300411886
uid           &lt;span class="o"&gt;[&lt;/span&gt; unknown] Linus Torvalds &amp;lt;torvalds@kernel.org&amp;gt;
sub   rsa2048 2011-09-20 &lt;span class="o"&gt;[&lt;/span&gt;E]

&lt;span class="c"&gt;# Verify the signature, can ignore the warning I just haven't trusted&lt;/span&gt;
➜ gpg &lt;span class="nt"&gt;--verify&lt;/span&gt; linux-5.4.46.tar.sign
gpg: assuming signed data &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'linux-5.4.46.tar'&lt;/span&gt;
gpg: Signature made Wed 10 Jun 2020 19:25:28 IST
gpg:                using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Good signature from &lt;span class="s2"&gt;"Greg Kroah-Hartman &amp;lt;gregkh@kernel.org&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 647F 2865 4894 E3BD 4571  99BE 38DB BDC8 6092 693E

&lt;span class="c"&gt;# I haven't trusted the key so I got a warning, to do so&lt;/span&gt;
➜ gpg &lt;span class="nt"&gt;--tofu-policy&lt;/span&gt; good 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Setting TOFU trust policy &lt;span class="k"&gt;for &lt;/span&gt;new binding &amp;lt;key: 647F28654894E3BD457199BE38DBBDC86092693E, user &lt;span class="nb"&gt;id&lt;/span&gt;: Greg Kroah-Hartman &amp;lt;gregkh@kernel.org&amp;gt;&amp;gt; to good.

➜ gpg &lt;span class="nt"&gt;--trust-model&lt;/span&gt; tofu &lt;span class="nt"&gt;--verify&lt;/span&gt; linux-5.4.46.tar.sign
gpg: assuming signed data &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'linux-5.4.46.tar'&lt;/span&gt;
gpg: Signature made Wed 10 Jun 2020 19:25:28 IST
gpg:                using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
gpg: Good signature from &lt;span class="s2"&gt;"Greg Kroah-Hartman &amp;lt;gregkh@kernel.org&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;full]
gpg: gregkh@kernel.org: Verified 1 signatures &lt;span class="k"&gt;in &lt;/span&gt;the past 0 seconds.  Encrypted
     0 messages.

➜ &lt;span class="nb"&gt;tar &lt;/span&gt;xf linux-5.4.46.tar
➜ &lt;span class="nb"&gt;cd &lt;/span&gt;linux-5.4.46/

&lt;span class="c"&gt;# Copy the current kernels config&lt;/span&gt;
➜ &lt;span class="nb"&gt;cp&lt;/span&gt; /boot/config-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; .config

&lt;span class="c"&gt;# Once menuconfig loads just save and exit, no need to change anything&lt;/span&gt;
➜ make menuconfig

&lt;span class="c"&gt;# This can take a while...&lt;/span&gt;
➜ &lt;span class="nb"&gt;time &lt;/span&gt;make &lt;span class="nt"&gt;-j12&lt;/span&gt;
  HOSTCC  scripts/basic/fixdep
  DESCEND  objtool
...

&lt;span class="c"&gt;# Install the modules&lt;/span&gt;
➜ &lt;span class="nb"&gt;sudo &lt;/span&gt;make modules_install
...

&lt;span class="c"&gt;# Install the kernel&lt;/span&gt;
➜ &lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make install&lt;/code&gt; should setup the boot config needed in grub, I just need to reboot now. Reboot... and nothing!&lt;/p&gt;

&lt;h1&gt;
  
  
  Silence and broken graphics
&lt;/h1&gt;

&lt;p&gt;This time &lt;code&gt;dmesg&lt;/code&gt; is giving a different error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;  159.097340] usb 1-1.1: Product: Scarlett 6i6 USB
&lt;span class="o"&gt;[&lt;/span&gt;  159.097343] usb 1-1.1: Manufacturer: Focusrite
&lt;span class="o"&gt;[&lt;/span&gt;  159.097344] usb 1-1.1: SerialNumber: 00009810
&lt;span class="o"&gt;[&lt;/span&gt;  164.318711] usb 1-1.1: Scarlett Gen 2 USB response result cmd 0 was &lt;span class="nt"&gt;-110&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of that, I broke the nvidia drivers 😄 these need to be compiled for the kernel you are on. This is my lovely 1440p display:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--efTLQjRb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/mzd7y6lef23xtyd1vm1j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--efTLQjRb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/mzd7y6lef23xtyd1vm1j.png" alt="Alt Text" width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Purge them and flip back to noveau for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get remove &lt;span class="nt"&gt;--purge&lt;/span&gt; nvidia-&lt;span class="k"&gt;*&lt;/span&gt;
➜ reboot
➜ lshw &lt;span class="nt"&gt;-c&lt;/span&gt; video
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="nt"&gt;-display&lt;/span&gt;
       description: VGA compatible controller
       product: GP102 &lt;span class="o"&gt;[&lt;/span&gt;GeForce GTX 1080 Ti]
       vendor: NVIDIA Corporation
       physical &lt;span class="nb"&gt;id&lt;/span&gt;: 0
       bus info: pci@0000:03:00.0
       version: a1
       width: 64 bits
       clock: 33MHz
       capabilities: vga_controller bus_master cap_list rom
       configuration: &lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nouveau &lt;span class="nv"&gt;latency&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="c"&gt;# running noveau drivers now&lt;/span&gt;
       resources: irq:73 memory:fa000000-faffffff memory:c0000000-cfffffff memory:d0000000-d1ffffff ioport:e000&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;128&lt;span class="o"&gt;)&lt;/span&gt; memory:c0000-dffff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t0IfXbxK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/irt9crchbspnlkf8ig96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t0IfXbxK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/irt9crchbspnlkf8ig96.png" alt="Alt Text" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back to the &lt;code&gt;dmesg&lt;/code&gt; error - &lt;code&gt;Scarlett Gen 2 USB response result cmd 0 was -110&lt;/code&gt;. Looking at &lt;code&gt;alsamixer&lt;/code&gt; this shows it is not recognizing the device properly.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EV3irJ3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3th7jjskezo7qs17qvs8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EV3irJ3c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/3th7jjskezo7qs17qvs8.png" alt="Alt Text" width="362" height="195"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;I wonder if it is the USB port I'm using... It is! Using a different port:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NyKw6PIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/cx6rye29y7ejo2c6vvl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NyKw6PIF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/cx6rye29y7ejo2c6vvl4.png" alt="Alt Text" width="309" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I get the expected "Front left", "Front center" sound output when running &lt;code&gt;speaker-test -t wav -c 6&lt;/code&gt;. This is weird, as other devices work fine on that port and it was previously working there too. Something else to investigate another time I guess.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In the end there were a number of issues. This is the first time a driver issue has caused me trouble on linux in years. But my hardware is getting old, it's probably time to upgrade a few things. Also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't try to debug and fix an issue when you have just woken up and have had no caffeine injected into your system yet.&lt;/li&gt;
&lt;li&gt;All computers are broken.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Rust and GitHub Actions</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sun, 07 Jun 2020 17:18:42 +0000</pubDate>
      <link>https://forem.com/wayofthepie/rust-and-github-actions-3kob</link>
      <guid>https://forem.com/wayofthepie/rust-and-github-actions-3kob</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Setting up GitHub Actions is entirely optional for this series. I do not link back to anything in this post in any future post, so you don't need to set this up if you don't want to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Before we continue implementing our CLI, let's take time to set up some &lt;a href="https://help.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; to build and test our commits. We'll use the actions defined in the &lt;a href="https://github.com/actions-rs" rel="noopener noreferrer"&gt;actions-rs GitHub Organization&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Build and test&lt;/li&gt;
&lt;li&gt;
Clippy lint

&lt;ul&gt;
&lt;li&gt;
Block PR's on failing checks

&lt;ul&gt;
&lt;li&gt;Clippy deny lints&lt;/li&gt;
&lt;li&gt;Protected branch rule&lt;/li&gt;
&lt;li&gt;Clean up and merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Security audit&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Build and test
&lt;/h1&gt;

&lt;p&gt;The workflow I normally follow when coding is to create a short-lived branch on which all commits are built and tested then create a Pull Request (PR) into the master branch. Let's set this up. Check out a new branch, let's call it actions. Then, create a file called &lt;code&gt;build.yml&lt;/code&gt; under &lt;code&gt;.github/workflows/&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .github/workflows
➜ &lt;span class="nb"&gt;touch&lt;/span&gt; .github/workflows/build.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll create a simple action that builds and tests our CLI. Add the following to &lt;code&gt;build.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/toolchain@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;toolchain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/cargo@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/cargo@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;on&lt;/code&gt; defines the event(s)&lt;sup id="fnref1"&gt;1&lt;/sup&gt; which should trigger this workflow. We set it to run on a &lt;code&gt;push&lt;/code&gt; to any branch. The workflow has a single job, called &lt;code&gt;build&lt;/code&gt;. This job runs on the &lt;code&gt;ubuntu-latest&lt;/code&gt; &lt;a href="https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners" rel="noopener noreferrer"&gt;Virtual Environment&lt;/a&gt;. It checks out our code, installs the stable Rust toolchain and runs a &lt;code&gt;cargo build&lt;/code&gt; then runs a &lt;code&gt;cargo test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Commit this change and push to your repo. GitHub Actions will then run this workflow. Once complete, when you click the &lt;code&gt;Actions&lt;/code&gt; tab on your repo, you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F243h3td9vvz0t2unxdts.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F243h3td9vvz0t2unxdts.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had two builds, as I rebased a commit, you will likely only see one. Click into the build, then click the &lt;code&gt;build&lt;/code&gt; job on the left with the checkmark and it will show you the steps in the workflow.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbc5tgf2sd36xh13pwcj9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbc5tgf2sd36xh13pwcj9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a good start. All commits will be built and tested. We can add a check to PR's so that they will block merges if this workflow fails. But first, let's add some improvements. The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/da88d93a4a4aacc47b3b9286951f2d96f7d5841c" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;
&lt;h1&gt;
  
  
  Clippy lint
&lt;/h1&gt;

&lt;p&gt;One thing we did not do in the last post was to run a linter. Rust has an awesome linter called &lt;a href="https://github.com/rust-lang/rust-clippy" rel="noopener noreferrer"&gt;Clippy&lt;/a&gt; which can highlight quite a lot of issues. Let's run it in our project now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo clippy
    Finished dev &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.01s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No issues, great! Like any test, let's see it fail. Or in this case, make Clippy give out to us. Let's add something Clippy does not like to &lt;code&gt;main.rs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// clippy won't like this &lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assertions on a constant are optimized out by the compiler&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, so Clippy will warn about this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo clippy
warning: &lt;span class="sb"&gt;`&lt;/span&gt;assert!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; should probably be replaced
  &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:35:5
   |
35 |     assert!&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   |     ^^^^^^^^^^^^^^^
   |
   &lt;span class="o"&gt;=&lt;/span&gt; note: &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="c"&gt;#[warn(clippy::assertions_on_constants)]` on by default&lt;/span&gt;
   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;help&lt;/span&gt;: use &lt;span class="sb"&gt;`&lt;/span&gt;panic!&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; or &lt;span class="sb"&gt;`&lt;/span&gt;unreachable!&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;help&lt;/span&gt;: &lt;span class="k"&gt;for &lt;/span&gt;further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
   &lt;span class="o"&gt;=&lt;/span&gt; note: this warning originates &lt;span class="k"&gt;in &lt;/span&gt;a macro &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;Nightly builds, run with &lt;span class="nt"&gt;-Z&lt;/span&gt; macro-backtrace &lt;span class="k"&gt;for &lt;/span&gt;more info&lt;span class="o"&gt;)&lt;/span&gt;

warning: 1 warning emitted

    Finished dev &lt;span class="o"&gt;[&lt;/span&gt;unoptimized + debuginfo] target&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;0.01s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes on a subsequent run, Clippy will just say it's successful and not run correctly. See &lt;a href="https://github.com/rust-lang/rust-clippy/issues/2604" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; for more information. If you think this is happening you can force a full re-run by modifying a file, e.g. &lt;code&gt;touch src/main.rs; cargo clippy&lt;/code&gt;. It seems this should be fixed, but might only be fixed in nightly Rust, not stable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Great! Just what we want. Let's leave this assertion here and update our existing workflow to add a lint step which runs Clippy. The &lt;a href="https://github.com/actions-rs/clippy-check" rel="noopener noreferrer"&gt;clippy-check&lt;/a&gt; action will allow us to easily do this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/toolchain@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;toolchain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;table&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/cargo@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/cargo@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lint&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/clippy-check@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit both the assertion and the &lt;code&gt;lint&lt;/code&gt; addition to the workflow and push. Once the workflow run finishes, click into the Clippy step and you should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F09lmv3xjnatve52jzfd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F09lmv3xjnatve52jzfd2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The warning is now attached to a commit and links to a specific file. Click the text "Check warning on line 35 in src/main.rs" in the annotation and it should bring you to exactly where this issue occurs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fijucub284y9ocst4dogn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fijucub284y9ocst4dogn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is nice! Create a PR and you will see this annotation exactly as it is above in the &lt;code&gt;Files Changed&lt;/code&gt; tab. I find this useful, even if I am the sole developer. Sometimes I forget to run Clippy and I always do a quick review of the code in a PR. Create a PR into the master branch for this and you will also see the checks have all passed. Don't merge it yet!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foek8tfiyyu9c5hhulzog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Foek8tfiyyu9c5hhulzog.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's make one last change. The code up this point can be found &lt;a href="https://github.com/wayofthepie/cert-decoder/commit/134827d780528b43d1181f07d65e5b397e2867e4" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Block PR's on failing checks
&lt;/h2&gt;

&lt;p&gt;If we somehow manage to check something in which does not compile, has failing tests, or has Clippy lint issues which it is set to deny, we should block the PR, i.e. not allow it to be merged until these issues are fixed. This may seem like overkill if you are the only dev on a repo, but I do this all the time because I make these mistakes! No matter how careful I try to be.&lt;/p&gt;

&lt;p&gt;Before we configure our repo to block PRs, a quick note on Clippy lints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clippy deny lints
&lt;/h3&gt;

&lt;p&gt;The lint we have in our PR above is just a warning. Clippy also has several lints that will cause it to fail if detected. These are lints at the &lt;code&gt;Deny&lt;/code&gt; level in the list of &lt;a href="https://rust-lang.github.io/rust-clippy/master/index.html" rel="noopener noreferrer"&gt;Clippy lints&lt;/a&gt;. For example, the lint &lt;a href="https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant" rel="noopener noreferrer"&gt;approx_constant&lt;/a&gt; checks for constants which are defined under &lt;a href="https://doc.rust-lang.org/stable/std/f32/consts/#constants" rel="noopener noreferrer"&gt;std::f32::consts&lt;/a&gt; or &lt;a href="https://doc.rust-lang.org/stable/std/f64/consts/#constants" rel="noopener noreferrer"&gt;std::f64::consts&lt;/a&gt;. If it finds matching constants in your code it will error out.&lt;/p&gt;

&lt;p&gt;To see this in action, update &lt;code&gt;main&lt;/code&gt; with an unused variable which approximates pi. Before you do that, make sure you have committed your changes up to here, so we can easily reset. I've commented out the &lt;code&gt;assert!(false)&lt;/code&gt; we added earlier so we don't also get a warning for that from Clippy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// assert!(false);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Clippy and it will give an error, and return a non-zero exit code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo clippy
    Checking cert-decode v0.1.0 &lt;span class="o"&gt;(&lt;/span&gt;/home/chaospie/repos/blog-cert-decode/cert-decode&lt;span class="o"&gt;)&lt;/span&gt;
error: approximate value of &lt;span class="sb"&gt;`&lt;/span&gt;f&lt;span class="o"&gt;{&lt;/span&gt;32, 64&lt;span class="o"&gt;}&lt;/span&gt;::consts::PI&lt;span class="sb"&gt;`&lt;/span&gt; found. Consider using it directly
  &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; src/main.rs:36:13
   |
36 |     &lt;span class="nb"&gt;let &lt;/span&gt;_ &lt;span class="o"&gt;=&lt;/span&gt; 3.14&lt;span class="p"&gt;;&lt;/span&gt;
   |             ^^^^
   |
   &lt;span class="o"&gt;=&lt;/span&gt; note: &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="c"&gt;#[deny(clippy::approx_constant)]` on by default&lt;/span&gt;
   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;help&lt;/span&gt;: &lt;span class="k"&gt;for &lt;/span&gt;further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant

error: aborting due to previous error

error: could not compile &lt;span class="sb"&gt;`&lt;/span&gt;cert-decode&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

To learn more, run the &lt;span class="nb"&gt;command &lt;/span&gt;again with &lt;span class="nt"&gt;--verbose&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

➜ &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
101
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;echo $?&lt;/code&gt; will print the return code of the last command executed in the shell. Don't forget to reset these changes, either manually or by running &lt;code&gt;git checkout src/main.rs&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protected branch rule
&lt;/h3&gt;

&lt;p&gt;The configuration for blocking PRs is defined with a protected branch&lt;sup id="fnref3"&gt;3&lt;/sup&gt; rule. Go to &lt;em&gt;Settings -&amp;gt; Branches -&amp;gt; Branch protection rules -&amp;gt; Add rule&lt;/em&gt; in you repo. Set the &lt;em&gt;Branch name pattern&lt;/em&gt; to &lt;code&gt;master&lt;/code&gt; and select the status checks to verify in PRs as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Filw3mavleoj3y4g9xazu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Filw3mavleoj3y4g9xazu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have an extra status check here called &lt;code&gt;build_and_test&lt;/code&gt; because I had initially defined the job in our workflow YAML &lt;code&gt;build_and_test&lt;/code&gt;. It is now called &lt;code&gt;build&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now if we raise a PR with any failure in our &lt;code&gt;build&lt;/code&gt; job - the job in our workflow YAML - or the Clippy step specifically, the PR will be blocked from merging. This is what it would look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frhfhl07nrisjhm877qzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frhfhl07nrisjhm877qzb.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the details of the lint error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3x7q1px8fo4n2isuydmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3x7q1px8fo4n2isuydmr.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that because I am the only dev, I am an admin on this repo so I can still merge. But it is glaringly obvious something is wrong. This has saved me a few times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean up and merge
&lt;/h3&gt;

&lt;p&gt;The PR you raised, before our Clippy deny lints digression, has a warning because of the &lt;code&gt;assert!(false)&lt;/code&gt; call in &lt;code&gt;main&lt;/code&gt;. Clean that up, commit, push, and merge it. &lt;code&gt;main&lt;/code&gt; should look as follows again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only change in your PR should be the &lt;code&gt;build.yml&lt;/code&gt; file. Once merged the workflow will run again on &lt;code&gt;master&lt;/code&gt; and be successful. There is one more workflow I usually add to Rust repositories. So let's add that also. See &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/50da0793f35ecdb5b0ea18196d0b16a24d426902" rel="noopener noreferrer"&gt;here&lt;/a&gt; for the code up to this point.&lt;/p&gt;

&lt;h1&gt;
  
  
  Security audit
&lt;/h1&gt;

&lt;p&gt;GitHub tracks vulnerabilities from several &lt;a href="https://help.github.com/en/github/visualizing-repository-data-with-graphs/listing-the-packages-that-a-repository-depends-on#supported-languages" rel="noopener noreferrer"&gt;supported language package managers&lt;/a&gt;. It will alert you if your repository contains a vulnerable dependency. It doesn't support &lt;code&gt;cargo&lt;/code&gt; yet, I'm not sure if they have plans to support it either. However, &lt;code&gt;cargo&lt;/code&gt; has the &lt;a href="https://github.com/RustSec/cargo-audit" rel="noopener noreferrer"&gt;cargo-audit&lt;/a&gt; plugin and we can use this in a workflow. &lt;a href="https://github.com/actions-rs" rel="noopener noreferrer"&gt;actions-rs&lt;/a&gt; has an action just for this, &lt;a href="https://github.com/actions-rs/audit-check" rel="noopener noreferrer"&gt;audit-check&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's create a new branch called &lt;code&gt;audit-workflow&lt;/code&gt; and add a new workflow at &lt;code&gt;.github/workflows/security-audit.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security audit&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;8&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/Cargo.*"&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security_audit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-rs/audit-check@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run the audit check at 8 am UTC every day, whenever we commit and push a change to any branch which updates &lt;code&gt;Cargo.toml&lt;/code&gt; or &lt;code&gt;Cargo.lock&lt;/code&gt; and whenever we create a PR into master. &lt;/p&gt;

&lt;p&gt;Why all these cases? Well, we do it daily at 8 am as a vulnerability can be found in dependencies at any time, so we should check daily that nothing new was found. We do it on changes to &lt;code&gt;Cargo.*&lt;/code&gt; files so we can immediately detect if a change in dependencies has introduced and vulnerability. Finally, we do it on a PR into &lt;code&gt;master&lt;/code&gt; so we can be sure, at least at the time of the PR, that we are not pushing known vulnerabilities to our &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;Commit and push this workflow change to our branch. Let's test that it works. To do so add a dependency to &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cert-decode"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Stephen OBrien &amp;lt;wayofthepie@users.noreply.github.com&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2018"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;x509-parser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.7.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a build with &lt;code&gt;cargo build&lt;/code&gt; so &lt;code&gt;Cargo.lock&lt;/code&gt; gets updated. Because this is just a test, we can edit the last commit and then later remove the dependency and edit again. Don't ever do this on a shared branch! This is how to do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git add Cargo.&lt;span class="k"&gt;*&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin +HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;+HEAD&lt;/code&gt; in the &lt;code&gt;git push&lt;/code&gt; command says to force push to the branch &lt;code&gt;HEAD&lt;/code&gt;&lt;sup id="fnref4"&gt;4&lt;/sup&gt; points to. A force push rewrites history, this is why you should not do it on a shared branch. &lt;code&gt;HEAD&lt;/code&gt; is the current branch. As we have changed the &lt;code&gt;Cargo.*&lt;/code&gt; files the audit workflow should run. It will hopefully be successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo5li9a2zaz3p1josqrw2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo5li9a2zaz3p1josqrw2.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To go back to the previous state you can remove the dependency from &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cert-decode"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;span class="py"&gt;authors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Stephen OBrien &amp;lt;wayofthepie@users.noreply.github.com&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;edition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2018"&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build with &lt;code&gt;cargo build&lt;/code&gt;, and edit the last commit again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git add Cargo.&lt;span class="k"&gt;*&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin +HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before creating another PR for this, you should update the branch protection settings and add &lt;code&gt;security_audit&lt;/code&gt; as a status check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqs67468bry5mgbr4cc1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqs67468bry5mgbr4cc1m.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can create a PR into &lt;code&gt;master&lt;/code&gt; and this should run again. If it's green, merge it. The code up to the end of this post can be found &lt;a href="https://github.com/wayofthepie/cert-decoder/commit/a9d761e1ba306587a332e97e9fd4e654f1049ab9" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This audit workflow can take some time to run as it first needs to install &lt;code&gt;cargo-audit&lt;/code&gt;. This takes about 5 minutes looking at the logs for a run. We can improve this with caching, but I'll leave that for another post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A build and test workflow for all our commits.&lt;/li&gt;
&lt;li&gt;Security auditing on our dependencies.&lt;/li&gt;
&lt;li&gt;Checks which block PR's if we have broken builds, lint issues, or vulnerabilities in dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is something I always set up at the start of a project. We'll go back to implementing the CLI in the next post. &lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;See &lt;a href="https://help.github.com/en/actions/reference/events-that-trigger-workflows" rel="noopener noreferrer"&gt;Events that trigger workflows&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;See the &lt;a href="https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants" rel="noopener noreferrer"&gt;assertions_on_constants&lt;/a&gt; lint. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;See &lt;a href="https://help.github.com/en/github/administering-a-repository/configuring-protected-branches" rel="noopener noreferrer"&gt;Configuring protected branches&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;For more on the &lt;code&gt;HEAD&lt;/code&gt; reference see &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-References#ref_the_ref" rel="noopener noreferrer"&gt;here&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>rust</category>
      <category>github</category>
      <category>actions</category>
    </item>
    <item>
      <title>A Rust CLI for decoding certs</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Tue, 02 Jun 2020 17:40:03 +0000</pubDate>
      <link>https://forem.com/wayofthepie/a-rust-cli-for-decoding-certs-280i</link>
      <guid>https://forem.com/wayofthepie/a-rust-cli-for-decoding-certs-280i</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this series of posts, we'll start to build a simple cli in &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt; for decoding X.509 certificates. I'll try to keep it as beginner-friendly as possible, by explaining things as best I can when they may be unclear. Some basic Rust knowledge should hopefully be all you need. Feel free to ask any questions in the comments below!  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are completely new to Rust and don't even have it installed, you can install it using &lt;a href="https://rustup.rs/"&gt;rustup&lt;/a&gt;. You should hopefully still be able to follow along. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This first post will mainly focus on a Test Driven Development workflow. I wrote this as I was developing so mistakes or things which I overlooked and ended up factoring out afterward are all kept in.&lt;/p&gt;

&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;What are we building?&lt;/li&gt;
&lt;li&gt;Some constraints to simplify the cli&lt;/li&gt;
&lt;li&gt;Test list&lt;/li&gt;
&lt;li&gt;
Validate args

&lt;ul&gt;
&lt;li&gt;A note on this workflow&lt;/li&gt;
&lt;li&gt;Add an error message&lt;/li&gt;
&lt;li&gt;Check argument is a path that exists&lt;/li&gt;
&lt;li&gt;Factor out IO&lt;/li&gt;
&lt;li&gt;Check argument is a file&lt;/li&gt;
&lt;li&gt;Another small refactor&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;In the next post&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What are we building?
&lt;/h1&gt;

&lt;p&gt;I generally use the &lt;code&gt;openssl&lt;/code&gt; cli for pulling information out of certificates. Specifically the &lt;code&gt;x509&lt;/code&gt; command, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# get certificate&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; google.com:443 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'/BEGIN CERTIFICATE/,/END CERTIFICATE/p'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; google.com.crt 

&lt;span class="c"&gt;# extract info&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;openssl x509 &lt;span class="nt"&gt;-text&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; google.com.crt | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 5
Certificate:
    Data:
        Version: 3 &lt;span class="o"&gt;(&lt;/span&gt;0x2&lt;span class="o"&gt;)&lt;/span&gt;
        Serial Number:
            1a:86:8b:0d:af:9b:c7:34:08:00:00:00:00:3e:bd:97
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All this does is print out a certificate as a human-readable string. I've written about what information certificates contain &lt;a href="https://dev.to/wayofthepie/structure-of-an-ssl-x-509-certificate-16b"&gt;here&lt;/a&gt; if interested. What we want to do in the next few posts is to see if we can build something similar to the &lt;code&gt;openssl x509 -text&lt;/code&gt; output from above in a Rust cli. &lt;/p&gt;

&lt;h1&gt;
  
  
  Some constraints to simplify the cli
&lt;/h1&gt;

&lt;p&gt;Let's think about the API for this cli, how a user will call it. To keep things simple, it should just take a file path to a single certificate. So we should end up with a call like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cert-decoder /path/to/some/cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another constraint to keep this simple at the beginning is the encoding of the certificate. Let's say the certificate must be PEM &lt;sup id="fnref1"&gt;1&lt;/sup&gt; (Privacy Enhanced Mail) encoded. This is the encoding you generally see certificates with. It's a string which begins with a line comprised of &lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt;, then some base64 encoded DER (Distinguished Encoding Rules) and ends with a line comprised of &lt;code&gt;-----END CERTIFICATE-----&lt;/code&gt;. It looks as follows, with some of the base64 string stripped out and replaced with &lt;code&gt;...&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----BEGIN CERTIFICATE-----
MIIJTzCCCDegAwIBAgIQGoaLDa+bxzQIAAAAAD69lzANBgkqhkiG9w0BAQsFADBC
...
DMCTA95gzVKezFCaUidRU9UyHOFzltfYDt7HRlp7MwWoPLM=
-----END CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Test list
&lt;/h1&gt;

&lt;p&gt;We have a basic API and some constraints now. Let's make a test list before we do anything else. This is simply a list of tests we should write to cover some piece of functionality. Let's just cover the most simple thing initially, validating the arguments. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Validate args&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;☐ Only allow a single argument
&lt;/li&gt;
&lt;li&gt;☐ Argument is a path that exists
&lt;/li&gt;
&lt;li&gt;☐ Argument should be a single file &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Implementing this we can start to use it and see where we should go next.&lt;/p&gt;

&lt;h1&gt;
  
  
  Validate args
&lt;/h1&gt;

&lt;p&gt;First, create a new project with &lt;code&gt;cargo new cert-decoder&lt;/code&gt;. &lt;code&gt;main.rs&lt;/code&gt;  should look as follows initially.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's create a test for the first item on our test list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// create fake args list, with no args&lt;/span&gt;
        &lt;span class="c1"&gt;// run function with args&lt;/span&gt;
        &lt;span class="c1"&gt;// check that it returns an error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above I've outlined what we need to do in this test in comments. It's not easy to test this through the &lt;code&gt;main&lt;/code&gt; function as &lt;code&gt;main&lt;/code&gt; does not take any arguments in its signature. In Rust to read program arguments you use the function &lt;a href="https://doc.rust-lang.org/stable/std/env/fn.args.html"&gt;std::env::args&lt;/a&gt;, more on that later.&lt;/p&gt;

&lt;p&gt;Let's create a new function called &lt;code&gt;execute&lt;/code&gt;. We know we want this function to return an error so we will make it return a &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html"&gt;Result&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// create fake args list, with no args&lt;/span&gt;
        &lt;span class="c1"&gt;// run function with args&lt;/span&gt;
        &lt;span class="c1"&gt;// check that it returns an error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now execute just returns an &lt;code&gt;Ok&lt;/code&gt; result with a value of &lt;strong&gt;&lt;a href="https://doc.rust-lang.org/std/primitive.unit.html"&gt;()&lt;/a&gt;&lt;/strong&gt;. This is called the &lt;em&gt;unit&lt;/em&gt; type. Now let's write our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run this with &lt;code&gt;cargo -q test&lt;/code&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt; it will fail. There will also be a warning about the &lt;code&gt;args&lt;/code&gt; parameter in &lt;code&gt;execute&lt;/code&gt; not being used, but we can ignore that for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
...
running 1 &lt;span class="nb"&gt;test
&lt;/span&gt;F
failures:

&lt;span class="nt"&gt;----&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_not_given_a_single_argument stdout &lt;span class="nt"&gt;----&lt;/span&gt;
thread &lt;span class="s1"&gt;'test::should_error_if_not_given_a_single_argument'&lt;/span&gt; panicked at &lt;span class="s1"&gt;'assertion failed: result.is_err()'&lt;/span&gt;, src/ma
&lt;span class="k"&gt;in&lt;/span&gt;.rs:18:9
note: run with &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="sb"&gt;`&lt;/span&gt; environment variable to display a backtrace


failures:
    &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_not_given_a_single_argument

&lt;span class="nb"&gt;test &lt;/span&gt;result: FAILED. 0 passed&lt;span class="p"&gt;;&lt;/span&gt; 1 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out

error: &lt;span class="nb"&gt;test &lt;/span&gt;failed, to rerun pass &lt;span class="s1"&gt;'--bin cert-decode'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make it pass by making a tiny change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's re-run and it should be green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;
...
running 1 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now we just need to make it return an error only in the case we mention in our first test list item. When we do not receive just a single argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-run and it should still be green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 1 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! We should change this to return an error message. But first a quick note about this workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on this workflow
&lt;/h2&gt;

&lt;p&gt;This is how I generally write code. What's the smallest useful change I can make to add functionality? In this case, to get started it was argument validation. With that in mind, I decomposed this further into items that look like individually testable parts of the behavior we want in argument validation. I made a list of them so I can tick them off as I go. This helps to keep the focus on a single small piece of functionality. It's especially useful when decomposing complex changes.&lt;/p&gt;

&lt;p&gt;To implement, write a test that specifies the behavior of one of the items. Then, make it fail, make it pass with &lt;em&gt;any&lt;/em&gt; code, change to implement the real behavior, and finally refactor where appropriate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add an error message
&lt;/h2&gt;

&lt;p&gt;Let's improve the argument length check by returning a useful error message. One thing I always try to keep in mind is to make the error message actionable. For example, here we could just say "Error: did not receive a single argument.". However, it would be better to also add an action - "Error: did not receive a single argument. Please invoke cert-decoder as follows: './cert-decoder /path/to/cert'.". Not only are we telling a user what went wrong, but we are also telling them how to fix it.&lt;/p&gt;

&lt;p&gt;Let's update our test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// arrange&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// act&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// assert&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html#method.is_err"&gt;result.err()&lt;/a&gt; will return an &lt;a href="https://doc.rust-lang.org/std/option/enum.Option.html"&gt;Option&lt;/a&gt;. The &lt;code&gt;Option&lt;/code&gt; will be &lt;code&gt;Some&lt;/code&gt; if the value of the &lt;code&gt;Result&lt;/code&gt; is &lt;code&gt;Err&lt;/code&gt;. This &lt;code&gt;Some&lt;/code&gt; will contain the same value &lt;code&gt;Err&lt;/code&gt; contained. The &lt;code&gt;Option&lt;/code&gt; will be &lt;code&gt;None&lt;/code&gt; if the &lt;code&gt;Result&lt;/code&gt; is &lt;code&gt;Ok&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In this case we know for certain the &lt;code&gt;Result&lt;/code&gt;'s value is &lt;code&gt;Err&lt;/code&gt; as we asserted it before with &lt;a href="https://doc.rust-lang.org/std/result/enum.Result.html#method.is_err"&gt;is_err&lt;/a&gt;. So the &lt;code&gt;Option&lt;/code&gt; returned from &lt;code&gt;result.err()&lt;/code&gt; will be &lt;code&gt;Some&lt;/code&gt; and we can safely call &lt;a href="https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap"&gt;unwrap&lt;/a&gt; on that &lt;code&gt;Some&lt;/code&gt; to get the value it contains. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This won't compile because we are trying to compare two different types now. &lt;code&gt;result.err().unwrap()&lt;/code&gt; will return &lt;code&gt;()&lt;/code&gt; and we are trying to compare this to a string. So we need to update the return type of &lt;code&gt;execute&lt;/code&gt;. We could also add the correct return value, the string we are asserting &lt;code&gt;execute&lt;/code&gt; returns. But I like to do things in tiny changes so let's just update the type first and return an empty string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if we run &lt;code&gt;cargo -q test&lt;/code&gt; it will compile but the test will fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ cargo -q test

running 1 test
F
failures:

---- test::should_error_if_not_given_a_single_argument stdout ----
thread 'test::should_error_if_not_given_a_single_argument' panicked at 'assertion failed: `(left == right)`
  left: `""`,
 right: `"Error: did not receive a single argument, please invoke cert-decoder as follows: ./cert-decoder /path/to/cert"`', src/main.rs:27:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So let's make some changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And re-run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 1 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 1 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Our test is green and we have an argument length check with a nice error message. It won't do anything if we try to run it though. To fix that, let's update &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we add a real call to get the args in &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;std::env::args().skip(1).collect()&lt;/code&gt;. The name of the binary will be part of the args, that is why we skip one argument.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;📝 Note&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Let's breakdown the &lt;code&gt;std::env::args().skip(1).collect()&lt;/code&gt;. If you understood it you can skip this note. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;std::env::args()&lt;/code&gt; will return a value of type &lt;a href="https://doc.rust-lang.org/stable/std/env/struct.Args.html"&gt;Args&lt;/a&gt;. &lt;code&gt;Args&lt;/code&gt; implements the &lt;a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html"&gt;Iterator&lt;/a&gt; trait, which means it has &lt;a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.skip"&gt;skip&lt;/a&gt; and &lt;a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect"&gt;collect&lt;/a&gt; methods. We skip one item, which is the binary name and collect the rest. Because we are using this as an argument to &lt;code&gt;execute&lt;/code&gt;, &lt;code&gt;Rust&lt;/code&gt; can infer its type which is &lt;code&gt;Vec&amp;lt;String&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can run with no argument using &lt;code&gt;cargo run&lt;/code&gt; and it will give an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; run
Error: &lt;span class="s2"&gt;"Error: did not receive a single argument, please invoke cert-decoder as follows: ./cert-decoder /path/to/c
ert."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we pass an argument it will print nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; run &lt;span class="nt"&gt;--&lt;/span&gt; something

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

&lt;/div&gt;



&lt;p&gt;Anything after the &lt;code&gt;--&lt;/code&gt; will be passed to our program as arguments. We can check off the first item on our test list now.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Validate args&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Only allow a single argument
&lt;/li&gt;
&lt;li&gt;☐ Argument is a path that exists
&lt;/li&gt;
&lt;li&gt;☐ Argument should be a single file &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Check argument is a path that exists
&lt;/h2&gt;

&lt;p&gt;First, a test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_argument_is_not_a_path_which_exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// arrange&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"does-not-exist"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;

    &lt;span class="c1"&gt;// act&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// assert&lt;/span&gt;
    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"Error: path given as argument does not exist, it must be a path to a certificate!"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As always, run the test to see it fail first. It will fail because the result will not be an error. Let's make it return an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given as argument does not exist, it must be a path to a certificate!"&lt;/span&gt;
                &lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the test will pass. &lt;a href="https://doc.rust-lang.org/std/path/struct.Path.html"&gt;Path&lt;/a&gt; contains many operations for interacting with filesystems. The call to &lt;a href="https://doc.rust-lang.org/std/path/struct.Path.html#method.exists"&gt;exists&lt;/a&gt; above will check the real filesystem to see if the path we give as an argument exists. This is generally not something you want to do in your tests. &lt;/p&gt;

&lt;p&gt;Before we factor out this IO, let's tick this item off on our test list as it is working.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Validate args&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Only allow a single argument
&lt;/li&gt;
&lt;li&gt;✔️ Argument is a path that exists
&lt;/li&gt;
&lt;li&gt;☐ Argument should be a single file &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/b5c242bb92eb125117554d2692f5bc8c93082ca3"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factor out IO
&lt;/h2&gt;

&lt;p&gt;Almost any code you write is going to do some kind of IO, e.g. network calls, file reads. Normally we don't want this to happen in a test. Beyond just keeping IO out of tests, making it clearer where IO does happen is very useful for readability and refactoring. Especially as a system grows.&lt;/p&gt;

&lt;p&gt;Right now what we want to do is make our code testable under different scenarios without touching the real filesystem. There are a few ways to do this, but I generally use traits and have a real and fake implementation of the trait. Let's define a trait that has an &lt;code&gt;exists&lt;/code&gt; method, just like &lt;code&gt;Path&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It took me a while to come up with a name I was somewhat happy with for this trait. Naming is hard. Now that we have a trait, let's implement it for the real case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our tests need a version of this also.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.is_path&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;   
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we need to refactor the &lt;code&gt;execute&lt;/code&gt; function to take something that implements &lt;code&gt;PathValidator&lt;/code&gt; as a parameter and update our tests to reflect this. The change as a whole is as follows, with some notes in comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Here we change the signature of execute to also take a value of type `impl PathValidator`, &lt;/span&gt;
&lt;span class="c1"&gt;// see the note after this code block for more information. &lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Instead of calling Path's exists method, we call exists on our &lt;/span&gt;
    &lt;span class="c1"&gt;// PathValidator implementation.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given as argument does not exist, it must be a path to a certificate!"&lt;/span&gt;
                &lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Here we create our real PathValidator implementation,&lt;/span&gt;
    &lt;span class="c1"&gt;// CertValidator, which touches the filesystem.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.is_path&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_not_given_a_single_argument&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// arrange&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// We construct a FakeValidator that says all paths exist.&lt;/span&gt;
        &lt;span class="c1"&gt;// It will never be called in this test, however.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// act&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// assert&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_argument_is_not_a_path_which_exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// arrange&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"does-not-exist"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;

        &lt;span class="c1"&gt;// We construct a validator that says no path exists.&lt;/span&gt;
        &lt;span class="c1"&gt;// This will cause our test to fail and return the error we want,&lt;/span&gt;
        &lt;span class="c1"&gt;// mimicing a path which does not exist without touching the real&lt;/span&gt;
        &lt;span class="c1"&gt;// filesystem.&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// act&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// assert&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given as argument does not exist, it must be a path to a certificate!"&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 Note&lt;/p&gt;

&lt;p&gt;The type of &lt;code&gt;validator&lt;/code&gt; in the &lt;code&gt;execute&lt;/code&gt; function is &lt;code&gt;impl PathValidator&lt;/code&gt;. This simply says &lt;code&gt;validator&lt;/code&gt; can be a value of any type which implements &lt;code&gt;PathValidator&lt;/code&gt;. You can read more about this in the &lt;a href="https://doc.rust-lang.org/book/title-page.html"&gt;Rust book&lt;/a&gt; in the section on &lt;a href="https://doc.rust-lang.org/book/ch10-02-traits.html?highlight=impl,trait#traits-as-parameters"&gt;Traits as Parameters&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the tests again and they should still be green! If you run with &lt;code&gt;cargo run&lt;/code&gt; as we did earlier, it should still work as expected. The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/19fee59da8d1e4625e2693c1e388be1912e2d9ee"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check argument is a file
&lt;/h2&gt;

&lt;p&gt;What's left on our test list?&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Validate args&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Only allow a single argument
&lt;/li&gt;
&lt;li&gt;✔️ Argument is a path that exists
&lt;/li&gt;
&lt;li&gt;☐ Argument should be a single file &lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Right now the argument we pass can be several file types other than a regular file, for example, a directory. As we only validate if the given path exists, not what that path points to. So let's validate it is also a file. First, a test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.is_path&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.is_file&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt; 

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;should_error_if_argument_is_not_a_regular_file&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// arrange&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"not-a-regular-file"&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;()];&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FakeValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;is_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// act&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// assert&lt;/span&gt;
        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.is_err&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.err&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given is not a regular file, please update to point to a certificate."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This won't compile as there is no &lt;code&gt;is_file&lt;/code&gt; method on our &lt;code&gt;PathValidator&lt;/code&gt; trait. Here I've also updated the &lt;code&gt;FakeValidator&lt;/code&gt; in the other test, setting &lt;code&gt;is_file&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;. Let's update that and implement the real version next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;CertValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.is_file&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a new &lt;code&gt;is_file&lt;/code&gt; method. If we re-run the test it should fail as we are not returning an error. You can run a single test with &lt;code&gt;cargo test&lt;/code&gt; by passing its name, or a string that is contained in its name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test &lt;/span&gt;should_error_if_argument_is_not_a_regular_file

running 1 &lt;span class="nb"&gt;test
&lt;/span&gt;F
failures:

&lt;span class="nt"&gt;----&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_argument_is_not_a_regular_file stdout &lt;span class="nt"&gt;----&lt;/span&gt;
thread &lt;span class="s1"&gt;'test::should_error_if_argument_is_not_a_regular_file'&lt;/span&gt; panicked at &lt;span class="s1"&gt;'assertion failed: result.is_err()'&lt;/span&gt;, src
/main.rs:122:9
note: run with &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nv"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="sb"&gt;`&lt;/span&gt; environment variable to display a backtrace


failures:
    &lt;span class="nb"&gt;test&lt;/span&gt;::should_error_if_argument_is_not_a_regular_file

&lt;span class="nb"&gt;test &lt;/span&gt;result: FAILED. 0 passed&lt;span class="p"&gt;;&lt;/span&gt; 1 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 2 filtered out

error: &lt;span class="nb"&gt;test &lt;/span&gt;failed, to rerun pass &lt;span class="s1"&gt;'--bin cert-decode'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make it return an error if the path given is not a regular file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PathValidator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"{}{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"Error: did not receive a single argument, "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"please invoke cert-decoder as follows: ./cert-decoder /path/to/cert."&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="nf"&gt;.exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given as argument does not exist, it must be a path to a certificate!"&lt;/span&gt;
                &lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;validator&lt;/span&gt;&lt;span class="nf"&gt;.is_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Error: path given is not a regular file, please update to point to a certificate."&lt;/span&gt;
                &lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-run and all tests should be green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ cargo &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nb"&gt;test

&lt;/span&gt;running 3 tests
...
&lt;span class="nb"&gt;test &lt;/span&gt;result: ok. 3 passed&lt;span class="p"&gt;;&lt;/span&gt; 0 failed&lt;span class="p"&gt;;&lt;/span&gt; 0 ignored&lt;span class="p"&gt;;&lt;/span&gt; 0 measured&lt;span class="p"&gt;;&lt;/span&gt; 0 filtered out

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

&lt;/div&gt;



&lt;p&gt;Great! The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/a992dd190de61a922cec95233e0fdbd43c500a8f"&gt;here&lt;/a&gt;. We can now tick this off on the test list.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Validate args&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✔️ Only allow a single argument
&lt;/li&gt;
&lt;li&gt;✔️ Argument is a path that exists
&lt;/li&gt;
&lt;li&gt;✔️ Argument should be a single file &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Another small refactor
&lt;/h2&gt;

&lt;p&gt;We can refactor this a bit. If we have a regular file then it must be a path that exists. This means we can get rid of the exists check and its test entirely. Here is a diff of this change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/src/main.rs b/src/main.rs
index 171545b..2ec2d92 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/src/main.rs
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/src/main.rs
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,17 +1,12 @@&lt;/span&gt;
 use std::path::Path;

 trait PathValidator {
&lt;span class="gd"&gt;-    fn exists(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool;
&lt;/span&gt;     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool;
 }

 struct CertValidator;

 impl PathValidator for CertValidator {
&lt;span class="gd"&gt;-    fn exists(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool {
-        Path::new(path).exists()
-    }
-
&lt;/span&gt;     fn is_file(&amp;amp;self, path: &amp;amp;str) -&amp;gt; bool {
         Path::new(path).is_file()
     }
&lt;span class="p"&gt;@@ -27,12 +22,6 @@&lt;/span&gt; fn execute(validator: impl PathValidator, args: Vec&amp;lt;String&amp;gt;) -&amp;gt; Result&amp;lt;(), Strin
         return Err(error);
     }
     let path = &amp;amp;args[0];
&lt;span class="gd"&gt;-    if !validator.exists(path) {
-        return Err(
-            "Error: path given as argument does not exist, it must be a path to a certificate!"
-                .to_owned(),
-        );
-    }
&lt;/span&gt;     if !validator.is_file(path) {
         return Err(
             "Error: path given is not a regular file, please update to point to a certificate."
&lt;span class="p"&gt;@@ -54,15 +43,10 @@&lt;/span&gt; mod test {
     use crate::{execute, PathValidator};

     struct FakeValidator {
&lt;span class="gd"&gt;-        is_path: bool,
&lt;/span&gt;         is_file: bool,
     }

     impl PathValidator for FakeValidator {
&lt;span class="gd"&gt;-        fn exists(&amp;amp;self, _: &amp;amp;str) -&amp;gt; bool {
-            self.is_path
-        }
-
&lt;/span&gt;         fn is_file(&amp;amp;self, _: &amp;amp;str) -&amp;gt; bool {
             self.is_file
         }
&lt;span class="p"&gt;@@ -72,10 +56,7 @@&lt;/span&gt; mod test {
     fn should_error_if_not_given_a_single_argument() {
         // arrange
         let args = Vec::new();
&lt;span class="gd"&gt;-        let validator = FakeValidator {
-            is_path: true,
-            is_file: false,
-        };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeValidator { is_file: false };
&lt;/span&gt;
         // act
         let result = execute(validator, args);
&lt;span class="p"&gt;@@ -92,34 +73,11 @@&lt;/span&gt; mod test {
         );
     }

-    #[test]
&lt;span class="gd"&gt;-    fn should_error_if_argument_is_not_a_path_which_exists() {
-        // arrange
-        let args = vec!["does-not-exist".to_owned()];
-        let validator = FakeValidator {
-            is_path: false,
-            is_file: false,
-        };
-
-        // act
-        let result = execute(validator, args);
-
-        // assert
-        assert!(result.is_err());
-        assert_eq!(
-            result.err().unwrap(),
-            "Error: path given as argument does not exist, it must be a path to a certificate!"
-        );
-    }
-
&lt;/span&gt;     #[test]
     fn should_error_if_argument_is_not_a_regular_file() {
         // arrange
         let args = vec!["not-a-regular-file".to_owned()];
&lt;span class="gd"&gt;-        let validator = FakeValidator {
-            is_path: true,
-            is_file: false,
-        };
&lt;/span&gt;&lt;span class="gi"&gt;+        let validator = FakeValidator { is_file: false };
&lt;/span&gt;
         // act
         let result = execute(validator, args);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/cert-decoder/tree/5c79d66dc65f42ad3750d776f1fafa8994355fb8"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  In the next post
&lt;/h1&gt;

&lt;p&gt;In the next few posts, we'll look at reading information out of the certificate using the &lt;a href="https://docs.rs/x509-parser/0.7.0/x509_parser/"&gt;x509_parser&lt;/a&gt; crate. We'll also switch to using &lt;a href="https://docs.rs/structopt/0.3.14/structopt/"&gt;structopt&lt;/a&gt; for argument parsing. I didn't use &lt;code&gt;structopt&lt;/code&gt; here as I wanted to keep things simple and mainly focus on the workflow, show how we can evolve functionality in small testable steps. I'm hoping the value of this workflow will be more apparent as we add more functionality to this cli, and things get more complex.&lt;/p&gt;

&lt;p&gt;There are also a couple of things implemented here which you would not see in general Rust code. For example, returning a &lt;code&gt;String&lt;/code&gt; as the &lt;code&gt;Err&lt;/code&gt; value of a &lt;code&gt;Result&lt;/code&gt;. This is ok, we will refactor them as we go. It's better to start with something small, get it working, then refactor than to try to make it perfect right away.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;code&gt;PEM&lt;/code&gt; encoding - see &lt;a href="https://tools.ietf.org/html/rfc7468#section-2"&gt;Section 2&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc7468"&gt;RFC 7468&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The &lt;code&gt;-q&lt;/code&gt; in &lt;code&gt;cargo -q test&lt;/code&gt; is short for &lt;code&gt;--quiet&lt;/code&gt;.   ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>rust</category>
      <category>certificate</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Structure of an SSL (X.509) certificate</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Fri, 22 May 2020 10:45:35 +0000</pubDate>
      <link>https://forem.com/wayofthepie/structure-of-an-ssl-x-509-certificate-16b</link>
      <guid>https://forem.com/wayofthepie/structure-of-an-ssl-x-509-certificate-16b</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;I've been working on some tooling for pulling certificate information out of the &lt;a href="https://www.certificate-transparency.org/"&gt;certificate transparency logs&lt;/a&gt; on and off for a while. I started looking at this again after a few weeks away from it and I've forgotten quite a lot! I've even forgotten some of the basics of what makes a certificate. In this post I want to dive into the structure of a certificate, what it is made of at a high level. I won't talk much about how certificates are used in protocols (e.g. Transport Layer Security (TLS)). &lt;/p&gt;

&lt;p&gt;This post started as a reference for myself but other folks may find it interesting or useful. It has a lot of external references to RFC's which are stored in footnotes. As of now there is a small bug with how footnotes work where the footnote itself is hidden behind the top bar, see &lt;a href="https://github.com/thepracticaldev/dev.to/issues/7760"&gt;dev.to #7760&lt;/a&gt; for more info. I might try fix it myself this weekend. &lt;/p&gt;

&lt;p&gt;Quick note, SSL certificates are X.509 certificates. The term SSL certificate is deeply ingrained on the web, and even though the SSL protocol should no longer be used this term is still used everywhere. &lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
Information in a certificate

&lt;ul&gt;
&lt;li&gt;Side note on the openssl command&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A breakdown of the main fields&lt;/li&gt;
&lt;li&gt;
Certificate

&lt;ul&gt;
&lt;li&gt;Signature and Signature Algorithm&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
TBSCertificate

&lt;ul&gt;
&lt;li&gt;Version&lt;/li&gt;
&lt;li&gt;Serial Number&lt;/li&gt;
&lt;li&gt;
Signature Algorithm

&lt;ul&gt;
&lt;li&gt;This field is duplicated, why?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Issuer&lt;/li&gt;
&lt;li&gt;
Validity

&lt;ul&gt;
&lt;li&gt;Certificate validity and the Brazilian government&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Subject and Subject Public Key Info&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Information in a certificate
&lt;/h1&gt;

&lt;p&gt;We'll use the &lt;code&gt;openssl&lt;/code&gt; cli to retrieve a certificate, then we can start looking into its structure. If the &lt;code&gt;openssl&lt;/code&gt; cli is not installed you should be able to install it through your operating system's package manager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; google.com:443 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'/BEGIN CERTIFICATE/,/END CERTIFICATE/p'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; google.com.crt

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;google.com.crt
&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIJRDCCCCygAwIBAgIRAJvxi9ebRliZAgAAAABjmHEwDQYJKoZIhvcNAQELBQAw
QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET
MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0yMDA0MTUyMDE2NDdaFw0yMDA3MDgyMDE2
...
V+hT9mqgeN10ryOWyN74CvBaw73K3hobSkDAyQS1HkbAqJP9VTuvjZl4PE0ndaIN
yiz/84k5xbSwxO++BuJgMUwj+WaLcvDW
&lt;span class="nt"&gt;-----END&lt;/span&gt; CERTIFICATE-----

&lt;span class="nv"&gt;$ &lt;/span&gt;openssl x509 &lt;span class="nt"&gt;-text&lt;/span&gt; &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-in&lt;/span&gt; google.com.crt
Certificate:
    Data:
        Version: 3 &lt;span class="o"&gt;(&lt;/span&gt;0x2&lt;span class="o"&gt;)&lt;/span&gt;
        Serial Number:
            9b:f1:8b:d7:9b:46:58:99:02:00:00:00:00:63:98:71
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C &lt;span class="o"&gt;=&lt;/span&gt; US, O &lt;span class="o"&gt;=&lt;/span&gt; Google Trust Services, CN &lt;span class="o"&gt;=&lt;/span&gt; GTS CA 1O1
        Validity
            Not Before: Apr 15 20:16:47 2020 GMT
            Not After : Jul  8 20:16:47 2020 GMT
        Subject: C &lt;span class="o"&gt;=&lt;/span&gt; US, ST &lt;span class="o"&gt;=&lt;/span&gt; California, L &lt;span class="o"&gt;=&lt;/span&gt; Mountain View, O &lt;span class="o"&gt;=&lt;/span&gt; Google LLC, CN &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;.google.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: &lt;span class="o"&gt;(&lt;/span&gt;256 bit&lt;span class="o"&gt;)&lt;/span&gt;
                pub:
                    04:8e:a4:03:0d:0c:a7:1d:52:28:80:ba:89:51:b9:
                    45:7a:7a:60:33:a5:ab:25:a4:05:c8:32:d9:b6:5c:
                    ...
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                D0:7D:02:36:9B:CD:47:0B:C5:9C:51:0F:27:A7:70:65:5A:C5:50:E9
            X509v3 Authority Key Identifier:
                keyid:98:D1:F8:6E:10:EB:CF:9B:EC:60:9F:18:90:1B:A0:EB:7D:09:FD:2B

            Authority Information Access:
                OCSP - URI:http://ocsp.pki.goog/gts1o1
                CA Issuers - URI:http://pki.goog/gsr2/GTS1O1.crt

            X509v3 Subject Alternative Name:
                DNS:&lt;span class="k"&gt;*&lt;/span&gt;.google.com, DNS:&lt;span class="k"&gt;*&lt;/span&gt;.android.com, DNS:&lt;span class="k"&gt;*&lt;/span&gt;.appengine.google.com, ... 
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.2
                Policy: 1.3.6.1.4.1.11129.2.5.3

            X509v3 CRL Distribution Points:

                Full Name:
                  URI:http://crl.pki.goog/GTS1O1.crl

            CT Precertificate SCTs:
                Signed Certificate Timestamp:
                    Version   : v1 &lt;span class="o"&gt;(&lt;/span&gt;0x0&lt;span class="o"&gt;)&lt;/span&gt;
                    Log ID    : B2:1E:05:CC:8B:A2:CD:8A:20:4E:87:66:F9:2B:B9:8A:
                                25:20:67:6B:DA:FA:70:E7:B2:49:53:2D:EF:8B:90:5E
                    Timestamp : Apr 15 21:16:49.089 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:20:26:77:E7:A4:C6:F9:D3:C0:0E:95:15:3C:
                                A2:08:F0:DB:77:9F:1F:7A:EC:7A:26:9B:E8:82:95:33:
                                ...
                Signed Certificate Timestamp:
                    Version   : v1 &lt;span class="o"&gt;(&lt;/span&gt;0x0&lt;span class="o"&gt;)&lt;/span&gt;
                    Log ID    : 5E:A7:73:F9:DF:56:C0:E7:B5:36:48:7D:D0:49:E0:32:
                                7A:91:9A:0C:84:A1:12:12:84:18:75:96:81:71:45:58
                    Timestamp : Apr 15 21:16:49.137 2020 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:FC:5A:10:9B:63:81:BB:16:81:8B:D5:
                                88:AF:09:A1:D8:83:FD:C3:86:CB:B1:CD:55:71:FF:76:
                                ...
    Signature Algorithm: sha256WithRSAEncryption
         20:69:ba:0b:e5:b4:7a:36:f7:4f:d2:b2:0f:0d:c1:10:b0:12:
         7e:13:f9:f1:ca:6c:a0:c2:46:21:fb:8a:fd:a8:66:a9:96:43:
         ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's quite a lot of information in the certificate. Before we break this down, a quick side note on the &lt;code&gt;openssl&lt;/code&gt; command we used in the above code block. Feel free to skip this next section if you understood it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side note on the openssl command
&lt;/h2&gt;

&lt;p&gt;The command we used was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;openssl s_client &lt;span class="nt"&gt;-connect&lt;/span&gt; google.com:443 2&amp;gt;/dev/null &amp;lt; /dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'/BEGIN CERTIFICATE/,/END CERTIFICATE/p'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; google.com.crt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;openssl s_client&lt;/code&gt; is used for connecting to hosts over TLS (and originally SSL, but no server should be using this anymore...). With &lt;code&gt;-connect&lt;/code&gt; we tell it to connect to &lt;code&gt;google.com&lt;/code&gt; on port &lt;code&gt;443&lt;/code&gt;. &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; says to redirect anything that goes to &lt;code&gt;stderr&lt;/code&gt; in the output of the &lt;code&gt;openssl s_client&lt;/code&gt; command into &lt;code&gt;/dev/null&lt;/code&gt;. Essentially this says ignore &lt;code&gt;stderr&lt;/code&gt;. &lt;code&gt;&amp;lt; /dev/null&lt;/code&gt; says read &lt;code&gt;/dev/null&lt;/code&gt; into the &lt;code&gt;stdin&lt;/code&gt; of the process, in thi s case &lt;code&gt;stdin&lt;/code&gt; of &lt;code&gt;openssl&lt;/code&gt;. Doing this always returns an end of file.&lt;br&gt;
+&lt;code&gt;openssl s_client&lt;/code&gt; is used for connecting to hosts over TLS (and originally SSL, but no site should be using this anymore...).&lt;/p&gt;

&lt;p&gt;If you don't do this &lt;code&gt;stdin&lt;/code&gt; redirect the &lt;code&gt;openssl s_client&lt;/code&gt; command will hang waiting for input on &lt;code&gt;stdin&lt;/code&gt;, doing this redirect means that once it tries to read from &lt;code&gt;stdin&lt;/code&gt; it will read an end of file and the read will finish. The last part of the command, &lt;code&gt;| sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' &amp;gt; google.com.crt&lt;/code&gt;, pipes the output from &lt;code&gt;openssl s_client&lt;/code&gt; into &lt;code&gt;sed&lt;/code&gt; which pulls the certificate out of the output and redirects it to a file called &lt;code&gt;google.com.crt&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So why does it try to wait for input? There are few commands you can send it. Normally I'd just send &lt;code&gt;Q&lt;/code&gt; to quit, however you can also tell it to send a &lt;code&gt;http&lt;/code&gt; request. Try it! Run &lt;code&gt;openssl s_client -connect google.com:443&lt;/code&gt; then when it hangs type in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET / HTTP/1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And press &lt;code&gt;enter&lt;/code&gt; twice. It should return the JS/html/css that makes up google.com. That was a bit of a digression, back to certs now.&lt;/p&gt;

&lt;h1&gt;
  
  
  A breakdown of the main fields
&lt;/h1&gt;

&lt;p&gt;Now we have google.com's cert, what do all the fields mean? Let's break it down one by one. As we do this I will also mention the type and structure of the given field in the ASN.1 (Abstract Syntax Notation&lt;sup id="fnref1"&gt;1&lt;/sup&gt;) specification for certificates and try to explain things which may not be obvious. Wherever I outline a part of the definition I will start it with a comment &lt;code&gt;# ASN.1&lt;/code&gt;. I will also strip out pieces of information which are not relevant to a particular section and replace them with &lt;code&gt;...&lt;/code&gt;. The full ASN.1 definition can be found in &lt;a href="https://tools.ietf.org/html/rfc5280#appendix-A.1"&gt;Appendix A.1&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280 - X.509 Public Key Infrastructure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Note that the ASN.1 spec for certificates describes a high level representation of certificate information. As bytes, this information is further encoded as DER (Distinguished Encoding Rules). I won't go into the details of ASN.1 -&amp;gt; DER encoding&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, but this post should lay some groundwork to make this encoding clearer in a future post.&lt;/p&gt;

&lt;p&gt;Above we used &lt;code&gt;openssl&lt;/code&gt; to pull out the information from an existing cert. The fields mentioned there can have many possible values. The main structure is defined as follows in ASN.1 - don't worry if you don't understand it, we will cover each field in depth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
Certificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     tbsCertificate       TBSCertificate,
     signatureAlgorithm   AlgorithmIdentifier,
     signature            BIT STRING  &lt;span class="o"&gt;}&lt;/span&gt;

TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     version         &lt;span class="o"&gt;[&lt;/span&gt;0]  Version DEFAULT v1,
     serialNumber         CertificateSerialNumber,
     signature            AlgorithmIdentifier,
     issuer               Name,
     validity             Validity,
     subject              Name,
     subjectPublicKeyInfo SubjectPublicKeyInfo,
     issuerUniqueID  &lt;span class="o"&gt;[&lt;/span&gt;1]  IMPLICIT UniqueIdentifier OPTIONAL,
                          &lt;span class="nt"&gt;--&lt;/span&gt; If present, version MUST be v2 or v3
     subjectUniqueID &lt;span class="o"&gt;[&lt;/span&gt;2]  IMPLICIT UniqueIdentifier OPTIONAL,
                          &lt;span class="nt"&gt;--&lt;/span&gt; If present, version MUST be v2 or v3
     extensions      &lt;span class="o"&gt;[&lt;/span&gt;3]  Extensions OPTIONAL
                          &lt;span class="nt"&gt;--&lt;/span&gt; If present, version MUST be v3 &lt;span class="nt"&gt;--&lt;/span&gt;  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Certificate
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;Certificate&lt;/code&gt; is an ASN.1 &lt;code&gt;SEQUENCE&lt;/code&gt;. A &lt;code&gt;SEQUENCE&lt;/code&gt; is an ordered list of values. In this case a list with &lt;code&gt;tbsCertificate&lt;/code&gt;, &lt;code&gt;signatureAlgorithm&lt;/code&gt; and &lt;code&gt;signature&lt;/code&gt;. First let's look at &lt;code&gt;signature&lt;/code&gt; and &lt;code&gt;signatureAlgorithm&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signature and Signature Algorithm
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;openssl&lt;/code&gt; output for google.com's cert both &lt;code&gt;Signature&lt;/code&gt;&lt;sup id="fnref3"&gt;3&lt;/sup&gt; and &lt;code&gt;Signature Algorithm&lt;/code&gt;&lt;sup id="fnref4"&gt;4&lt;/sup&gt; can be seen right at the bottom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signature Algorithm: sha256WithRSAEncryption
     20:69:ba:0b:e5:b4:7a:36:f7:4f:d2:b2:0f:0d:c1:10:b0:12:
     7e:13:f9:f1:ca:6c:a0:c2:46:21:fb:8a:fd:a8:66:a9:96:43:
     ... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've stripped out part of the &lt;code&gt;Signature&lt;/code&gt; and replaced it with &lt;code&gt;...&lt;/code&gt; as it's not relevant. The &lt;code&gt;Signature Algorithm&lt;/code&gt; field indicates the algorithm used by the issuing Certificate Authority (CA) to sign this certificate. Here its value is &lt;code&gt;sha256WithRSAEncryption&lt;/code&gt;. For more information on this algorithm see &lt;a href="https://tools.ietf.org/html/rfc4055#section-5"&gt;Section 5&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc4055"&gt;RFC 4055 - Additional Algorithms and Identifiers for RSA ...&lt;/a&gt; and &lt;a href="https://tools.ietf.org/html/rfc2313"&gt;RFC 2313 - PKCS #1: RSA Encryption&lt;/a&gt;. &lt;code&gt;Signature&lt;/code&gt; and &lt;code&gt;SignatureAlgorithm&lt;/code&gt; are defined as follows in the ASN.1 spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
Certificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     ...
     signatureAlgorithm   AlgorithmIdentifier,
     signature            BIT STRING  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;signature&lt;/code&gt; has type &lt;code&gt;BIT STRING&lt;/code&gt;, this is simply a string of bits. In this case those bits contain a digital signature computed from the &lt;code&gt;tbsCertificate&lt;/code&gt; field of this cert, using the algorithm defined in &lt;code&gt;signatureAlgorithm&lt;/code&gt;. &lt;code&gt;signatureAlgorithm&lt;/code&gt; is of type &lt;code&gt;AlgorithmIdentifier&lt;/code&gt;. &lt;code&gt;openssl&lt;/code&gt; encodes this as &lt;code&gt;:&lt;/code&gt; separated hexadecimal, e.g. &lt;code&gt;20:69:ba:0b:e5:b4...&lt;/code&gt;. &lt;code&gt;AlgorithmIdentifier&lt;/code&gt; itself is a &lt;code&gt;SEQUENCE&lt;/code&gt;, an ordered list with the given items. In this case a list with two values, &lt;code&gt;algorithm&lt;/code&gt; and &lt;code&gt;parameters&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
AlgorithmIdentifier  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     algorithm               OBJECT IDENTIFIER,
     parameters              ANY DEFINED BY algorithm OPTIONAL  &lt;span class="o"&gt;}&lt;/span&gt;
                                &lt;span class="nt"&gt;--&lt;/span&gt; contains a value of the &lt;span class="nb"&gt;type&lt;/span&gt;
                                &lt;span class="nt"&gt;--&lt;/span&gt; registered &lt;span class="k"&gt;for &lt;/span&gt;use with the
                                &lt;span class="nt"&gt;--&lt;/span&gt; algorithm object identifier value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;algorithm&lt;/code&gt; has type &lt;code&gt;OBJECT IDENTIFIER&lt;/code&gt;. An &lt;code&gt;OBJECT IDENTIFIER&lt;/code&gt;, or &lt;code&gt;OID&lt;/code&gt;, is a standard way of identifying objects defined by the International Telecommunications Union (ITU). It is defined in &lt;a href="https://tools.ietf.org/html/rfc3061"&gt;RFC 3061 - A URN Namespace of Object Identifiers&lt;/a&gt; and is definitely not something I'll go into more detail on in this post, it is a big topic! For the curious, the OID for &lt;code&gt;sha256WithRSAEncryption&lt;/code&gt; is &lt;code&gt;1.2.840.113549.1.1.11&lt;/code&gt;&lt;sup id="fnref5"&gt;5&lt;/sup&gt;. It is defined in &lt;a href="https://tools.ietf.org/html/rfc4055#section-5"&gt;RFC 4055 Section 5&lt;/a&gt;, to fully understand that definition you may have to go down a rabbit hole of RFC's...&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parameters&lt;/code&gt; defines parameters to the specified algorithm. We will see an example of this in the Subject and Subject Public Key Info section.&lt;/p&gt;

&lt;h1&gt;
  
  
  TBSCertificate
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;TBSCertificate&lt;/code&gt; contains the information on the subject of the certificate and the issuing CA. We will cover all fields except &lt;code&gt;extensions&lt;/code&gt;, &lt;code&gt;issuerUniqueID&lt;/code&gt; and &lt;code&gt;subjectUniqueID&lt;/code&gt;. There is quite a lot in &lt;code&gt;extensions&lt;/code&gt;, so I will leave it for another post. &lt;code&gt;issuerUniqueID&lt;/code&gt; and &lt;code&gt;subjectUniqueID&lt;/code&gt; are optional and do not appear in google.com's cert. It is also recommended that these not be set&lt;sup id="fnref6"&gt;6&lt;/sup&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Version
&lt;/h2&gt;

&lt;p&gt;In the cert we decoded, the &lt;code&gt;Version&lt;/code&gt;&lt;sup id="fnref7"&gt;7&lt;/sup&gt; is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version: 3 (0x2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Version&lt;/code&gt; is defined as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     version         &lt;span class="o"&gt;[&lt;/span&gt;0]  Version DEFAULT v1,
     ...
&lt;span class="o"&gt;}&lt;/span&gt;
...
Version  ::&lt;span class="o"&gt;=&lt;/span&gt;  INTEGER  &lt;span class="o"&gt;{&lt;/span&gt;  v1&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;, v2&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;, v3&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says the &lt;code&gt;version&lt;/code&gt; field of a certificate can be one of three values - 0 which means the version is v1, 1 which means the version is v2 and 2 which means the version is v3. It defaults to v1, which is 0. This explains why there is a &lt;code&gt;3&lt;/code&gt; and a &lt;code&gt;0x2&lt;/code&gt; in the output from &lt;code&gt;openssl&lt;/code&gt;, it's showing the version number (3) &lt;em&gt;and&lt;/em&gt; the real value of the field (2).&lt;/p&gt;

&lt;p&gt;So what does &lt;code&gt;version&lt;/code&gt; mean? There are three versions of X.509 certificates out in the wild, it just indicates which version of the X.509 spec a given cert is using. I will gloss over the differences in the versions in this post as its not too important for an overview. You will mainly see X.509 v3 certificates in the wild.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serial Number
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Serial Number&lt;/code&gt;&lt;sup id="fnref8"&gt;8&lt;/sup&gt; is a unique integer given to the certificate. It is unique for all certificates issued from the same CA, e.g. Digicert or Lets Encrypt, not globally unique. Meaning two certs from different CA's could potentially have the same &lt;code&gt;Serial Number&lt;/code&gt;. &lt;code&gt;Serial Number&lt;/code&gt; looks as follows in the cert we decoded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serial Number:
    9b:f1:8b:d7:9b:46:58:99:02:00:00:00:00:63:98:71
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its ASN.1 definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     ...
     serialNumber         CertificateSerialNumber,
     ...
&lt;span class="o"&gt;}&lt;/span&gt;

CertificateSerialNumber  ::&lt;span class="o"&gt;=&lt;/span&gt;  INTEGER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is of type &lt;code&gt;CertificateSerialNumber&lt;/code&gt;, which is just an alias for an integer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signature Algorithm
&lt;/h2&gt;

&lt;p&gt;This field &lt;sup id="fnref9"&gt;9&lt;/sup&gt; &lt;em&gt;must&lt;/em&gt; contain the same algorithm as the &lt;code&gt;signatureAlgorithm&lt;/code&gt; field outside the &lt;code&gt;tbsCertficate&lt;/code&gt; which we discussed in the Signature and Signature Algorithm section. And in this case it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Signature Algorithm: sha256WithRSAEncryption
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the &lt;code&gt;signature&lt;/code&gt; field in the &lt;code&gt;TBSCertificate&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1 &lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     ...
     signature            AlgorithmIdentifier,
     ...
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We looked at &lt;code&gt;AlgorithmIdentifier&lt;/code&gt; in Signature and Signature Algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  This field is duplicated, why?
&lt;/h3&gt;

&lt;p&gt;This was interesting as the reason for the duplication was not clear. We saw the &lt;code&gt;Signature Algorithm&lt;/code&gt; appear in the top level &lt;code&gt;Certificate&lt;/code&gt; and it also appears inside the &lt;code&gt;TBSCertificate&lt;/code&gt;. &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.3"&gt;Section 4.1.2.3&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc6211"&gt;RFC 5280&lt;/a&gt;, which talks about the &lt;code&gt;signature&lt;/code&gt; field within &lt;code&gt;TBSCertificate&lt;/code&gt;, states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This field MUST contain the same algorithm identifier as the signatureAlgorithm field in the sequence Certificate (Section 4.1.1.2)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But nowhere in that RFC does it give a reason. &lt;a href="https://tools.ietf.org/html/rfc6211#section-1"&gt;Section 1&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc6211"&gt;RFC 6211 - Cryptographic Message Syntax (CMS)&lt;/a&gt; gives some clue as to a possible reason - to prevent algorithm substitution attacks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Cryptographic Message Syntax [CMS], unlike X.509/PKIX certificates [RFC5280], is vulnerable to algorithm substitution attacks.  In an algorithm substitution attack, the attacker changes either the algorithm being used or the parameters of the algorithm in order to change the result of a signature verification process. In X.509 certificates, the signature algorithm is protected because it is duplicated in the TBSCertificate.signature field with the proviso that the validator is to compare both fields as part of the signature validation process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't know enough about this to comment further. But I am currently investigating. Once I understand more I will write about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issuer
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Issuer&lt;/code&gt;&lt;sup id="fnref10"&gt;10&lt;/sup&gt; field is a unique identifier for the CA issuing this certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Issuer: C = US, O = Google Trust Services, CN = GTS CA 1O1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cert we decoded was issued by Google Trust Services. Google have a number of CA's under Google Trust Services see &lt;a href="https://pki.goog/"&gt;https://pki.goog/&lt;/a&gt; for more details. The &lt;code&gt;Issuer&lt;/code&gt; field along with the &lt;code&gt;Serial Number&lt;/code&gt; will uniquely identify a certificate, as long as the &lt;code&gt;Issuer&lt;/code&gt; is a globally trusted CA.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Issuer&lt;/code&gt; is defined as a &lt;code&gt;Name&lt;/code&gt; in the spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     ...
     issuer               Name,
     ...
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Name&lt;/code&gt; itself is a bit weird:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1 &lt;/span&gt;
Name ::&lt;span class="o"&gt;=&lt;/span&gt; CHOICE &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; only one possibility &lt;span class="k"&gt;for &lt;/span&gt;now &lt;span class="nt"&gt;--&lt;/span&gt;
      rdnSequence  RDNSequence &lt;span class="o"&gt;}&lt;/span&gt;

RDNSequence ::&lt;span class="o"&gt;=&lt;/span&gt; SEQUENCE OF RelativeDistinguishedName 

...

RelativeDistinguishedName ::&lt;span class="o"&gt;=&lt;/span&gt; SET SIZE &lt;span class="o"&gt;(&lt;/span&gt;1..MAX&lt;span class="o"&gt;)&lt;/span&gt; OF AttributeTypeAndValue

&lt;span class="c"&gt;# AttributeTypeAndValue is defined as follows&lt;/span&gt;
AttributeTypeAndValue ::&lt;span class="o"&gt;=&lt;/span&gt; SEQUENCE &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="nb"&gt;type     &lt;/span&gt;AttributeType,
     value    AttributeValue &lt;span class="o"&gt;}&lt;/span&gt;

AttributeType ::&lt;span class="o"&gt;=&lt;/span&gt; OBJECT IDENTIFIER

AttributeValue ::&lt;span class="o"&gt;=&lt;/span&gt; ANY &lt;span class="nt"&gt;--&lt;/span&gt; DEFINED BY AttributeType
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CHOICE&lt;/code&gt; defines a list of options to pick from. So, &lt;code&gt;Name&lt;/code&gt; is a choice with a single option, an &lt;code&gt;RDNSequence&lt;/code&gt;, and it even says there is only one possibility for now. I don't really know why this is, it could be for forwards compatibility on changes to the spec.&lt;/p&gt;

&lt;p&gt;Have a look at the definition of &lt;code&gt;RDNSequence&lt;/code&gt;, it is defined as &lt;code&gt;SEQUENCE OF RelativeDistinguishedName&lt;/code&gt;. This is different than &lt;code&gt;SEQUENCE&lt;/code&gt; which we saw in the Signature Algorithm section. &lt;code&gt;SEQUENCE OF&lt;/code&gt; defines a list of values which are all the same type, in this case they are all &lt;code&gt;RelativeDistinguishedName&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We haven't seen &lt;code&gt;SET&lt;/code&gt; yet either. This defines a set of objects. &lt;code&gt;SET SIZE (1..MAX) OF AttributeTypeAndValue&lt;/code&gt; defines a set of &lt;code&gt;AttributeTypeAndValue&lt;/code&gt;'s of max size &lt;code&gt;MAX&lt;/code&gt;, I'm not sure what value &lt;code&gt;MAX&lt;/code&gt; is bound to here either, will edit and add this information once I realise what it is.&lt;/p&gt;

&lt;p&gt;In short, &lt;code&gt;Name&lt;/code&gt; is a list of OID/value pairs where the value is some object bound by that OID. &lt;a href="https://tools.ietf.org/html/rfc2253#section-2"&gt;Section 2.3&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc2253"&gt;RFC 2253 - LDAP v3&lt;/a&gt; describes &lt;code&gt;RelativeDistinguishedName&lt;/code&gt; and its encoding in more depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validity
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Validity&lt;/code&gt;&lt;sup id="fnref11"&gt;11&lt;/sup&gt; specifies the time window a certificate is valid between.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validity
    Not Before: Apr 15 20:16:47 2020 GMT
    Not After : Jul  8 20:16:47 2020 GMT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cert we decoded is valid from &lt;code&gt;Apr 15 20:16:47 2020 GMT&lt;/code&gt; to &lt;code&gt;Jul  8 20:16:47 2020 GMT&lt;/code&gt;. The certificate is invalid outside of this timeframe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
     ...
     validity             Validity,
     ...
&lt;span class="o"&gt;}&lt;/span&gt;
...
Validity ::&lt;span class="o"&gt;=&lt;/span&gt; SEQUENCE &lt;span class="o"&gt;{&lt;/span&gt;
     notBefore      Time,
     notAfter       Time  &lt;span class="o"&gt;}&lt;/span&gt;

Time ::&lt;span class="o"&gt;=&lt;/span&gt; CHOICE &lt;span class="o"&gt;{&lt;/span&gt;
     utcTime        UTCTime,
     generalTime    GeneralizedTime &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Validity&lt;/code&gt; has two fields which are both of type &lt;code&gt;Time&lt;/code&gt;. &lt;code&gt;Time&lt;/code&gt; can be one of two types, &lt;code&gt;UTCTime&lt;/code&gt;&lt;sup id="fnref12"&gt;12&lt;/sup&gt; or &lt;code&gt;GeneralizedTime&lt;/code&gt;&lt;sup id="fnref13"&gt;13&lt;/sup&gt;. Dates before the year 2050 &lt;em&gt;must&lt;/em&gt; be encoded as &lt;code&gt;UTCTime&lt;/code&gt; and dates on or after the year 2050 &lt;em&gt;must&lt;/em&gt; be encoded as &lt;code&gt;GeneralizedTime&lt;/code&gt;. This is outlined in the definition of &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.5"&gt;Validity&lt;/a&gt; in &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Certificate validity and the Brazilian government
&lt;/h3&gt;

&lt;p&gt;I stumbled across &lt;a href="https://github.com/pyca/cryptography/issues/3927"&gt;this issue&lt;/a&gt; on github while on my certificate information seeking adventures. As mentioned above, dates before the year 2050 should be encoded as &lt;code&gt;UTCTime&lt;/code&gt;, but the Brazilian government had their own specification which required the use of &lt;code&gt;GeneralizedTime&lt;/code&gt; for all dates. This is a good example of what you see in an RFC/specification not being exactly what you see in a real system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subject and Subject Public Key Info
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Subject&lt;/code&gt;&lt;sup id="fnref14"&gt;14&lt;/sup&gt; identifies the owner of the public key in the &lt;code&gt;Subject Public Key Info&lt;/code&gt;&lt;sup id="fnref15"&gt;15&lt;/sup&gt; section , which defines the "thing" this certificate identifies. In this case it's identifying google domains.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subject: C = US, ST = California, L = Mountain View, O = Google LLC, CN = *.google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its definition is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
    ... 
    subject              Name,
    ...
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Name&lt;/code&gt; was explained in the Issuer section, so it should be clear what this is. &lt;code&gt;Subject Public Key Info&lt;/code&gt; has a few fields. Let's look at its ASN.1 definition first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ASN.1&lt;/span&gt;
TBSCertificate  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
    ...
    subjectPublicKeyInfo SubjectPublicKeyInfo,
    ...
&lt;span class="o"&gt;}&lt;/span&gt;
...
SubjectPublicKeyInfo  ::&lt;span class="o"&gt;=&lt;/span&gt;  SEQUENCE  &lt;span class="o"&gt;{&lt;/span&gt;
        algorithm            AlgorithmIdentifier,
        subjectPublicKey     BIT STRING  &lt;span class="o"&gt;}&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see the actual value of &lt;code&gt;Subject Public Key Info&lt;/code&gt; in the certifcate again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subject Public Key Info:
  Public Key Algorithm: id-ecPublicKey
      Public-Key: (256 bit)
      pub:
          04:0f:45:4e:2f:0c:a7:88:9a:b9:24:ff:57:50:dc:
          f1:ab:6e:dd:3e:7f:82:26:30:a7:12:9f:81:8a:27:
          9d:7d:06:2e:d3:e2:50:3b:ce:6c:2d:2e:5b:32:ce:
          7d:eb:86:06:7c:8c:29:2b:47:61:de:f0:ca:f8:b7:
          98:00:21:6a:34
      ASN1 OID: prime256v1
      NIST CURVE: P-256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Public Key Algorithm&lt;/code&gt; defines the algorithm the key can be used with. This is the &lt;code&gt;algorithm&lt;/code&gt; field in the &lt;code&gt;SubjectPublicKeyInfo&lt;/code&gt; field defined above. The rest is the &lt;code&gt;BIT STRING&lt;/code&gt;. The &lt;code&gt;BIT STRING&lt;/code&gt; itself is constrained by the type of &lt;code&gt;algorithm&lt;/code&gt;. The information in the fields the &lt;code&gt;BIT STRING&lt;/code&gt; decodes to is beyond this post. If interested see &lt;a href="https://tools.ietf.org/html/rfc3279#section-2.3.5"&gt;Section 2.3.5&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc3279"&gt;RFC 3279 - Algorithms and Identifiers X.509 PKI ...&lt;/a&gt; for more on &lt;code&gt;id-ecPublicKey&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;If you made it to here, kudos! I hope this has given you a better understanding of what really makes up a certificate, and the sheer complexity around X.509 in general. There is a lot I glossed over in this post which I hope to drill deeper into in the future. &lt;/p&gt;

&lt;p&gt;In the next post I'll cover the &lt;code&gt;TBSCertificate&lt;/code&gt; &lt;code&gt;extensions&lt;/code&gt; fields. &lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;ASN.1 - see &lt;a href="https://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx"&gt;Introduction to ASN.1&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;ASN.1 to DER encoding - see &lt;a href="https://www.itu.int/rec/T-REC-X.690-201508-I/en"&gt;ASN.1 encoding rules&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;code&gt;Signature&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.1.3"&gt;Section 4.1.1.3&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;code&gt;Signature Algorithm&lt;/code&gt; in &lt;code&gt;Certificate&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.3"&gt;Section 4.1.2.3&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;OID for &lt;code&gt;sha256WithRSAEncryption&lt;/code&gt; - see &lt;a href="https://oidref.com/1.2.840.113549.1.1.11"&gt;https://oidref.com/1.2.840.113549.1.1.11&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;code&gt;issuerUniqueID&lt;/code&gt;  and &lt;code&gt;subjectUniqueID&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.8"&gt;Section 4.1.2.8&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;code&gt;Version&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.1"&gt;Section 4.1.2.1&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;code&gt;Serial Number&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.2"&gt;Section 4.1.2.2&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;&lt;code&gt;Signature Algorithm&lt;/code&gt; in &lt;code&gt;TBSCertificate&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.1.2"&gt;Section 4.1.1.2&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn10"&gt;
&lt;p&gt;&lt;code&gt;Issuer&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.4"&gt;Secion 4.2.1.4&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn11"&gt;
&lt;p&gt;&lt;code&gt;Validity&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.5"&gt;Secion 4.2.1.5&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn12"&gt;
&lt;p&gt;&lt;code&gt;UTCTime&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1"&gt;Section 4.1.2.5.1&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn13"&gt;
&lt;p&gt;&lt;code&gt;GeneralizedTime&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.5.2"&gt;Section 4.1.2.5.2&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn14"&gt;
&lt;p&gt;&lt;code&gt;Subject&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.5"&gt;Secion 4.2.1.6&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn15"&gt;
&lt;p&gt;&lt;code&gt;Subject Public Key&lt;/code&gt; - see &lt;a href="https://tools.ietf.org/html/rfc5280#section-4.1.2.6"&gt;Secion 4.2.1.6&lt;/a&gt; of &lt;a href="https://tools.ietf.org/html/rfc5280"&gt;RFC 5280&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>x509</category>
      <category>certificate</category>
      <category>security</category>
    </item>
    <item>
      <title>Improving Our GitHub Actions Runner Orchestrator</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sat, 08 Feb 2020 22:22:22 +0000</pubDate>
      <link>https://forem.com/wayofthepie/improving-our-actions-runner-orchestrator-4ahh</link>
      <guid>https://forem.com/wayofthepie/improving-our-actions-runner-orchestrator-4ahh</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
Github checks api

&lt;ul&gt;
&lt;li&gt;Making use of this new information&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Using kubernetes jobs to schedule actions runners

&lt;ul&gt;
&lt;li&gt;Storing our token properly&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Running our script as a kubernetes cronjob

&lt;ul&gt;
&lt;li&gt;Assigning the correct permissions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Fixing a bug in our logic&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the last post we got a simple actions runner orchestrator running with bash and cron. We also noted a few issues with that version. In this post we will fix up the following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of launching a runner per commit, we will instead launch a runner per check request.&lt;/li&gt;
&lt;li&gt;Instead of running local docker containers, we will run kubernetes jobs.&lt;/li&gt;
&lt;li&gt;Instead of just running locally with cron, we will create a kubernetes 
&lt;code&gt;CronJob&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's do it!&lt;/p&gt;

&lt;h1&gt;
  
  
  Github checks api
&lt;/h1&gt;

&lt;p&gt;When a check run is requested, the &lt;a href="https://developer.github.com/v3/checks/"&gt;github checks api&lt;/a&gt; will reflect this. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/commits/e13119b0/check-runs
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"total_count"&lt;/span&gt;: 1,
  &lt;span class="s2"&gt;"check_runs"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: 433544203,
      &lt;span class="s2"&gt;"node_id"&lt;/span&gt;: &lt;span class="s2"&gt;"MDg6Q2hlY2tSdW40MzM1NDQyMDM="&lt;/span&gt;,
      &lt;span class="s2"&gt;"head_sha"&lt;/span&gt;: &lt;span class="s2"&gt;"e13119b07d81e4c587882b2f7c9d7a730810f709"&lt;/span&gt;,
      &lt;span class="s2"&gt;"external_id"&lt;/span&gt;: &lt;span class="s2"&gt;"ca395085-040a-526b-2ce8-bdc85f692774"&lt;/span&gt;,
      &lt;span class="s2"&gt;"url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://api.github.com/repos/wayofthepie/gh-app-test/check-runs/433544203"&lt;/span&gt;,
      &lt;span class="s2"&gt;"html_url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://github.com/wayofthepie/gh-app-test/runs/433544203"&lt;/span&gt;,
      &lt;span class="s2"&gt;"details_url"&lt;/span&gt;: &lt;span class="s2"&gt;"https://help.github.com/en/actions"&lt;/span&gt;,
      &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"queued"&lt;/span&gt;,
      &lt;span class="s2"&gt;"conclusion"&lt;/span&gt;: null,
      &lt;span class="s2"&gt;"started_at"&lt;/span&gt;: &lt;span class="s2"&gt;"2020-02-08T14:40:27Z"&lt;/span&gt;,
      &lt;span class="s2"&gt;"completed_at"&lt;/span&gt;: null,
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return the status of all the check runs for the given commit (above in the url &lt;code&gt;e13119b0&lt;/code&gt; is the short ref for the given commit). As you can see above the &lt;code&gt;status&lt;/code&gt; of the first check run is &lt;code&gt;queued&lt;/code&gt;, in this case it means it's awaiting a runner to be executed on. &lt;/p&gt;

&lt;p&gt;Using this information will also allow our script to be completely stateless. In the previous post it had to keep track of the last commit in a file, with the checks API we no longer need this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making use of this new information
&lt;/h2&gt;

&lt;p&gt;Now we can change the logic in our orchestration script as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the latest commit.&lt;/li&gt;
&lt;li&gt;Get all requested check runs for that commit.&lt;/li&gt;
&lt;li&gt;For each requested run launch an actions runner.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the updated script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the latest commit&lt;/span&gt;
&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# for each check run requested for this commit, get the "status"&lt;/span&gt;
&lt;span class="c"&gt;# field and assign to the "check_status" variable &lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;check_status &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/check-runs &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.check_runs[] | "\(.status)"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="c"&gt;# if "check_status" is queued launch an action runner&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"queued"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Launching actions runner ..."&lt;/span&gt;
        docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/92c0af22d721cbed5c694cc58bec1e3d9caf505c"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Add a new commit to the repository you have been running the actions against and run &lt;code&gt;./orc.sh ${PAT} ${OWNER} ${REPO}&lt;/code&gt;, it should start a container and run the build.&lt;/p&gt;

&lt;h1&gt;
  
  
  Using kubernetes jobs to schedule actions runners
&lt;/h1&gt;

&lt;p&gt;If we move to using kubernetes instead of our local docker daemon we can scale out much easier. First let's update to launch the actions runners as kubernetes jobs instead of direct docker containers.&lt;/p&gt;

&lt;p&gt;First let's create a cluster. There are many ways to do this, I use Google Cloud so I'm going to create a cluster on google cloud. This has a cost, see &lt;a href="https://kubernetes.io/docs/setup/"&gt;https://kubernetes.io/docs/setup/&lt;/a&gt; for examples of local cluster setups.&lt;/p&gt;

&lt;p&gt;To create a cluster on google cloud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud container clusters create actions-spawner &lt;span class="nt"&gt;--region&lt;/span&gt; europe-west2-c &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; 1
WARNING: Currently VPC-native is not ...
WARNING: Newly created clusters ...
...
Creating cluster actions-spawner &lt;span class="k"&gt;in &lt;/span&gt;europe-west2-c... Cluster is being health-checked &lt;span class="o"&gt;(&lt;/span&gt;master is healthy&lt;span class="o"&gt;)&lt;/span&gt;...done.
Created &lt;span class="o"&gt;[&lt;/span&gt;https://container.googleapis.com/v1/projects/monthly-hacking/zones/europe-west2-c/clusters/actions-spawner].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/europe-west2-c/actions-spawner?project&lt;span class="o"&gt;=&lt;/span&gt;monthly-
hacking
kubeconfig entry generated &lt;span class="k"&gt;for &lt;/span&gt;actions-spawner.
NAME             LOCATION        MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION    NUM_NODES  STATUS
actions-spawner  europe-west2-c  1.13.11-gke.23  35.189.78.16  n1-standard-1  1.13.11-gke.23  1          RUNNING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a cluster with one node, we don't need more than that to test. Make sure you have &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/"&gt;kubectl installed&lt;/a&gt;. Auth should be setup automatically for &lt;code&gt;kubectl&lt;/code&gt;. To test let's try to list the nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes
NAME                                             STATUS   ROLES    AGE     VERSION
gke-actions-spawner-default-pool-f1380f72-3wm5   Ready    &amp;lt;none&amp;gt;   4m47s   v1.13.11-gke.23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good! Now, let's update our orchestration script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the latest commit&lt;/span&gt;
&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# for each check run requested for this commit, get the "status"&lt;/span&gt;
&lt;span class="c"&gt;# field and assign to the "check_status" variable&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;check_status &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/check-runs &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.check_runs[] | "\(.status)"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="c"&gt;# if "check_status" is queued launch an action runner&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"queued"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, launching job ..."&lt;/span&gt;
        &lt;span class="nb"&gt;cat &lt;/span&gt;job.yaml &lt;span class="se"&gt;\&lt;/span&gt;
            | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/NAME/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/; s/OWNER/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/; s/REPO/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; s/TOKEN/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', nothing to do ..."&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And create the &lt;code&gt;job.yaml&lt;/code&gt;, the specification for our kubernetes job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wayofthepie/actions-image&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{OWNER}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{REPO}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{TOKEN}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
  &lt;span class="na"&gt;backoffLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ WARNING ⚠️&lt;br&gt;
The token here should be a stored as a kubernetes secret. Using the token as I have above is not good practice. I will fix this later in this post.&lt;/p&gt;

&lt;p&gt;Let's test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Found check run request with status &lt;span class="s1"&gt;'completed'&lt;/span&gt;, nothing to &lt;span class="k"&gt;do&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, it works when there are no runs requested. Commit to the repo you are testing against and run again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Found check run request with status queued, launching job ...
job.batch/990a0d3d-bb98-419e-abc5-ca4fa48ca328 created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like it worked. Let's see what's running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get &lt;span class="nb"&gt;jobs
&lt;/span&gt;NAME                                   COMPLETIONS   DURATION   AGE
990a0d3d-bb98-419e-abc5-ca4fa48ca328   0/1           5s         5s

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                         READY   STATUS      RESTARTS   AGE
990a0d3d-bb98-419e-abc5-ca4fa48ca328-wxn5c   1/1     Running     0          8s

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl logs 990a0d3d-bb98-419e-abc5-ca4fa48ca328-wxn5c &lt;span class="nt"&gt;-f&lt;/span&gt;
Unrecognized command-line input arguments: &lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; For usage refer to: .&lt;span class="se"&gt;\c&lt;/span&gt;onfig.cmd &lt;span class="nt"&gt;--help&lt;/span&gt; or ./config.sh &lt;span class="nt"&gt;--help&lt;/span&gt;

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;

Enter the name of runner: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;990a0d3d-bb98-419e-abc5-ca4fa48ca328-wxn5c]
√ Runner successfully added
√ Runner connection is good
&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-08 16:35:49Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
2020-02-08 16:35:53Z: Running job: build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! It kicked off a build. However, notice the warning at the start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unrecognized command-line input arguments: 'name'. For usage refer to: .\config.cmd --help or ./config.sh --help
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something is wrong... A quick look through &lt;code&gt;orc.sh&lt;/code&gt; and &lt;code&gt;job.yaml&lt;/code&gt; highlights the issue - we are missing the fourth argument for the &lt;code&gt;wayofthepie/actions-runner&lt;/code&gt; image! This sets the name of the actions runner. Let's fix it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wayofthepie/actions-image&lt;/span&gt;
        &lt;span class="c1"&gt;# here we add the name argument&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{OWNER}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{REPO}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{TOKEN}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{NAME}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; 
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
  &lt;span class="na"&gt;backoffLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's commit to the test repo and run again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Found check run request with status queued, launching job ...
job.batch/7abcb7a1-b1bb-4641-88af-fc4562e29bb7 created

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get &lt;span class="nb"&gt;jobs
&lt;/span&gt;NAME                                   COMPLETIONS   DURATION   AGE
7abcb7a1-b1bb-4641-88af-fc4562e29bb7   0/1           4s         4s
990a0d3d-bb98-419e-abc5-ca4fa48ca328   1/1           46s        12m

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                         READY   STATUS      RESTARTS   AGE
7abcb7a1-b1bb-4641-88af-fc4562e29bb7-q5jdd   1/1     Running     0          13s
990a0d3d-bb98-419e-abc5-ca4fa48ca328-wxn5c   0/1     Completed   0          12m

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl logs 7abcb7a1-b1bb-4641-88af-fc4562e29bb7-q5jdd

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-08 16:48:30Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
2020-02-08 16:48:34Z: Running job: build
2020-02-08 16:48:52Z: Job build completed with result: Succeeded

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;


√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! No warnings, all working as expected. The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/7eced1c83d11948eae48fe370d6a9e72c330eac9"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing our token properly
&lt;/h2&gt;

&lt;p&gt;Right now our personal access token gets stored in the definition of our job! If we retrieve our job we can see it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get &lt;span class="nb"&gt;jobs &lt;/span&gt;990a0d3d-bb98-419e-abc5-ca4fa48ca328 &lt;span class="nt"&gt;-o&lt;/span&gt; json
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"apiVersion"&lt;/span&gt;: &lt;span class="s2"&gt;"batch/v1"&lt;/span&gt;,
    &lt;span class="s2"&gt;"kind"&lt;/span&gt;: &lt;span class="s2"&gt;"Job"&lt;/span&gt;,
    &lt;span class="s2"&gt;"metadata"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        ...
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"spec"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        ...
        &lt;span class="s2"&gt;"template"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
           ...
            &lt;span class="s2"&gt;"spec"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"containers"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                    &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="s2"&gt;"args"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                            &lt;span class="s2"&gt;"wayofthepie"&lt;/span&gt;,
                            &lt;span class="s2"&gt;"gh-app-test"&lt;/span&gt;,
                            &lt;span class="s2"&gt;"THE TOKEN IS IN HERE!!!"&lt;/span&gt;
                        &lt;span class="o"&gt;]&lt;/span&gt;,
                        &lt;span class="s2"&gt;"image"&lt;/span&gt;: &lt;span class="s2"&gt;"wayofthepie/actions-image"&lt;/span&gt;,
                        &lt;span class="s2"&gt;"imagePullPolicy"&lt;/span&gt;: &lt;span class="s2"&gt;"Always"&lt;/span&gt;,
                        &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"990a0d3d-bb98-419e-abc5-ca4fa48ca328"&lt;/span&gt;,
                        ...
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;]&lt;/span&gt;,
    ...
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not good! We should be storing this as a kubernetes secret. Let's do that. First create a &lt;code&gt;secret.yaml&lt;/code&gt; defining our secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-token&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;stringData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;secret.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;TOKEN&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YOUR_PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
secret/github-token created

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get secrets
NAME                  TYPE                                  DATA   AGE
...
github-token          Opaque                                1      30s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that created let's update our job spec in &lt;code&gt;job.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Job&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wayofthepie/actions-image&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{OWNER}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{REPO}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$(GITHUB_TOKEN)"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{NAME}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB_TOKEN&lt;/span&gt;
          &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-token&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
      &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
  &lt;span class="na"&gt;backoffLimit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#define-container-environment-variables-using-secret-data"&gt;these docs&lt;/a&gt; for more on reading secret data into env vars. &lt;/p&gt;

&lt;p&gt;Also note the syntax to reference the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; it uses &lt;code&gt;$()&lt;/code&gt; and not &lt;code&gt;${}&lt;/code&gt;, see &lt;a href="https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#use-environment-variables-to-define-arguments"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Re-run and everything should work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Found check run request with status queued, launching job ...
job.batch/eb1e314d-594b-4253-ae8a-74c797a2cd76 created

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                         READY   STATUS              RESTARTS   AGE
eb1e314d-594b-4253-ae8a-74c797a2cd76-x47lr   0/1     ContainerCreating   0          3s

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; eb1e314d-594b-4253-ae8a-74c797a2cd76-x47lr
...
2020-02-08 17:41:08Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
2020-02-08 17:41:12Z: Running job: build
2020-02-08 17:41:30Z: Job build completed with result: Succeeded

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;


√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! We should also clean up &lt;code&gt;orc.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the latest commit&lt;/span&gt;
&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# for each check run requested for this commit, get the "status"&lt;/span&gt;
&lt;span class="c"&gt;# field and assign to the "check_status" variable&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;check_status &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/check-runs &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.check_runs[] | "\(.status)"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

    &lt;span class="c"&gt;# if "check_status" is queued launch an action runner&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"queued"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, launching job ..."&lt;/span&gt;
        &lt;span class="nb"&gt;cat &lt;/span&gt;job.yaml &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="c"&gt;# we removed the {TOKEN} replacement here&lt;/span&gt;
            | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;NAME&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/g; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;OWNER&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;REPO&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', nothing to do ..."&lt;/span&gt;
    &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/46512bb98386d15ce5cb4fbb71e08d8c8f734456"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Running our script as a kubernetes cronjob
&lt;/h1&gt;

&lt;p&gt;To run our orchestrator script as a kubernetes cronjob we first need to create a docker image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="se"&gt;\
&lt;/span&gt;    jq &lt;span class="se"&gt;\
&lt;/span&gt;    uuid-runtime

&lt;span class="k"&gt;RUN &lt;/span&gt;curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://storage.googleapis.com/kubernetes-release/release/v1.17.0/bin/linux/amd64/kubectl &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;kubectl /usr/local/bin &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/local/bin/kubectl

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; orc.sh .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./orc.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I built this as &lt;code&gt;wayofthepie/actions-orchestrator&lt;/code&gt; and pushed to the public docker registry. Next, let' create a &lt;code&gt;CronJob&lt;/code&gt; spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;batch/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CronJob&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-orchestrator&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*/1&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
  &lt;span class="na"&gt;jobTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-orchestrator&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wayofthepie/actions-orchestrator&lt;/span&gt;
            &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$(GITHUB_TOKEN)"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{OWNER}"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{REPO}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
            &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GITHUB_TOKEN&lt;/span&gt;
              &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-token&lt;/span&gt;
                  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
          &lt;span class="na"&gt;restartPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Never&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run our orchestrator every minute. To create the job, replace with your own repo and owner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;cron.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;OWNER&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/wayofthepie/; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;REPO&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/gh-app-test/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
cronjob.batch/actions-orchestrator created

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get cronjob
NAME                   SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
actions-orchestrator   &lt;span class="k"&gt;*&lt;/span&gt;/1 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;   False     0        &amp;lt;none&amp;gt;          7s

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                         READY   STATUS              RESTARTS   AGE
actions-orchestrator-1581193620-4j8jz        0/1     ContainerCreating   0          1s

&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl logs actions-orchestrator-1581193620-4j8jz
Found check run request with status queued, launching job ...
Error from server &lt;span class="o"&gt;(&lt;/span&gt;Forbidden&lt;span class="o"&gt;)&lt;/span&gt;: error when retrieving current configuration of:
...
from server &lt;span class="k"&gt;for&lt;/span&gt;: &lt;span class="s2"&gt;"STDIN"&lt;/span&gt;: jobs.batch &lt;span class="s2"&gt;"316b08ed-89e7-4321-a521-897c7a40fa50"&lt;/span&gt; is forbidden: User &lt;span class="s2"&gt;"system:serviceaccount:default:default"&lt;/span&gt; cannot get resource &lt;span class="s2"&gt;"
jobs"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;API group &lt;span class="s2"&gt;"batch"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;the namespace &lt;span class="s2"&gt;"default"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An error! Let's delete the cronjob so it doesn't keep running, &lt;code&gt;kubectl delete cronjob actions-orchestrator&lt;/code&gt;, and investigate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assigning the correct permissions
&lt;/h2&gt;

&lt;p&gt;It seems the default service account we get in the pod does not have access to the jobs resource. To fix this we need to create a &lt;code&gt;ClusterRole&lt;/code&gt; and &lt;code&gt;ClusterRoleBinding&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jobs-manager&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jobs-manager&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;batch"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extensions"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jobs"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;list"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;watch"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;update"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;patch"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;delete"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And create it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; cluster-role.yaml
clusterrole.rbac.authorization.k8s.io/default created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Re-create our cronjob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;cron.yaml | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;OWNER&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/wayofthepie/; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;REPO&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/gh-app-test/"&lt;/span&gt; | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
cronjob.batch/actions-orchestrator created

&lt;span class="c"&gt;# it should run every minute&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
actions-orchestrator-1581196020-tmbll   1/1     Running   0          4s

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

&lt;/div&gt;



&lt;p&gt;Great! Now if we commit to the test repo it should create a new job for the requested check runs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Fixing a bug in our logic
&lt;/h1&gt;

&lt;p&gt;We still only check the last commit for check requests meaning we can still miss requests, leaving check runs for some commits idle. This is a bug.. The real fix for this would require either a lot of API calls or using webhooks. But for now we can look at the last 5 minutes of commits rather than just the last commit. If we run the script every minute there is a much smaller chance of a missing check runs.&lt;/p&gt;

&lt;p&gt;The updates to &lt;code&gt;orc.sh&lt;/code&gt; are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the date format in the format the github api wants&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;five_minutes_ago &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;--iso-8601&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;seconds &lt;span class="nt"&gt;--date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'5 minutes ago'&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'+'&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Getting commits from the last 5 minutes ..."&lt;/span&gt;
&lt;span class="nv"&gt;commits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits?since&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;five_minutes_ago&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;Z"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;commit &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Checking &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for check requests ..."&lt;/span&gt;

    &lt;span class="c"&gt;# for each check run requested for this commit, get the "status"&lt;/span&gt;
    &lt;span class="c"&gt;# field and assign to the "check_status" variable&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;check_status &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"accept: application/vnd.github.antiope-preview+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
        https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/check-runs &lt;span class="se"&gt;\&lt;/span&gt;
        | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.check_runs[] | "\(.status)"'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

        &lt;span class="c"&gt;# if "check_status" is queued launch an action runner&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"queued"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, launching job ..."&lt;/span&gt;
            &lt;span class="nb"&gt;cat &lt;/span&gt;job.yaml &lt;span class="se"&gt;\&lt;/span&gt;
                | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;NAME&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/g; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;OWNER&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/; s/&lt;/span&gt;&lt;span class="se"&gt;\{&lt;/span&gt;&lt;span class="s2"&gt;REPO&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
        &lt;span class="k"&gt;else
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Found check run request with status '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;check_status&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;', nothing to do ..."&lt;/span&gt;
        &lt;span class="k"&gt;fi
    done
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild the actions orchestrator image, push and it should all work! The image up to this point is tagged as &lt;a href="https://hub.docker.com/layers/wayofthepie/actions-orchestrator/8-2-2020/images/sha256-78f457ec6722cad9fe80d5bba14fb5b93f8535c661805ecbf987c8de4bd38bf7"&gt;wayofthepie/actions-orchestrator:8-2-2020&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/d0100253bf2b8f8b6225d7615ad78936a507b02e"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We now have away of orchestrating actions runners on kubernetes. There are still a few issues however:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no error recovery and the error messages are pretty bad. For example if for some reason the cronjob does not run for 5+ minutes we may miss commits and check runs.&lt;/li&gt;
&lt;li&gt;It would be much better to use webhooks here.&lt;/li&gt;
&lt;li&gt;We currently only support watching a single repository.&lt;/li&gt;
&lt;li&gt;Things are getting complicated with bash!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will tackle some of these in the next post.&lt;/p&gt;

</description>
      <category>github</category>
      <category>kubernetes</category>
      <category>bash</category>
      <category>docker</category>
    </item>
    <item>
      <title>Hacking Together A GitHub Actions Runner Orchestrator</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sun, 02 Feb 2020 19:59:05 +0000</pubDate>
      <link>https://forem.com/wayofthepie/hacking-together-an-actions-runner-orchestrator-5ef9</link>
      <guid>https://forem.com/wayofthepie/hacking-together-an-actions-runner-orchestrator-5ef9</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Run a single build and cleanup&lt;/li&gt;
&lt;li&gt;
A simple orchestrator

&lt;ul&gt;
&lt;li&gt;Detecting new commits&lt;/li&gt;
&lt;li&gt;An issue with our process&lt;/li&gt;
&lt;li&gt;Changing tactics&lt;/li&gt;
&lt;li&gt;Adding cron&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In the last post, we built a docker image that allows us to connect and disconnect actions runners when we want. But we need to do this manually when we need one.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to hack together an orchestrator that launches an actions runner per commit, with bash and cron!&lt;/p&gt;

&lt;h1&gt;
  
  
  Run a single build and cleanup
&lt;/h1&gt;

&lt;p&gt;Right now when we run a container and connect an actions runner, it will stay alive until we manually kill it, building any new commits. It would be nicer if it ran a single build then cleaned itself up.&lt;/p&gt;

&lt;p&gt;Back in the previous post we found the &lt;a href="https://github.com/actions/runner/blob/c59c0e2ded040f151ccd8845856def6b4cd7b58a/src/Runner.Common/Constants.cs#L174" rel="noopener noreferrer"&gt;Runner.Common Constants.cs&lt;/a&gt;. I mentioned that there were some other interesting pieces of code in that class, outside of what we were looking for at the time. One of those is the &lt;a href="https://github.com/actions/runner/blob/c59c0e2ded040f151ccd8845856def6b4cd7b58a/src/Runner.Common/Constants.cs#L117-L126" rel="noopener noreferrer"&gt;CommandLine Flags&lt;/a&gt; class. This contains the &lt;code&gt;help&lt;/code&gt; option, which looks like it maps to &lt;code&gt;config.sh --help&lt;/code&gt;. But it also contains a &lt;code&gt;once&lt;/code&gt; option. &lt;/p&gt;

&lt;p&gt;Digging about a bit more and seeing where this is used uncovers a variable called &lt;a href="https://github.com/actions/runner/blob/2e0e8eb8224d7b9ca5eb296205221fdb5335152d/src/Runner.Listener/CommandSettings.cs#L66" rel="noopener noreferrer"&gt;RunOnce&lt;/a&gt; in &lt;a href="https://github.com/actions/runner/blob/2e0e8eb8224d7b9ca5eb296205221fdb5335152d/src/Runner.Listener/CommandSettings.cs" rel="noopener noreferrer"&gt;Runner.Listener CommandSettings&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;RunOnce&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;TestFlag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommandLine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Once&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks promising. It is most likely an option we can pass to &lt;code&gt;./run.sh&lt;/code&gt;. Let's find out by updating entrypoint.sh!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions/runners/registration-token | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;
    ./config.sh remove &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions/runners/registration-token | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;

./config.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

&lt;span class="c"&gt;# add the --once option here&lt;/span&gt;
./run.sh &lt;span class="nt"&gt;--once&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Rebuild and launch the runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-02 18:05:11Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the next steps to work, you will need to have an action workflow set up, here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self-hosted&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run a one-line script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo Hello, world!&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run a multi-line script&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo Add other actions to build,&lt;/span&gt;
        &lt;span class="s"&gt;echo test, and deploy your project.&lt;/span&gt;
        &lt;span class="s"&gt;sleep 10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to the repository at &lt;em&gt;.github/workflows/ci.yml&lt;/em&gt;. It should appear under the &lt;em&gt;Actions&lt;/em&gt; tab in the repo. Now you can add a new commit to the repo, and your connected action runner should start the workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
2020-02-02 18:06:42Z: Running job: build
2020-02-02 18:07:00Z: Job build completed with result: Succeeded

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;

√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! It ran the action workflow on the new commit, exited, and cleaned up. Exactly what we wanted. Now we can build a simple orchestrator.&lt;/p&gt;

&lt;h1&gt;
  
  
  A simple orchestrator
&lt;/h1&gt;

&lt;p&gt;I'm going to keep this dead simple. Here is the workflow I am thinking of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every minute check for new commits&lt;/li&gt;
&lt;li&gt;If a new commit is detected, launch a runner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it! This has a few problems, for example if there are multiple new commits this will just launch a single runner which will build one commit and the other commits may never be built. However, if we get this flow working we can improve as we go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting new commits
&lt;/h2&gt;

&lt;p&gt;The simplest way I can think of detecting new commits is by using the &lt;code&gt;/repos/:owner/:repo/commits&lt;/code&gt; API. This returns a lot of info, all we need is the SHA1 hash of the last commit. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/commits &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;grep &lt;/span&gt;sha &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n1&lt;/span&gt; 
&lt;span class="s2"&gt;"sha"&lt;/span&gt;: &lt;span class="s2"&gt;"e0654f66fcb8061a439e404f1bf1b2c8b7116537"&lt;/span&gt;,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple! We can make this a bit cleaner with &lt;code&gt;jq&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/commits &lt;span class="se"&gt;\&lt;/span&gt;
    | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha
e0654f66fcb8061a439e404f1bf1b2c8b7116537
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jq -r .[0].sha&lt;/code&gt; says get me the first object in an array, and get the value of the &lt;em&gt;sha&lt;/em&gt; field from that. Give to me as a raw string (not wrapped in quotes).&lt;/p&gt;

&lt;p&gt;With this, we can write a shell script (&lt;em&gt;orc.sh&lt;/em&gt;) that detects new commits and launches a runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;##&lt;/span&gt;
&lt;span class="c"&gt;# Detects new commits in a given repo by checking every minute&lt;/span&gt;
&lt;span class="c"&gt;# and storing a reference to the last seen commit in a file called&lt;/span&gt;
&lt;span class="c"&gt;# prev.&lt;/span&gt;
&lt;span class="c"&gt;##&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prev

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# make sure the prev file exists&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the latest commit&lt;/span&gt;
&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# read the last commit we saw&lt;/span&gt;
&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Latest commit is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Previous commit is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# compare the commits, running an action runner if they differ&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Detected new commit, starting runner"&lt;/span&gt;
    docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# update the previous commit store with the latest commit&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test it, create a new commit in the repo you are connection your actions runners to and run this script against that repo, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Latest commit is 4ceb832f5386c812c0a296968197a1d242f8e8fd
Previous commit is c7e9b463a90a4c5eeab16ba9ccf2afdc1b28153b
Detected new commit, starting runner
68101fbf5c801afaa378ebbb6dc988c5e264fa784bc2c1353025f125f56bbc22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! To see the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
68101fbf5c80        actions-image       &lt;span class="s2"&gt;"./entrypoint.sh way…"&lt;/span&gt;   26 seconds ago      Up 25 seconds                           adoring_perlman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; adoring_perlman

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-02 18:38:25Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  An issue with our process
&lt;/h2&gt;

&lt;p&gt;Hmm it doesn't seem to be starting any jobs, and if we look at the repo we see that the check has failed!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F915cj77cm3w4bndarn6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F915cj77cm3w4bndarn6a.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason for this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv8ilkrd58cvyh101eq3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv8ilkrd58cvyh101eq3u.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a problem. It seems you always need at least one self-hosted runner running, or commits will initially fail. For now we can manually kick off the checks again from the UI with the &lt;em&gt;Re-run checks&lt;/em&gt; button, and our runner should start the job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd7b31hbg5qdia9q3d1z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd7b31hbg5qdia9q3d1z7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; adoring_perlman
...
2020-02-02 18:38:25Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
2020-02-02 18:42:37Z: Running job: build
2020-02-02 18:43:00Z: Job build completed with result: Succeeded

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;


√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given this issue, we need to change tactics!&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing tactics
&lt;/h2&gt;

&lt;p&gt;From a bit of testing it seems we can get around this issue by configuring a runner that we never actually run. Let's update the entrypoint script to allow registration only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

&lt;span class="c"&gt;# if set this script will only run ./config.sh&lt;/span&gt;
&lt;span class="c"&gt;# it will not run the actions runner&lt;/span&gt;
&lt;span class="nv"&gt;REGISTER_ONLY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$5&lt;/span&gt;

cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions/runners/registration-token | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;
    ./config.sh remove &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/actions/runners/registration-token | jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;

./config.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REGISTER_ONLY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    ./run.sh &lt;span class="nt"&gt;--once&lt;/span&gt;
    cleanup
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/878fc10e5b4834b2488811f38f5f0658c44bdc75" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Now let's build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; actions-image &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon    105kB
Step 1/9 : FROM ubuntu
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 775349758637
...
Successfully built f82508c0ebb8
Successfully tagged actions-image:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And run, but this time run it so that it only configures and does not stay waiting for commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run -ti --rm actions-image ${OWNER} ${REPO} ${PAT} test true

--------------------------------------------------------------------------------
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___(_) |_| | | |_   _| |__      / \   ___| |_(_) ___  _ __  ___      |
|      | |  _| | __| |_| | | | | '_ \    / _ \ / __| __| |/ _ \| '_ \/ __|     |
|      | |_| | | |_|  _  | |_| | |_) |  / ___ \ (__| |_| | (_) | | | \__ \     |
|       \____|_|\__|_| |_|\__,_|_.__/  /_/   \_\___|\__|_|\___/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
--------------------------------------------------------------------------------

# Authentication


√ Connected to GitHub

# Runner Registration


√ Runner successfully added
√ Runner connection is good

# Runner settings


√ Settings Saved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are setting the fifth parameter to &lt;code&gt;true&lt;/code&gt; here, this will in turn set the &lt;code&gt;${REGISTER_ONLY}&lt;/code&gt; variable in our &lt;code&gt;entrypoint.sh&lt;/code&gt; script, meaning &lt;code&gt;./run.sh&lt;/code&gt; will never be called. Now if we go to the UI and &lt;em&gt;Settings -&amp;gt; Actions&lt;/em&gt; in our repo, we should see an offline action called &lt;em&gt;test&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frk3p345e7dgp17w5xch1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frk3p345e7dgp17w5xch1.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Now let's commit to our repo again, and hopefully the check run will be queued and will not have failed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frqk20wdw1ovqe0ezanhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frqk20wdw1ovqe0ezanhl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! Finally let's spin up a runner to actually run the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Latest commit is 1fb4c5fd212e8aba177a15b6016c68b7c005b74a
Previous commit is
Detected new commit, starting runner
4ecebf7f0a5fdd75c216d490ef67afa7a9d3e0c3b30848c150b148238eccbd7c

&lt;span class="nv"&gt;$ &lt;/span&gt;docker logs suspicious_ardinghelli &lt;span class="nt"&gt;-f&lt;/span&gt;

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-02 19:14:33Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
2020-02-02 19:14:36Z: Running job: build
2020-02-02 19:14:54Z: Job build completed with result: Succeeded

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;


√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woop! All working again. Now that's working again, let's add some improvements to &lt;code&gt;orc.sh&lt;/code&gt; and add a scheduler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding cron
&lt;/h2&gt;

&lt;p&gt;Let's get &lt;code&gt;orc.sh&lt;/code&gt; running every minute with cron. Just a small update first so that it writes the &lt;code&gt;prev&lt;/code&gt; file in the directory itself is located in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;##&lt;/span&gt;
&lt;span class="c"&gt;# Detects new commits in a given repo by checking every minute&lt;/span&gt;
&lt;span class="c"&gt;# and storing a reference to the last seen commit in a file called&lt;/span&gt;
&lt;span class="c"&gt;# prev.&lt;/span&gt;
&lt;span class="c"&gt;##&lt;/span&gt;

&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;

&lt;span class="c"&gt;# the directory of this script &lt;/span&gt;
&lt;span class="nv"&gt;cur_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt; &lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt; &lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;cur_dir&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/prev"&lt;/span&gt;

&lt;span class="c"&gt;# make sure we have values for all our arguments&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nv"&gt;$REPO&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Incorrect usage, example: ./orc.sh personal-access-token owner some-repo"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# make sure the prev file exists&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# get the latest commit&lt;/span&gt;
&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;  &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/commits |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .[0].sha&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# read the last commit we saw&lt;/span&gt;
&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Latest commit is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Previous commit is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# compare the commits, running an action runner if they differ&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Detected new commit, starting runner"&lt;/span&gt;
    docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# update the previous commit store with the latest commit&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;latest_commit&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prev&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I took the &lt;code&gt;cur_dir&lt;/code&gt; var from this post: &lt;a href="https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/59895/how-to-get-the-source-directory-of-a-bash-script-from-within-the-script-itself&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Latest code up to this point is &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/878fc10e5b4834b2488811f38f5f0658c44bdc75" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To add &lt;code&gt;orc.sh&lt;/code&gt; to cron, first move it to &lt;em&gt;/var/tmp&lt;/em&gt; (we can pick a better location later) and then run &lt;code&gt;cronab -e&lt;/code&gt; to edit the &lt;em&gt;crontab&lt;/em&gt; for your user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;orc.sh /var/tmp
&lt;span class="nv"&gt;$ &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt; 

&lt;span class="c"&gt;# Edit this file to introduce tasks to be run by cron.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
...
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# For more information see the manual pages of crontab(5) and cron(8)&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# m h  dom mon dow   command&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /var/tmp/orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can make sure it is running by checking the cron logs, it should run every minute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; cron &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Logs begin at Sat 2019-08-10 18:05:10 IST. &lt;span class="nt"&gt;--&lt;/span&gt;
...
Feb 02 19:30:01 sky CRON[31363]: &lt;span class="o"&gt;(&lt;/span&gt;chaospie&lt;span class="o"&gt;)&lt;/span&gt; CMD &lt;span class="o"&gt;(&lt;/span&gt;/var/tmp/orc.sh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you commit, a container should be created and run your actions! Note that because &lt;code&gt;orc.sh&lt;/code&gt; did not know about any previous commit it will always launch a container on its initial run. &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We now have a super simple way of launching actions runners per commit. However the way we are doing this has a lot of of problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;orc.sh&lt;/code&gt; will launch a container when it initially runs, whether there is something to build or not.&lt;/li&gt;
&lt;li&gt;We launch a runner per commit, if there are multiple workflows defined in a repo we will only ever build one and the others will lay idle. We can fix this by using information from the checks API to figure out when to launch a runner and how many we may need.&lt;/li&gt;
&lt;li&gt;The cron checks every minute for new commits, we can do better than this. For example we could use webhooks so the builds run in realtime.&lt;/li&gt;
&lt;li&gt;In my testing I have seen some cases where a runner fails to be cleaned up and sometimes you cannot even remove them from the UI. When this has happened I've had to register a new runner with the same name and then remove that.&lt;/li&gt;
&lt;li&gt;Instead of bash + cron it would be better to create a webservice for this, especially if we are going down the webhooks route.&lt;/li&gt;
&lt;li&gt;This can currently only run on a single machine and isn't really scalable as is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next post I'll tackle some of these issues. The code up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/b7d1d159fbbfe76f54a72adfb1a63f11462efc86" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>docker</category>
      <category>bash</category>
    </item>
    <item>
      <title>Improving our github actions runner image</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sun, 02 Feb 2020 16:15:17 +0000</pubDate>
      <link>https://forem.com/wayofthepie/improving-our-github-actions-runner-image-46ff</link>
      <guid>https://forem.com/wayofthepie/improving-our-github-actions-runner-image-46ff</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;
Undocumented parameters for config.sh

&lt;ul&gt;
&lt;li&gt;Diving into the source&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Automated token retrieval

&lt;ul&gt;
&lt;li&gt;Generating a personal access token&lt;/li&gt;
&lt;li&gt;Automatically generating a registration token&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Automated runner removal&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In the last post we built a docker image which can launch an actions runner. The image definition up to the end of that post is &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/dd7929733d9eca77a3791773e787d15c2ce99d81" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a few issues with the setup in that image. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have a few hacks in the &lt;code&gt;entrypoint.sh&lt;/code&gt; script for configuring the runner - e.g. passing the name via an &lt;code&gt;echo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Anytime you want to start a new runner you need to get the latest token from the UI.&lt;/li&gt;
&lt;li&gt;You need to manually unregister runners which you want to remove.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm going to tackle these three issues now. To help, we should dive into the actions runner source code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Undocumented parameters for config.sh
&lt;/h1&gt;

&lt;p&gt;I was pretty sure the help section for &lt;code&gt;config.sh&lt;/code&gt; was missing some flags. All it prints is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh  &lt;span class="nt"&gt;--help&lt;/span&gt;

Commands:,
 ./config.sh          Configures the runner
 ./config.sh remove   Unconfigures the runner
 ./run.sh             Runs the runner interactively. Does not require any options.

Options:
 &lt;span class="nt"&gt;--version&lt;/span&gt;  Prints the runner version
 &lt;span class="nt"&gt;--commit&lt;/span&gt;   Prints the runner commit
 &lt;span class="nt"&gt;--help&lt;/span&gt;     Prints the &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;each &lt;span class="nb"&gt;command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure a runner we need the following four things at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The url to the repo to register the runner against, this has a &lt;code&gt;--url&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;The token this has a &lt;code&gt;--token&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;The name of the runner, we cheat in our &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/blob/dd7929733d9eca77a3791773e787d15c2ce99d81/entrypoint.sh#L8" rel="noopener noreferrer"&gt;entrypoint.sh&lt;/a&gt; by echoing a name into the execution of &lt;code&gt;config.sh&lt;/code&gt;. This sets the first prompt as the value we echo.&lt;/li&gt;
&lt;li&gt;The work directory, here we also cheat with &lt;code&gt;echo&lt;/code&gt; and because we echo a single value this defaults to &lt;code&gt;_work&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;./config.sh&lt;/code&gt; calls out to a &lt;code&gt;.net&lt;/code&gt; binary called Runner.Listener, the source code can be found &lt;a href="https://github.com/actions/runner/tree/a0a590fb48fa2db92dacdef5d9b94294ab7940b4" rel="noopener noreferrer"&gt;here&lt;/a&gt;. What can we find in this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Diving into the source
&lt;/h2&gt;

&lt;p&gt;Our aim is to find any flags which are not documented in &lt;code&gt;./config --help&lt;/code&gt; that may help us automate configuration. The &lt;a href="https://github.com/actions/runner/tree/a0a590fb48fa2db92dacdef5d9b94294ab7940b4" rel="noopener noreferrer"&gt;source code&lt;/a&gt; is large enough that blindly searching through would be tedious, so what information do we have that can narrow down our search?&lt;/p&gt;

&lt;p&gt;When we run &lt;code&gt;config.sh&lt;/code&gt; it defaults the work folder to &lt;em&gt;_work&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

...
Enter name of work folder: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;_work]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, their must be a string in the code with the value &lt;code&gt;_work&lt;/code&gt;. If we search in the &lt;a href="https://github.com/actions/runner/tree/a0a590fb48fa2db92dacdef5d9b94294ab7940b4" rel="noopener noreferrer"&gt;actions-runner repo&lt;/a&gt;, we find it in a few places. The most promising is &lt;a href="https://github.com/actions/runner/blob/c59c0e2ded040f151ccd8845856def6b4cd7b58a/src/Runner.Common/Constants.cs#L174" rel="noopener noreferrer"&gt;Runner.Common Constants.cs&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This file contains quite a few interesting constants which may be of use later on! For our current goal, there is the &lt;a href="https://github.com/actions/runner/blob/c59c0e2ded040f151ccd8845856def6b4cd7b58a/src/Runner.Common/Constants.cs#L85-L105" rel="noopener noreferrer"&gt;CommandLine Args&lt;/a&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Args&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;MonitorSocketAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"monitorsocketaddress"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"pool"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;StartupType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"startuptype"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;WindowsLogonAccount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"windowslogonaccount"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Work&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"work"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Secret args. Must be added to the "Secrets" getter as well.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;WindowsLogonPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"windowslogonpassword"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Secrets&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WindowsLogonPassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks promising. It mentions two args we already use - &lt;code&gt;--token&lt;/code&gt; and &lt;code&gt;--url&lt;/code&gt;. Let's see if we can set name and the work dir similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; my-runner &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! Now there are no prompts. We can update our docker image's &lt;code&gt;entrypoint.sh&lt;/code&gt; from the previous post to take this into account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

./config.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/5fa9582a97bba3be000ece0cc3da2eb185462133" rel="noopener noreferrer"&gt;here&lt;/a&gt; for the full code up to this point including the Dockerfile.&lt;/p&gt;

&lt;h1&gt;
  
  
  Automated token retrieval
&lt;/h1&gt;

&lt;p&gt;Currently anytime we want to run a new action we need to login to the UI and retrieve a new token. Recently github release an API to generate this token, see the &lt;a href="https://developer.github.com/v3/actions/self_hosted_runners/" rel="noopener noreferrer"&gt;self hosted runners API docs&lt;/a&gt;. We can take advantage of this in our &lt;code&gt;entrypoint.sh&lt;/code&gt; script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating a personal access token
&lt;/h2&gt;

&lt;p&gt;To generate an actions registration token we first need to generate a Personal Access Token that we can use to call the github API. I'll refer to these as PAT's from here on in so as not to confuse them with the registration token, as they are both tokens of a different type!&lt;/p&gt;

&lt;p&gt;To generate a PAT, go to &lt;em&gt;Settings&lt;/em&gt; in your account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq3smyupy13mjry777922.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq3smyupy13mjry777922.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then &lt;em&gt;Developer Settings&lt;/em&gt; in the left panel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fveozmpanpvojt78e8m3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fveozmpanpvojt78e8m3r.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;em&gt;Personal Access Tokens&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9psv0e5dncl4a9hwf93s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9psv0e5dncl4a9hwf93s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally click the &lt;em&gt;Generate a new token&lt;/em&gt; button:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3oqextxeht3kwary1fkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3oqextxeht3kwary1fkh.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your token a name that associates it with what it's doing. I've called it &lt;code&gt;actions-runner-registration&lt;/code&gt;. I am not 100% sure the exact scopes that it needs, setting all &lt;code&gt;repo&lt;/code&gt; scopes works however so we can do this for now. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpdantf1ziferdb06xelu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fpdantf1ziferdb06xelu.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll to the bottom and click &lt;em&gt;Generate Token&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fujlxpha8du0vipz9tkhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fujlxpha8du0vipz9tkhl.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to copy the token on the next page and store it in a safe place! This is the only chance you get to see it, and if you do not you will have to generate a new one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fis377cgtord3k1gkdzgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fis377cgtord3k1gkdzgw.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Automatically generating a registration token
&lt;/h2&gt;

&lt;p&gt;Now we have our PAT, we can use the API to generate an actions runner registration token. The docs for the different methods of auth with the github API can be found &lt;a href="https://developer.github.com/v3/auth/#authenticating-for-saml-sso" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Let's keep his simple and use &lt;code&gt;curl&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YOUR_PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# ${YOUR_PAT} is the PAT we generated above&lt;/span&gt;
    https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions/runners/registration-token
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"token"&lt;/span&gt;: &lt;span class="s2"&gt;"*******"&lt;/span&gt;,
  &lt;span class="s2"&gt;"expires_at"&lt;/span&gt;: &lt;span class="s2"&gt;"2020-02-02T16:21:21.410+00:00"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! With this we can update our &lt;code&gt;entrypoint.sh&lt;/code&gt; script again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/actions/runners/registration-token |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;

./config.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have never used the &lt;code&gt;jq&lt;/code&gt; command before, &lt;code&gt;jq .token&lt;/code&gt; means take the token field out of the given json. The json returned from the &lt;code&gt;curl&lt;/code&gt; call has two fields, token and expires_at. If we used &lt;code&gt;jq&lt;/code&gt; without the &lt;code&gt;-r&lt;/code&gt; flag it would return the token wrapped in quotes, we want it without quotes. So the full command is &lt;code&gt;jq -r .token&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We need to use &lt;code&gt;curl&lt;/code&gt; and &lt;a href="https://github.com/stedolan/jq" rel="noopener noreferrer"&gt;jq&lt;/a&gt; now, neither of these are in our docker image so we need to also update our &lt;em&gt;Dockerfile&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RUNNER_VERSION=2.164.0&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    wget &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="c"&gt;# add curl and jq&lt;/span&gt;
    curl \
    jq

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/actions/runner/releases/download/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions/actions-runner&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; actions ~actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /home/actions/actions-runner/bin/installdependencies.sh

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; entrypoint.sh .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source for the image up to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/bbf1478e158c03eba4ac0cff1e88f21c0869296c" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's build it and run, see if it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; actions-image &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  81.92kB
Step 1/9 : FROM ubuntu
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 775349758637
...
Step 9/9 : ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./entrypoint.sh"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 0dff3c9bac89
Successfully built 0dff3c9bac89
Successfully tagged actions-image:latest

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; my-runner

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


A runner exists with the same name
Would you like to replace the existing runner? &lt;span class="o"&gt;(&lt;/span&gt;Y/N&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;N]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah an issue! We registered a runner called &lt;em&gt;my-runner&lt;/em&gt; previously in this post and never removed it. We can manually remove for now and try again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; my-runner

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-02 15:42:22Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works! Now we don't have to generate a token manually each time we want to start a new runner. One major issue remains, the fact you have to manually remove runners.&lt;/p&gt;

&lt;h1&gt;
  
  
  Automated runner removal
&lt;/h1&gt;

&lt;p&gt;When we kill the container running an actions runner it should automatically unregister itself. To manually unregister we use the &lt;code&gt;./config remove&lt;/code&gt; command. Let's add some small changes to our entrypoint so it will do this automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        https://api.github.com/repos/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions/runners/registration-token |&lt;span class="se"&gt;\&lt;/span&gt;
        jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;
    ./config.sh remove &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"authorization: token &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    https://api.github.com/repos/wayofthepie/gh-app-test/actions/runners/registration-token |&lt;span class="se"&gt;\&lt;/span&gt;
    jq &lt;span class="nt"&gt;-r&lt;/span&gt; .token&lt;span class="si"&gt;)&lt;/span&gt;

./config.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--work&lt;/span&gt; _work

./run.sh

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

&lt;/div&gt;



&lt;p&gt;The cleanup function is run at the end of the script, it gets a new registration token and called &lt;code&gt;./config.sh remove --token ${TOKEN}&lt;/code&gt; to unregister the runner. The code to this point can be found &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/31fa3a14dcd0e0b8c7c270ff21cce4d8c9a439d4" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Does it all work?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; actions-image &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  88.06kB
Step 1/9 : FROM ubuntu
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 775349758637
...
Successfully built 1de80d7b74d6
Successfully tagged actions-image:latest

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; my-runner

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;


√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;


√ Settings Saved.


√ Connected to GitHub

2020-02-02 16:05:31Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
^CExiting...

&lt;span class="c"&gt;# Runner removal&lt;/span&gt;


√ Runner removed successfully
√ Removed .credentials
√ Removed .runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used CTRL-C to cancel and it ran the cleanup! &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;We can now easily register multiple actions runners on the fly and have the automatically clean up when we are finished with them. However, we still need to launch them manually. What if we could launch a runner per commit, have them run a single build, and disappear? That's the dream. We'll star working towards that in the next post.&lt;/p&gt;

</description>
      <category>github</category>
      <category>docker</category>
      <category>bash</category>
    </item>
    <item>
      <title>Ephemeral Self-Hosted Github Actions Runners</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Sat, 01 Feb 2020 22:13:21 +0000</pubDate>
      <link>https://forem.com/wayofthepie/ephemeral-self-hosted-github-actions-runners-1h5m</link>
      <guid>https://forem.com/wayofthepie/ephemeral-self-hosted-github-actions-runners-1h5m</guid>
      <description>&lt;h1&gt;
  
  
  Table of Contents
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Github actions&lt;/li&gt;
&lt;li&gt;What makes an action runner?&lt;/li&gt;
&lt;li&gt;
What is currently automatable?

&lt;ul&gt;
&lt;li&gt;Initial download&lt;/li&gt;
&lt;li&gt;Configuring the runner&lt;/li&gt;
&lt;li&gt;Running the runner&lt;/li&gt;
&lt;li&gt;Quick test in a docker container&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Dockerizing a runner&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Github actions
&lt;/h1&gt;

&lt;p&gt;Out of the box &lt;a href="https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners"&gt;self-hosted actions runners&lt;/a&gt; are static so must be run on their own VM or something like a StatefulSet in kubernetes. This makes them quite costly. Wouldn't it be nice if they spun up per commit, ran a single job, and cleaned up afterwards?  &lt;/p&gt;

&lt;p&gt;In the next few posts I'm going to attempt to build a system that does this. This first post will run step by step through how I initially dockerized a self-hosted action.&lt;/p&gt;

&lt;h1&gt;
  
  
  What makes an action runner?
&lt;/h1&gt;

&lt;p&gt;Self-hosted action runners can be registered against a given repo by following the instructions given in the &lt;em&gt;Settings -&amp;gt; Actions&lt;/em&gt; menu in a repo. In short, the instructions are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-2.164.0.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;./run.sh

√ Connected to GitHub

2020-02-01 21:01:15Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that ${TOKEN} here and in the rest of this post refers to the actions runner registration token which you receive from the UI.&lt;/p&gt;

&lt;p&gt;This will start a self-hosted action runner which will listen for new commits and run actions configured for those commits. Both the &lt;code&gt;config.sh&lt;/code&gt; and &lt;code&gt;run.sh&lt;/code&gt; scripts call out to a &lt;em&gt;.Net core&lt;/em&gt; binary which does the real work. &lt;/p&gt;

&lt;p&gt;This is great, now you can run actions on your own infrastructure. If you work in a large corporation this is a huge deal, as generally build tools and other resources your builds may need are only accessible from internal networks. However, there are a few issues with how these self-hosted actions run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you have many repositories - let's say you have a private github organization - you now have at the very least a VM, kubernetes pod, or some resource which must be constantly running. If your organization has 100+ repos this can be quite costly resource-wise.&lt;/li&gt;
&lt;li&gt;It's not straightforward to add new actions runners for new repos that are created.&lt;/li&gt;
&lt;li&gt;It's also not straightforward to scale these actions if a repo gets a lot of commits daily.&lt;/li&gt;
&lt;li&gt;The scripts which are run in the setup instructions require some manual intervention (at least by default, more on that later) so automating it may be tricky.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question is, can we fix these issues? Let's start by abstracting the setup, and create a docker image to do so.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is currently automatable?
&lt;/h1&gt;

&lt;p&gt;Before we begin to create an image for the runner, let's go through the setup steps and see what currently needs manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial download
&lt;/h2&gt;

&lt;p&gt;The initial download is fully automatable, no manual intervention necessary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/tmp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-2.164.0.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should have a directory with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 73680
drwxr-xr-x  4 chaospie chaospie     4096 Dec 18 20:41 ./
drwxrwxrwt 12 root     root         4096 Feb  1 20:13 ../
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt;  1 chaospie chaospie 75400617 Feb  1 20:14 actions-runner-linux-x64-2.164.0.tar.gz
drwxr-xr-x  2 chaospie chaospie    16384 Dec 18 20:41 bin/
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;  1 chaospie chaospie     2671 Dec 18 20:40 config.sh&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;  1 chaospie chaospie      623 Dec 18 20:40 env.sh&lt;span class="k"&gt;*&lt;/span&gt;
drwxr-xr-x  4 chaospie chaospie     4096 Dec 18 20:41 externals/
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;  1 chaospie chaospie     1666 Dec 18 20:40 run.sh&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring the runner
&lt;/h2&gt;

&lt;p&gt;Let's configure our runner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;

Enter the name of runner: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;sky]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems configuration wants manual input. Let's see if anything is mentioned in the help for &lt;code&gt;config.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ./config.sh &lt;span class="nt"&gt;--help&lt;/span&gt;

Commands:,
 ./config.sh          Configures the runner
 ./config.sh remove   Unconfigures the runner
 ./run.sh             Runs the runner interactively. Does not require any options.

Options:
 &lt;span class="nt"&gt;--version&lt;/span&gt;  Prints the runner version
 &lt;span class="nt"&gt;--commit&lt;/span&gt;   Prints the runner commit
 &lt;span class="nt"&gt;--help&lt;/span&gt;     Prints the &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;each &lt;span class="nb"&gt;command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing. It doesn't look there is a parameter for setting the name of the action runner. We can cheat for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;my-runner | ./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;

Enter the name of runner: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;sky]
√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;

Enter name of work folder: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;_work]
√ Settings Saved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, a fully automated config setup! Note this also defaults the second manual input request, the work folder, to &lt;code&gt;_work&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the runner
&lt;/h2&gt;

&lt;p&gt;There are no params or manual input requests for &lt;code&gt;run.sh&lt;/code&gt;, so this is clearly automatable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick test in a docker container
&lt;/h2&gt;

&lt;p&gt;Let's do a quick test in a docker container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; ubuntu:18.04 bash

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; https://github.com/actions/runner/releases/download/v2.164.0/actions-runner-linux-x64-2.164.0.tar.gz
bash: curl: &lt;span class="nb"&gt;command &lt;/span&gt;not found

&lt;span class="c"&gt;# Ah! There is no curl in the default ubuntu 18.04 image, let's just install it for now.&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; curl
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease &lt;span class="o"&gt;[&lt;/span&gt;242 kB]
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease &lt;span class="o"&gt;[&lt;/span&gt;88.7 kB]
Get:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease &lt;span class="o"&gt;[&lt;/span&gt;88.7 kB]
...
&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;xvf actions-runner-linux-x64-2.164.0.tar.gz
./
./bin/
./bin/System.Security.Cryptography.OpenSsl.dll
./bin/System.Memory.dll
./bin/System.Runtime.Serialization.Json.dll
./bin/System.IO.Compression.FileSystem.dll
...

&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
Must not run with &lt;span class="nb"&gt;sudo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems we can't run &lt;code&gt;config.sh&lt;/code&gt; as root, or with sudo! This makes sense, generally you shouldn't run builds with an elevated user. It seems like there will be a bit of work to get this containerized.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dockerizing a runner
&lt;/h1&gt;

&lt;p&gt;Let's create a simple &lt;em&gt;Dockerfile&lt;/em&gt; to fix this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RUNNER_VERSION=2.164.0&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/actions/runner/releases/download/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions/actions-runner&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;$ docker build -t actions-image .
Sending build context to Docker daemon  70.66kB
Step 1/9 : FROM ubuntu
 ---&amp;gt; 775349758637
...

$ docker run -ti --rm actions-image

$ ./config.sh --url https://github.com/${OWNER}/${REPO} --token ${TOKEN}
Libicu's dependencies is missing for Dotnet Core 3.0
Execute ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new error! Seems we are missing dependencies. Conveniently the actions runner setup gives us a script to set everything up. Let's update our Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RUNNER_VERSION=2.164.0&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/actions/runner/releases/download/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions/actions-runner&lt;/span&gt;

&lt;span class="c"&gt;# Here we install the dependencies needed by the runner&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;/home/actions/actions-runner/bin/installdependencies.sh

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build and run again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; actions-image &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  70.66kB
Step 1/9 : FROM ubuntu
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 775349758637
...

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image

&lt;span class="nv"&gt;$ &lt;/span&gt;./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt;: cannot &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s1"&gt;'.env'&lt;/span&gt;: Permission denied
./env.sh: line 36: .path: Permission denied
Unhandled exception. System.UnauthorizedAccessException: Access to the path &lt;span class="s1"&gt;'/home/actions/actions-runner/_diag'&lt;/span&gt; is denied.
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; System.IO.IOException: Permission denied
   &lt;span class="nt"&gt;---&lt;/span&gt; End of inner exception stack trace &lt;span class="nt"&gt;---&lt;/span&gt;
   at System.IO.FileSystem.CreateDirectory&lt;span class="o"&gt;(&lt;/span&gt;String fullPath&lt;span class="o"&gt;)&lt;/span&gt;
   at System.IO.Directory.CreateDirectory&lt;span class="o"&gt;(&lt;/span&gt;String path&lt;span class="o"&gt;)&lt;/span&gt;
   at GitHub.Runner.Common.HostTraceListener..ctor&lt;span class="o"&gt;(&lt;/span&gt;String logFileDirectory, String logFilePrefix, Int32 pageSizeLimit, Int32 retentionDays&lt;span class="o"&gt;)&lt;/span&gt;
   at GitHub.Runner.Common.HostContext..ctor&lt;span class="o"&gt;(&lt;/span&gt;String hostType, String logFile&lt;span class="o"&gt;)&lt;/span&gt;
   at GitHub.Runner.Listener.Program.Main&lt;span class="o"&gt;(&lt;/span&gt;String[] args&lt;span class="o"&gt;)&lt;/span&gt;
./config.sh: line 79:    44 Aborted                 &lt;span class="o"&gt;(&lt;/span&gt;core dumped&lt;span class="o"&gt;)&lt;/span&gt; ./bin/Runner.Listener configure &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ah, more errors! This time permissions issues. If we check the permissions on the files in that directory we can see the owner is wrong. The owner of all these files should be the &lt;em&gt;actions&lt;/em&gt; user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt;
total 73676
drwxr-xr-x 4    1001     115     4096 Dec 18 20:41 ./
drwxr-xr-x 1 actions actions     4096 Feb  1 20:43 ../
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root    root    75400617 Dec 18 20:44 actions-runner-linux-x64-2.164.0.tar.gz
drwxr-xr-x 2    1001     115    16384 Dec 18 20:41 bin/
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1    1001     115     2671 Dec 18 20:40 config.sh&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1    1001     115      623 Dec 18 20:40 env.sh&lt;span class="k"&gt;*&lt;/span&gt;
drwxr-xr-x 4    1001     115     4096 Dec 18 20:41 externals/
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1    1001     115     1666 Dec 18 20:40 run.sh&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another Dockerfile update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RUNNER_VERSION=2.164.0&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/actions/runner/releases/download/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions/actions-runner&lt;/span&gt;

&lt;span class="c"&gt;# Here we change owner to user actions on the actions user's home directory&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; actions ~actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /home/actions/actions-runner/bin/installdependencies.sh

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally run again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;$ docker build -t actions-image .
Sending build context to Docker daemon  70.66kB
Step 1/9 : FROM ubuntu
 ---&amp;gt; 775349758637
...

$ docker run -ti --rm actions-image

$ ./config.sh --url https://github.com/${OWNER}/${REPO} --token ${TOKEN}

--------------------------------------------------------------------------------
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___(_) |_| | | |_   _| |__      / \   ___| |_(_) ___  _ __  ___      |
|      | |  _| | __| |_| | | | | '_ \    / _ \ / __| __| |/ _ \| '_ \/ __|     |
|      | |_| | | |_|  _  | |_| | |_) |  / ___ \ (__| |_| | (_) | | | \__ \     |
|       \____|_|\__|_| |_|\__,_|_.__/  /_/   \_\___|\__|_|\___/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
--------------------------------------------------------------------------------

# Authentication


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;

Enter the name of runner: [press Enter for 0ccb204b3990] my-runner

√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;

Enter name of work folder: [press Enter for _work]

√ Settings Saved.

$ ./run.sh

√ Connected to GitHub

2020-02-01 21:22:24Z: Listening for Jobs

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

&lt;/div&gt;



&lt;p&gt;Great! Now let's make it generic for any repository. To do that, let's create an &lt;code&gt;entrypoint.sh&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;
&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | ./config.sh &lt;span class="nt"&gt;--url&lt;/span&gt; https://github.com/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

./run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And update our Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RUNNER_VERSION=2.164.0&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; actions &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; wget

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;actions-runner &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;actions-runner &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/actions/runner/releases/download/v&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar &lt;/span&gt;xzf ./actions-runner-linux-x64-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RUNNER_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.tar.gz
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/actions/actions-runner&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; actions ~actions &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /home/actions/actions-runner/bin/installdependencies.sh

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; actions&lt;/span&gt;

&lt;span class="c"&gt;# Add the script and make it the entrypoint&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; entrypoint.sh .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's build and test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused &lt;span class="s2"&gt;"exec: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;./entrypoint.sh&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: permission denied"&lt;/span&gt;: unknown.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woops! I forgot to make the script executable. Make the script executable to fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x entrypoint.sh

&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; actions-image &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  70.66kB
Step 1/9 : FROM ubuntu
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 775349758637
...
Step 9/9 : ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"./entrypoint.sh"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using cache
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; a6535399773a
Successfully built a6535399773a
Successfully tagged actions-image:latest

&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; actions-image &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; my-runner

&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;
|        ____ _ _   _   _       _          _        _   _                      |
|       / ___&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; |_| | | |_   _| |__      / &lt;span class="se"&gt;\ &lt;/span&gt;  ___| |_&lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; ___  _ __  ___      |
|      | |  _| | __| |_| | | | | &lt;span class="s1"&gt;'_ \    / _ \ / __| __| |/ _ \| '&lt;/span&gt;_ &lt;span class="se"&gt;\/&lt;/span&gt; __|     |
|      | |_| | | |_|  _  | |_| | |_&lt;span class="o"&gt;)&lt;/span&gt; |  / ___ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;__| |_| | &lt;span class="o"&gt;(&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt; | | | &lt;span class="se"&gt;\_&lt;/span&gt;_ &lt;span class="se"&gt;\ &lt;/span&gt;    |
|       &lt;span class="se"&gt;\_&lt;/span&gt;___|_|&lt;span class="se"&gt;\_&lt;/span&gt;_|_| |_|&lt;span class="se"&gt;\_&lt;/span&gt;_,_|_.__/  /_/   &lt;span class="se"&gt;\_\_&lt;/span&gt;__|&lt;span class="se"&gt;\_&lt;/span&gt;_|_|&lt;span class="se"&gt;\_&lt;/span&gt;__/|_| |_|___/     |
|                                                                              |
|                       Self-hosted runner registration                        |
|                                                                              |
&lt;span class="nt"&gt;--------------------------------------------------------------------------------&lt;/span&gt;

&lt;span class="c"&gt;# Authentication&lt;/span&gt;


√ Connected to GitHub

&lt;span class="c"&gt;# Runner Registration&lt;/span&gt;

Enter the name of runner: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;a05fbb6da3db]
√ Runner successfully added
√ Runner connection is good

&lt;span class="c"&gt;# Runner settings&lt;/span&gt;

Enter name of work folder: &lt;span class="o"&gt;[&lt;/span&gt;press Enter &lt;span class="k"&gt;for &lt;/span&gt;_work]
√ Settings Saved.


√ Connected to GitHub

2020-02-01 21:28:51Z: Listening &lt;span class="k"&gt;for &lt;/span&gt;Jobs

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

&lt;/div&gt;



&lt;p&gt;Great, it all works as expected! There are a few problems however:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to manually run a container for each runner you wish to start.&lt;/li&gt;
&lt;li&gt;Each runner you start needs to be manually cleaned up.&lt;/li&gt;
&lt;li&gt;Any runner that is started can take any job for a new commit if it's idle, so cleaning up runners has the potential to accidentally kill builds that are in progress.&lt;/li&gt;
&lt;li&gt;The registration token is a pain to retrieve. Recently github released an API to &lt;a href="https://developer.github.com/v3/actions/self_hosted_runners/#create-a-registration-token"&gt;generate this token&lt;/a&gt;. In the next post we will automate the retrieval of this token.&lt;/li&gt;
&lt;li&gt;In the current state runners will not unregister, meaning you must do this from the UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code up to this point can be seen &lt;a href="https://github.com/wayofthepie/ephemeral-actions-blog/tree/dd7929733d9eca77a3791773e787d15c2ce99d81"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;In this post we have created a very simple docker image for automating self-hosted action runner registration. In the next post we will improve this by looking into the &lt;a href="https://github.com/actions/runner"&gt;actions runner source code&lt;/a&gt; which contains many hidden parameters that will allow us to automate a lot more than we have so far.&lt;/p&gt;

</description>
      <category>github</category>
      <category>docker</category>
      <category>bash</category>
    </item>
    <item>
      <title>JVM Internals: Memory Overview</title>
      <dc:creator>wayofthepie</dc:creator>
      <pubDate>Wed, 22 Jan 2020 03:30:47 +0000</pubDate>
      <link>https://forem.com/wayofthepie/jvm-basic-memory-overview-535m</link>
      <guid>https://forem.com/wayofthepie/jvm-basic-memory-overview-535m</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;This was originally posted on my own blog a few years back, but I'd rather continue the series here, so, re-posting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is this post about?
&lt;/h2&gt;

&lt;p&gt;The goal of this post is to give an overview of the heap and non-heap memory regions of the JVM - with some small introduction to both - and also to show what happens in the event of a heap/non-heap memory issue within a &lt;code&gt;docker&lt;/code&gt; container. I assume some&lt;br&gt;
basic knowledge of Java, the JVM, docker and linux. You will need docker and openjdk 8 installed on a linux system (I used ubuntu 16.04 to write this post). &lt;/p&gt;
&lt;h1&gt;
  
  
  Containerizing a java app
&lt;/h1&gt;

&lt;p&gt;To start I'm going to keep things super simple. Let's build a program which prints "Hello world!" and waits forever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HelloWorld.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;read&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, a simple Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:8-jdk&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.java .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;javac HelloWorld.java
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; java HelloWorld&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that we can build and launch our application in a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;--tag&lt;/span&gt; jvm-test &lt;span class="nb"&gt;.&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; hello-jvm jvm-test
Hello world!

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

&lt;/div&gt;



&lt;p&gt;You can use CTRL-C to kill the container when you are done. Right, now we have a simple program running, what can we do? Let's analyze the JVM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic JVM analysis
&lt;/h2&gt;

&lt;p&gt;Lets get a list what objects we have on the heap within our application. First, get into the container (assuming it's still running from above) and get the JVM processes PID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; hello-jvm bash
root@5f20ae043968:/ &lt;span class="nv"&gt;$ &lt;/span&gt;ps aux|grep &lt;span class="o"&gt;[&lt;/span&gt;j]ava
root         1  0.1  0.0   4292   708 pts/0    Ss+  12:27   0:00 /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; java HelloWorld
root         7  0.2  0.1 6877428 23756 pts/0   Sl+  12:27   0:00 java HelloWorld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the above, we see the PID is 7. For analysis, the openjdk comes with a number of tools. &lt;code&gt;jmap&lt;/code&gt; is one such tool which allows us to view heap information about a JVM process. To get a list of objects, their number of instances and the space they take up in the heap you can use &lt;code&gt;jmap -histo &amp;lt;JVM_PID&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@5f20ae043968:/ &lt;span class="nv"&gt;$ &lt;/span&gt;jmap &lt;span class="nt"&gt;-histo&lt;/span&gt; 7

 num     &lt;span class="c"&gt;#instances         #bytes  class name&lt;/span&gt;
&lt;span class="nt"&gt;----------------------------------------------&lt;/span&gt;
   1:           422        2256744  &lt;span class="o"&gt;[&lt;/span&gt;I
   2:          1600         141520  &lt;span class="o"&gt;[&lt;/span&gt;C
   3:           364          58560  &lt;span class="o"&gt;[&lt;/span&gt;B
   4:           470          53544  java.lang.Class
   5:          1204          28896  java.lang.String
   6:           551          28152  &lt;span class="o"&gt;[&lt;/span&gt;Ljava.lang.Object&lt;span class="p"&gt;;&lt;/span&gt;
   7:           110           7920  java.lang.reflect.Field
   8:           258           4128  java.lang.Integer
   9:            97           3880  java.lang.ref.SoftReference
  10:           111           3552  java.util.Hashtable&lt;span class="nv"&gt;$Entry&lt;/span&gt;
  11:           133           3192  java.lang.StringBuilder
  12:             8           3008  java.lang.Thread
  13:            75           2400  java.io.File
  14:            54           2080  &lt;span class="o"&gt;[&lt;/span&gt;Ljava.lang.String&lt;span class="p"&gt;;&lt;/span&gt;
  15:            38           1824  sun.util.locale.LocaleObjectCache&lt;span class="nv"&gt;$CacheEntry&lt;/span&gt;
  16:            12           1760  &lt;span class="o"&gt;[&lt;/span&gt;Ljava.util.Hashtable&lt;span class="nv"&gt;$Entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  17:            55           1760  java.util.concurrent.ConcurrentHashMap&lt;span class="nv"&gt;$Node&lt;/span&gt;
  18:            27           1728  java.net.URL
  19:            20           1600  &lt;span class="o"&gt;[&lt;/span&gt;S
  ...
  222:             1             16  sun.reflect.ReflectionFactory
Total          6583        2642792
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see above there are 6583 instances of a mixture of 222 different classes, taking up over 2.6MB of the heap, for our simple HelloWorld program! When I first saw this it raised a lot of questions - what is &lt;code&gt;[I&lt;/code&gt;, why is there a &lt;code&gt;java.lang.String&lt;/code&gt; and a &lt;code&gt;[Ljava.lang.String&lt;/code&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  What are all these classes?
&lt;/h2&gt;

&lt;p&gt;The single letter class names you see above are all documented under &lt;a href="https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName()" rel="noopener noreferrer"&gt;Class.getName()&lt;/a&gt;.&lt;/p&gt;




&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Encoding&lt;/th&gt;
&lt;th&gt;Element Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Z&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;byte&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;char&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L*className*&lt;/td&gt;
&lt;td&gt;class/interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;double&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;float&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;I&lt;/td&gt;
&lt;td&gt;int&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;J&lt;/td&gt;
&lt;td&gt;long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S&lt;/td&gt;
&lt;td&gt;short&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you look back to the &lt;code&gt;jmap&lt;/code&gt; output, the first few instances all have &lt;code&gt;[&lt;/code&gt; prefixing them - e.g. &lt;code&gt;[I&lt;/code&gt;. &lt;code&gt;[&lt;/code&gt; denotes a 1 dimensional array of the type proceeding it - &lt;code&gt;[I&lt;/code&gt; denotes an array of &lt;code&gt;int&lt;/code&gt; e.g. &lt;code&gt;new int[3]&lt;/code&gt;. &lt;code&gt;[[I&lt;/code&gt; denotes a 2D array, &lt;code&gt;new int[2][3]&lt;/code&gt; and so on. Also in the &lt;code&gt;jmap&lt;/code&gt; output above were instances of &lt;code&gt;[L.java.lang.String&lt;/code&gt; which is just an array of String's - &lt;code&gt;new String[3]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see this for yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// InstanceName.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstanceName&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;is&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
      &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

      &lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;[][][]&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
      &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

      &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
      &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; 
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiling and running this we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;javac InstanceName.java 
&lt;span class="nv"&gt;$ &lt;/span&gt;java InstanceName 
&lt;span class="o"&gt;[&lt;/span&gt;I
&lt;span class="o"&gt;[[[&lt;/span&gt;Z
&lt;span class="o"&gt;[&lt;/span&gt;Ljava.lang.String&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a quick overview of one way to look at what's loaded on the heap. I mentioned other memory regions in the JVM earlier, what are these?&lt;/p&gt;

&lt;h1&gt;
  
  
  Heap and Non-Heap memory
&lt;/h1&gt;

&lt;p&gt;The JVM can be divided into many different memory segments (segments/regions/areas, I'll use these words interchangeably, but generally they mean the same thing), &lt;br&gt;
if we take a high level view first we have two segments - memory used for objects on the heap and non-heap memory. &lt;/p&gt;

&lt;p&gt;If we zoom in, the heap has different areas within which we can talk about, depending on what we want to discuss - there is the Eden space, where most new objects are initially created, the Survivor space, where objects go if they survive an Eden space garbage collection (GC) and the Old Generation which contains objects that have lived in Survivor space for a while. Specifically, it contains objects that have been initialized - e.g. &lt;code&gt;List&amp;lt;String&amp;gt; s = new ArrayList&amp;lt;String&amp;gt;();&lt;/code&gt; will create an &lt;code&gt;ArrayList&lt;/code&gt; object on the heap, and &lt;code&gt;s&lt;/code&gt; will point to this.&lt;/p&gt;

&lt;p&gt;In the previous section I ran through what objects are loaded into the heap for our HelloWorld program, so what about non-heap memory?&lt;/p&gt;
&lt;h2&gt;
  
  
  Non-Heap Memory
&lt;/h2&gt;

&lt;p&gt;If you have ever written a non-trivial java application with jdk8 you have probably heard of &lt;em&gt;Metaspace&lt;/em&gt;. This is an example of non-heap memory. It's where the JVM will store class definitions, static variables, methods, classloaders and other metadata. But there are many other non-heap memory regions the JVM will use. Let's list them! &lt;/p&gt;

&lt;p&gt;To do so, first we need to enable native memory tracking in our java app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:8-jdk&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.java .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;HelloWorld.java
&lt;span class="k"&gt;RUN &lt;/span&gt;javac HelloWorld.java
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; java -XX:NativeMemoryTracking=detail HelloWorld&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now build and re-run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;--tag&lt;/span&gt; jvm-test &lt;span class="nb"&gt;.&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; hello-jvm jvm-test
Hello world!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal, exec into the container and get a summary of overall memory usage with &lt;code&gt;jcmd&lt;/code&gt;'s &lt;code&gt;VM.native_memory&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; hello-jvm bash
root@aa5ae77e1305:/ &lt;span class="nv"&gt;$ &lt;/span&gt;jcmd 
33 sun.tools.jcmd.JCmd
7 HelloWorld

root@aa5ae77e1305:/ &lt;span class="nv"&gt;$ &lt;/span&gt;jcmd 7 VM.native_memory summary
7:

Native Memory Tracking:

Total: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5576143KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1117747KB
-                 Java Heap &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4069376KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;920064KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;mmap: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4069376KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;920064KB&lt;span class="o"&gt;)&lt;/span&gt; 

-                     Class &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1066121KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14217KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;classes &lt;span class="c"&gt;#405)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9353KB &lt;span class="c"&gt;#178) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;mmap: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1056768KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4864KB&lt;span class="o"&gt;)&lt;/span&gt; 

-                    Thread &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20646KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20646KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;thread &lt;span class="c"&gt;#21)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;stack: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20560KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20560KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;62KB &lt;span class="c"&gt;#110) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;arena&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;23KB &lt;span class="c"&gt;#40)&lt;/span&gt;

-                      Code &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249655KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2591KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;55KB &lt;span class="c"&gt;#346) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;mmap: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;249600KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2536KB&lt;span class="o"&gt;)&lt;/span&gt; 

-                        GC &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;159063KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;148947KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10383KB &lt;span class="c"&gt;#129) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;mmap: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;148680KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;138564KB&lt;span class="o"&gt;)&lt;/span&gt; 

-                  Compiler &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;134KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;134KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3KB &lt;span class="c"&gt;#37) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;arena&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;131KB &lt;span class="c"&gt;#3)&lt;/span&gt;

-                  Internal &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9455KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9455KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9423KB &lt;span class="c"&gt;#1417) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;mmap: &lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;32KB&lt;span class="o"&gt;)&lt;/span&gt; 

-                    Symbol &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1358KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1358KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;902KB &lt;span class="c"&gt;#85) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;arena&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;456KB &lt;span class="c"&gt;#1)&lt;/span&gt;

-    Native Memory Tracking &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;161KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;161KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;99KB &lt;span class="c"&gt;#1559) &lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;tracking &lt;span class="nv"&gt;overhead&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;61KB&lt;span class="o"&gt;)&lt;/span&gt;

-               Arena Chunk &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;reserved&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;175KB, &lt;span class="nv"&gt;committed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;175KB&lt;span class="o"&gt;)&lt;/span&gt;
                            &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;malloc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;175KB&lt;span class="o"&gt;)&lt;/span&gt; 


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

&lt;/div&gt;



&lt;p&gt;A lot more regions than just the heap! Our hello world program just got even more complex...&lt;/p&gt;

&lt;p&gt;What does all this mean? &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java Heap&lt;/strong&gt; : heap memory. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Class&lt;/strong&gt; : is the &lt;em&gt;Metaspace&lt;/em&gt; region we previously spoke about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread&lt;/strong&gt; : is the space taken up by threads on this JVM's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code&lt;/strong&gt; : is the code cache - this is used by the JIT to cache compiled code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GC&lt;/strong&gt; : space used by the garbage collector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compiler&lt;/strong&gt; : space used by the JIT when generating code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbols&lt;/strong&gt; : this is for symbols, by which I believe field names, method signatures fall under. &lt;sup id="fnref2"&gt;2&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native Memory Tracking&lt;/strong&gt; : memory used by the native memory tracker itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arena Chunk&lt;/strong&gt; : not entirely sure what this gets used for. &lt;sup id="fnref3"&gt;3&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical memory issues
&lt;/h2&gt;

&lt;p&gt;Ok, so why should you care about any of the above? Let's create an app that eats a tonne of memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MemEater.java&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Vector&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemEater&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1048576&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// allocate 1 MiB&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;Vector&lt;/code&gt; which contains 400 byte arrays of size 1 MiB &lt;sup id="fnref4"&gt;4&lt;/sup&gt;, so this will use ~400MiB memory on the heap. It will then sleep for 10 seconds so we can get the memory usage easily while it runs. Let's constrain the heap to 450MiB and run this locally we can see the actual memory usage of the process. RSS Resident Set Size &lt;sup id="fnref5"&gt;5&lt;/sup&gt; is how this is measured, note that this value also contains pages mapped from &lt;em&gt;shared memory&lt;/em&gt;, but we can gloss over that for this post.&lt;/p&gt;

&lt;p&gt;So, lets compile our app, run in the background and get its RSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;javac MemEater.java 
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;java &lt;span class="nt"&gt;-Xms450M&lt;/span&gt; &lt;span class="nt"&gt;-Xmx450M&lt;/span&gt; MemEater &amp;amp; 
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'NR==1; /[M]emEater/'&lt;/span&gt; 
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
chaospie 18019 10.5  3.0 3138368 494448 pts/19 Sl   16:06   0:00 java &lt;span class="nt"&gt;-Xms450M&lt;/span&gt; &lt;span class="nt"&gt;-Xmx450M&lt;/span&gt; MemEater
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In total, the JVM process needs about 500 MiB to run (RSS is 494448 KiB). What happens if we set the heap to a size lower than it needs?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;java &lt;span class="nt"&gt;-Xms400M&lt;/span&gt; &lt;span class="nt"&gt;-Xmx400M&lt;/span&gt; MemEater
Exception &lt;span class="k"&gt;in &lt;/span&gt;thread &lt;span class="s2"&gt;"main"&lt;/span&gt; java.lang.OutOfMemoryError: Java heap space
    at MemEater.main&lt;span class="o"&gt;(&lt;/span&gt;MemEater.java:7&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have used java (or any JVM language) before, you have more than likely come across this. It means that the JVM ran out of heap space to allocate objects. There are quite a few other types of &lt;code&gt;OutOfMemoryError&lt;/code&gt; the JVM can throw in certain situations &lt;sup id="fnref6"&gt;6&lt;/sup&gt;, but I won't go into more detail right now.&lt;/p&gt;

&lt;p&gt;Now we know what happens if the JVM does not have enough heap space, what about the case where you are running in a container and hit the overall memory limit for that container?&lt;/p&gt;

&lt;p&gt;The simplest way to reproduce this is to package up our &lt;code&gt;MemEater&lt;/code&gt; program into a docker image and run it with less memory than it needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:8-jdk&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; MemEater.java .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;MemEater.java
&lt;span class="k"&gt;RUN &lt;/span&gt;javac MemEater.java
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; java -Xms450M -Xmx450M MemEater&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we need to build the image. However this time when we are running we limit the memory the container is allowed to use to 5M:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;--tag&lt;/span&gt; jvm-test &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--memory&lt;/span&gt; 5M &lt;span class="nt"&gt;--memory-swappiness&lt;/span&gt; 0 &lt;span class="nt"&gt;--name&lt;/span&gt; memeater jvm-test
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
Killed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few seconds you should see the output above, &lt;code&gt;Killed&lt;/code&gt;. What happened? Before we dive into that, lets have a look at the &lt;code&gt;--memory&lt;/code&gt; and &lt;code&gt;--memory-swappiness&lt;/code&gt; flags used by &lt;code&gt;docker&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limiting memory with docker
&lt;/h3&gt;

&lt;p&gt;Lets digress for a second, and look at the two docker flags I used above for controlling memory settings &lt;sup id="fnref7"&gt;7&lt;/sup&gt;. First, for these flags to work, your kernel will need to have cgroup support enabled and the following boot parameters set (assuming &lt;code&gt;grub&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/default/grub
...
&lt;span class="nv"&gt;GRUB_CMDLINE_LINUX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgroup_enable=memory swapaccount=1"&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--memory&lt;/code&gt; sets an upper bound on the sum of all processes memory usage within a container, the smallest this can go is 4MiB, above we set it to 5m which is 5MiB. When this is set, the containers &lt;code&gt;cgroup&lt;/code&gt; &lt;code&gt;memory.limit_in_bytes&lt;/code&gt; is set to the value. I can't find the code that does this in &lt;code&gt;docker&lt;/code&gt;, however we can see it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--memory&lt;/span&gt; 500M &lt;span class="nt"&gt;--memory-swappiness&lt;/span&gt; 0 &lt;span class="nt"&gt;--name&lt;/span&gt; memeater jvm-test 
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
812dbc3417eacdaf221c2f0c93ceab41f7626dca17f959298a5700358f931897
&lt;span class="nv"&gt;$ CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;docker ps &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{if (NR!=1) print $1}'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_ID&lt;/span&gt;
812dbc3417eacdaf221c2f0c93ceab41f7626dca17f959298a5700358f931897
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory/docker/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/memory.swappiness 
0
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory/docker/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/memory.limit_in_bytes
524288000

&lt;span class="c"&gt;# Again, this time without limits to see the difference&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; memeater jvm-test 
d3e25423814ee1d79759aa87a83d416d63bdb316a305e390c2b8b98777484822
&lt;span class="nv"&gt;$ CONTAINER_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;docker ps &lt;span class="nt"&gt;--no-trunc&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{if (NR!=1) print $1}'&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_ID&lt;/span&gt;
d3e25423814ee1d79759aa87a83d416d63bdb316a305e390c2b8b98777484822
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory/docker/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/memory.swappiness 
60
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/fs/cgroup/memory/docker/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/memory.limit_in_bytes
9223372036854771712
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;WARNING&lt;/code&gt;, I'm not entirely sure why this appears as swap support is enabled, and seems to work. You can ignore this for now.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--memory-swappiness&lt;/code&gt; sets the &lt;em&gt;swappiness&lt;/em&gt; level of the cgroup herarchy the container runs in. This maps directly to the cgroup setting &lt;strong&gt;memory.swappiness&lt;/strong&gt; (at least in version 17.12 of &lt;em&gt;docker&lt;/em&gt; &lt;sup id="fnref8"&gt;8&lt;/sup&gt; ) as seen above. Setting this to 0 disables swap for the container. &lt;/p&gt;

&lt;h2&gt;
  
  
  What kills the container?
&lt;/h2&gt;

&lt;p&gt;So, why was the container killed? Lets run it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;$ docker run -ti --rm --memory 5M --memory-swappiness 0 --name memeater jvm-test
WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
Killed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see the cause of this kill, run &lt;code&gt;journalctl -k&lt;/code&gt; and search for &lt;code&gt;oom-killer&lt;/code&gt;, you should see logs like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-k&lt;/span&gt;
...
Feb 18 17:34:47  kernel: java invoked oom-killer: &lt;span class="nv"&gt;gfp_mask&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x14000c0&lt;span class="o"&gt;(&lt;/span&gt;GFP_KERNEL&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;nodemask&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;null&lt;span class="o"&gt;)&lt;/span&gt;,  &lt;span class="nv"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0, &lt;span class="nv"&gt;oom_score_adj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
Feb 18 17:34:47  kernel: java &lt;span class="nv"&gt;cpuset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;35f18c48d432510c76e76f2e7a962e64a1372de1dc4abd830417263907bea6e0 &lt;span class="nv"&gt;mems_allowed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
Feb 18 17:34:47  kernel: CPU: 0 PID: 16432 Comm: java Tainted: G           OE   4.13.0-32-generic &lt;span class="c"&gt;#35~16.04.1-Ubuntu&lt;/span&gt;
Feb 18 17:34:47  kernel: Hardware name: Dell Inc. Precision 5520/0R6JFH, BIOS 1.3.3 05/08/2017
Feb 18 17:34:47  kernel: Call Trace:
Feb 18 17:34:47  kernel:  dump_stack+0x63/0x8b
Feb 18 17:34:47  kernel:  dump_header+0x97/0x225
Feb 18 17:34:47  kernel:  ? mem_cgroup_scan_tasks+0xc4/0xf0
Feb 18 17:34:47  kernel:  oom_kill_process+0x219/0x420
Feb 18 17:34:47  kernel:  out_of_memory+0x11d/0x4b0
Feb 18 17:34:47  kernel:  mem_cgroup_out_of_memory+0x4b/0x80
Feb 18 17:34:47  kernel:  mem_cgroup_oom_synchronize+0x325/0x340
Feb 18 17:34:47  kernel:  ? get_mem_cgroup_from_mm+0xa0/0xa0
Feb 18 17:34:47  kernel:  pagefault_out_of_memory+0x36/0x7b
Feb 18 17:34:47  kernel:  mm_fault_error+0x8f/0x190
Feb 18 17:34:47  kernel:  ? handle_mm_fault+0xcc/0x1c0
Feb 18 17:34:47  kernel:  __do_page_fault+0x4c3/0x4f0
Feb 18 17:34:47  kernel:  do_page_fault+0x22/0x30
Feb 18 17:34:47  kernel:  ? page_fault+0x36/0x60
Feb 18 17:34:47  kernel:  page_fault+0x4c/0x60
Feb 18 17:34:47  kernel: RIP: 0033:0x7fdeafb0fe2f
Feb 18 17:34:47  kernel: RSP: 002b:00007fdeb0e1db80 EFLAGS: 00010206
Feb 18 17:34:47  kernel: RAX: 000000000001dff0 RBX: 00007fdea802d490 RCX: 00007fdeac17b010
Feb 18 17:34:47  kernel: RDX: 0000000000003bff RSI: 0000000000075368 RDI: 00007fdeac17b010
Feb 18 17:34:47  kernel: RBP: 00007fdeb0e1dc20 R08: 0000000000000000 R09: 0000000000000000
Feb 18 17:34:47  kernel: R10: 0000000000000022 R11: 0000000000000246 R12: 0000000000000000
Feb 18 17:34:47  kernel: R13: 00007fdeb0e1db90 R14: 00007fdeafff851b R15: 0000000000075368
Feb 18 17:34:47  kernel: Task &lt;span class="k"&gt;in&lt;/span&gt; /docker/35f18c48d432510c76e76f2e7a962e64a1372de1dc4abd830417263907bea6e0 killed as a result of limit of /docker/35f18c48d432510c76e76f2e7a962e64a137
Feb 18 17:34:47  kernel: memory: usage 5120kB, limit 5120kB, failcnt 69
Feb 18 17:34:47  kernel: memory+swap: usage 0kB, limit 9007199254740988kB, failcnt 0
Feb 18 17:34:47  kernel: kmem: usage 1560kB, limit 9007199254740988kB, failcnt 0
Feb 18 17:34:47  kernel: Memory cgroup stats &lt;span class="k"&gt;for&lt;/span&gt; /docker/35f18c48d432510c76e76f2e7a962e64a1372de1dc4abd830417263907bea6e0: cache:176KB rss:3384KB rss_huge:0KB shmem:144KB mapped_fil
Feb 18 17:34:47  kernel: &lt;span class="o"&gt;[&lt;/span&gt; pid &lt;span class="o"&gt;]&lt;/span&gt;   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
Feb 18 17:34:47  kernel: &lt;span class="o"&gt;[&lt;/span&gt;16360]     0 16360     1073      178       8       3        0             0 sh
Feb 18 17:34:47  kernel: &lt;span class="o"&gt;[&lt;/span&gt;16426]     0 16426   609544     3160      47       4        0             0 java
Feb 18 17:34:47  kernel: Memory cgroup out of memory: Kill process 16426 &lt;span class="o"&gt;(&lt;/span&gt;java&lt;span class="o"&gt;)&lt;/span&gt; score 2508 or sacrifice child
Feb 18 17:34:47  kernel: Killed process 16426 &lt;span class="o"&gt;(&lt;/span&gt;java&lt;span class="o"&gt;)&lt;/span&gt; total-vm:2438176kB, anon-rss:3200kB, file-rss:9440kB, shmem-rss:0kB
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The kernels OOM killer killed the application because it violated it's &lt;code&gt;cgroup&lt;/code&gt; memory limit. From the logs above: &lt;code&gt;memory: usage 5120kB, limit 5120kB, failcnt 69&lt;/code&gt; shows it hit the limit, &lt;code&gt;Killed process 16426 (java) total-vm:2438176kB, anon-rss:3200kB, file-rss:9440kB, shmem-rss:0kB&lt;/code&gt; shows that it decided to kill process &lt;strong&gt;16426&lt;/strong&gt; which was our java process. There is a lot more information in the logs which can help identify the reason why the OOM killer killed your process, however in our case we know why - we violated the container memory limit. &lt;/p&gt;

&lt;p&gt;With a heap issue, if we hit an out of memory error with &lt;code&gt;Java Heap Space&lt;/code&gt; as the cause, we know immediately that the cause is the heap and we are either allocating too much, or we need to increase the heap (actually identifying the underlying cause of this overallocation in the code is another issue...). When the OOM killer kills our process, it's not so straightforward - it could be direct buffers, unconstrained non-heap memory areas (&lt;em&gt;Metaspace&lt;/em&gt;, Code cache etc...) or even another process within the container. There is quite a bit to cover when investigating. On that note, I'll finish this post.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;There is quite a lot more that could be said about heap/non-heap memory in the JVM, docker and the oom-killer - but I want to keep this initial post short, it's just meant to be a basic intro to JVM memory usage. Hopefully, if you took anything away from this post, it's that there is much more to think about than just the heap when using the JVM, especially in memory bound containers.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;See &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr022.html" rel="noopener noreferrer"&gt;NMT details&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;This one I need to look up more in-depth, as I have not been able to find solid information on it. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Arena Chunk seems to be related to malloc arenas, will definitely look into this in-depth. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;1 MiB = 1024 KiB = 1048576 bytes. Why use MiB? Because MB is ambiguous and can mean 1000 KB or 1024 KB, whereas MiB is always 1024 KiB. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;See &lt;a href="https://stackoverflow.com/questions/7880784/what-is-rss-and-vsz-in-linux-memory-management#answer-21049737" rel="noopener noreferrer"&gt;this great answer&lt;/a&gt; for a description of RSS. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;A detailed description of them can be found &lt;a href="https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/memleaks002.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;The &lt;code&gt;docker&lt;/code&gt; documentation on this subject is excellent - see &lt;a href="https://docs.docker.com/config/containers/resource_constraints/" rel="noopener noreferrer"&gt;resource constraints&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;See &lt;a href="https://github.com/docker/docker-ce/blob/17.12/components/engine/pkg/sysinfo/sysinfo_linux.go#L90" rel="noopener noreferrer"&gt;docker memory swappiness&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>jvm</category>
      <category>java</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
