<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: andrea</title>
    <description>The latest articles on Forem by andrea (@andreav).</description>
    <link>https://forem.com/andreav</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%2F225180%2F33c0516f-a78c-41c8-9152-b1c2df084e1f.jpeg</url>
      <title>Forem: andrea</title>
      <link>https://forem.com/andreav</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andreav"/>
    <language>en</language>
    <item>
      <title>AWS profile chooser</title>
      <dc:creator>andrea</dc:creator>
      <pubDate>Sat, 25 Jun 2022 13:37:17 +0000</pubDate>
      <link>https://forem.com/andreav/aws-profile-chooser-4ekf</link>
      <guid>https://forem.com/andreav/aws-profile-chooser-4ekf</guid>
      <description>&lt;p&gt;Super short AWS hint.&lt;/p&gt;

&lt;p&gt;Having different AWS profiles, I do not want to pick default one just because I forgot &lt;code&gt;--profile&lt;/code&gt; command line option.&lt;/p&gt;

&lt;p&gt;Just looking for a way of forcing me having to choose an AWS profile before issuing any command.&lt;/p&gt;

&lt;p&gt;My solution was (for bash only):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Removing default profile in ~/.aws/credentials&lt;br&gt;
No command will work from now on without a &lt;code&gt;--profile&lt;/code&gt; or an AWS_PROFILE environment variable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating a one-liner alias for choosing the desired profile and setting $AWS_PROFILE environment variable.&lt;br&gt;&lt;br&gt;
An alias let me reuse the same shell I'm in.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;aws-profile-chooser&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'x() { select prof in $( aws configure list-profiles ) ; do  export AWS_PROFILE="$prof"; break; done }; x'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(you can see the &lt;a href="https://gist.github.com/andreav/b5188a8640a9e612d39bc52743abab99"&gt;gist here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This command will show me available profiles, and after choosing the right one, it will set AWS_PROFILE environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws-profile-choose 
1&lt;span class="o"&gt;)&lt;/span&gt; default
2&lt;span class="o"&gt;)&lt;/span&gt; amplify-udemy
3&lt;span class="o"&gt;)&lt;/span&gt; aws-lambda-deploy
4&lt;span class="o"&gt;)&lt;/span&gt; test-aws
&lt;span class="c"&gt;#?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on if I forget to choose a profile, any command will break.&lt;/p&gt;

&lt;p&gt;Choosing and setting an AWS profile...is now a breeze! ;)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using dotnet IConfiguration inside xUnit projects</title>
      <dc:creator>andrea</dc:creator>
      <pubDate>Sun, 19 Jun 2022 15:57:32 +0000</pubDate>
      <link>https://forem.com/andreav/using-dotnet-iconfiguration-inside-xunit-projects-4o6b</link>
      <guid>https://forem.com/andreav/using-dotnet-iconfiguration-inside-xunit-projects-4o6b</guid>
      <description>&lt;p&gt;In a .NET core XUnit project, configuration functionality is not readily available.&lt;/p&gt;

&lt;p&gt;My need is using secrets inside UI tests (i.e. for signing in before testing the web app) without committing them into the repository.&lt;/p&gt;

&lt;p&gt;I read different solutions and found many third party libraries but in the end I chose a simple approach, a few lines of code easily adaptable to different needs, still functional both for local development and CI/CD environments.&lt;/p&gt;

&lt;p&gt;I created a simple &lt;a href="https://github.com/andreav/xunit-dotnet-iconfig"&gt;github repo&lt;/a&gt; for demonstration purposes.&lt;/p&gt;

&lt;p&gt;Basically there are 2 files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;appsettings.json - you can commit it, but without sensitive data. Here you put your config, using placeholders for sensitive data.&lt;/li&gt;
&lt;li&gt;appsettings.local.json - you must not commit it, list it in your .gitignore file. Here you put you sensitive data, overriding placeholders from appsettings.json.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then you have to copy those files to output directory, adding this to your .csproj:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class="na"&gt;Condition=&lt;/span&gt;&lt;span class="s"&gt;"'$(Configuration)' == 'Debug'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;None&lt;/span&gt; &lt;span class="na"&gt;Update=&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt; &lt;span class="na"&gt;CopyToOutputDirectory=&lt;/span&gt;&lt;span class="s"&gt;"PreserveNewest"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class="na"&gt;Condition=&lt;/span&gt;&lt;span class="s"&gt;"'$(Configuration)' == 'Debug'"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;None&lt;/span&gt; &lt;span class="na"&gt;Update=&lt;/span&gt;&lt;span class="s"&gt;"appsettings.local.json"&lt;/span&gt; &lt;span class="na"&gt;CopyToOutputDirectory=&lt;/span&gt;&lt;span class="s"&gt;"PreserveNewest"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the code. &lt;/p&gt;

&lt;p&gt;Create an utilty class (&lt;code&gt;TestConfigHelper&lt;/code&gt;) for setting up IConfiguration in every test (you can adjust this class as needed, an example will be reported in the following).&lt;/p&gt;

&lt;p&gt;This is where you configure the Configuration service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IConfigurationRoot&lt;/span&gt; &lt;span class="nf"&gt;GetIConfigurationRoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.local.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&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;In this code we take out configuration from three places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first appsetting.json&lt;/li&gt;
&lt;li&gt;then appsetting.local.json&lt;/li&gt;
&lt;li&gt;in the end from the environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The order matters. appsetting.local.json will override appsetting.json, and environment variables will override everything.&lt;/p&gt;

&lt;p&gt;In this way you can use appsetting.local.json for local development, but also inject sensitive data through environment variables in you CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;Your tests should inherit from a BaseTestClass which loads configuration invoking &lt;code&gt;TestConfigHelper::GetIConfigurationRoot&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;And that's all. You can now access the configuration using the object returned from &lt;code&gt;GetIConfigurationRoot&lt;/code&gt; in every test.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bonus #1 - configuration class
&lt;/h1&gt;

&lt;p&gt;I like creating a class representing my application configuration, and bind it to the configuration.&lt;br&gt;
&lt;code&gt;TestConfigHelper&lt;/code&gt; has an example of this. The code is like this (where &lt;code&gt;MyAppConfig&lt;/code&gt; is the class representing the configuration, stored under "MyApp" root node in the configuration file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;MyAppConfig&lt;/span&gt; &lt;span class="nf"&gt;GetMyAppConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MyAppConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;GetIConfigurationRoot&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MyApp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;configuration&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;h1&gt;
  
  
  Bonus #2: user secrets
&lt;/h1&gt;

&lt;p&gt;Moreover, for local development, you can configure user secrets. This requires some more configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating and setting a secret
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet user-secrets init
dotnet user-secrets set key value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Adding the user secrets key to the &lt;code&gt;.csproj&lt;/code&gt; file:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;UserSecretsId&amp;gt;&lt;/span&gt;your-secret-id-here&lt;span class="nt"&gt;&amp;lt;/UserSecretsId&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Configuring also Secrets in &lt;code&gt;TestConfigHelper.cs&lt;/code&gt;, something like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IConfigurationRoot&lt;/span&gt; &lt;span class="nf"&gt;GetIConfigurationRoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConfigurationBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJsonFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appsettings.local.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reloadOnChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddUserSecrets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"e3dfcccf-0cb3-423a-b302-e3e92e95c128"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;// &amp;lt;-- this is the new line !!&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEnvironmentVariables&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&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;



</description>
    </item>
    <item>
      <title>QA - no excuses!</title>
      <dc:creator>andrea</dc:creator>
      <pubDate>Mon, 24 Jan 2022 17:17:19 +0000</pubDate>
      <link>https://forem.com/andreav/qa-no-excuses-1779</link>
      <guid>https://forem.com/andreav/qa-no-excuses-1779</guid>
      <description>&lt;p&gt;I made a very interesting journey into the world of Continuous Integration and QA. The result is a &lt;a href="https://github.com/andreav/jenkins-docker-qa" rel="noopener noreferrer"&gt;project&lt;/a&gt; based on Docker, Jenkins and some really interesting tools I've tried to put together.&lt;/p&gt;

&lt;p&gt;Not only do I ask you to download and try it, but above all to recommend new tools to integrate to get the best pipeline ever!&lt;/p&gt;

&lt;p&gt;This project is a starter kit for setting up a Jenkins docker installation for CI and QA.&lt;br&gt;&lt;br&gt;
&lt;em&gt;I developed on Windows, with Docker Desktop with WSL 2 engine and VS Code, but with minor adjustments (before all slash notation for command line commands) it should run on Linux too.&lt;/em&gt;  &lt;/p&gt;
&lt;h2&gt;
  
  
  Tools integrated
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Jenkins is run inside Docker and pipelines run inside docker agents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The automation server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.jenkins.io/projects/jcasc/" rel="noopener noreferrer"&gt;Jenkins Configuration as Code&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Jenkins plugin for run an image pre-provisioned / pre-configured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://plugins.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins plugins&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Many Jenkins plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.sonarqube.org/" rel="noopener noreferrer"&gt;SonarQube&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Static code analyser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Database for storing Sonarqube results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://xunit.net/" rel="noopener noreferrer"&gt;xUnit.Net&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Testing framework - used for Unit test, UI tests, Coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.selenium.dev/" rel="noopener noreferrer"&gt;Selenium&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Web browser automation for UI tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;k6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Load testing tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Visualization tool for analyzing load test results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.influxdata.com/" rel="noopener noreferrer"&gt;influxdb&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Database for storing k6 results used by k6 and Grafana&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  docker compose up
&lt;/h2&gt;

&lt;p&gt;Project ships with some docker compose files for bringing up main parts of the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;/docker-compose.yml&lt;br&gt;
Brings up the core infrastructure: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jenkins&lt;/li&gt;
&lt;li&gt;local docker registry (insecure version)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/sonarqube/docker-compose.yml&lt;br&gt;
Brings up sonarqube infrastructure&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;postregs database&lt;/li&gt;
&lt;li&gt;sonarqube server&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/selenium/docker-compose.yml&lt;br&gt;
Brings up selenium infrastructure&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;selenium-hub&lt;/li&gt;
&lt;li&gt;chrome node&lt;/li&gt;
&lt;li&gt;firefox node&lt;/li&gt;
&lt;li&gt;edge node&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;/k6/docker-compose.yml&lt;br&gt;
Brings up Grafana infrastructure&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;influxdb:1.8&lt;/li&gt;
&lt;li&gt;Grafana with pre-provisioned dashboards&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the full system up&amp;amp;running with just one command you can issue (&lt;strong&gt;from the root of the repo&lt;/strong&gt;):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose -f .\docker-compose.yaml -f .\sonarqube\docker-compose.yaml -f .\k6\docker-compose.yaml up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You should see all containers coming up (following picture is taken from VS Code Docker extension):&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%2Fm4sxqi4tn0oqsdhx79ut.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%2Fm4sxqi4tn0oqsdhx79ut.png" title="Containers up&amp;amp;running" alt="Containers up&amp;amp;running"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: selenium containers are not run with this command because the grid will be automatically run on demand by the Jenkins before running UI tests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: before running Sonarqube, be sure to meet &lt;a href="https://hub.docker.com/_/sonarqube" rel="noopener noreferrer"&gt;Docker Host Requirements&lt;/a&gt;&lt;br&gt;
Usually you should issue these command:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  sysctl -w vm.max_map_count=524288
  sysctl -w fs.file-max=131072`
  ulimit -n 131072
  ulimit -u 8192
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;If everything went well, you now have many systems ready:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;URL&lt;/th&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:8181" rel="noopener noreferrer"&gt;http://localhost:8181&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Sonarqube server - User: admin / Pasw: admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:4444" rel="noopener noreferrer"&gt;http://localhost:4444&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Selenium Grid (only available while UI tests runs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;⚠️ Security Notes ⚠️&lt;br&gt;
This stack is not production ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For running docker agents from Jenkins, I exposed docker daemon on tcp://localhost:2375 without TLS locally &lt;/li&gt;
&lt;li&gt;I relaxed some Jenkins &lt;a href="https://content-security-policy.com/" rel="noopener noreferrer"&gt;CSP&lt;/a&gt; to correctly visualize k6 HTML reports.
If you do not need this feature, feel free to remove &lt;code&gt;hudson.model.DirectoryBrowserSupport.CSP&lt;/code&gt; directive inside /jenkins/Dockerfile  file.&lt;/li&gt;
&lt;li&gt;Docker registry is just meant for hosting local built images ( used as Jenkins Pipeline Docker agents) so it runs insecure (without certificates)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Jenkins
&lt;/h2&gt;

&lt;p&gt;The first piece of the puzzle is Jenkins, the automation tool.&lt;br&gt;
A custom docker image is built starting from the official one for minor adjustments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disable wizard setup&lt;/li&gt;
&lt;li&gt;setting up Jenkins Configuration as Code plugin directory&lt;/li&gt;
&lt;li&gt;setting up pre-provisioned jobs&lt;/li&gt;
&lt;li&gt;modifying Content Security Policy for supporting k6 HTML reports&lt;/li&gt;
&lt;li&gt;adding docker and docker-compose to the image. This is useful when using the default agent, for running docker commands like bringing up a selenium grid&lt;/li&gt;
&lt;li&gt;pre-provisioning the image with a bunch of plugins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to the JCasC and Seed Job plugins when we start Jenkins we will find all the job folder containing test jobs alredy in place:&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%2Falb2clq2vd5k9q84b6bb.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%2Falb2clq2vd5k9q84b6bb.png" title="Jenkins job folders" alt="Jenkins job folders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: all the Jenkins configuration (JCasC and Jobs) is stored into separate files into /jenkins/casc_configs folder. You can add files here to automatically create new jobs or modify Jenkins configuration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: remember to &lt;a href="https://stackoverflow.com/a/45771737/1966366" rel="noopener noreferrer"&gt;approve&lt;/a&gt; scripts before runnig them otherwise they'll fail.&lt;br&gt;
You can also edit and re-save job configuration for approving jobs.&lt;/em&gt;&lt;/p&gt;



&lt;h2&gt;
  
  
  Test job folder &lt;em&gt;"docker"&lt;/em&gt;:
&lt;/h2&gt;

&lt;p&gt;Here you will find some tests for basic usage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;testing the default agent&lt;/li&gt;
&lt;li&gt;testing a docker agent with an image from &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;docker hub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;locally building a test image and pushing it lo the local Docker registry&lt;/li&gt;
&lt;li&gt;testing a docker agent with a custom local image pulled by the local registry (the one previously built)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these pieces are used in following pipelines, so it seems a good idea to test the basic blocks&lt;/p&gt;



&lt;h2&gt;
  
  
  Test job folder &lt;em&gt;"sonarqube"&lt;/em&gt;:
&lt;/h2&gt;

&lt;p&gt;In this folder you'll find jobs for executing &lt;em&gt;static code analysis&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For this purpose I used Sonarqube. Sonarqube has a client/server architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;server - started with the docker compose command previously described&lt;/li&gt;
&lt;li&gt;client - the scanner - analyses the project and sends data to the server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Jenkins, I'm running &lt;a href="https://dotnet.microsoft.com/en-us/" rel="noopener noreferrer"&gt;dotnet core&lt;/a&gt; static analysis in two falvours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with a netcore scanner built on the fly&lt;/li&gt;
&lt;li&gt;with a netcore scanner pre-built, pulled from the local registry. This is meant to speed up this phase of the pipeline, using an agent with java and the netcore scanner already installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose the very interesting &lt;a href="https://github.com/EdwinVW/pitstop" rel="noopener noreferrer"&gt;pitstop netcore project&lt;/a&gt; to be analyzed.&lt;/p&gt;

&lt;p&gt;Before running Sonarqube jobs, you need to create a Sonarqube token for the scanner to login into the server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Sonarqube - create a token for admin user

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;, user: admin, password: admin
&lt;/li&gt;
&lt;li&gt;Go to: Administration -&amp;gt; Security -&amp;gt; Users -&amp;gt; "Administrator" Tokens&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;In jenkins - config Sonarqube credential.

&lt;ul&gt;
&lt;li&gt;JCasC has already provisioned a placeholder Sonarqube token, just update its password  with the token just created.
&lt;/li&gt;
&lt;li&gt; Go to  Manage Jenkins -&amp;gt; Manage Credentials -&amp;gt; sonarqube-token-admin -&amp;gt; Update -&amp;gt; Change Password&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These are the jobs in the Sonarqube folder: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;01_build_netcore_sonarscanner_image_and_push_to_local_registry:
Builds a netcore image with java &amp;amp; sonarscanner already installed and puh it to local registry&lt;/li&gt;
&lt;li&gt;02_sonarscanner-in-docker-agent:
Example of integrating jenkins - netcore - sonarqube
After running this job login to Sonarqube &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt; and you'll see:

&lt;ul&gt;
&lt;li&gt;Static code analysis&lt;/li&gt;
&lt;li&gt;Code coverage&lt;/li&gt;
&lt;li&gt;Test results&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;03_sonarscanner-in-docker-agent-netcore-scanner:
Like the job before but faster.
This job uses a docker agent pre-configured for scanning net core projects.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In Sonarqube, the result should looks like this:&lt;/p&gt;

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

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



&lt;h2&gt;
  
  
  Test job folder &lt;em&gt;"unit tests"&lt;/em&gt;:
&lt;/h2&gt;

&lt;p&gt;Here you will find a job example for running netcore unit tests with xUnit.&lt;/p&gt;

&lt;p&gt;We again use the &lt;a href="https://github.com/EdwinVW/pitstop" rel="noopener noreferrer"&gt;pitstop netcore project&lt;/a&gt; which ships with xUnit unit tests.&lt;/p&gt;

&lt;p&gt;This job collects tests results and publishes results using &lt;a href="https://plugins.jenkins.io/xunit/" rel="noopener noreferrer"&gt;xUnit Jenkins plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This job also collects cove coverage and publishes results using &lt;a href="https://plugins.jenkins.io/cobertura/" rel="noopener noreferrer"&gt;cobertura Jenkins plugin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results should look like this after running a couple of builds:&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%2Fwvpktzqpogh66zwh1lah.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%2Fwvpktzqpogh66zwh1lah.png" title="Jenkins xUnit cobertura results" alt="Jenkins xUnit cobertura results"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;h2&gt;
  
  
  Test job folder &lt;em&gt;"selenium"&lt;/em&gt;:
&lt;/h2&gt;

&lt;p&gt;The job runs xunit + selenium UI tests against a basic grid (3 browsers)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; selenium grid is automatically run on demand by the jenkins job and discarded when the pipeline ends.&lt;/p&gt;

&lt;p&gt;You can follow tests progress pointing your browser to &lt;a href="http://localhost:4444" rel="noopener noreferrer"&gt;http://localhost:4444&lt;/a&gt;&lt;/p&gt;

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



&lt;h2&gt;
  
  
  Test job folder &lt;em&gt;"k6"&lt;/em&gt;:
&lt;/h2&gt;

&lt;p&gt;k6 is a wonderful tool for performance and load testing.&lt;/p&gt;

&lt;p&gt;In this job folder we try two integrations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a pipeline running k6 load test and publishing HTML report directly in Jenkins. &lt;br&gt;
After job runs, you should see a new report published like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80i2t72icure36pmhit7.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%2F80i2t72icure36pmhit7.png" title="Jenkins HTML report" alt="Jenkins HTML report"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a pipeline running k6 load test and publishing k6 metrics to Grafana&lt;br&gt;&lt;br&gt;
If you browse to the dashboard from Grafana ("Dashboards" -&amp;gt; "Browse" -&amp;gt; "k6 Load Testing Results") after you run the load test, you will see the dashboard populated like this:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Ftuosvko48xogu4a9bnql.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%2Ftuosvko48xogu4a9bnql.png" title="Grafana k6 dashboard" alt="Grafana k6 dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
HTML Results are obtained converting k6 output to HTML. &lt;br&gt;
This is done using the &lt;a href="https://github.com/benc-uk/k6-reporter" rel="noopener noreferrer"&gt;k6-reporter project&lt;/a&gt;.&lt;br&gt;
The HTML generated is not compliant with Jenkins Content Security Policy so I &lt;a href="https://www.jenkins.io/doc/book/security/configuring-content-security-policy/" rel="noopener noreferrer"&gt;modified it&lt;/a&gt; for allowing correct visualization.&lt;br&gt;
If you do not need this feature, feel free to remove &lt;code&gt;hudson.model.DirectoryBrowserSupport.CSP&lt;/code&gt; directive inside /jenkins/Dockerfile file.&lt;/p&gt;

&lt;p&gt;For visualizing k6 results into Grafana I chose &lt;a href="https://k6.io/docs/results-visualization/influxdb-+-grafana/" rel="noopener noreferrer"&gt;influxdb+grafana integration&lt;/a&gt;.&lt;br&gt;
Grafana is pre-provisioned with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a datasource pointing to influxdb (version 1.8 because version 2.0 is not still supported by k6)&lt;/li&gt;
&lt;li&gt;a &lt;a href="https://grafana.com/grafana/dashboards/2587" rel="noopener noreferrer"&gt;k6 dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Due to &lt;a href="https://github.com/grafana/grafana/issues/10786" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; it is not still possible to provision dashboard with variables in grafana.&lt;br&gt;
So I modified the k6 dashbord replacing the variable "${DS_K6}" with the provisioned datasource UID:  "influxdb-k6-uid".&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I always take great inspiration from open-source projects.&lt;br&gt;
This time I hope to be able to reciprocate.&lt;/p&gt;

&lt;p&gt;This project is still a "work-in-progress", &lt;br&gt;
please let me know which tools can be integrated to release more and more quality software!&lt;/p&gt;

&lt;p&gt;Thank you for reading.&lt;/p&gt;

</description>
      <category>ci</category>
      <category>qa</category>
      <category>devops</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
