<?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>DEV Community: gasparev</title>
    <description>The latest articles on DEV Community by gasparev (@gasparev).</description>
    <link>https://dev.to/gasparev</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%2F276218%2F28ced027-cbef-4244-9cf3-b366f1394e03.jpg</url>
      <title>DEV Community: gasparev</title>
      <link>https://dev.to/gasparev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gasparev"/>
    <language>en</language>
    <item>
      <title>How to test Bash scripts (2020 guide)</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Thu, 03 Dec 2020 11:07:54 +0000</pubDate>
      <link>https://dev.to/gasparev/how-to-test-bash-scripts-2020-guide-lh1</link>
      <guid>https://dev.to/gasparev/how-to-test-bash-scripts-2020-guide-lh1</guid>
      <description>&lt;p&gt;How do you test your bash scripts? &lt;strong&gt;Do you test them at all?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Bash scripting is one of the handiest tools every developer has to automate tasks, ease the everyday activities, help to reduce the toil. But automation tools can actually increase the toil if are not treated the same way as code. Bash scripts can get out of date and stop working, became hard to maintain, and increase your technical debt. You need to check them in version control, write tests, and use CI.&lt;/p&gt;

&lt;p&gt;In this article, we'll go through how you can improve the quality of your automation scripts writing tests for Bash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bach
&lt;/h2&gt;

&lt;p&gt;Bach is a testing framework for Bash that provides the possibility to write unit tests for your Bash scripts. Bach makes any command in the PATH an external dependency of the tested Bash script so that no command will be actually executed but will run as "dry run". In this way, you will be able to test the logic of the script and not the commands themself. Bach mocks all commands by default and provides a set of APIs for executing real commands if necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to install Bach
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/bash/"&gt;Bash&lt;/a&gt; v4.3+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/coreutils/coreutils.html"&gt;Coreutils&lt;/a&gt; (&lt;em&gt;GNU/Linux&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/diffutils/diffutils.html"&gt;Diffutils&lt;/a&gt; (&lt;em&gt;GNU/Linux&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To install Bach Testing Framework download &lt;a href="https://github.com/bach-sh/bach/raw/master/bach.sh"&gt;bach.sh&lt;/a&gt; to your project, use the &lt;code&gt;source&lt;/code&gt; command to import &lt;code&gt;bach.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&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;&lt;span class="nb"&gt;source &lt;/span&gt;path/to/bach.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What can be done
&lt;/h3&gt;

&lt;p&gt;In Bach, we can test what the Bash script will actually execute.&lt;/p&gt;

&lt;p&gt;Every test case in Bach is made of two functions: one for running tests and the other for asserting.&lt;/p&gt;

&lt;p&gt;When you run your tests Bach will execute the two functions separately and will compare the sequence of commands executed by both functions. Every testing function must start with the name test-, the asserting function must end with -assert.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write test cases
&lt;/h2&gt;

&lt;p&gt;Let's see some practical examples of how to write test cases.&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="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail
&lt;span class="nb"&gt;source &lt;/span&gt;bach.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable the Bach Testing Framework the first thing to do is to source the bash.sh.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;test-rm-your-dot-git&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    @mock find ~ &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-name&lt;/span&gt; .git &lt;span class="o"&gt;===&lt;/span&gt; @stdout ~/src/your-awesome-project/.git &lt;span class="se"&gt;\&lt;/span&gt;
                                                ~/src/code/.git
    find ~ &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-name&lt;/span&gt; .git | xargs &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
test-rm-your-dot-git-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/src/your-awesome-project/.git ~/src/code/.git
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first script we test is a command to find all the .git folders and remove them. We can test it by mocking the &lt;code&gt;find&lt;/code&gt; command with those parameters to output two directories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;test-mock-script-with-custom-complex-action&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    @mock ./path/to/script &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;\&lt;/span&gt;&lt;span class="no"&gt;SCRIPT&lt;/span&gt;&lt;span class="sh"&gt;
if [[ "&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="sh"&gt;" == foo ]]; then
  @echo bar
else
  @echo anything
fi
&lt;/span&gt;&lt;span class="no"&gt;SCRIPT
&lt;/span&gt;    ./path/to/script foo
    ./path/to/script something
&lt;span class="o"&gt;}&lt;/span&gt;
test-mock-script-with-custom-complex-action-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    bar
    anything
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second script, we use Bach to test a script that returns a different output based on the input. In this case, we mock the script using &lt;code&gt;@mock ./path/to/script&lt;/code&gt; and then we define the script behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;test-bach-framework-mock-commands&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    @mock find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; fn &lt;span class="o"&gt;===&lt;/span&gt; @stdout file1 file2

    &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; fn&lt;span class="si"&gt;)&lt;/span&gt;

    @mock &lt;span class="nb"&gt;ls &lt;/span&gt;file1 file2 &lt;span class="o"&gt;===&lt;/span&gt; @stdout file2 file1

    &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; fn&lt;span class="si"&gt;)&lt;/span&gt; | xargs &lt;span class="nt"&gt;-n1&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-something&lt;/span&gt;

    @mock &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; @stdout foo bar foobar
    &lt;span class="nb"&gt;ls&lt;/span&gt; | xargs &lt;span class="nt"&gt;-n2&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'do-something ${@}'&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
test-bach-framework-mock-commands-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;ls &lt;/span&gt;file1 file2

    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-something&lt;/span&gt; file2
    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-something&lt;/span&gt; file1

    bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'do-something ${@}'&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; foo bar
    bash &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'do-something ${@}'&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; foobar
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this script, you can see how to perform some complex operations like mocking a command and execute it in a &lt;code&gt;$(...)&lt;/code&gt; expression and using pipes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;test-bach-framework-set--e-should-work&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-this&lt;/span&gt;
    &lt;span class="nb"&gt;builtin false

    &lt;/span&gt;should-not-do-this

&lt;span class="o"&gt;}&lt;/span&gt;
test-bach-framework-set--e-should-work-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-this&lt;/span&gt;
    @fail
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we test the behavior of &lt;code&gt;set -e&lt;/code&gt; so we make the script fail with &lt;code&gt;builtin false&lt;/code&gt; and we test the failure using &lt;code&gt;@fail&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;test-no-double-quote-star&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    @touch bar1 bar2 bar3 &lt;span class="s2"&gt;"bar*"&lt;/span&gt;

    &lt;span class="k"&gt;function &lt;/span&gt;cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# We want to remove the file "bar*", not the others&lt;/span&gt;
    cleanup &lt;span class="s2"&gt;"bar*"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
test-no-double-quote-star-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Without double quotes, all bar files are removed!&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"bar*"&lt;/span&gt; bar1 bar2 bar3
&lt;span class="o"&gt;}&lt;/span&gt;

test-double-quote-star&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    @touch bar1 bar2 bar3 &lt;span class="s2"&gt;"bar*"&lt;/span&gt;

    &lt;span class="k"&gt;function &lt;/span&gt;cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;# We want to remove the file "bar*", not the others&lt;/span&gt;
    cleanup &lt;span class="s2"&gt;"bar*"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
test-double-quote-star-assert&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# Yes, with double quotes, only the file "bar*" is removed&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"bar*"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we define a &lt;code&gt;function&lt;/code&gt; that takes an argument and removes it. In the first example, we don't use double quotes in the function, this will remove all the files starting with bar-. In the second example, the function is using double quotes so only the file called bar* is removed.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run Bach tests
&lt;/h2&gt;

&lt;p&gt;You can write all your test cases in a single &lt;code&gt;.sh&lt;/code&gt; file remembering to add the &lt;code&gt;source bach.sh&lt;/code&gt; in it. At this point to run the tests you just need to execute the &lt;code&gt;.sh&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;In this post, we've seen how to write unit tests for your Bash scripts to improve the quality and reliability of your automation.&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your thoughts!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://www.gasparevitta.com/posts/how-to-test-bash-scripts-bach/"&gt;my blog&lt;/a&gt;. Head over t​h​e​r​e if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>codequality</category>
      <category>bash</category>
      <category>testing</category>
    </item>
    <item>
      <title>Speed up Docker build time with cache warming</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Wed, 25 Nov 2020 13:26:42 +0000</pubDate>
      <link>https://dev.to/gasparev/speed-up-docker-build-time-with-cache-warming-29on</link>
      <guid>https://dev.to/gasparev/speed-up-docker-build-time-with-cache-warming-29on</guid>
      <description>&lt;p&gt;Every time you run a build, Docker will try to use the local cache if present. What if you have never built the image locally but the image is already on a remote docker registry?&lt;/p&gt;

&lt;p&gt;In this post, we'll go through how you can use a remote docker registry to warm the docker cache and speed up the local build. This can be also very useful in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to warm the cache in docker
&lt;/h2&gt;

&lt;p&gt;From Docker 1.13 you can use a small trick. You can use the &lt;code&gt;--cache-from&lt;/code&gt; flag to instruct docker to use as a cache the layer of another image. The only prerequisite is that you have the image locally.&lt;/p&gt;

&lt;p&gt;Let's say you have your Dockerfile locally that you build and push:&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; my-docker &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker push my-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, on another machine or in your CI, you want to build my-docker again. Here you can use the image you pushed on your docker registry to speed up 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;docker pull my-docker &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;--cache-from&lt;/span&gt; my-docker &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use &lt;code&gt;|| true&lt;/code&gt; to avoid failures if the image has not been pushed and then we use the image as a cache for the build.&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%2F6ml6y8qtpckkkb0zxcr3.jpg" 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%2F6ml6y8qtpckkkb0zxcr3.jpg" alt="How to warm the cache in docker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to warm the cache with Buildkit
&lt;/h2&gt;

&lt;p&gt;With the classical approach, you must pull the image before running the build. We can avoid it using Buildkit and &lt;code&gt;BUILDKIT_INLINE_CACHE&lt;/code&gt;. You can check on a previous post on &lt;a href="https://www.gasparevitta.com/posts/advanced-docker-multistage-parallel-build-buildkit/" rel="noopener noreferrer"&gt;how to set up Buildkit on your machine.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we really need, together with the cache, is the JSON with cache metadata. We can use Buildkit to run the Docker build and add the cache information to the Docker image.&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;export &lt;/span&gt;&lt;span class="nv"&gt;DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; my-docker &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;BUILDKIT_INLINE_CACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker push my-docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we enable Buildkit with &lt;code&gt;export DOCKER_BUILDKIT=1&lt;/code&gt; and then we use  &lt;code&gt;--build-arg BUILDKIT_INLINE_CACHE=1&lt;/code&gt; during the build. At this point, the built image can be used as a cache for the other builds.&lt;/p&gt;

&lt;p&gt;After that, we can run the build on another machine or in CI using the same syntax as before:&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;--cache-from&lt;/span&gt; my-docker &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What will happen this time is that the builder will pull the JSON metadata from the registry to figure out if there's a cache that can be used from the remote image. If there's a cache hit, only the needed layers will be fetched locally and will be used by the builder.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;In this post we've seen how to warm up the Docker build cache using another image, this technique will help in speeding up the build time.&lt;/p&gt;

&lt;p&gt;If you are looking for other ways to speed up the build time you can try to use a &lt;a href="https://www.gasparevitta.com/posts/advanced-docker-multistage-parallel-build-buildkit/" rel="noopener noreferrer"&gt;multistage parallel Docker build&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta" rel="noopener noreferrer"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your thoughts!&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;This article was originally published on &lt;a href="https://www.gasparevitta.com/posts/docker-warm-up-cache-from-image-buildkit/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. Head over t​h​e​r​e if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Docker best practices: lint your Dockerfile!</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Mon, 09 Nov 2020 16:06:36 +0000</pubDate>
      <link>https://dev.to/gasparev/docker-best-practices-lint-your-dockerfile-5869</link>
      <guid>https://dev.to/gasparev/docker-best-practices-lint-your-dockerfile-5869</guid>
      <description>&lt;p&gt;In software development, &lt;strong&gt;best practices are the way to go.&lt;/strong&gt;&lt;br&gt;
You must do the same while developing the infrastructure code!&lt;br&gt;
In this post, we’ll go through how a linter can increase your productivity, how to use it with a Dockerfile, and how to implement it in a CI pipeline.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is a linter? Why we need it?
&lt;/h2&gt;

&lt;p&gt;According to Wikipedia, a &lt;a href="https://en.wikipedia.org/wiki/Lint_%28software%29" rel="noopener noreferrer"&gt;linter&lt;/a&gt; is &lt;em&gt;a static code analysis tool used to flag programming errors, bugs, stylistic errors, and suspicious constructs&lt;/em&gt;.&lt;br&gt;
As a static code analysis tool, linters can’t be used to detect compiling time errors but are very useful in finding typos and syntax errors. Using a linter will allow you to detect errors early, fixing them faster, and reduce bugs before execution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hadolint
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4f0hytkjoarn76yng7sd.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%2F4f0hytkjoarn76yng7sd.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool we will use is called &lt;a href="https://github.com/hadolint/hadolint" rel="noopener noreferrer"&gt;Hadolint&lt;/a&gt; and as you can recall from the name is a linter. It’s built to help you follow the &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/" rel="noopener noreferrer"&gt;docker best practices&lt;/a&gt;, and it also uses &lt;a href="https://github.com/koalaman/shellcheck" rel="noopener noreferrer"&gt;ShellCheck&lt;/a&gt; to inspect your &lt;code&gt;RUN&lt;/code&gt; instructions.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to set it up
&lt;/h3&gt;

&lt;p&gt;It very easy to use both in a local environment and CI, you can find the &lt;a href="https://github.com/hadolint/hadolint/blob/master/docs/INTEGRATION.md" rel="noopener noreferrer"&gt;integration docs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are a VS Code user, there is the  &lt;a href="https://marketplace.visualstudio.com/items?itemName=exiasr.hadolint" rel="noopener noreferrer"&gt;Hadolint extension&lt;/a&gt;. If you want to use it directly in Github, there is the &lt;a href="https://github.com/marketplace/actions/hadolint-action" rel="noopener noreferrer"&gt;Hadolint Github action&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Define custom rules
&lt;/h2&gt;

&lt;p&gt;If you don’t want to follow all the rules defined by Hadolint, you can easily deactivate some of them. You only need to create a file called  &lt;code&gt;~/.config/hadolint.yaml&lt;/code&gt;, a full &lt;a href="https://github.com/hadolint/hadolint#rules" rel="noopener noreferrer"&gt;list of rules here&lt;/a&gt;. An example of a custom rule file is:&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;ignored&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DL3000&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;SC1010&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to run it in CI
&lt;/h2&gt;

&lt;p&gt;To enforce this best practice, you can add a test in your Docker deployment pipeline. We can implement it in the &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible pipeline&lt;/a&gt; we used to execute &lt;a href="https://dev.to/posts/docker-unit-test-dockerfile-image/"&gt;unit tests for Docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s add a new role called “Run hadolint on Dockerfile”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run hadolint on Dockerfile&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;docker run --rm -i \&lt;/span&gt;
      &lt;span class="s"&gt;-v "{{ role_path }}/files/hadolint.yaml":/root/.config/hadolint.yaml hadolint/hadolint \&lt;/span&gt;
      &lt;span class="s"&gt;&amp;lt; {{ dockerfile_name }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we directly run the official hadolint docker image against the Dockerfile. I’m mounting the &lt;code&gt;hadolint.yaml&lt;/code&gt; file to use my custom rules configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;Now you should know all you need to use Hadolint for your Dockerfile.&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta" rel="noopener noreferrer"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your thoughts!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can find the code snippets on &lt;a href="https://github.com/gasparev/blog-code-snippets/tree/master/docker-unit-test/roles/dockerfile-linter" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;br&gt;
&lt;em&gt;This article was originally published on &lt;a href="https://www.gasparevitta.com/posts/docker-best-practices-dockerfile-hadolint/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. Head over there if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Advanced Docker: how to use secrets the right way</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Tue, 27 Oct 2020 15:11:02 +0000</pubDate>
      <link>https://dev.to/gasparev/advanced-docker-how-to-use-secrets-the-right-way-1l67</link>
      <guid>https://dev.to/gasparev/advanced-docker-how-to-use-secrets-the-right-way-1l67</guid>
      <description>&lt;p&gt;Secrets are one of the sneakiest &lt;strong&gt;vulnerability issues&lt;/strong&gt; you can have in a Docker image if you don't know how to handle them.&lt;/p&gt;

&lt;p&gt;If you need to clone a private repository or to download a private package you must use sensitive data during your &lt;code&gt;docker build&lt;/code&gt;, there's no easy way around that.&lt;/p&gt;

&lt;p&gt;In this tutorial on the advanced usage of Docker series, I'll explain how to use a build secret in a &lt;strong&gt;safe way&lt;/strong&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%2Fd7lmh5izuqo4dp8ruqbv.jpg" 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%2Fd7lmh5izuqo4dp8ruqbv.jpg" alt="Tutorial on the advanced usage of Docker build secrets in a safe way"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Buildkit
&lt;/h2&gt;

&lt;p&gt;I explained last week what is the Buildkit build engine, how to set it up, and how you can &lt;a href="https://www.gasparevitta.com/posts/docker-unit-test-dockerfile-image/" rel="noopener noreferrer"&gt;use Buildkit to speed up &lt;code&gt;docker build&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Never use &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;rm&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you are dealing with secrets during your development, I'm sure the first thing you've tried is to &lt;code&gt;COPY&lt;/code&gt; a file with credentials from your Dockerfile and then remove it with &lt;code&gt;rm&lt;/code&gt; when you don't need it anymore...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is so wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;because you are just deleting the file from that layer but the credentials are still in the layer above!&lt;/p&gt;

&lt;h3&gt;
  
  
  Unsafe code example
&lt;/h3&gt;

&lt;p&gt;Let's use this Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FROM ubuntu:bionic
COPY .netrc /
RUN &lt;span class="nb"&gt;rm&lt;/span&gt; .netrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And let's build 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 build &lt;span class="nt"&gt;-t&lt;/span&gt; unsafe &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.not-safe
Sending build context to Docker daemon  4.096kB
Step 1/3 : FROM ubuntu:bionic
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; c14bccfdea1c
Step 2/3 : COPY .netrc /
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 18d1eb74c6da
Step 3/3 : RUN &lt;span class="nb"&gt;rm&lt;/span&gt; .netrc
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;fafd31acf728
Removing intermediate container fafd31acf728
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; d7d4315738a6
Successfully built d7d4315738a6
Successfully tagged unsafe:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we want to use the command &lt;code&gt;docker history&lt;/code&gt; to list all the layers of the image.&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;history &lt;/span&gt;d7d4315738a6
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
d7d4315738a6        10 seconds ago      /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; .netrc                            0B
18d1eb74c6da        14 seconds ago      /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop) COPY file:a0fa732884be950b…   19B&lt;/span&gt;
c14bccfdea1c        4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["/bin/bash"]            0B&lt;/span&gt;
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /run/systemd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'do…   7B
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh -c [ -z "$(apt-get indextargets)" ]     0B
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh -c set -xe   &amp;amp;&amp;amp; echo '&lt;/span&gt;&lt;span class="c"&gt;#!/bin/sh' &amp;gt; /…   745B&lt;/span&gt;
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop) ADD file:84f8ddc4d76e1e867…   63.2MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see the latest layer created, &lt;code&gt;d7d4315738a6&lt;/code&gt;, but you don't care about it.&lt;br&gt;
The best part is the &lt;em&gt;previous&lt;/em&gt; layer, &lt;code&gt;18d1eb74c6da&lt;/code&gt;, which we can analyze deeper.&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;-it&lt;/span&gt; 18d1eb74c6da
root@09fb719ec3dc:/# &lt;span class="nb"&gt;cat&lt;/span&gt; .netrc
my secret password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the deal: &lt;strong&gt;every layer of your image is available, including the ones with your secrets!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about it next time you do &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;rm&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buildkit to the rescue with &lt;code&gt;--secret&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Buildkit adds a new flag called &lt;code&gt;--secret&lt;/code&gt; for the docker build command. You can use it to provide &lt;strong&gt;safely&lt;/strong&gt; a secret to your Dockerfile at build time! Buildkit mounts the secret using &lt;code&gt;tmpfs&lt;/code&gt; in a temporary file located in &lt;code&gt;/run/secrets&lt;/code&gt; that we can use to access a secret in the Dockerfile.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Using this feature we are sure that no secrets will remain in the image!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Safe code example
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Dockerfile
&lt;/h4&gt;

&lt;p&gt;Let's use the following Dockerfile&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;# syntax = docker/dockerfile:1.0-experimental&lt;/span&gt;

FROM ubuntu:bionic

RUN &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret,id&lt;span class="o"&gt;=&lt;/span&gt;mynetrc,dst&lt;span class="o"&gt;=&lt;/span&gt;/.netrc &lt;span class="nb"&gt;cat&lt;/span&gt; /.netrc
RUN &lt;span class="nb"&gt;cat&lt;/span&gt; /.netrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing to notice is &lt;code&gt;# syntax = docker/dockerfile:1.0-experimental&lt;/code&gt;, we tell Docker to use the new syntax to exploit the new Buildkit functionality.&lt;br&gt;
Then, with the first &lt;code&gt;RUN&lt;/code&gt; command, the magic happens. We tell Docker to &lt;code&gt;mount&lt;/code&gt; a &lt;code&gt;secret&lt;/code&gt; with the id &lt;code&gt;mynetrc&lt;/code&gt; to the destination &lt;code&gt;/.netrc&lt;/code&gt; and in the same line, we execute the &lt;code&gt;cat&lt;/code&gt; command just for the sake of the example.&lt;br&gt;
Then we &lt;code&gt;RUN&lt;/code&gt; again the cat command on the same file.&lt;/p&gt;
&lt;h4&gt;
  
  
  Build command
&lt;/h4&gt;

&lt;p&gt;To build our Dockerfile this is the 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;$ DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker build &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mynetrc,src&lt;span class="o"&gt;=&lt;/span&gt;.netrc &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.safe &lt;span class="nt"&gt;-t&lt;/span&gt; safe &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can notice here the flag &lt;code&gt;--secret&lt;/code&gt; which tells Docker the &lt;strong&gt;secret name and location&lt;/strong&gt;. We also need to set &lt;code&gt;DOCKER_BUILDKIT=1&lt;/code&gt; to use the Buildkit build engine.&lt;/p&gt;

&lt;p&gt;OK, let's build 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;$ DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker build &lt;span class="nt"&gt;--secret&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mynetrc,src&lt;span class="o"&gt;=&lt;/span&gt;.netrc &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.safe &lt;span class="nt"&gt;-t&lt;/span&gt; safe &lt;span class="nb"&gt;.&lt;/span&gt;

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

&lt;span class="c"&gt;#8 [1/3] FROM docker.io/library/ubuntu:bionic&lt;/span&gt;
&lt;span class="c"&gt;#8 CACHED&lt;/span&gt;

&lt;span class="c"&gt;#9 [2/3] RUN --mount=type=secret,id=mynetrc,dst=/.netrc cat /.netrc&lt;/span&gt;
&lt;span class="c"&gt;#9 0.808 my secret password&lt;/span&gt;
&lt;span class="c"&gt;#9 DONE 1.7s&lt;/span&gt;

&lt;span class="c"&gt;#10 [3/3] RUN cat /.netrc&lt;/span&gt;
&lt;span class="c"&gt;#10 DONE 2.0s&lt;/span&gt;

&lt;span class="c"&gt;#11 exporting to image&lt;/span&gt;
&lt;span class="c"&gt;#11 exporting layers&lt;/span&gt;
&lt;span class="c"&gt;#11 exporting layers 0.7s done&lt;/span&gt;
&lt;span class="c"&gt;#11 writing image sha256:b86ed6e0585c2f2e5cb14796b896dae0004f75004ccece0949a3de0ca600b113 0.0s done&lt;/span&gt;
&lt;span class="c"&gt;#11 naming to docker.io/library/safe 0.0s done&lt;/span&gt;
&lt;span class="c"&gt;#11 DONE 1.0s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, in the &lt;code&gt;RUN --mount=type=secret,id=mynetrc,dst=/.netrc cat /.netrc&lt;/code&gt; we can access the content of the file, instead on the following &lt;code&gt;RUN&lt;/code&gt; there's no output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;.netrc&lt;/code&gt; file, in fact, is present in the final layer of the image but it's empty.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's use the command &lt;code&gt;docker history&lt;/code&gt; to list all the layers of this new image.&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;history &lt;/span&gt;b86ed6e0585c
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
b86ed6e0585c        5 hours ago         RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; /.netrc &lt;span class="c"&gt;# buildkit           0B                  buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;           5 hours ago         RUN /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; /.netrc &lt;span class="c"&gt;# buildkit           0B                  buildkit.dockerfile.v0&lt;/span&gt;
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop)  CMD ["/bin/bash"]            0B                  &lt;/span&gt;
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /run/systemd &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'do…   7B                  
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh -c [ -z "$(apt-get indextargets)" ]     0B                  
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh -c set -xe   &amp;amp;&amp;amp; echo '&lt;/span&gt;&lt;span class="c"&gt;#!/bin/sh' &amp;gt; /…   745B                &lt;/span&gt;
&amp;lt;missing&amp;gt;           4 weeks ago         /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="c"&gt;#(nop) ADD file:84f8ddc4d76e1e867…   63.2MB              &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it's not possible now to load an older layer to read the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;I hope this was useful for you, now go and refactor your old Dockerfile!&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta" rel="noopener noreferrer"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know how you use manage secrets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the code snippets on &lt;a href="https://github.com/gasparev/blog-code-snippets/tree/master/docker-secrets" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://www.gasparevitta.com/posts/advanced-docker-secrets-buildkit/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. Head over there if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>tutorial</category>
      <category>devops</category>
      <category>codequality</category>
    </item>
    <item>
      <title>3 steps to drastically improve your docker build performances</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Mon, 19 Oct 2020 14:08:43 +0000</pubDate>
      <link>https://dev.to/gasparev/3-steps-to-drastically-improve-your-docker-build-performances-17jg</link>
      <guid>https://dev.to/gasparev/3-steps-to-drastically-improve-your-docker-build-performances-17jg</guid>
      <description>&lt;p&gt;Docker is the tool we use every day in our development, but how much time do you waste waiting for Docker build to complete? And how do you deal with gigantic image size?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What if I tell you there's a better way to build your containers?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your favorite next tool is called Buildkit!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll dive into the advanced usage of Docker to optimize your development process either in build time and in the size of the image itself. We will do it using Buildkit &lt;strong&gt;parallel multistage builds&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buildkit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/moby/buildkit"&gt;Buildkit&lt;/a&gt; is a toolkit developed by the &lt;a href="https://mobyproject.org/"&gt;Moby project&lt;/a&gt; to enhance the build and the packaging of software using containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Main features
&lt;/h3&gt;

&lt;p&gt;Among the different features, Buildkit offers automatic garbage collection to clean up unneeded resources, concurrent dependency resolution, and efficient instruction caching. Buildkit is part of &lt;code&gt;docker build&lt;/code&gt; since Docker 18.06.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to enable Buildkit
&lt;/h3&gt;

&lt;p&gt;If you want to use the Buildkit powered build engine you can do it using the environment variable &lt;code&gt;DOCKER_BUILDKIT=1 docker build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's also possible to enable Buildkit by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit the daemon configuration in &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; and add
&lt;/li&gt;
&lt;/ul&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; &lt;span class="s2"&gt;"features"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"buildkit"&lt;/span&gt;: &lt;span class="nb"&gt;true&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;ul&gt;
&lt;li&gt;Restart the daemon with
&lt;/li&gt;
&lt;/ul&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;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code example
&lt;/h2&gt;

&lt;p&gt;For this tutorial, we are going to prepare an image to deploy an instance of &lt;a href="https://github.com/prometheus/prometheus"&gt;Prometheus&lt;/a&gt; in production. We will start from a standard Dockerfile and we will refactor it to improve performances.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy Dockerfile
&lt;/h3&gt;

&lt;p&gt;We are going to build Prometheus from source code, to do that we need a Docker image with all its build dependencies: &lt;code&gt;golang&lt;/code&gt;, &lt;code&gt;nodejs&lt;/code&gt;, &lt;code&gt;yarn&lt;/code&gt;, and &lt;code&gt;make&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;FROM ubuntu:bionic

ENV &lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/go
ENV &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/go/bin:&lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/bin

RUN 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; curl git build-essential &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_14.x | bash - &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; nodejs &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yarn &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://storage.googleapis.com/golang/go1.15.2.linux-amd64.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; &lt;span class="nt"&gt;-xvf&lt;/span&gt; go1.15.2.linux-amd64.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;mv &lt;/span&gt;go /usr/local &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone https://github.com/prometheus/prometheus.git prometheus/ &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;cd &lt;/span&gt;prometheus/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make build 
&lt;span class="c"&gt;# RUN ./prometheus --config.file=your_config.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and let's build it with:&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;time &lt;/span&gt;docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; prometheus &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.prometheus

...

Successfully built 54b5d99ef76a
Successfully tagged prometheus:latest

real    19m56,395s
user    0m0,506s
sys     0m0,334s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image size 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;docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED              SIZE
prometheus                    latest              54b5d99ef76a        25 minutes ago       2.38GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Legacy build performance
&lt;/h4&gt;

&lt;p&gt;Looking at the results we needed almost &lt;strong&gt;20 minutes&lt;/strong&gt; to create an instance of Prometheus that has a size of &lt;strong&gt;2.38GB&lt;/strong&gt;.&lt;br&gt;
This will be our starting point.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multistage build
&lt;/h2&gt;

&lt;p&gt;Now we have an image ready for production, &lt;em&gt;so we are happy, right?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No, we definitely are not&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As you may have noticed, the image we've just created is huuuge, we can definitely do better using an advanced Docker feature called &lt;em&gt;multistage build&lt;/em&gt;.&lt;br&gt;
The multistage build is available in Docker since the 17.05 version and it is the go-to way to optimize image size. You can use the &lt;code&gt;FROM ... AS ...&lt;/code&gt; instruction to define a build stage and the &lt;code&gt;COPY --from&lt;/code&gt; instruction to share artifacts between stages.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refactor legacy Dockerfile to use multistage build
&lt;/h3&gt;

&lt;p&gt;Let's apply these concepts to the old Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FROM ubuntu:bionic as base-builder

ENV &lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/go
ENV &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/go/bin:&lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/bin

RUN 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; curl git build-essential &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_14.x | bash - &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; nodejs &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yarn &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://storage.googleapis.com/golang/go1.15.2.linux-amd64.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; &lt;span class="nt"&gt;-xvf&lt;/span&gt; go1.15.2.linux-amd64.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;mv &lt;/span&gt;go /usr/local &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone https://github.com/prometheus/prometheus.git prometheus/ &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;cd &lt;/span&gt;prometheus/ &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make build
FROM ubuntu:bionic as final
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;base-builder prometheus/prometheus prometheus
&lt;span class="c"&gt;# RUN ./prometheus --config.file=your_config.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we need to do is to create a tiny &lt;code&gt;final&lt;/code&gt; stage that contains only the Prometheus executable. We can do it with &lt;code&gt;COPY --from&lt;/code&gt; the previous stage.&lt;/p&gt;

&lt;p&gt;It's time to build the Docker image.&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;time &lt;/span&gt;docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; prometheus-multistage &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.prometheus-multistage
...

Successfully built ab2217626102
Successfully tagged prometheus-multistage:latest

real    19m19,570s
user    0m0,418s
sys     0m0,459s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image size 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;docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
prometheus-multistage   latest              ab2217626102        31 seconds ago      151MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Multistage build performance
&lt;/h4&gt;

&lt;p&gt;Looking at the new results we spent 19 minutes to build the image but the improvement on the size is a significant &lt;strong&gt;99.94% reduction!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Parallel multistage build
&lt;/h2&gt;

&lt;p&gt;So we were able to reduce the image size but the build time is still too much. We can still optimize that by exploiting the &lt;strong&gt;Buildkit build engine&lt;/strong&gt;.&lt;br&gt;
The legacy Docker build engine executes the build of the stages sequentially, on the other hand, Buildkit computes the &lt;em&gt;dependency graph&lt;/em&gt; of the stages and parallelize the builds. With this in mind, we can refactor the Dockerfile to speed up the build time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refactor Dockerfile to use parallel multistage build
&lt;/h3&gt;

&lt;p&gt;Let's see how this can be done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;FROM ubuntu:bionic as base-builder

ENV &lt;span class="nv"&gt;GOPATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/go
ENV &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/go/bin:&lt;span class="nv"&gt;$GOPATH&lt;/span&gt;/bin

RUN 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; curl git build-essential

FROM base-builder as base-builder-extended
RUN curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_14.x | bash - &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; nodejs &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yarn

FROM base-builder as golang
RUN curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://storage.googleapis.com/golang/go1.15.2.linux-amd64.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; &lt;span class="nt"&gt;-xvf&lt;/span&gt; go1.15.2.linux-amd64.tar.gz

FROM base-builder as source-code
RUN git clone https://github.com/prometheus/prometheus.git prometheus/

FROM base-builder-extended as builder
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;golang go /usr/local
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;source-code prometheus/ prometheus/
RUN &lt;span class="nb"&gt;cd &lt;/span&gt;prometheus/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make build

FROM ubuntu:bionic as final
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder prometheus/prometheus prometheus
&lt;span class="c"&gt;# RUN ./prometheus --config.file=your_config.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a first stage called &lt;code&gt;base-builder&lt;/code&gt; that contains the basic tools and will act as a base for the next layers.&lt;br&gt;
Inheriting from &lt;code&gt;base-builder&lt;/code&gt; we define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;golang&lt;/code&gt;, that contains &lt;code&gt;go&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;source-code&lt;/code&gt;, that we use to fetch Prometheus source code;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base-builder-extended&lt;/code&gt; that is an enhancement of &lt;code&gt;base-builder&lt;/code&gt; that contains &lt;code&gt;nodejs&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt;;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 3 stages don't depend on each other so the build will be parallelized.&lt;/p&gt;

&lt;p&gt;At this point we are ready to build the code, we use &lt;code&gt;builder&lt;/code&gt; for that. In this stage, we &lt;code&gt;COPY --from&lt;/code&gt; the previous stages the artifacts we need to run the build. Then again we create a tiny &lt;code&gt;final&lt;/code&gt; stage that contains only the Prometheus executable.&lt;/p&gt;

&lt;p&gt;We can run the build 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;$ DOCKER_BUILDKIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; prometheus-parallel-multistage &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.prometheus-parallel-multistage
&lt;span class="o"&gt;[&lt;/span&gt;+] Building 734.4s &lt;span class="o"&gt;(&lt;/span&gt;13/13&lt;span class="o"&gt;)&lt;/span&gt; FINISHED                                                                                                  
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load build definition from Dockerfile.prometheus-parallel-multistage                                              1.1s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring dockerfile: 963B                                                                                             0.1s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load .dockerignore                                                                                                0.8s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; transferring context: 2B                                                                                                  0.1s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;internal] load metadata &lt;span class="k"&gt;for &lt;/span&gt;docker.io/library/ubuntu:bionic                                                                 0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; CACHED &lt;span class="o"&gt;[&lt;/span&gt;final 1/2] FROM docker.io/library/ubuntu:bionic                                                                      0.0s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;base-builder 2/2] RUN 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; curl git build-essential                                   195.6s
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;source-code 1/1] RUN git clone https://github.com/prometheus/prometheus.git prometheus/                                    77.6s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;base-builder-extended 1/1] RUN curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://deb.nodesource.com/setup_14.x | bash -     &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; nodejs   102.1s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;golang 1/1] RUN curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://storage.googleapis.com/golang/go1.15.2.linux-amd64.tar.gz     &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvf&lt;/span&gt; go1.15.2.linux  149.8s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;builder 1/3] COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;golang go /usr/local                                                                              13.6s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;builder 2/3] COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;source-code prometheus/ prometheus/                                                                9.5s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;builder 3/3] RUN &lt;span class="nb"&gt;cd &lt;/span&gt;prometheus/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make build                                                                             338.6s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;final 2/2] COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;builder prometheus/prometheus prometheus                                                             2.6s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting to image                                                                                                           1.9s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; exporting layers                                                                                                          1.6s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; writing image sha256:c0e59c47a790cb2a6b1229a5fec0014aa2b4540fc79c51531185c9466c9d5584                                     0.1s 
 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/prometheus-parallel-multistage                                                                0.1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check the image size.&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 images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
prometheus-parallel-multistage   latest              c0e59c47a790        About a minute ago  151MB
prometheus-multistage            latest              ab2217626102        9 minutes ago       151MB
prometheus                       latest              54b5d99ef76a        39 minutes ago      2.38GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parallel multistage build performance
&lt;/h4&gt;

&lt;p&gt;Looking at the new results we spent almost 12.5 minutes to build the image, &lt;strong&gt;a 30% reduction&lt;/strong&gt;, keeping the same image size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results recap
&lt;/h2&gt;

&lt;p&gt;The table below summarizes the build time and the image size in the three different examples.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dockerfile&lt;/th&gt;
&lt;th&gt;Build time&lt;/th&gt;
&lt;th&gt;Image size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;prometheus-parallel-multistage&lt;/td&gt;
&lt;td&gt;12.5 m&lt;/td&gt;
&lt;td&gt;151MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus-multistage&lt;/td&gt;
&lt;td&gt;19 m&lt;/td&gt;
&lt;td&gt;151MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prometheus&lt;/td&gt;
&lt;td&gt;20 m&lt;/td&gt;
&lt;td&gt;2.38GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you can see the improvement, both in build time and in image size, is really huge. Using the multistage parallel build approach can be useful in production where a smaller Docker image can make the difference. All you have to do is to keep in mind how Buildkit works, think of what can be parallelized in your Dockerfile and develop it accordingly. You can easily integrate Buildkit in your Docker &lt;a href="https://www.gasparevitta.com/posts/docker-unit-test-dockerfile-image/"&gt;build/test/tag/push pipeline&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;I hope this was useful for you, now go and refactor your old Dockerfile!&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your performance improvements&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can find the code snippets on &lt;a href="https://github.com/gasparev/blog-code-snippets/tree/master/docker-multistage-build"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://www.gasparevitta.com/posts/advanced-docker-multistage-parallel-build-buildkit/"&gt;blog&lt;/a&gt;. Head over there if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Code security: evaluation of the new Github code scanning function</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Tue, 13 Oct 2020 16:44:55 +0000</pubDate>
      <link>https://dev.to/gasparev/code-security-evaluation-of-the-new-github-code-scanning-function-29ce</link>
      <guid>https://dev.to/gasparev/code-security-evaluation-of-the-new-github-code-scanning-function-29ce</guid>
      <description>&lt;p&gt;Last week Github released their first official version of Code Scanning. It allows users to quickly scan their source code to identify vulnerabilities. But I have a question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How reliable is it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, I'll try to give an answer to that based on a sample C code.&lt;/p&gt;

&lt;p&gt;I will use CodeQl to scan a deliberately bugged code and then we will analyze the results to check the performance of the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  CodeQl code scanning
&lt;/h2&gt;

&lt;p&gt;Github code scanning uses &lt;a href="https://semmle.com/codeql"&gt;CodeQl&lt;/a&gt;, a semantic code analysis engine. It runs queries on your code to identify potential threats and bad patterns. It supports a huge variety of languages (C/C++, Go, Java, JavaScript, Python, ecc.) and it has a cool action called &lt;code&gt;autobuild&lt;/code&gt; that you can use to build your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to set it up
&lt;/h3&gt;

&lt;p&gt;To &lt;strong&gt;enable&lt;/strong&gt; CodeQl in your GitHub repository, you can follow the &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/enabling-code-scanning-for-a-repository"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you are a VS code user&lt;/em&gt; there's also an &lt;a href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql"&gt;extension&lt;/a&gt; for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference Environment
&lt;/h2&gt;

&lt;p&gt;For this evaluation I start from the standard CodeQl configuration, then I specify the language of the source code for the analysis: &lt;code&gt;language: ['cpp']&lt;/code&gt; plus an extended set of queries: &lt;code&gt;queries: +security-extended, security-and-quality&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CodeQL"&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;push&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="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The branches below must be a subset of the branches above&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="nv"&gt;main&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="s1"&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;13&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;3'&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;analyze&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;Analyze&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;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cpp'&lt;/span&gt;&lt;span class="pi"&gt;]&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&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="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

    &lt;span class="pi"&gt;-&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;git checkout HEAD^2&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'pull_request' }}&lt;/span&gt;

    &lt;span class="c1"&gt;# Initializes the CodeQL tools for scanning.&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;Initialize CodeQL&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;github/codeql-action/init@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;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.language }}&lt;/span&gt;
        &lt;span class="na"&gt;queries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;+security-extended, security-and-quality&lt;/span&gt;

    &lt;span class="pi"&gt;-&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;make&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;Perform CodeQL Analysis&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;github/codeql-action/analyze@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reference code
&lt;/h2&gt;

&lt;p&gt;I'm using a C code that contains, on purpose, some traits, and errors. The reference code is the fuzzgoat repository from &lt;a href="https://github.com/fuzzstati0n"&gt;fuzzstation&lt;/a&gt; and it contains 4 main vulnerabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use of memory after a free:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/******************************************************************************
WARNING: Fuzzgoat Vulnerability

The line of code below frees the memory block referenced by *top if
the length of a JSON array is 0. The program attempts to use that memory
block later in the program.
Diff       - Added: free(*top);
Payload    - An empty JSON array: []
Input File - emptyArray
Triggers   - Use after free in json_value_free()
******************************************************************************/&lt;/span&gt;

               &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cm"&gt;/****** END vulnerable code **************************************************/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;2 invalid frees:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;   &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mem_free&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
               &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/******************************************************************************
  WARNING: Fuzzgoat Vulnerability

  The line of code below incorrectly decrements the value of 
  value-&amp;gt;u.object.length, causing an invalid read when attempting to free the 
  memory space in the if-statement above.
  Diff       - [--value-&amp;gt;u.object.length] --&amp;gt; [value-&amp;gt;u.object.length--]
  Payload    - Any valid JSON object : {"":0}
  Input File - validObject
  Triggers   - Invalid free in the above if-statement
******************************************************************************/&lt;/span&gt;

            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/****** END vulnerable code **************************************************/&lt;/span&gt;

            &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;json_string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="cm"&gt;/******************************************************************************
  WARNING: Fuzzgoat Vulnerability

  The code below decrements the pointer to the JSON string if the string
  is empty. After decrementing, the program tries to call mem_free on the
  pointer, which no longer references the JSON string.
  Diff       - Added: if (!value-&amp;gt;u.string.length) value-&amp;gt;u.string.ptr--;
  Payload    - An empty JSON string : ""
  Input File - emptyString
  Triggers   - Invalid free on decremented value-&amp;gt;u.string.ptr
******************************************************************************/&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
              &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/****** END vulnerable code **************************************************/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Null pointer dereference:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="cm"&gt;/******************************************************************************
  WARNING: Fuzzgoat Vulnerability
  The code below creates and dereferences a NULL pointer if the string
  is of length one.
  Diff       - Check for one byte string - create and dereference a NULL pointer
  Payload    - A JSON string of length one : "A"
  Input File - oneByteString
  Triggers   - NULL pointer dereference
******************************************************************************/&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&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="p"&gt;{&lt;/span&gt;
              &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;null_pointer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="n"&gt;printf&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;null_pointer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/****** END vulnerable code **************************************************/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code analysis results
&lt;/h2&gt;

&lt;p&gt;These are the results from CodeQl. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p8NIkprU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qdmqoc9uhc5hxkqlpz4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p8NIkprU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qdmqoc9uhc5hxkqlpz4n.png" alt="CodeQl source code analysis results code quality"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Known vulnerabilities
&lt;/h3&gt;

&lt;p&gt;As you can see in the analysis above, none of the vulnerability that were introduced on purpose on fuzzgoat are discovered by CodeQl.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;free&lt;/code&gt; errors are difficult to spot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;but this is not true for a NULL pointer dereference! It can easily be caught by other tools like clang:&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;scan-build-8 make all
scan-build: Using &lt;span class="s1"&gt;'/usr/lib/llvm-8/bin/clang'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;static analysis
/usr/share/clang/scan-build-8/bin/../libexec/ccc-analyzer &lt;span class="nt"&gt;-o&lt;/span&gt; fuzzgoat &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; main.c fuzzgoat.c &lt;span class="nt"&gt;-lm&lt;/span&gt;
fuzzgoat.c:298:29: warning: Dereference of null pointer &lt;span class="o"&gt;(&lt;/span&gt;loaded from variable &lt;span class="s1"&gt;'null_pointer'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
              &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"%d"&lt;/span&gt;, &lt;span class="k"&gt;*&lt;/span&gt;null_pointer&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;h3&gt;
  
  
  New findings
&lt;/h3&gt;

&lt;p&gt;On the other side, let's analyze the actual results.&lt;/p&gt;

&lt;p&gt;CodeQl was able to locate a potential buffer overflow&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XU-bOiJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/txt2m9t6awu9ijecg51k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XU-bOiJJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/txt2m9t6awu9ijecg51k.png" alt="source code quality analysis buffer overflow scanning evaluation"&gt;&lt;/a&gt; which is a &lt;strong&gt;crucial&lt;/strong&gt; vulnerability.&lt;br&gt;
That one is probably a false positive since the size of the buffer is fixed, but in general, it's a good practice to be cautious when performing a buffer write operation.&lt;/p&gt;

&lt;p&gt;Apart from that, the other findings are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing enum case in the switch;&lt;/li&gt;
&lt;li&gt;Poorly documented large function;&lt;/li&gt;
&lt;li&gt;Time-of-check time-of-use filesystem race condition;&lt;/li&gt;
&lt;li&gt;Uncontrolled data used in path expression;&lt;/li&gt;
&lt;li&gt;Potentially uninitialized local variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;After this evaluation, I think that CodeQl is a &lt;strong&gt;good tool for the daily use&lt;/strong&gt; to get insight on the code quality: it's easy to use and configure, it can be quickly integrated into your GitHub repository and it provides small actionable results.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;downside&lt;/strong&gt; is that you can have a high number of &lt;a href="https://news.ycombinator.com/item?id=24646960&amp;amp;goto=news"&gt;false positives&lt;/a&gt; to deal with.&lt;/p&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your thoughts!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can find the code snippets on &lt;a href="https://github.com/gasparev/blog-code-snippets/tree/master/codeql-evaluation"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on my &lt;a href="https://www.gasparevitta.com/posts/github-code-scanning-evaluation-codeql/"&gt;blog&lt;/a&gt;. Head over there if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>git</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker unit test: how to test a Dockerfile (Guide 2020)</title>
      <dc:creator>gasparev</dc:creator>
      <pubDate>Wed, 07 Oct 2020 13:50:38 +0000</pubDate>
      <link>https://dev.to/gasparev/docker-unit-test-how-to-test-a-dockerfile-guide-2020-435d</link>
      <guid>https://dev.to/gasparev/docker-unit-test-how-to-test-a-dockerfile-guide-2020-435d</guid>
      <description>&lt;p&gt;You know you should &lt;em&gt;test everything&lt;/em&gt;...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, writing unit test for Docker should be part of your daily routine while developing a new Dockerfile. It can save you a loooot of time spent running a Docker image trying to figure out why is not working and it will drastically reduce your fear of rebuilding and updating a container (If you still don't believe me on testing, read this article by &lt;a href="http://www.jamesshore.com/v2/books/aoad1/test_driven_development"&gt;James Shore&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this guide you will learn:&lt;/strong&gt; which tools can help you testing your Dockerfile, how to write a unit test for Docker and how to automate it in a continuous integration pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker container structure
&lt;/h2&gt;

&lt;p&gt;The best tool I can raccomand to write a unit test for a Docker is the &lt;a href="https://github.com/GoogleContainerTools/container-structure-test"&gt;Container Structure Test&lt;/a&gt; framework.&lt;br&gt;
This framework, developed by Google, makes super easy to test the structure of your container image.&lt;/p&gt;
&lt;h3&gt;
  
  
  How to install
&lt;/h3&gt;

&lt;p&gt;If you are using Linux run:&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;-LO&lt;/span&gt; https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x container-structure-test-linux-amd64 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo mv &lt;/span&gt;container-structure-test-linux-amd64 /usr/local/bin/container-structure-test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test options
&lt;/h3&gt;

&lt;p&gt;Container Structure Test offers 4 types of test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Command Tests&lt;/em&gt;: execute a command in your image and check the output&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;File Existence Tests&lt;/em&gt;: check if a file is, or isn't, present in the image&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;File Content Tests&lt;/em&gt;: check the content of a file&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Metadata Test&lt;/em&gt;: check if a container metadata is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to write a docker unit test
&lt;/h2&gt;

&lt;p&gt;All you need is a Dockerfile and a &lt;code&gt;.yaml&lt;/code&gt; or &lt;code&gt;.json&lt;/code&gt; file that contains your test cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write your first Docker unit test
&lt;/h3&gt;

&lt;p&gt;For this example we will use the following Dockerfile for an image that can be used in the CI to build the code using &lt;a href="https://bazel.build/"&gt;Bazel&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;FROM ubuntu:bionic

RUN 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; curl gnupg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://bazel.build/bazel-release.pub.gpg | gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; bazel.gpg &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;bazel.gpg /etc/apt/trusted.gpg.d/ &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;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/bazel.list &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; bazel &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;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

RUN groupadd &lt;span class="nt"&gt;-g&lt;/span&gt; 1000 user &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; useradd &lt;span class="nt"&gt;-d&lt;/span&gt; /home/user &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; 1000 &lt;span class="nt"&gt;-g&lt;/span&gt; 1000 user &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;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; user:user /home/user &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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /bazel/cache &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;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; user:user /bazel

RUN &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"build --repository_cache=/bazel/cache"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/home/user/.bazelrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And can be built with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; docker-unit-test &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a Docker image that we set up as root but on the CI we want to mimic the developer build environment as much as possible, to do this we will run the build as a non root user.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What could go wrong?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A lot of things actually!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do the user own build configuration files? Or the cache folder? Well you can check all of that before deploying your Docker image anywhere.&lt;/p&gt;

&lt;p&gt;Let's create &lt;code&gt;unit-test.yaml&lt;/code&gt; to 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;schemaVersion: &lt;span class="s1"&gt;'2.0.0'&lt;/span&gt;
fileExistenceTests:
  - name: &lt;span class="s1"&gt;'Check bazel cache folder'&lt;/span&gt;
    path: &lt;span class="s1"&gt;'/bazel/cache'&lt;/span&gt;
    shouldExist: &lt;span class="nb"&gt;true
    &lt;/span&gt;uid: 1000
    gid: 1000
    isExecutableBy: &lt;span class="s1"&gt;'group'&lt;/span&gt;
fileContentTests:
  - name: &lt;span class="s1"&gt;'Cache folder config'&lt;/span&gt;
    path: &lt;span class="s1"&gt;'/home/user/.bazelrc'&lt;/span&gt;
    expectedContents: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'.*build --repository_cache=/bazel/cache.*'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first test &lt;code&gt;Check bazel cache folder&lt;/code&gt; will check that the cache folder exists and is owned by the non-root user. The second test &lt;code&gt;Cache folder config&lt;/code&gt; will check that the Bazel build configuration file content is as expected.&lt;/p&gt;

&lt;p&gt;Everything is set, we can run our test in this way:&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;container-structure-test &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt; docker-unit-test &lt;span class="nt"&gt;--config&lt;/span&gt; unit-test.yaml

&lt;span class="o"&gt;=======================================&lt;/span&gt;
&lt;span class="o"&gt;======&lt;/span&gt; Test file: unit-test.yaml &lt;span class="o"&gt;======&lt;/span&gt;
&lt;span class="o"&gt;=======================================&lt;/span&gt;
&lt;span class="o"&gt;===&lt;/span&gt; RUN: File Content Test: cache folder config
&lt;span class="nt"&gt;---&lt;/span&gt; PASS
duration: 0s
&lt;span class="o"&gt;===&lt;/span&gt; RUN: File Existence Test: Check bazel cache folder
&lt;span class="nt"&gt;---&lt;/span&gt; PASS
duration: 0s

&lt;span class="o"&gt;=======================================&lt;/span&gt;
&lt;span class="o"&gt;===============&lt;/span&gt; RESULTS &lt;span class="o"&gt;===============&lt;/span&gt;
&lt;span class="o"&gt;=======================================&lt;/span&gt;
Passes:      2
Failures:    0
Duration:    0s
Total tests: 2

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

&lt;/div&gt;



&lt;p&gt;This framework can be super useful for testing your Docker image before shipping it, it's fast and easy to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate the testing of Docker containers
&lt;/h2&gt;

&lt;p&gt;Ok now we have our Dockerfile and tests ready, it's time to automate the testing process!&lt;/p&gt;

&lt;p&gt;In this example I'm assuming that you have an &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; pipeline that you use in Continuos Integration to build, tag and push a docker image. We are going to create a new task for that pipeline to execute the Docker unit test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;- name: unit &lt;span class="nb"&gt;test &lt;/span&gt;Docker Image
  shell: |
    container-structure-test &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--image&lt;/span&gt; &lt;span class="o"&gt;{{&lt;/span&gt; docker_image &lt;span class="o"&gt;}}&lt;/span&gt; &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="o"&gt;{{&lt;/span&gt; test_file &lt;span class="o"&gt;}}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&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;"Test Failed"&lt;/span&gt;
      &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;else
      &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Test Succeeded"&lt;/span&gt;
      &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This is it!
&lt;/h2&gt;

&lt;p&gt;Reach me on Twitter &lt;a href="https://twitter.com/gasparevitta"&gt;@gasparevitta&lt;/a&gt; and &lt;strong&gt;let me know your thoughts!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I hope you find it useful and that you will start testing your Dockerfile from now on.&lt;/p&gt;

&lt;p&gt;You can find the code snippets on &lt;a href="https://github.com/gasparev/blog-code-snippets/tree/master/docker-unit-test"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://www.gasparevitta.com/posts/docker-unit-test-dockerfile-image/"&gt;my blog&lt;/a&gt;. Head over there if you like this post and want to read others like it!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>codequality</category>
    </item>
  </channel>
</rss>
