<?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: Ben Crinion</title>
    <description>The latest articles on DEV Community by Ben Crinion (@b3ncr).</description>
    <link>https://dev.to/b3ncr</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%2F397773%2F8653966d-a48b-426d-abea-ace65e565619.jpeg</url>
      <title>DEV Community: Ben Crinion</title>
      <link>https://dev.to/b3ncr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/b3ncr"/>
    <language>en</language>
    <item>
      <title>Serverless Test Strategy</title>
      <dc:creator>Ben Crinion</dc:creator>
      <pubDate>Tue, 07 May 2024 23:08:53 +0000</pubDate>
      <link>https://dev.to/b3ncr/serverless-test-strategy-57i7</link>
      <guid>https://dev.to/b3ncr/serverless-test-strategy-57i7</guid>
      <description>&lt;p&gt;I was chatting with some colleagues today and the subject of test strategy for Serverless applications came up. I've worked with a few clients on Serverless applications over the last few years and this is the test strategy I tend to come back to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/serverless-application-testing/best-practices.html"&gt;AWS say best practice is to prioritise testing in the cloud&lt;/a&gt;. It's hard to argue with them, they've helped more clients with Serverless than I'll ever dream of helping, but I'll give it a shot anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
   Unit Tests
&lt;/h2&gt;

&lt;p&gt;The feedback cycle of testing in the cloud is just too slow for me. We can use CDK + hot swap deployments to make the deployment as fast as possible but, for me it still doesn't have the same pace as doing TDD and running tests on save.&lt;/p&gt;

&lt;p&gt;This is why the bulk of my testing is still unit tests.&lt;/p&gt;

&lt;h3&gt;
  
  
   Outside In Tests
&lt;/h3&gt;

&lt;p&gt;Naming things is hard so it's no surprise that I don't have a name for the style of unit testing I prefer. Some people call it "outside in" testing. Martin Fowler calls them &lt;a href="https://dev.to"&gt;https://martinfowler.com/articles/2021-test-shapes.html&lt;/a&gt;. I've tended to call it "integration style unit testing" because the modules remain integrated rather than testing each unit separately. &lt;/p&gt;

&lt;p&gt;My mocking approach stems from the principle "test the behaviour, not the implementation". Which to me means, verify that the right data eventually gets stored in the database or sent to a downstream API, rather than test that module A calls function X in module B.&lt;/p&gt;

&lt;p&gt;Testing the behaviour, not the implementation means we can refactor the system under test to our hearts content and be confident that it's still correct. Testing the implementation means we have to re-write the tests when we refactor, that's more work than I'd prefer and raises the risk of introducing bugs due to incorrect tests.&lt;/p&gt;

&lt;p&gt;With outside in testing the tests wouldn't need to change to verify this refactor to add an additional module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ckyvmme7rv1wdwojqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6ckyvmme7rv1wdwojqy.png" alt="System under test with mocked dependencies" width="549" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkwbaoqe3lgzs0quyd81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkwbaoqe3lgzs0quyd81.png" alt="Refactored system under test with mocked dependencies" width="549" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This doesn't mean there's no place for focused unit tests which isolate individual modules. I still use TDD at the unit  scale to implement complex business logic. These focused tests may even make up the largest number of test cases but that's usually because they're &lt;a href="https://jestjs.io/docs/api#testeachtablename-fn-timeout"&gt;data driven&lt;/a&gt; to cover the full range of possible inputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('.add(%i, %i)', (a, b, expected) =&amp;gt; {
  expect(a + b).toBe(expected);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
   Integration Testing
&lt;/h2&gt;

&lt;p&gt;Now we've got a solid foundation of unit tests we can get to the AWS recommendation, testing in the cloud.&lt;/p&gt;

&lt;p&gt;The aim here is to be able to deploy "ephemeral" environments. Every developer and every merge request should be able to create a stack that is isolated from the other environments to run their own tests. I've used Terraform Workspaces for this most recently but it's supported by CDK, Cloudfromation, Serverless Framework and presumably all the other IAC frameworks.&lt;/p&gt;

&lt;p&gt;Ideally these environments be isolated from 3rd party downstream dependencies and other components of the wider system. This prevents unnecessary cost of 3rd party licenses, PAYG consumption, etc and it prevents flakyness caused by downstream collaborators outside your control. Use additional CloudFormation/CDK/Terraform stacks to deploy fake or alternative stand ins.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about data?
&lt;/h3&gt;

&lt;p&gt;The Burning Monk has some articles about &lt;a href="https://theburningmonk.com/2023/01/this-is-why-you-should-keep-stateful-and-stateless-resources-together/"&gt;monolithic deployments&lt;/a&gt; which I won't reproduce (although a lot of this test strategy aligns with his approach too). I totally agree with this approach. It simplifies and de-risks the deployment but I've had issues with taking this approach too far.&lt;/p&gt;

&lt;p&gt;For example, when deploying an API with a WAF included in the monolithic deployment we quickly ran into limits with the number of WAFs allowed in a region. I've also ran into issues with secrets in the monolithic deployment. Secrets are created but they are empty and need to be populated each time we created a new ephemeral stack.&lt;/p&gt;

&lt;p&gt;The "fix" for us was to create some "shared" infrastructure outside the monolithic deployment. We called it shared but it was only really shared between stacks in the dev account. In higher environments they were used by the single instance of the workload.&lt;/p&gt;

&lt;p&gt;Simple scripts deployed as Lambdas can be used to populate standing data. These serve double purpose for setting up production without us having to do "click-ops" through the console or connect to prod from developer machines.&lt;/p&gt;

&lt;h2&gt;
  
  
   A Necessary Evil
&lt;/h2&gt;

&lt;p&gt;I can't stand e2e tests, they're the most brittle tests, particularly in a Serverless environment where we're using asynchronous patterns a lot. &lt;/p&gt;

&lt;p&gt;Annoyingly I also can't feel totally confident without a thin veneer of them. It's such a thin veneer that it'd be quick enough to run manually but then how would we do continuous delivery?&lt;/p&gt;

&lt;h2&gt;
  
  
   Test in production
&lt;/h2&gt;

&lt;p&gt;Each layer of the test pyramid should provide more confidence than the last that we can release, but what provides confidence that the release to production is actually working? Just because we've got the config right for the integration environment, our prod config might be trash.&lt;/p&gt;

&lt;p&gt;We absolutely have to have some level of testing in production, ideally the e2e tests will run after deployment to prod.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else?
&lt;/h2&gt;

&lt;p&gt;This is a fairly traditional test strategy and it's really not complete for modern engineering.&lt;/p&gt;

&lt;p&gt;I've not talked about static code analysis and linting which you could describe as testing.&lt;/p&gt;

&lt;p&gt;We should also be including consumer driven contract testing, mutation testing, performance testing, operational acceptance testing, accessibility testing and probably more but I'm going to save all those for another blog.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>testing</category>
      <category>devops</category>
    </item>
    <item>
      <title>Is consultancy right for you?</title>
      <dc:creator>Ben Crinion</dc:creator>
      <pubDate>Tue, 03 Jan 2023 00:06:20 +0000</pubDate>
      <link>https://dev.to/b3ncr/is-consultancy-right-for-you-4h72</link>
      <guid>https://dev.to/b3ncr/is-consultancy-right-for-you-4h72</guid>
      <description>&lt;p&gt;I wanted to reflect on 2022 and with January usually being a popular time for people to move jobs hopefully my reflections can help someone with their next move.&lt;br&gt;
At the start of 2022 I was wrapping up my first year as a consultant at Infinity Works and a year of working with Cinch. What an incredible experience that was, my first exposure to serverless (with a small 's' and a big 'S'). Cinch have an incredible engineering culture. They play a big part in the local meet-up scene and featured in Werner Vogels' reInvent keynote talk. Check out Toli's talk at GOTO EDA Day from September &lt;a href="https://www.youtube.com/watch?v=wM-dTroS0FA" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=wM-dTroS0FA&lt;/a&gt;.&lt;br&gt;
What's my point?&lt;br&gt;
I imagine you're thinking "that sounds great, how is that helpful to me?". I've done a fair bit of interviewing at IW of the last couple of years. One of the worries people have about joining consulting is that people don't like the sound of short projects. The start of 2023 finds me almost a year in with my second client. I've definitely had shorter tenures in previous non consultancy roles. Some of my colleagues have spent over 2 years at a single client.&lt;br&gt;
Don't let short engagements put you off consultancy. They exist but there are lots of opportunities for longer stints if you want them.&lt;br&gt;
In February I rotated to a different client as a tech lead for a mixed team of onshore and offshore colleagues. The tech stack was similar but the culture is very different. This client is in the health space and as such have a very different approach to governance and risk. I found it difficult to accept the change of pace. Going from multiple deployments to production a day, to maybe one a week, felt like exchanging a sports car for a people carrier (probably, I've never driven either). Also, considering the reduced appetite for risk the team thought that bundling a weeks worth of changes into each release wasn't the ideal strategy. With that in mind we started making a plan for introducing continuous deployment.&lt;br&gt;
What's my point?&lt;br&gt;
Another of the common questions we get in interviews is about the impact consultants can have at a client. It's a great question to ask. Lots of people are familiar with Daniel Pink and the intrinsic motivators: autonomy, mastery and purpose. Some candidates worry about being a "bum on a seat" without much input into technology decisions or how a company runs and that leaves them without that autonomy and purpose. That's not been my, admittedly short, experience. I think the continuous delivery working group is an example of this, when it's complete it'll be a massive change from the current way of working (although we'll get there through evolution, not revolution). It's not the only example, we've helped with technology strategy, company culture, even recruitment (which might seem unusual for a consultancy to help a client with something that'll end with fewer billable hours but that's what we did). If you're thinking about consultancy check out some of the things we've worked on over at our website. &lt;a href="https://www.infinityworks.com/work/" rel="noopener noreferrer"&gt;https://www.infinityworks.com/work/&lt;/a&gt;&lt;br&gt;
In April I became a certified architect and a certified Kanban professional and in December I completed a 2 week data engineering course. I know there are mixed opinions about certification. For what it's worth I learned a tonne on both courses and found them valuable. I've also been to a couple of conferences: GOTO EDA day and AWS community summit.&lt;br&gt;
What's my point?&lt;br&gt;
My point is that I've received as much training in 2 years of consultancy as probably the last 10 years working for product companies. There's more to it than this but the fact is that the more we know, the more useful we are to our clients and the more billable we can be. There's a clear return on investment for the training we receive and most of my colleagues have been given time and money to work on something whether it's cloud certification, agile or leadership training, books or something else.&lt;br&gt;
I don't want to give the impression that consultants are just billable resources to our leadership. We're absolutely not, our leadership are great and show a genuine interest in us. There's no pressure to train on a path set out for us. Our career journeys are in our own hands.&lt;br&gt;
One final quick thing to mention. I ruled out consultancy in the past because I didn't like the idea of lots of travel. I think I've spent approximately 6 nights away from home in the last 2 years. Take this with a pinch of salt because there's been this unheard of thing called COVID that's somewhat limited travel but Infinity Works have regional offices and aim to place our consultants with local clients. It's not always possible, but considering that everyone is much more remote friendly these days and there are increasingly more companies and government agencies setting up outside London, being away from home might not be the deal breaker that it used to be.&lt;br&gt;
I work with some amazing colleagues at IW, consultancy can be a career fast track. I wish I'd made the leap years ago.&lt;br&gt;
Best of luck with whatever you decide to do in 2023!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>cli</category>
      <category>tutorial</category>
      <category>windows</category>
    </item>
    <item>
      <title>Make onboarding simple using VS Code Remote Containers</title>
      <dc:creator>Ben Crinion</dc:creator>
      <pubDate>Sun, 28 Nov 2021 12:12:41 +0000</pubDate>
      <link>https://dev.to/b3ncr/make-onboarding-simple-using-vs-code-remote-containers-2emg</link>
      <guid>https://dev.to/b3ncr/make-onboarding-simple-using-vs-code-remote-containers-2emg</guid>
      <description>&lt;p&gt;Note: this article was written before the Docker Desktop license change but I still think it's a valuable technique. I believe that the Docker Desktop license will still be good value for money compared with the time it takes to setup a dev environment.&lt;/p&gt;

&lt;p&gt;Over the last few weeks our team has grown rapidly. Each time a new engineer joins the team (or an existing engineer gets a new machine) we dig out the laptop onboarding guide and spend a chunk of time installing the right frameworks and tools to get our teammate up and running. This can be fairly painful: the onboarding doc isn't always updated, links die and toolchains evolve. To add to this we have a mix of Apple, Windows and Linux users which means we might be trying to support someone using a platform we're not familiar with.&lt;/p&gt;

&lt;p&gt;Another issue we have is that our squad is responsible for multiple services. These have slightly different dependencies. Different versions of NodeJS, Python, Serverless Framework or CDK, different test runners etc. Add consultancy into the mix and we might have people working on several services at multiple clients and managing the dependency mix gets difficult.&lt;/p&gt;

&lt;p&gt;Wouldn't it be useful if we had some light weight, isolated operating systems? Something we could run on any machine and that we can configure separately without them impacting each other?&lt;/p&gt;

&lt;p&gt;Luckily for us &lt;a href="https://www.docker.com"&gt;Docker&lt;/a&gt; exists and can do exactly this. Even better, Microsoft have created the &lt;a href="https://code.visualstudio.com/docs/remote/containers"&gt;Visual Studio Code Remote - Containers&lt;/a&gt; extension which lets you use a Docker container as a full-featured development environment within VS Code.&lt;/p&gt;

&lt;p&gt;This is how we solved some of the problems we came up against using Dev Container and &lt;a href="https://serverless.com/"&gt;Serverless framework&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not using dev containers
&lt;/h2&gt;

&lt;p&gt;The first problem we have is that not everyone on our team wants to use VS Code. Because of this, everything we change to enable dev containers needs to also work natively and with our CI/CD pipeline. This baiscally boils down to replacing &lt;code&gt;localhost&lt;/code&gt; with the container hostname which is available by default in a Docker container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HOSTNAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Docker
&lt;/h2&gt;

&lt;p&gt;We use &lt;a href="https://github.com/localstack/localstack"&gt;LocalStack&lt;/a&gt; for integration testing so we need to be able to run containers from within our dev container. &lt;/p&gt;

&lt;p&gt;It's possible to install a container engine within a container and create "child" containers but it's complex and there's a simpler solution. &lt;/p&gt;

&lt;p&gt;We can use Docker on the host machine to create "sibling" containers by installing the Docker CLI and mounting &lt;code&gt;/var/run/docker.sock&lt;/code&gt;. The devcontainer.json settings file has a &lt;code&gt;mounts&lt;/code&gt; property which can be used to have some control over the dev container file system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"mounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Sock Permissions
&lt;/h3&gt;

&lt;p&gt;If you're &lt;a href="https://code.visualstudio.com/docs/remote/containers-advanced#_adding-a-nonroot-user-to-your-dev-container"&gt;using a non-root user&lt;/a&gt; inside your dev container (and you probably should) then you need to give that user permissions to use &lt;code&gt;docker.sock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You could run this as sudo and it will persist until you rebuild the container or it can be automated using a post run command in the &lt;code&gt;devcontainer.json&lt;/code&gt; file which means no-one has to remember to do it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"postCreateCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sudo chown vscode:vscode /var/run/docker.sock"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using AWS and Git
&lt;/h2&gt;

&lt;p&gt;We need to use the AWS CLI and Github. We could duplicate the credentials and keys in our dev container file system but they would not persist if we had to rebuild the container and aren't reusable between different projects. &lt;/p&gt;

&lt;p&gt;We can share the host's ssh keys and AWS credentials by mounting the host file system in the container (again using the &lt;code&gt;mounts&lt;/code&gt; property in devcontainer.json).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"mounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"source=${localEnv:HOME}${localEnv:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Filesystem Performance Issues
&lt;/h2&gt;

&lt;p&gt;We're using the &lt;code&gt;serverless-webpack&lt;/code&gt; plugin but we were getting errors during packaging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serverless: Packing external modules: .....

 Error ---------------------------------------------------

  Error: npm install failed with code 1
      at ChildProcess.&amp;lt;anonymous&amp;gt; (/workspace/node_modules/serverless-webpack/lib/utils.js:91:16)
      at ChildProcess.emit (events.js:314:20)
      at ChildProcess.EventEmitter.emit (domain.js:483:12)
      at maybeClose (internal/child_process.js:1022:16)
      at Process.ChildProcess._handle.onexit (internal/child_process.js:287:5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message doesn't give any pointers to what's going wrong but there were some clues when we tried to clean up the &lt;code&gt;.webpack&lt;/code&gt; folder. Running &lt;code&gt;ls&lt;/code&gt; from inside the container showed it to be enpty but it wouldn't allow us to delete it because it wasn't empty on the host.&lt;/p&gt;

&lt;p&gt;This is because the default source code mount uses the &lt;code&gt;cached&lt;/code&gt; consistency model. The &lt;code&gt;cached&lt;/code&gt; consistency model is more appropriate for files which the host modifies. There's a good description of the different modes in &lt;a href="https://stackoverflow.com/a/63437557"&gt;this StackOverflow answer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our solution was to use a &lt;code&gt;volume&lt;/code&gt; for the webpack and node_modules folders as "&lt;a href="https://docs.docker.com/storage/volumes/"&gt;volumes are the preferred mechanism for persisting data generated by and used by Docker containers&lt;/a&gt;". &lt;code&gt;mounts&lt;/code&gt; property to the rescue again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"mounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"source=node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"source=webpack,target=${containerWorkspaceFolder}/.webpack,type=volume"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These folders will be owned by &lt;code&gt;root&lt;/code&gt; so we'll use the &lt;code&gt;postCreateCommand&lt;/code&gt; again to change their ownership back to &lt;code&gt;vscode&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"postCreateCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sudo chown vscode:vscode node_modules &amp;amp;&amp;amp; sudo chown vscode:vscode .webpack"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we need to modify the webpack config slightly. It's not possible for the container to delete the volume so we've set the webpack output path to a sub folder in the &lt;code&gt;webpack.config.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;libraryTarget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commonjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.webpack/build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name].js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option would be to use a &lt;code&gt;delegated&lt;/code&gt; mount which are more appropriate when the container's view of the filesystem is authoritive or &lt;a href="https://code.visualstudio.com/docs/remote/containers#_quick-start-open-a-git-repository-or-github-pr-in-an-isolated-container-volume"&gt;clone the whole repo into a container volume&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Networking
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, we're using LocalStack for integration testing and we have a bash script which uses &lt;code&gt;docker-compose&lt;/code&gt; to manage that container. Docker compose creates a network for the workload, this allows all the containers in the workload to communicate easily but it isolates them from other workloads and individual containers. This meant that Serverless offline and the tests which were running in the dev container couldn't access the database running in LocalStack. &lt;/p&gt;

&lt;p&gt;Docker containers can be attached to more than one network at a time so we've solved this by creating a dedicated network and attaching the dev-container and LocalStack container to it. There are another couple of properties in the settings file which can help us with this. We can ensure the network exists before we start the dev container using the &lt;code&gt;initializeCommand&lt;/code&gt; property, and use &lt;code&gt;runArgs&lt;/code&gt; to provide additional arguments to the dev container (we append &lt;code&gt;|| true&lt;/code&gt; to the &lt;code&gt;initializeCommand&lt;/code&gt; to ensure the command succeeds if the network already exists.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"initializeCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker network create payment_network || true"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"runArgs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--network=payment_network"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is only half the job. We also need to attach the LocalStack container to the network and we still can't use &lt;code&gt;localhost&lt;/code&gt; for addressing. This is another area where we've had to consider the CI/CD pipeline and users who don't want to use VS Code. &lt;/p&gt;

&lt;p&gt;In our test setup shell script we inspect an environment variable which will only be present in our dev container and combine settings from more than one YAML file by using the &lt;code&gt;-f&lt;/code&gt; parameter. We can set environment variables in the dev container using the &lt;code&gt;containerEnv&lt;/code&gt; property in &lt;code&gt;devcontainer.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOCALSTACK_HOST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml up &lt;span class="nt"&gt;-d&lt;/span&gt; localstack
&lt;span class="k"&gt;else
    &lt;/span&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.yml &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.devcontainer.yml  up &lt;span class="nt"&gt;-d&lt;/span&gt; localstack
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.5'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localstack/localstack:0.12.15&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEFAULT_REGION=eu-west-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DEBUG=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LAMBDA_EXECUTOR=docker&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock'&lt;/span&gt;
    &lt;span class="na"&gt;ports&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;4567:4566'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.devcontainer.yml&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.5'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;localstack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;paymentslocalstack&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HOSTNAME_EXTERNAL=paymentslocalstack&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&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;payment_network&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"containerEnv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"LOCALSTACK_HOST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paymentslocalstack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"LOCALSTACK_PORT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4566"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Specifying the &lt;code&gt;container_name&lt;/code&gt; in the devcontainer compose file means we've got a consistent hostname we can use to address the LocalStack container and we expose that inside the dev container using an environment variable.Another thing to remember about container networking is that containers on the same network don't need to use the mapped external port. That's only required for the host to container communication. We've also added this as an environment variable so we can use it in our tests.&lt;/p&gt;

&lt;p&gt;The final issue we had with networking was LocalStack specific. Many AWS services publish metadata which includes the host name i.e. SQS queue URLs. This metadata is fundamental to how they operate. We need to tell LocalStack the new hostname by setting the &lt;code&gt;HOSTNAME_EXTERNAL&lt;/code&gt; environment variable in that container which you can see in the second docker-compose yaml file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Now we've got a repeatable way to onboard new team members, no one should ever install the wrong version of Python again. &lt;/p&gt;

&lt;p&gt;Instead of taking hours or even days to get their system setup, possibly being guided by someone else on the squad, new team members can get themselves up and running in minutes.&lt;/p&gt;

&lt;p&gt;Hopefully some of these fixes will be useful for you when you setup a dev container for your project.&lt;/p&gt;

&lt;p&gt;The next step for us is to investigate how we can use this with GitHub Code Spaces.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Date handling bites again</title>
      <dc:creator>Ben Crinion</dc:creator>
      <pubDate>Wed, 23 Sep 2020 08:11:17 +0000</pubDate>
      <link>https://dev.to/b3ncr/date-handling-bites-again-5f1</link>
      <guid>https://dev.to/b3ncr/date-handling-bites-again-5f1</guid>
      <description>&lt;p&gt;Hi&lt;/p&gt;

&lt;p&gt;One of my team mates found an interesting issue in our code-base so I thought I'd do a write-up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR: Don't compare a DateTimeOffset to DateTime.MinValue, compare it with DateTimeOffset.MinValue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Technically you could compare DateTimeOffset with DateTime.MinValue if you can guarantee that the system timezone will always be UTC or UTC minus some offset.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Into The Detail
&lt;/h2&gt;

&lt;p&gt;Every request my teammate mate to our API was failing with the same exception, but despite running the master branch and the exact same request, I couldn't reproduce the error.&lt;/p&gt;

&lt;p&gt;The exception was being thrown by the constructor of an &lt;code&gt;AbstractValidator&lt;/code&gt; from the FluentValidation package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class TestValidator : AbstractValidator&amp;lt;ThingWithDate&amp;gt;
{
    public TestValidator()
    {
        RuleFor(x =&amp;gt; x.Date)
            .GreaterThan(default(DateTime));
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This validator was injected into nearly every controller in our API via a BaseController (👍).&lt;/p&gt;

&lt;p&gt;It turns out that to reproduce the issue I needed to run the service using the same system timezone as my colleague in the Balkans (UTC+3).&lt;/p&gt;

&lt;p&gt;You can see from the stack trace that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We're initialising the &lt;code&gt;TestValidator&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;That is hitting the &lt;code&gt;DateTimeOffset&lt;/code&gt; implicit cast operator to convert the &lt;code&gt;DateTime&lt;/code&gt; to a &lt;code&gt;DateTimeOffset&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;That calls the &lt;code&gt;DateTimeOffset&lt;/code&gt; constructor which accepts one &lt;code&gt;DateTime&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;The constructor calls &lt;code&gt;ValidateDate(DateTime, Timespan)&lt;/code&gt;. The &lt;code&gt;TimeSpan&lt;/code&gt; is the difference between UTC and the local system timezone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Internally a &lt;code&gt;DateTimeOffset&lt;/code&gt; is a &lt;code&gt;DateTime&lt;/code&gt; and an offset in minutes. &lt;code&gt;ValidateDate&lt;/code&gt;, unsurprisingly, ensures that the &lt;code&gt;DateTime&lt;/code&gt; is valid when the offset is applied. When you apply a negative offset to &lt;code&gt;DateTime.MinValue&lt;/code&gt; you get a date that can't be represented by the &lt;code&gt;DateTime&lt;/code&gt; type and you get an &lt;code&gt;ArgumentOutOfRangeException&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Here is the code for the validate method  &lt;a href="https://github.com/microsoft/referencesource/blob/master/mscorlib/system/datetimeoffset.cs"&gt;lifted from here&lt;/a&gt;. Comments are mine...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static DateTime ValidateDate(DateTime dateTime, TimeSpan offset)
{
    // +2 is 72000000000 ticks
    // utcTicks is converting the "local" dateTime to UTC by subtracting the offset
    Int64 utcTicks = dateTime.Ticks - offset.Ticks;

    // This section validates that utcTicks is a valid time that can be represented

    // utcTicks = -72000000000
    // DateTime.MinTicks = 0
    // -72000000000 &amp;lt; 0 == true =&amp;gt; Kablamo
    if (utcTicks &amp;lt; DateTime.MinTicks || utcTicks &amp;gt; DateTime.MaxTicks)
    {
        throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("Argument_UTCOutOfRange"));
    }
    return new DateTime(utcTicks, DateTimeKind.Unspecified);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
