<?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: Tom Harvey</title>
    <description>The latest articles on DEV Community by Tom Harvey (@tomharvey).</description>
    <link>https://dev.to/tomharvey</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%2F75087%2F029f1198-cb43-4535-bec4-d6cd62fccff9.jpeg</url>
      <title>DEV Community: Tom Harvey</title>
      <link>https://dev.to/tomharvey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tomharvey"/>
    <language>en</language>
    <item>
      <title>Integration tests with CDK and Python - a more real world app, less hello world</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Mon, 26 Feb 2024 21:25:11 +0000</pubDate>
      <link>https://dev.to/tomharvey/integration-tests-with-cdk-and-python-a-more-real-world-app-less-hello-world-2b5c</link>
      <guid>https://dev.to/tomharvey/integration-tests-with-cdk-and-python-a-more-real-world-app-less-hello-world-2b5c</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/tomharvey/integration-tests-with-cdk-and-python-is-my-cloud-native-app-doing-what-i-want-2805"&gt;previous post&lt;/a&gt; I took the fastest path to building a CDK app with Python with a passing Integration Test. It showed how to create AWS services using CDK and then use &lt;code&gt;integ-runner&lt;/code&gt; to automate the deployment of the services, use the services and assert that the result is as expected.&lt;/p&gt;

&lt;p&gt;But, the CDK stack, the test runner, and the assertions were all written in one file.&lt;/p&gt;

&lt;p&gt;In a traditional CDK we would have a more sensible application layout. We want to define our Constructs and Stacks in the main application, then import those into a test file. This allows us to have re-usable Constructs and Stack, which is a core benefit of CDK.&lt;/p&gt;

&lt;p&gt;We want an application layout more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-cdk-app/
├─ my_cdk_app/
│  ├─ my_stack.py
│  ├─ lambda/
│  │ ├─ index.py
├─ test/
│  ├─ integ_my_stack.py
├─ app.py
├─ cdk.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a green, passing test, we will refactor the code into more of a real world CDK app than a "hello world" app. We will also cover some of the rough edges in using &lt;code&gt;integ-runner&lt;/code&gt; - a developer preview tool, which very much feels developer preview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tomharvey/integration-tests-with-CDK-and-python/tree/part-two"&gt;You can see the full code for this post here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test ... or tests
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;cdk init&lt;/code&gt; command will bootstrap your CDK app, including a "unit test" inside the &lt;code&gt;/tests&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;But, out of the box, &lt;code&gt;integ-runner&lt;/code&gt; expects your tests to live in the &lt;code&gt;/test&lt;/code&gt; (singular) folder.&lt;/p&gt;

&lt;p&gt;Let's first fix this small inconsistency, and (more importantly) introduce ourselves to how we can better configure our integration tests.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;integ.config.json&lt;/code&gt;. This file contains the runtime options you want for your testing. Here's mine:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxWorkers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"parallelRegions"&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;"eu-west-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&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;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inspect-failures"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"update-on-failed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"directory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tests"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see what each of these options does in the &lt;a href="https://github.com/aws/aws-cdk/tree/main/packages/%40aws-cdk/integ-runner#options"&gt;integ-runner docs. These are the command line options, made into JSON&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The important thing we will do here is change the value of the &lt;code&gt;directory&lt;/code&gt; config to &lt;code&gt;"tests"&lt;/code&gt; and then move all of the contents of the &lt;code&gt;test&lt;/code&gt; directory in there. We will create a new folder in tests called &lt;code&gt;integration&lt;/code&gt; to keep these tidy and separate from the unit tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir tests/integration
mv test/* tests/integration/
rm -r test/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, now, we can re-run &lt;code&gt;integ-runner&lt;/code&gt; to check that we're still green.&lt;/p&gt;

&lt;p&gt;While it took 3 or 4 minutes to run initially, when we re-run it here it'll pass in around 10 seconds. &lt;/p&gt;

&lt;p&gt;We've moved the files around, but the deployed stack would have been the same as before, so it doesn't need to deploy the services in AWS.&lt;/p&gt;

&lt;p&gt;You should see a successful test run output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Verifying integration &lt;span class="nb"&gt;test &lt;/span&gt;snapshots...

  UNCHANGED  integ_hello 13.031s

Snapshot Results: 

Tests:    1 passed, 1 total

Running integration tests &lt;span class="k"&gt;for &lt;/span&gt;failed tests...

Running &lt;span class="k"&gt;in &lt;/span&gt;parallel across regions: eu-west-1

Test Results: 

Tests:    0 passed, 0 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reusable constructs
&lt;/h2&gt;

&lt;p&gt;Our test file also contains the Stack that we would want to use in our App.&lt;/p&gt;

&lt;p&gt;We can't import the Stack from that &lt;code&gt;integ_hello.py&lt;/code&gt; file into our main App. So, we can &lt;strong&gt;only&lt;/strong&gt; deploy it through the test.&lt;/p&gt;

&lt;p&gt;Not much use.&lt;/p&gt;

&lt;p&gt;Let's create a new file in our main application directory &lt;code&gt;touch integration_tests_with_cdk_and_python/hello_world_stack.py&lt;/code&gt; and add the stack definition to that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# integration_tests_with_cdk_and_python/hello_world_stack.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorldStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyFunction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getcwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/lambda&lt;/span&gt;&lt;span class="sh"&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;Note that I’m now defining the path to the lambda relative to the ‘cwd’ instead of relative to the test file; which we were doing previously. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The cwd is the Current Working Directory; which is where you run your scripts from, almost always the root of your app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, move the lambda code out of the test directory into the root of the app:&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;mv &lt;/span&gt;tests/integration/lambda/ &lt;span class="nb"&gt;.&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, we should be able to import that into our &lt;code&gt;integ_hello.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/integration/integ_hello.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;integration_tests_with_cdk_and_python.hello_world_stack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HelloWorldStack&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;When we run &lt;code&gt;integ-runner&lt;/code&gt; now, we get an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ModuleNotFoundError: No module named &lt;span class="s1"&gt;'integration_tests_with_cdk_and_python'&lt;/span&gt;
  ERROR      integ_hello.py &lt;span class="o"&gt;(&lt;/span&gt;undefined/eu-west-1&lt;span class="o"&gt;)&lt;/span&gt; 5.054s
      Error during integration &lt;span class="nb"&gt;test&lt;/span&gt;: Error: Command exited with status 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can't find our Stack now that we've defined it in our main app.&lt;/p&gt;

&lt;p&gt;I've raised this bug  &lt;a href="https://github.com/aws/aws-cdk/issues/29196"&gt;as an issue here&lt;/a&gt;, so if it's fixed there, then this section might not be a problem anymore.&lt;/p&gt;

&lt;p&gt;When integ-runner runs, it can only see files in the same directory (or below) the directory where our test file lives. So, only files inside &lt;code&gt;tests/integration/&lt;/code&gt; for us. That's no use.&lt;/p&gt;

&lt;p&gt;To make this easier to work around, I created a package called &lt;a href="https://pypi.org/project/cdk-integ-runner-cwd-fix/"&gt;cdk-integ-runner-cwd-fix&lt;/a&gt; which we can use.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;cdk-integ-runner-cwd-fix&lt;/code&gt; to your &lt;code&gt;requirements-dex.txt&lt;/code&gt; file and re-run &lt;code&gt;pip install -r requirements-dev.txt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So, now, our &lt;code&gt;tests/integration/integ_hello.py&lt;/code&gt; file can be updated to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Import the Stack from the main directory&lt;/li&gt;
&lt;li&gt;Use cdk-integ-runner-cwd-fix to fix the bug so that import works&lt;/li&gt;
&lt;li&gt;Remove the old Stack definition we had in the test file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/integration/integ_hello.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cdk_integ_runner_cwd_fix&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fix_cwd&lt;/span&gt;

&lt;span class="c1"&gt;# This needs to be before any import from your CDK app to work around a bug in the CDK
&lt;/span&gt;&lt;span class="nf"&gt;fix_cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;integration_tests_with_cdk_and_python.hello_world_stack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HelloWorldStack&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloWorldStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HelloTestStack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;integration_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntegTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Integ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;function_invoke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integration_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;function_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpectedResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;StatusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="s"&gt;Hello world&lt;/span&gt;&lt;span class="sh"&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;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now, you need to set &lt;code&gt;CDK_INTEG_RUNNER_CWD&lt;/code&gt; to your project's root directory before running integ-runner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CDK_INTEG_RUNNER_CWD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; integ-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, we should be back to a green test.&lt;/p&gt;

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

&lt;p&gt;Now, we have a Stack defined in the main application, as we would usually write a CDK app. We can import that into a test file and run tests against it. We can import that into an app.py file and deploy it for real.&lt;/p&gt;

&lt;p&gt;So, from here, you should be able to return to your usual CDK workflow, but use integration tests to check that your lambdas behave, and your S3 buckets can accept files, or IAM roles can perform certain tasks ... whatever AWS service you want to automate the deployment, execution and assertion of.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>development</category>
      <category>python</category>
    </item>
    <item>
      <title>Integration Tests with CDK and Python - is my cloud native app doing what I want?</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Tue, 20 Feb 2024 17:12:41 +0000</pubDate>
      <link>https://dev.to/tomharvey/integration-tests-with-cdk-and-python-is-my-cloud-native-app-doing-what-i-want-2805</link>
      <guid>https://dev.to/tomharvey/integration-tests-with-cdk-and-python-is-my-cloud-native-app-doing-what-i-want-2805</guid>
      <description>&lt;p&gt;CDK is a great way to setup your infrastructure, but all too often you deploy your new services and then need to go "click", "click", "click". But, we're all developers who like automated tests, and hate click-ops.&lt;/p&gt;

&lt;p&gt;Here, I will show you how to build an AWS Lambda service and use the command line to run an automated test which will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deploy an AWS service (in 3 regions concurrently!)&lt;/li&gt;
&lt;li&gt;Invoke the lambda&lt;/li&gt;
&lt;li&gt;Assert that the response matches what you expect&lt;/li&gt;
&lt;li&gt;Destroy the service and clean up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That all sounds like a slow process, but the testing tool (&lt;a href="https://github.com/aws/aws-cdk/tree/main/packages/%40aws-cdk/integ-runner"&gt;integ-runner&lt;/a&gt;) creates snapshots of your stacks and only tests those which have changed.&lt;/p&gt;

&lt;p&gt;We show how to test a lambda deployment, but you can use this approach to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check that you can (or cannot - because of permissions) get and put objects to an S3 bucket&lt;/li&gt;
&lt;li&gt;Call an API gateway or Load Balancer to test your web service running in Fargate&lt;/li&gt;
&lt;li&gt;Put a message into an SQS queue and wait for a timeout to check some async process completed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the complete code for this on GitHub - &lt;a href="https://github.com/tomharvey/integration-tests-with-CDK-and-python/tree/part-one"&gt;https://github.com/tomharvey/integration-tests-with-CDK-and-python/tree/part-one&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test first
&lt;/h2&gt;

&lt;p&gt;We will write the test first and take the quickest path to making that test pass.&lt;/p&gt;

&lt;p&gt;If you're new to CDK and python, &lt;a href="https://dev.to/tomharvey/getting-started-with-aws-cdk-using-python-and-vscode-dev-containers-2lpa"&gt;see my blog post here for getting a running development environment&lt;/a&gt;. If you follow that post, you'll be ready to start.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup a new cdk project
&lt;/h3&gt;

&lt;p&gt;This is all python, so &lt;code&gt;cdk init --language python&lt;/code&gt; will bootstrap a new CDK project for us.&lt;/p&gt;

&lt;p&gt;And, you'll want to install integ-runner with &lt;code&gt;npm install -g @aws-cdk/integ-runner&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning - this “integ-runner” tool is in developer preview mode! So it will change and there are rough edges. Some very rough ones. But, please use the comments if you're following along and something stops working.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, add &lt;code&gt;aws_cdk.integ_tests_alpha&lt;/code&gt; to the requirements-dev.txt file and install all the requirements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
pip install -r requirements-dev.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Create your test file
&lt;/h3&gt;

&lt;p&gt;CDK has bootstrapped a lot of directories and files for us. Including a &lt;code&gt;tests&lt;/code&gt; directory. How kind.&lt;/p&gt;

&lt;p&gt;That seems like a nice place to put your integration tests. But integ-runner expects them to be in a &lt;code&gt;test&lt;/code&gt; (singular) directory. How irritating (the rough edges get rougher, don't worry). And, while we can tell integ-runner to use any directory, let's go ahead and stick with the defaults.&lt;/p&gt;

&lt;p&gt;So, make a test folder with &lt;code&gt;mkdir test&lt;/code&gt; in the terminal, or however you like to create new tests in your project.&lt;/p&gt;

&lt;p&gt;And create a file in there called &lt;code&gt;integ_.hello.py&lt;/code&gt; to create a new test file for the "hello world" Lambda we will create.&lt;/p&gt;

&lt;p&gt;Use the terminal &lt;code&gt;touch test/integ_.hello.py&lt;/code&gt; or however you like to add files to your project.&lt;/p&gt;

&lt;p&gt;It's important that this file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Starts with &lt;code&gt;integ_.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ends with &lt;code&gt;.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So be careful how you name these files. But anything can go between the integ_. and the .py&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Write a CDK app and stack
&lt;/h3&gt;

&lt;p&gt;We're going to write this directly in the test file.&lt;/p&gt;

&lt;p&gt;You might be used to writing the app in &lt;code&gt;app.py&lt;/code&gt; and creating your stacks elsewhere. Maybe even building re-usable constructs, but we will leave that tidy approach to another day. This is about getting quickly to a passing test.&lt;/p&gt;

&lt;p&gt;So, below is a single file with the:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App&lt;/li&gt;
&lt;li&gt;Stack&lt;/li&gt;
&lt;li&gt;Test deployment of your services to AWS&lt;/li&gt;
&lt;li&gt;Instruction to invoke the lambda&lt;/li&gt;
&lt;li&gt;Assertion of the response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will go though this file line-by-line at the end of the article. But, right now, it's a race to get a passing test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;aws_cdk&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;constructs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloTestStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyFunction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./lambda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloTestStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HelloTestStack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;integration_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntegTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Integ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;function_invoke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integration_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;function_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpectedResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;StatusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="s"&gt;Hello world&lt;/span&gt;&lt;span class="sh"&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;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And you will also need your lambda function code in a folder.&lt;/p&gt;

&lt;p&gt;You can create this folder and file in the terminal 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="nb"&gt;mkdir test&lt;/span&gt;/lambda
&lt;span class="nb"&gt;touch test&lt;/span&gt;/lambda/index.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or however you like to add folders and files to your project. But, yes, we're going to create our lambda code package inside the test folder for now. There is a "rough edge" otherwise, and we're aiming for tests passing.&lt;/p&gt;

&lt;p&gt;And in that file in &lt;code&gt;test/lambda/index.py&lt;/code&gt; you will want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/lambda/index.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is a lambda function, which, when invoked will return "Hello world".&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Run your test for the first time
&lt;/h3&gt;

&lt;p&gt;Now, you can run &lt;code&gt;integ-runner&lt;/code&gt; to run the test, and we will get our first failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Verifying integration test snapshots...

  NEW        integ_.hello 5.812s

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

&lt;/div&gt;



&lt;p&gt;This is an expected error.&lt;/p&gt;

&lt;p&gt;Because it takes time to deploy services and run them, integ-runner will create "snapshots" of your stacks. It saves what your stack looks like, so when you next run the test it knows if anything has changed.&lt;/p&gt;

&lt;p&gt;But the first time you run it - there is no snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Really run your test
&lt;/h3&gt;

&lt;p&gt;You want to use an additional option to tell integ-runner to go ahead and test any changed, or new stacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;integ-runner &lt;span class="nt"&gt;--update-on-failed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take a few minutes...&lt;/p&gt;

&lt;p&gt;You'll see that "NEW" error that you saw above, but then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running integration tests for failed tests...
Running in parallel across regions: us-east-1, us-east-2, us-west-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is cool - it's deploying your lambda to 3 regions and running the tests in all 3 regions at the same time.&lt;/p&gt;

&lt;p&gt;You can change the regions that you want to use. But let’s leave that for another time. &lt;/p&gt;

&lt;p&gt;You can also change the props passed into your stack. So you could write tests to deploy your lambda with python version 3.12 as well as 3.11 and test things before you deploy updated 3.12 to production. Or ARM alongside x86. But, not today. We're going for the quickest route to a passing test.&lt;/p&gt;

&lt;p&gt;Once we have a green test we can refactor and do more complex things.&lt;/p&gt;

&lt;p&gt;After about 3 or 4 min you should get a passing result! Looking like the below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Verifying integration test snapshots...

  NEW        integ_.hello 5.715s

Snapshot Results: 

Tests:    1 failed, 1 total
Failed: /workspaces/py-integ-runner/test/integ_.hello.py

Running integration tests for failed tests...

Running in parallel across regions: us-east-1, us-east-2, us-west-2
Running test /workspaces/python-aws-cdk-integration-test-playground/py-integ-runner/test/integ_.hello.py in us-east-1
  SUCCESS    integ_.hello-Integ/DefaultTest 189.601s
       AssertionResultsLambdaInvoke24cf31b9b6a07a940ece1b49bb7eb7b2 - success

Test Results: 

Tests:    1 passed, 1 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the test file doing?
&lt;/h2&gt;

&lt;p&gt;First up is the Stack that you would create. Here, it just defines a single lambda, but in a real world example it could define a lot more.&lt;/p&gt;

&lt;p&gt;Note how the &lt;code&gt;Code.from_asset&lt;/code&gt; sets the path to the lambda code files (‘./lambda’) and how the &lt;code&gt;handler&lt;/code&gt; specifies that it should execute the function called “handler” inside the file called “index.py”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloTestStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;construct_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MyFunction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PYTHON_3_11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.handler&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;aws_lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_asset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./lambda&lt;/span&gt;&lt;span class="sh"&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;Next, we instantiate the App and the Stack. This is usually in &lt;code&gt;app.py&lt;/code&gt; on the root of your CDK project. But we need a separate App for this testing process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloTestStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HelloTestStack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we define a Test, and one single test case (this is where you could otherwise pass multiple test cases, where your lambda would have different python versions, architectures, or entirely different domain code).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;integration_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntegTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Integ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_cases&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We tell the Test what actions we want to take on each test_case stack. Here, we invoke the lambda.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;function_invoke&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;integration_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lastly (almost), we assert that the response is as expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;function_invoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;integ_tests_alpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExpectedResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object_like&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;StatusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="s"&gt;Hello world&lt;/span&gt;&lt;span class="sh"&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;Finally - for real - and this is often missing from the IntegTest documentation, you need to &lt;code&gt;app.synth()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Don't forget that final line there. And this synth function call needs to come after all of your assertions. &lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;We now have a green and passing test. It's time to refactor.&lt;/p&gt;

&lt;p&gt;But, there are some very rough edges to refactoring. See &lt;a href="https://pypi.org/project/cdk-integ-runner-cwd-fix/"&gt;https://pypi.org/project/cdk-integ-runner-cwd-fix/&lt;/a&gt; for some hints on how to get around those edges.&lt;/p&gt;

&lt;p&gt;You also have a pattern for how to test and run deployed services end to end. So, it's time to apply that to your SQS/RDS/S3 or whatever other services.&lt;/p&gt;

&lt;p&gt;Finally, you have an integration test, but that doesn't mean you don't still benefit from unit tests. A unit test to check that the handler returns "Hello world" - without deploying infrastructure - would run in milliseconds. So, this 4 minute integration test is only one piece of your development framework.&lt;/p&gt;

&lt;p&gt;How will you fit it into your testing approach?&lt;/p&gt;

</description>
      <category>aws</category>
      <category>testing</category>
      <category>development</category>
      <category>python</category>
    </item>
    <item>
      <title>Cloud native apps with AWS CDK, Python and VSCode Dev Containers</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Sat, 17 Feb 2024 15:02:23 +0000</pubDate>
      <link>https://dev.to/tomharvey/getting-started-with-aws-cdk-using-python-and-vscode-dev-containers-2lpa</link>
      <guid>https://dev.to/tomharvey/getting-started-with-aws-cdk-using-python-and-vscode-dev-containers-2lpa</guid>
      <description>&lt;p&gt;When starting a new project I reach for Python and AWS.&lt;/p&gt;

&lt;p&gt;To help me build cloud-native Python apps, I've been using AWS's CDK. This "Cloud Development Kit" allows me to create storage or compute using Python and have my infrastructure live alongside my domain logic.&lt;/p&gt;

&lt;p&gt;And I write all of this in VSCode, using their "Developer Containers" features to keep things standardised and 'simple'.&lt;/p&gt;

&lt;p&gt;When I use a Dev Container, I don't need to worry about having python installed on my laptop. I don't need to fuss around with virtualenvs. My laptop can be left with the base OS and no funny business with Brew or Pyenv or Conda or whatever else I need to experiment with.&lt;/p&gt;

&lt;p&gt;I just need my laptop to have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Docker Desktop (or an equivalent)&lt;/li&gt;
&lt;li&gt;VSCode&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's it.&lt;/p&gt;

&lt;p&gt;I can now write apps in whatever Python version I want. And you'll see how easy it would be to extend to TypeScript, Go, Rust or whatever.&lt;/p&gt;

&lt;p&gt;So, install Docker Desktop (or an equivalent) and VSCode and then these few simple steps will get you building a cloud native app in no time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are "Dev Containers"?
&lt;/h3&gt;

&lt;p&gt;These are machines running inside of your "host" machine. The "host" machine is your laptop (or desktop).&lt;/p&gt;

&lt;h5&gt;
  
  
  Self contained
&lt;/h5&gt;

&lt;p&gt;Dev containers are self-contained environments. They contain Python and all of the other dependancies that you need to get the work done.&lt;/p&gt;

&lt;p&gt;One doesn't conflict with another. So, if one project needs Python 3.7 and another needs 3.12 - no big deal. For all that you're using your one laptop these Dev Containers might as well be separate machines.&lt;/p&gt;

&lt;p&gt;Your dev container will exist on your laptop, and your desktop in an identical way. Or on your colleague's laptop in an identical way. Or on a big powerful AWS instance in an identical way.&lt;/p&gt;

&lt;p&gt;So, team mates can have identical environments. And, as a solo-coder you can rough out the easy stuff on your laptop and then easily run that dev container on an AWS instance with 96 CPUs 768GB of RAM, 8 GPUS and a 100Gbps network connection (if you have the $18.30/hour to spend on such hardware).&lt;/p&gt;

&lt;p&gt;The point is, your developer environment is the same regardless of the underlying hardware. It's repeatable and reliable. It doensdoesn't rely on you remembering to &lt;code&gt;brew install foo&lt;/code&gt; and if there is a version conflict you need to perform some magical steps.&lt;/p&gt;

&lt;p&gt;Finally, these dev containers can be scrapped easily when you're done with some experiment; without leaving a trail of old versions of Python on your 'host machine'.&lt;/p&gt;

&lt;h3&gt;
  
  
  The template project
&lt;/h3&gt;

&lt;p&gt;I have a template project at &lt;a href="https://github.com/tomharvey/python-aws-cdk-devcontainer" rel="noopener noreferrer"&gt;https://github.com/tomharvey/python-aws-cdk-devcontainer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is really just one file!&lt;/p&gt;

&lt;p&gt;Inside a directory called &lt;code&gt;.devcontainer&lt;/code&gt; i have a &lt;code&gt;devcontainer.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It very very heavily commented, but as an overview, this file tells VSCode that you want a certain flavour of developer environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Python 3.12 on Debian Bullseye - yes, even if you're on a mac or on windows.&lt;/li&gt;
&lt;li&gt;With some additional tools installed:

&lt;ol&gt;
&lt;li&gt;AWS CLI so we can interact with AWS&lt;/li&gt;
&lt;li&gt;AWS CDK so we can build infrastructure&lt;/li&gt;
&lt;li&gt;AWS SSO Util so we can login to AWS using SSO (if we need)&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;It's setup so we share the AWS Config folder on our machine with this new developer environment. That way our AWS Auth can be shared between the host machine and the dev environment.&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  Usage
&lt;/h3&gt;

&lt;h5&gt;
  
  
  1. Get my template
&lt;/h5&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/tomharvey/python-aws-cdk-devcontainer" rel="noopener noreferrer"&gt;https://github.com/tomharvey/python-aws-cdk-devcontainer&lt;/a&gt; and select the "Use this template" option to "create a new repository".&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%2Fuploads%2Farticles%2F98053b80hc99ghu8ftm6.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%2Fuploads%2Farticles%2F98053b80hc99ghu8ftm6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  2. Create a new Project from the tempalte.
&lt;/h5&gt;

&lt;p&gt;You can now setup your new project as a new GitHub repository.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give it a name. "My Awesome new App" or "Python CDK Playground"&lt;/li&gt;
&lt;li&gt;Keep it private or make it public. Your decision.&lt;/li&gt;
&lt;/ol&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%2Fuploads%2Farticles%2Fdo39v0wyhtf839px146e.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%2Fuploads%2Farticles%2Fdo39v0wyhtf839px146e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  3. Wait for it ....
&lt;/h5&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%2Fuploads%2Farticles%2Ft0ra0hmahym3e884vofb.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%2Fuploads%2Farticles%2Ft0ra0hmahym3e884vofb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  4. Copy that Github URL
&lt;/h5&gt;

&lt;p&gt;You now have a new repo in your own github account. Time to open that in VSCode. So, copy the repo URL using SSH or HTTPS - however you like to interact with GitHub.&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%2Fuploads%2Farticles%2Fqbn4kwutrzz098w2rf98.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%2Fuploads%2Farticles%2Fqbn4kwutrzz098w2rf98.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  5. Open VSCode
&lt;/h5&gt;

&lt;p&gt;Now, go into VSCode and ensure you have installed the Dev Containers extension. &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers" rel="noopener noreferrer"&gt;See here to install that extension&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  6. Use Dev Containers Extension
&lt;/h5&gt;

&lt;p&gt;And bring up the "Command palette" from the "View" menu, or by ⌘-shift-P (ctrl-shift-p on Windows) and within that "Command palette" type "dev con clone" so it offers you the option you want:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dev Containers: Clone Repository in Named Container Volume...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click on that.&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%2Fuploads%2Farticles%2Fieuvyreak5r5n8qpk12e.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%2Fuploads%2Farticles%2Fieuvyreak5r5n8qpk12e.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  7. Clone the github repo
&lt;/h5&gt;

&lt;p&gt;Then paste in the GitHub repo URL you copied in step 3 and hit the enter button.&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%2Fuploads%2Farticles%2F2kzxex1f6x7bwwhumw6c.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%2Fuploads%2Farticles%2F2kzxex1f6x7bwwhumw6c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then select the &lt;code&gt;main&lt;/code&gt; branch (which is likely the only option available to you)&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%2Fuploads%2Farticles%2Fle1fjdeqqq3bp3zgihwf.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%2Fuploads%2Farticles%2Fle1fjdeqqq3bp3zgihwf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally enter a new "volume name" - this is what Docker will name the volume which stores all your code - use the same name as the git hub repo.&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%2Fuploads%2Farticles%2F7l22rootw0w2l7w43wm1.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%2Fuploads%2Farticles%2F7l22rootw0w2l7w43wm1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  8. Use your new code environment
&lt;/h5&gt;

&lt;p&gt;After a few minutes (depending on your network connection) you will have a fully running Python environment with all of the AWS and CDK dependancies installed and ready to go.&lt;/p&gt;

&lt;p&gt;Setup the name of your project. In the template I've named it "My awesome developement environment", but you will want to make it your own.&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%2Fuploads%2Farticles%2Focyjntwrs57qqhnrwu6l.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%2Fuploads%2Farticles%2Focyjntwrs57qqhnrwu6l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open a terminal from the "terminal" menu - or with the &lt;code&gt;ctrl shift ~&lt;/code&gt; keyboard shortcut. And run &lt;code&gt;python --version&lt;/code&gt; to see that you do indeed have python running as expected at version 3.12&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%2Fuploads%2Farticles%2Fth7vvuwo76ybzqha89dz.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%2Fuploads%2Farticles%2Fth7vvuwo76ybzqha89dz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;You can now run the cdk command to setup a new cdk app and follow along with the AWS CDK Workshop or use the CDK API reference to build a cloud native app.&lt;/p&gt;

&lt;p&gt;Your developer environment is entirely separate from your "host machine". It's transportable, and its definition lives alongside your code. Any new developer can pick up your app and use VSCode to immediately get an exact copy of your developer environment.&lt;/p&gt;

&lt;p&gt;And, you didn't need to think one time about installing Python, setting up a virtualenv or whether you should use &lt;code&gt;brew install&lt;/code&gt; or &lt;code&gt;apt install&lt;/code&gt; or whatever when things went wrong.&lt;/p&gt;

&lt;p&gt;Finally - while this will get you up and running, the &lt;code&gt;devcontainer.json&lt;/code&gt; &lt;a href="https://github.com/tomharvey/python-aws-cdk-devcontainer/blob/main/.devcontainer/devcontainer.json" rel="noopener noreferrer"&gt;file is heavily commented&lt;/a&gt; so it should be self-explanitory. Please read through it and make it your own. Fork it. Raise a Pull Request to add or change things. And let me know how you prefer your developer environment.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>cloud</category>
      <category>development</category>
    </item>
    <item>
      <title>Top Resources To Get A Role In Climate Change</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Mon, 05 Feb 2024 12:50:49 +0000</pubDate>
      <link>https://dev.to/tomharvey/top-resources-to-get-a-role-in-climate-change-326f</link>
      <guid>https://dev.to/tomharvey/top-resources-to-get-a-role-in-climate-change-326f</guid>
      <description>&lt;p&gt;So, you want to make the world a better place.&lt;/p&gt;

&lt;p&gt;Your generation's defining challenge is right in front of you, and it's hard to waste your time and skills on anything else.&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%2Fuploads%2Farticles%2Fjw3qsb5eyv4u6usll72d.gif" 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%2Fuploads%2Farticles%2Fjw3qsb5eyv4u6usll72d.gif" alt="Don't waste your time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a market of layoffs and downturns, climate companies are thriving and need your valuable skills.&lt;/p&gt;

&lt;p&gt;So, to help you find a job that pays you to truly save the world, here are the best places to find your next climate impact role.&lt;/p&gt;

&lt;p&gt;If you've got a great one to add - please post to the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Job boards, communities and fellowships?
&lt;/h2&gt;

&lt;p&gt;Most of these resources will provide at least one of these services.&lt;/p&gt;

&lt;p&gt;The job board is one you likely know - there will be a list of jobs and you can apply for them. Simple!&lt;/p&gt;

&lt;p&gt;The communities around these job boards are invaluable too. You'll find very active Slack, Whatsapp or Discord communities where people will share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;online job fairs where you can meet the companies who are hiring&lt;/li&gt;
&lt;li&gt;hackathons&lt;/li&gt;
&lt;li&gt;local and online events&lt;/li&gt;
&lt;li&gt;reading recommendations to keep you informed&lt;/li&gt;
&lt;li&gt;more jobs, or short term gigs&lt;/li&gt;
&lt;li&gt;zoom workshops&lt;/li&gt;
&lt;li&gt;open source projects&lt;/li&gt;
&lt;li&gt;experiences in getting a job in climate&lt;/li&gt;
&lt;li&gt;and so much more...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get involved so you're first to know of new jobs and you're informed when you get that first interview.&lt;/p&gt;

&lt;p&gt;Most require that you fill out a short form to talk about why you're joining. Once you're in, you will find me online - so fell free to find a "Thomas Harvey" and say "hi" 👋&lt;/p&gt;

&lt;h3&gt;
  
  
  Fellowships
&lt;/h3&gt;

&lt;p&gt;Some of these communities provide "fellowships" or "accelerators" where they offer courses (for a price) to help you move into a climate career.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I get no affiliate payments from these - I want to mention them so you can decide if/when to use them for yourself&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both &lt;a href="https://www.terra.do/climate-education/cohort-courses/climate-change-learning-for-action/" rel="noopener noreferrer"&gt;Terra.do&lt;/a&gt; and &lt;a href="https://climatebase.org/fellowship" rel="noopener noreferrer"&gt;ClimateBase&lt;/a&gt; provide a really high caliber program with established industry contacts and both currently have open applications for fellowships - until Feb 23 2024 - so if you're interested, don't delay.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where do I click?
&lt;/h2&gt;

&lt;p&gt;Here are my favorites - plus one that I've never used. Dive in, and good luck! We need you over here building important solutions!&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%2Fuploads%2Farticles%2F53f8vsbesc4wg5cc7exf.gif" 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%2Fuploads%2Farticles%2F53f8vsbesc4wg5cc7exf.gif" alt="Join us"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Climatebase
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://climatebase.org" rel="noopener noreferrer"&gt;https://climatebase.org&lt;/a&gt;&lt;br&gt;
A busy job board where you can browse by focus and location.&lt;/p&gt;

&lt;p&gt;If you've a specific interest within climate - agriculture, electricity, carbon capture - then climatebase's job board will help you filter by your interest and location.&lt;/p&gt;

&lt;p&gt;It's also got a very active, and growing community - &lt;a href="https://climatebase.org/onboarding" rel="noopener noreferrer"&gt;here's a direct link to getting signed up&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terra.do
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.terra.do" rel="noopener noreferrer"&gt;https://www.terra.do&lt;/a&gt;&lt;br&gt;
A community focused on learning.&lt;/p&gt;

&lt;p&gt;I've heard great things about their &lt;a href="https://www.terra.do/climate-education/cohort-courses/climate-change-learning-for-action/" rel="noopener noreferrer"&gt;accelerator course&lt;/a&gt; from alumni - some of whom I've hired. &lt;/p&gt;

&lt;p&gt;Their focus on providing you with knowledge will ensure that you're confident when you go into that job interview.&lt;/p&gt;

&lt;h3&gt;
  
  
  Work on climate
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://workonclimate.org" rel="noopener noreferrer"&gt;https://workonclimate.org&lt;/a&gt;&lt;br&gt;
A thriving community full of events&lt;/p&gt;

&lt;p&gt;WoCl (yip - you gotta know the acronym) have been a driving force in making climate job hunting easy. Their strength is really in the events and the slack community that they organise.&lt;/p&gt;

&lt;p&gt;You'll find all of the resources from WoCl to be really welcoming, and the fact that they and Terra.do work so closely together is testament to the collaborative and welcoming values they hold. Who cares who makes the world a better place - so long as it gets better!&lt;/p&gt;

&lt;h3&gt;
  
  
  Voyagers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://voyagers.io/climate/" rel="noopener noreferrer"&gt;https://voyagers.io/climate/&lt;/a&gt;&lt;br&gt;
Super focused on community and growing together&lt;/p&gt;

&lt;p&gt;If you're interested in networking with people in climate then this is the place for you. It's open for people at all stages of career, so you really get exposure to a broad spectrum of engineers, designers, investors, managers and executive.&lt;/p&gt;

&lt;p&gt;This isn't a message board you can lurk around on, they will push you to grow your network. Their activities are often outside in nature, so get out and breathe that fresh air you're fighting to save.&lt;/p&gt;

&lt;h3&gt;
  
  
  Women and climate
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.womenandclimate.co" rel="noopener noreferrer"&gt;https://www.womenandclimate.co&lt;/a&gt;&lt;br&gt;
I'm not part of this one because I don't meet the criteria. But, I hear great things about it.&lt;/p&gt;

&lt;p&gt;If you're looking to get into climate, it's super welcoming. If you're an experienced speaker, they are very pro-active in making sure there is more representation on speaking panels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leafr
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.leafr.work" rel="noopener noreferrer"&gt;https://www.leafr.work&lt;/a&gt;&lt;br&gt;
Freelancers and short term gig opportunities.&lt;/p&gt;

&lt;p&gt;The other communities usually have a "gigs" channel announcing short term contract work. This one is focused specifically on short-term engagements.&lt;/p&gt;

&lt;p&gt;If you're looking to dip your toes in before you commit, or you want to spend some of your free time on a climate impact role, then this would be a great place to get listed and look out for that role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;Getting involved is the first step.&lt;/p&gt;

&lt;p&gt;Now, really get involved, take part in that thriving community. Find the local chapters and meetups that you can join ... or start.&lt;/p&gt;

&lt;p&gt;And, if you're interested in what kind of skills help you stand out in this job market we can talk more about that another time.&lt;/p&gt;

&lt;p&gt;You'll find me on the slacks I mentioned above, or ask questions in the comments.&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%2Fuploads%2Farticles%2Flvaw7qg6zygy63sbbjum.gif" 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%2Fuploads%2Farticles%2Flvaw7qg6zygy63sbbjum.gif" alt="Be the change"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>climate</category>
      <category>career</category>
      <category>jobs</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Doubly meaningful variable names</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Mon, 07 Nov 2022 17:58:00 +0000</pubDate>
      <link>https://dev.to/inrange/doubly-meaningful-variable-names-jg8</link>
      <guid>https://dev.to/inrange/doubly-meaningful-variable-names-jg8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Header image by&lt;/em&gt; &lt;a href="https://www.pexels.com/photo/thoughtful-mature-man-standing-behind-glass-near-wall-4347190/"&gt;&lt;em&gt;Lucian Petrean CC&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Giving meaningful names to our functions and variables helps communicate the context around our code.&lt;/p&gt;

&lt;p&gt;Inspired by a &lt;a href="https://www.linkedin.com/posts/lowellbander_so-giddy-to-be-writing-important-code-activity-6991156524966432768-c5Oc"&gt;LinkedIn post from Lowell Bander&lt;/a&gt; I recognised another layer of meaning in my code; one that communicates the motivation for why I code.&lt;/p&gt;

&lt;p&gt;Here’s a snippet I was working on:&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%2Fztbam0kuepj4izgeqbzf.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%2Fztbam0kuepj4izgeqbzf.png" alt="Meaningful code about solar generation and carbon savings" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve already posted about &lt;a href="https://dev.to/inrange/put-a-spark-in-your-career-path-37mn"&gt;why now is the best time to work in the energy industry&lt;/a&gt; and it’s nice to have frequent reminders that the work is important. We’re installing solar and avoiding carbon emissions - tonnes of it!&lt;/p&gt;

&lt;p&gt;Just as we all prefer different naming conventions, we all find motivation from different places. I hope that you can look at your code today, put aside debates about &lt;a href="https://en.m.wikipedia.org/wiki/Hungarian_notation"&gt;Hungarian notation&lt;/a&gt;, &lt;a href="https://en.m.wikipedia.org/wiki/Camel_case"&gt;camelCasing&lt;/a&gt; or &lt;a href="https://peps.python.org/pep-0008/"&gt;PEP 8&lt;/a&gt; and recognise something which gives you a sense of meaning and purpose.&lt;/p&gt;

</description>
      <category>career</category>
      <category>motivation</category>
      <category>climate</category>
    </item>
    <item>
      <title>Put a spark in your career</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Tue, 20 Sep 2022 10:02:15 +0000</pubDate>
      <link>https://dev.to/inrange/put-a-spark-in-your-career-path-37mn</link>
      <guid>https://dev.to/inrange/put-a-spark-in-your-career-path-37mn</guid>
      <description>&lt;p&gt;The best time to work in energy is right now. It's the original high-tech industry but has been slow-moving. This stagnation, alongside recent transformations, has created the challenges and opportunities that make it a rewarding, engaging, and vital space to work.&lt;/p&gt;

&lt;p&gt;Climate change is our generation's biggest problem, but you know that already. Solving that problem was the hook that drew &lt;em&gt;me&lt;/em&gt; in, and it remains a daily driver, keeping me engaged. I won't be joining the &lt;a href="[https://www.cnbc.com/2022/07/26/tech-to-climate-career-changes-why-these-workers-left-jobs-like-google.html]"&gt;chorus of voices convincing you to start your journey on a mission-led career path&lt;/a&gt;; you're already convinced. Right now, a confluence of factors is creating massive opportunity, and that's why now is the best time to join us, building in energy.&lt;/p&gt;

&lt;p&gt;Decarbonization requires that the industry decentralizes generation and embraces digitization. This industry currently relies on &lt;a href="https://www.bbc.co.uk/rd/blog/2016-01-35-million-people-didnt-notice-a-thing-dot-dot-dot"&gt;'test match cricket'&lt;/a&gt; as a communication protocol; dragging it towards current technologies is today's most exciting engineering challenge.&lt;/p&gt;

&lt;p&gt;The existing grid operates under a century-old assumption that millions of consumers can and will rely on a few large generators. Energy flows in one direction from one place. That assumption is already broken and grows more wrong by the day. As more small local generation comes online, we each become both a source and a consumer of energy. &lt;a href="https://en.wikipedia.org/wiki/Duck_curve"&gt;Incumbents see this as a problem&lt;/a&gt;, while we see that as democratisation; and as a challenge that brings opportunity.&lt;/p&gt;

&lt;p&gt;Commercial and industrial energy purchasers already ask for more than the industry and their energy suppliers can provide. They want real-time, high-frequency data from a supplier who measures latency in days and frequencies in half-hours. The physical grid operates at 50Hz, and as a software engineer, you're no stranger to serving inside of 20 milliseconds.&lt;/p&gt;

&lt;p&gt;These are seismic shifts for something that underpins everything in our society; these shifts are only now beginning to accelerate, and InRange is taking advantage.&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%2Fxb313xzrnispucfm779v.jpg" 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%2Fxb313xzrnispucfm779v.jpg" alt="Messy electricity grid" width="768" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Image by mckaysavage is licensed under CC BY 2.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At InRange, we use AI to understand, forecast, and optimize generation and consumption within local energy markets, extracting more value from each electron. The more value we can generate, the quicker we grow and decarbonize.&lt;/p&gt;

&lt;p&gt;We're using modern data pipelines to enable high-frequency measurement of renewable generation, hyper-local consumption, and carbon avoidance. Energy consumers need this level of data for real-time, 24/7 matching of their demand with zero-carbon energy.&lt;/p&gt;

&lt;p&gt;We're productizing a buying experience that has traditionally been a slow enterprise-led process. Our product pairs the user with AI to deliver our business proposition quickly, efficiently, and with minimal input. Business buyers have tremendous purchasing power, so giving them access to great software allows them to buy more faster.&lt;/p&gt;

&lt;p&gt;These technologies and approaches have never been applied to the electricity industry like this before. Our success will write the operating system for the emerging energy market - decentralized, digitized, and decarbonized.&lt;/p&gt;

&lt;p&gt;Across the industry, there are exciting opportunities in product, data, and engineering. Beyond "just" a chance to work on an exciting and vital problem, at InRange, we're building a team that holds its values above all else. &lt;a href="http://inrange.io/careers"&gt;Here's what we're looking to hire&lt;/a&gt;. We'd love to see you join us.&lt;/p&gt;

</description>
      <category>career</category>
      <category>climate</category>
      <category>developer</category>
    </item>
    <item>
      <title>Running remote GUI apps with the infinite power of the cloud.</title>
      <dc:creator>Tom Harvey</dc:creator>
      <pubDate>Tue, 20 Sep 2022 09:42:48 +0000</pubDate>
      <link>https://dev.to/inrange/running-remote-gui-apps-with-the-infinite-power-of-the-cloud-2clp</link>
      <guid>https://dev.to/inrange/running-remote-gui-apps-with-the-infinite-power-of-the-cloud-2clp</guid>
      <description>&lt;p&gt;Remote development environments allow us to shift our computing needs to the infinite processing and high bandwidth offered by the cloud. It creates a shared, collaborative space that's close to our production environment. The growing toolchain around them is an exciting part of a new developer experience. But I'd never used it with a GUI app until now.&lt;/p&gt;

&lt;p&gt;The below takes about 10-15minutes, and you get a remote, powerful desktop environment ready to run whatever Linux GUI tool you need; scientific research, 3D modelling, or just renting a faster computer for a while.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://qgis.org"&gt;QGIS&lt;/a&gt; is a tool to help load, analyse and visualise geospatial data. But they don't have a build for my M1-based Mac; plus, I want to load a lot of data into it.&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%2Fzl7fbn5roxe4fhx6snb8.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%2Fzl7fbn5roxe4fhx6snb8.png" alt="GeoSpatial layers" width="750" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd turn to &lt;a href="https://code.visualstudio.com/docs/remote/remote-overview"&gt;VSCode remote development&lt;/a&gt; or a &lt;a href="https://aws.amazon.com/sagemaker/studio/"&gt;SageMaker Studio&lt;/a&gt; Notebook for command line or scripting work. For a GUI app, I need to add a desktop environment to Ubuntu and access that through VNC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start with an Ubuntu 22.04 EC2 instance.
&lt;/h3&gt;

&lt;p&gt;I've used &lt;a href="https://aws.amazon.com/cdk/"&gt;CDK&lt;/a&gt; to spin this up, but go with the console, the AWS CLI, or whatever is comfortable for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;instance = ec2.Instance(self, "DevInstance",
    instance_type=ec2.InstanceType('t3.micro'),
    machine_image=ec2.MachineImage.generic_linux(
        {'eu-west-1': 'ami-0d75513e7706cf2d9'}
        # The above is Ubuntu22.04 in the Ireland region
        # If you use another region, you'll want to find
        # the AMI for your region
    ),
    block_devices=[
        ec2.BlockDevice(
        device_name="/dev/sda1",
        volume=ec2.BlockDeviceVolume.ebs(
        )
    ],
    vpc=ec2.Vpc.from_lookup(self, "VPC", is_default=True)
)
instance.role.add_managed_policy(
    policy=iam.ManagedPolicy.from_aws_managed_policy_name(
        "AmazonSSMManagedInstanceCore"
    )
)
CfnOutput(self, "InstancePublicIp",
    value=instance.instance_id,
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gain CLI access to the EC2 instance.
&lt;/h3&gt;

&lt;p&gt;I'm using AWS's SSM Session Manager, so I don't need to worry about ssh keys or opening ports.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ssm start-session --target $AWS_INSTANCE_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install a desktop environment and a VNC server.
&lt;/h3&gt;

&lt;p&gt;I'm using Tiger VNC and securing it with a password. Do this as the ubuntu user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo su
su ubuntu

sudo apt update

sudo apt install -y \
    xfce4 xfce4-goodies \
    tigervnc-standalone-server tigervnc-common tigervnc-tools

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

&lt;/div&gt;



&lt;p&gt;You'll now need to do a "first run" of the vnc server to do some initial configuration - like setting a password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vncserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will prompt you to create a password and optionally create a "view only" password so that user can only view. I didn't set a view only, you probably don't need that either.&lt;/p&gt;

&lt;p&gt;We can now continue with the setup of the VNC server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;gt; /home/ubuntu/.vnc/xstartup&amp;lt;&amp;lt; EOF
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
/usr/bin/startxfce4
[ -x /etc/vnc/xstartup ] &amp;amp;&amp;amp; exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] &amp;amp;&amp;amp; xrdb $HOME/.Xresources
x-window-manager &amp;amp;
EOF

chmod u+x /home/ubuntu/.vnc/xstartup

echo ':1=ubuntu' | sudo tee -a /etc/tigervnc/vncserver.users

sudo systemctl start tigervncserver@:1.service
sudo systemctl enable tigervncserver@:1.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Allow access to port 5901 on the server.
&lt;/h3&gt;

&lt;p&gt;I've used SSM Session Manager to create a tunnel. This way, localhost:5901 tunnels across to remote:5901; saving me from opening that port in AWS and making everything more secure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws ssm start-session --target  $AWS_INSTANCE_ID \
    --document-name AWS-StartPortForwardingSession \
    --parameters \
    '{"portNumber":["5901"], "localPortNumber":["5901"]}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Open a VNC client connection.
&lt;/h3&gt;

&lt;p&gt;macOS comes with one! &lt;a href="https://www.realvnc.com/en/connect/download/viewer/windows"&gt;Or, download a VNC client for your machine&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Mac's finder, use the "Go" menu, select "Connect to server" (or press command-K), and add the VNC connection path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vnc://127.0.0.1:5901
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fsrecjm1et1zwgxl7wrdi.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%2Fsrecjm1et1zwgxl7wrdi.png" alt="macOS VNC Connection" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I have the desktop environment of that Ubuntu server in a window on my local Mac. I can install QGIS on that server or whatever GUI app I want. That remote machine has an ultra-fast network connection and only needs to stream the (low bandwidth) screen activity down my network connection. I can keep costs down by using a small AWS instance, or if I need to load more data, I can have up to 24TB of RAM and hundreds of CPUs! My lowly laptop can really be a wolf in sheep's clothing.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>gis</category>
      <category>gui</category>
      <category>vnc</category>
    </item>
  </channel>
</rss>
