<?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: C. Dylan Shearer</title>
    <description>The latest articles on Forem by C. Dylan Shearer (@dshearer).</description>
    <link>https://forem.com/dshearer</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%2F53672%2F46881601-0141-46f2-91b9-cc407501f3dc.png</url>
      <title>Forem: C. Dylan Shearer</title>
      <link>https://forem.com/dshearer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dshearer"/>
    <language>en</language>
    <item>
      <title>RESTful Security: Plug the Leaks!</title>
      <dc:creator>C. Dylan Shearer</dc:creator>
      <pubDate>Sat, 07 Apr 2018 21:03:02 +0000</pubDate>
      <link>https://forem.com/dshearer/restful-security-plug-the-leaks-npa</link>
      <guid>https://forem.com/dshearer/restful-security-plug-the-leaks-npa</guid>
      <description>&lt;p&gt;TL;DR: Don't leak information through HTTP error codes.&lt;/p&gt;

&lt;p&gt;Here is a possible vulnerability that is probably not very serious but easy to overlook.  Suppose we get hired to redo a local bank's website.  Being the hip coders that we are, we decide to make a nice RESTful API.&lt;/p&gt;

&lt;p&gt;One of our API's operations is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/accounts/{acctId}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this operation succeeds only if an account with ID &lt;code&gt;acctID&lt;/code&gt; exists and the request contains valid credentials for the customer that owns that account.  If one of these conditions does not hold, we return an appropriate HTTP error code.&lt;/p&gt;

&lt;p&gt;What should that error code be?  We know that RESTful APIs return the most appropriate status code for the situation.  So, it seems to make sense to return 404 when the account doesn't exist, and 401 when it does but the user is unauthenticated or isn't its owner.  &lt;/p&gt;

&lt;p&gt;But this may well be a bad idea, because an unauthenticated user can now do something 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;$ for i in `seq 300 305`; do curl -sI https://exammple.com/api/accounts/${i} | head -n 1; done
HTTP/1.1 404 Not Found
HTTP/1.1 401 Unauthorized
HTTP/1.1 401 Unauthorized
HTTP/1.1 404 Not Found
HTTP/1.1 401 Unauthorized
HTTP/1.1 404 Not Found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This unauthenticated user has now learned something: that there are no accounts with IDs 300, 303, and 305, but there are with IDs 301, 302, and 304.  And of course running this command with a wider range of IDs would reveal even more information.&lt;/p&gt;

&lt;p&gt;How bad is this?  That depends on a lot of other things; at this point, the most we can say is that it is not obviously terrible.  But still, this could be used to learn other things --- for example, the rate at which new accounts are created, which might be interesting to a competitor.&lt;/p&gt;

&lt;p&gt;The important point is that this API leaks information to users that have no legitimate reason to have it.  And it is easy to fix: we should just return 404 even if the account does in fact exist.&lt;/p&gt;

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

&lt;p&gt;While we focus on the obvious security concerns --- keeping account balances secret, preventing unauthorized transactions --- it is easy to overlook how the behavior of our system leaks sensitive information.&lt;/p&gt;

&lt;p&gt;An attacker's goal is to gain information about the state of our system by observing its behavior.  So our job is to make sure that the system behaves (from the attacker's point of view) identically regardless of its state.  And remember: unauthenticated users are not the only potential attackers.  Even logged-in customers should get 404 in response to requests for accounts that they don't own.&lt;/p&gt;

</description>
      <category>rest</category>
      <category>api</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Support Multiple OSes with One Mac</title>
      <dc:creator>C. Dylan Shearer</dc:creator>
      <pubDate>Tue, 16 Jan 2018 03:27:51 +0000</pubDate>
      <link>https://forem.com/dshearer/how-to-support-multiple-oses-with-one-mac-4145</link>
      <guid>https://forem.com/dshearer/how-to-support-multiple-oses-with-one-mac-4145</guid>
      <description>&lt;p&gt;Making operating-system specific packages of your project can be a great service to your users.  But it's also a pain.  Linux distributions differ in which package-management system they use, and you need to take the time to learn how to do it correctly.  In the end, the scripts you write to make these packages are really just more pieces of your project, and they should be covered by automated tests along with the rest of it.&lt;/p&gt;

&lt;p&gt;In an enterprise setting (or an open-source project with funding), we would have a Jenkins server for each OS that we support, and building and testing packages would be part of our Continuous Integration routine.  Unfortunately, this is not an option for most open-source projects.&lt;/p&gt;

&lt;p&gt;In this article, I present a scheme (really, a bunch of Make files and a certain directory structure) that can test these packaging scripts for an arbitrary number of different (Unix) OSes on your own dev box.  I came up with it while working on making packages for my project &lt;a href="https://dshearer.github.io/jobber/"&gt;Jobber&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An important benefit of this scheme is that it can easily incorporate any automated system tests you may have, making it very easy for you to ensure that your program works on all the OSes you claim to support.&lt;/p&gt;

&lt;p&gt;I have made a toy project that uses this system: &lt;a href="https://github.com/dshearer/polly"&gt;polly&lt;/a&gt;, named after a cat I had who was happy to pack up and travel with me regularly.  I'll use it as an example, showing how this scheme can be used to add support for CentOS 7 and Debian 9.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging: Out of Scope
&lt;/h2&gt;

&lt;p&gt;This article will not go into the details of how to make packages for different OSes.  However, the toy project does provide a good starting point if you need to make RPMs or Debian packages for non-daemon programs.  If you'd like an example of how to do it for a daemon, take a look at &lt;a href="https://github.com/dshearer/jobber/tree/master/packaging"&gt;Jobber&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prereqs
&lt;/h2&gt;

&lt;p&gt;Here are the tools you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/software/make/"&gt;GNU Make&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.vagrantup.com/"&gt;Vagrant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.virtualbox.org/wiki/Downloads"&gt;VirtualBox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VirtualBox is open-source virtualization software.&lt;/p&gt;

&lt;p&gt;Vagrant is the key to this scheme.  It is a tool that makes it easy to automate creation, booting, shutdown, etc. of VMs.  It's like Docker for VMs.&lt;/p&gt;

&lt;p&gt;The toy project is written in Go, but you don't need the Go compiler on your system, as we'll do all the compilation on VMs.&lt;/p&gt;

&lt;h1&gt;
  
  
  What It Does
&lt;/h1&gt;

&lt;p&gt;To get started, please clone polly and then check out tag &lt;code&gt;initial&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;git clone https://github.com/dshearer/polly.git
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;polly
&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout initial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wonderful Go project initially 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;|- src/github.com/dshearer/polly
    |- main.go
    |- meow.go
    |- meow_test.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have Go installed, you can play around with 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;go &lt;span class="nb"&gt;test
&lt;/span&gt;PASS
ok      .../polly   0.006s
&lt;span class="nv"&gt;$ &lt;/span&gt;go build
&lt;span class="nv"&gt;$ &lt;/span&gt;./polly
meow! meow! meow! meow! meow!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now take a look at how our scheme adds support for CentOS 7 and Debian 9.  Please check out the tip of master (&lt;code&gt;git checkout master&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The project now 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;|- src/github.com/dshearer/polly
    |- Makefile
    |- main.go
    |- meow.go
    |- meow_test.go
    |- packaging/
        |- Makefile
        |- centos_7/
            |- Makefile
            |- Vagrantfile
            |- polly.spec
            |- sources.mk
        |- debian_9/
            |- Makefile
            |- Vagrantfile
            |- debian-pkg/
                ...
            |- sources.mk
        |- head.mk
        |- sources.mk
        |- tail.mk
    |- system_test/
        |- meow_test.sh
    |- sources.mk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, there's a lot of new crap, but making Linux packages isn't exactly simple.  Most of the  new files are in the directory &lt;code&gt;packaging&lt;/code&gt;, in which we have one subdirectory for each of the OSes we wish to support --- &lt;code&gt;centos_7&lt;/code&gt; and &lt;code&gt;debian_9&lt;/code&gt;.  &lt;code&gt;packaging/centos_7/polly.spec&lt;/code&gt; is our RPM spec file that we'll use to make the CentOS 7 package, and &lt;code&gt;packaging/debian_9/debian-pkg&lt;/code&gt; contains all the standard files needed for making a Debian package.&lt;/p&gt;

&lt;p&gt;We also have a new file at &lt;code&gt;system_test/meow_test.sh&lt;/code&gt;.  This script contains any system tests that should be done on the program after it is installed.&lt;/p&gt;

&lt;p&gt;So what does this give us?  If you have installed Vagrant and VirtualBox, try this:&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;make &lt;span class="nt"&gt;-C&lt;/span&gt; packaging &lt;span class="nt"&gt;-j&lt;/span&gt; test-vm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The "-j" option causes this to be done for each OS in parallel.  Occasionally, I have seen this brick the VMs, which will cause the command to hang for a while.  If this happens to you, try the command without "-j".)&lt;/p&gt;

&lt;p&gt;When this command is done, you will find a shiny new RPM at &lt;code&gt;packaging/results/centos_9/polly-1.0-1.el7.centos.x86_64.rpm&lt;/code&gt; and a shiny new Debian package at &lt;code&gt;packaging/results/debian_9/polly_1.0-1_amd64.deb&lt;/code&gt;.  Also, those packages will have been tested to ensure that they install polly correctly, and polly will have been tested to ensure it works on each of those OSes, using &lt;code&gt;system_test/meow_test.sh&lt;/code&gt;.  You can see the results of the tests thus:&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;tail &lt;/span&gt;results/centos_7/test-vm.log
Installed:
  polly.x86_64 0:1.0-1.el7.centos                                               

Complete!
&lt;span class="c"&gt;# run test&lt;/span&gt;
vagrant ssh &lt;span class="nt"&gt;--no-tty&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'make -C polly-1.0/packaging/centos_7 test-local'&lt;/span&gt;
make: Entering directory &lt;span class="sb"&gt;`&lt;/span&gt;/home/vagrant/polly-1.0/packaging/centos_7&lt;span class="s1"&gt;'
"/home/vagrant/polly-1.0/system_test/meow_test.sh"
PASS
make: Leaving directory `/home/vagrant/polly-1.0/packaging/centos_7'&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;tail &lt;/span&gt;results/debian_9/test-vm.log 
&lt;span class="o"&gt;(&lt;/span&gt;Reading database ... 41645 files and directories currently installed.&lt;span class="o"&gt;)&lt;/span&gt;
Preparing to unpack polly_1.0-1_amd64.deb ...
Unpacking polly &lt;span class="o"&gt;(&lt;/span&gt;1.0-1&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up polly &lt;span class="o"&gt;(&lt;/span&gt;1.0-1&lt;span class="o"&gt;)&lt;/span&gt; ...
&lt;span class="c"&gt;# run test&lt;/span&gt;
vagrant ssh &lt;span class="nt"&gt;--no-tty&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'make -C polly-1.0/packaging/debian_9 test-local'&lt;/span&gt;
make: Entering directory &lt;span class="s1"&gt;'/home/vagrant/polly-1.0/packaging/debian_9'&lt;/span&gt;
&lt;span class="s2"&gt;"/home/vagrant/polly-1.0/system_test/meow_test.sh"&lt;/span&gt;
PASS
make: Leaving directory &lt;span class="s1"&gt;'/home/vagrant/polly-1.0/packaging/debian_9'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  How It Does It (Overview)
&lt;/h1&gt;

&lt;p&gt;This whole process is orchestrated by Make files.  I know Make isn't used as much anymore, but it really does work well.  Moreover, it makes it much easier to build packages if your project can be built and installed with Make.&lt;/p&gt;

&lt;p&gt;Making a package on both CentOS and Debian involves several OS-specific steps and OS-specific tools (namely, rpmbuild for CentOS and dpkg-buildpackage for Debian).  We of course need to automate those steps.  We also need to automate the steps that will be executed on our host machine --- for example, creating and starting the VMs.  We might expect to be able to break our automation code into the following mutually exclusive categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automation code that is to be run on the host&lt;/li&gt;
&lt;li&gt;Automation code that is to be run on a CentOS 7 VM&lt;/li&gt;
&lt;li&gt;Automation code that is to be run on a Debian 9 VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it turns out that there's some overlap --- specifically, we need to run some of the code from the first category on the VMs.  So our solution takes this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make one system of Make files that does everything we need for every platform --- builds the program, builds the packages, runs unit tests, etc. --- ignoring the fact that not all these commands can actually be run on the same OS&lt;/li&gt;
&lt;li&gt;Add logic that "magically" switches from the host to, say, the Debian VM, and then resumes execution on the VM&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  How It Does It (Details)
&lt;/h1&gt;

&lt;p&gt;At the root of the project is the main Make file --- &lt;code&gt;Makefile&lt;/code&gt;.  Its important targets are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt;: build the program (actually, it calls &lt;code&gt;go install&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;install&lt;/code&gt;: install the program to the appropriate place (for example, &lt;code&gt;/usr/local/bin&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;check&lt;/code&gt;: run unit tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dist&lt;/code&gt;: make a source tarball&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(The &lt;code&gt;dist&lt;/code&gt; target is the reason for all those &lt;code&gt;sources.mk&lt;/code&gt; files: those files list the source files in their respective directories, and &lt;code&gt;Makefile&lt;/code&gt; imports them all to make the final list of all source files to be included in the tarball.)&lt;/p&gt;

&lt;p&gt;Importantly, the main Make file does not concern itself with making packages or any other OS-specific activities.  That stuff is covered by &lt;code&gt;packaging/centos_7/Makefile&lt;/code&gt; and &lt;code&gt;packaging/debian_9/Makefile&lt;/code&gt;.  Both of these contain the following targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pkg-local&lt;/code&gt;: build the OS-specific package (assuming we are on a VM)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test-local&lt;/code&gt;: Run &lt;code&gt;system_test/meow_test.sh&lt;/code&gt; (assuming we are on a VM)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pkg-vm&lt;/code&gt;: "Magically" run &lt;code&gt;pkg-local&lt;/code&gt; on a VM with the appropriate OS (assuming we are on the host)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test-vm&lt;/code&gt;: Run &lt;code&gt;pkg-vm&lt;/code&gt;, then install the package on a VM with the appropriate OS, and finally "magically" run &lt;code&gt;test-local&lt;/code&gt; on the VM (assuming we are on the host)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to be clear about something.  &lt;code&gt;packaging/centos_7/Makefile&lt;/code&gt; and &lt;code&gt;packaging/debian_9/Makefile&lt;/code&gt; &lt;em&gt;contain&lt;/em&gt; &lt;code&gt;test-local&lt;/code&gt;, &lt;code&gt;pkg-vm&lt;/code&gt;, and &lt;code&gt;test-vm&lt;/code&gt;, but these targets are &lt;em&gt;defined&lt;/em&gt; in &lt;code&gt;packaging/tail.mk&lt;/code&gt;, which those two Make files import.  In general, &lt;code&gt;packaging/&amp;lt;some_os&amp;gt;/Makefile&lt;/code&gt; should define only OS-specific stuff.&lt;/p&gt;

&lt;p&gt;For your convenience, here is the only target in &lt;code&gt;packaging/debian_9/Makefile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;pkg-local&lt;/span&gt;
&lt;span class="nl"&gt;pkg-local &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;${WORK_DIR}/${SRC_TARBALL}&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${WORK_DIR}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;${SRC_TARBALL}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${SRC_ROOT}&lt;/span&gt;&lt;span class="s2"&gt;/../polly_&lt;/span&gt;&lt;span class="nv"&gt;${VERSION}&lt;/span&gt;&lt;span class="s2"&gt;.orig.tar.gz"&lt;/span&gt;
    &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; debian-pkg &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${SRC_ROOT}&lt;/span&gt;&lt;span class="s2"&gt;/debian"&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${SRC_ROOT}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; dpkg-buildpackage &lt;span class="nt"&gt;-us&lt;/span&gt; &lt;span class="nt"&gt;-uc&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${DESTDIR}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
    &lt;span class="nb"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${SRC_ROOT}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/../&lt;span class="k"&gt;*&lt;/span&gt;.deb &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${DESTDIR}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, &lt;code&gt;packaging/Makefile&lt;/code&gt; also has targets &lt;code&gt;pkg-vm&lt;/code&gt; and &lt;code&gt;test-vm&lt;/code&gt;, but they just recursively call the same targets in each of the OS-specific subdirectories' Make files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Magic" Parts
&lt;/h2&gt;

&lt;p&gt;The implementation of the "magic" parts is actually quite straightforward.  When &lt;code&gt;make -C packaging/&amp;lt;some_os&amp;gt; pkg-vm&lt;/code&gt; is called, the Make file&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uses the main Make file's &lt;code&gt;dist&lt;/code&gt; target to make a source tarball&lt;/li&gt;
&lt;li&gt;Makes (or starts) a VM with the needed OS&lt;/li&gt;
&lt;li&gt;Copies the tarball to the VM&lt;/li&gt;
&lt;li&gt;Expands the tarball on the VM&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;make -C packaging/&amp;lt;some_os&amp;gt; pkg-local&lt;/code&gt; on the VM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;test-vm&lt;/code&gt; calls &lt;code&gt;test-local&lt;/code&gt; on the VM in a similar way.&lt;/p&gt;

&lt;p&gt;Vagrant comes into play in steps 2-5.  &lt;code&gt;packaging/centos_7/Vagrantfile&lt;/code&gt; and &lt;code&gt;packaging/debian_9/Vagrantfile&lt;/code&gt; specify the VM image we wish to start with and the steps needed to set it up.  For your convenience, here's the CentOS one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"centos/7"&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;network&lt;/span&gt; &lt;span class="ss"&gt;:forwarded_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest: &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host: &lt;/span&gt;&lt;span class="mi"&gt;2223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;id: &lt;/span&gt;&lt;span class="s2"&gt;"ssh"&lt;/span&gt;

    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;provision&lt;/span&gt; &lt;span class="s2"&gt;"shell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;SHELL&lt;/span&gt;&lt;span class="sh"&gt;
        yum install -y epel-release rpm-build
        yum install -y golang
&lt;/span&gt;&lt;span class="no"&gt;    SHELL&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Make file uses the &lt;code&gt;vagrant&lt;/code&gt; command to create, start, etc. the VM, Vagrant looks to &lt;code&gt;Vagrantfile&lt;/code&gt; for the details.&lt;/p&gt;

&lt;p&gt;When you do all this for the first time, it takes a while for Vagrant to download the VM images and create the VMs.  Happily, Vagrant will cache the images so that subsequent runs will not download them again.&lt;/p&gt;

&lt;p&gt;In addition, the Make file takes a snapshot of each VM just after it is made.  If you run &lt;code&gt;make -C packaging/&amp;lt;some_os&amp;gt; pkg-vm&lt;/code&gt; or &lt;code&gt;make -C packaging/&amp;lt;some_os&amp;gt; test-vm&lt;/code&gt; multiple times, they will reuse the VM, and any files from previous runs will still be on the VM.  To revert to the pristine snapshot, do &lt;code&gt;make -C packaging/&amp;lt;some_os&amp;gt; clean&lt;/code&gt; before calling the other targets.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Next Step
&lt;/h1&gt;

&lt;p&gt;I mentioned in the intro that this scheme naturally supports running system tests on each of these OSes.  In polly, &lt;code&gt;system_test/meow_test.sh&lt;/code&gt; is a tiny toy system test for a tiny toy program.  In a real project, this opportunity to make system testing a part of your development process should not be ignored.&lt;/p&gt;

&lt;p&gt;Jobber has &lt;a href="https://github.com/dshearer/jobber/tree/master/platform_tests"&gt;a good example&lt;/a&gt;, containing tests using &lt;a href="http://robotframework.org/"&gt;Robot Framework&lt;/a&gt; of every major feature.  When I'm working on Jobber, I just need to do &lt;code&gt;make -C packaging -j test-vm&lt;/code&gt; and watch the tests run on every supported OS, in parallel.  When they are done, there will be a beautiful Robot test report for each OS waiting for me in &lt;code&gt;packaging/results&lt;/code&gt;.  &lt;/p&gt;

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

&lt;p&gt;Supporting multiple operating systems is hard, especially if you don't have infrastructure that automates away a lot of the tedious parts.  The scheme presented in this article provides such infrastructure, and I hope it has some ideas that would be useful in your own projects.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
