<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Untitled Publication]]></title><description><![CDATA[Untitled Publication]]></description><link>https://blog.mwanje.me</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 00:23:40 GMT</lastBuildDate><atom:link href="https://blog.mwanje.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Configure a MongoDB cluster with Ansible]]></title><description><![CDATA[Manual server configuration is always a nightmare, especially when there is a large fleet of servers to manage. However, with Infrastructure as Code(IaC) automation tools like Ansible, all that pain goes away. In this blog, we shall leverage the auto...]]></description><link>https://blog.mwanje.me/configure-a-mongodb-cluster-with-ansible</link><guid isPermaLink="true">https://blog.mwanje.me/configure-a-mongodb-cluster-with-ansible</guid><category><![CDATA[mongodb-cluster]]></category><category><![CDATA[ansible]]></category><category><![CDATA[ansible-playbook]]></category><category><![CDATA[MongoDB]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Fri, 12 Jan 2024 20:45:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cijiWIwsMB8/upload/b0a31d2b1d23b671ba2cf5ae28115d5f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Manual server configuration is always a nightmare, especially when there is a large fleet of servers to manage. However, with Infrastructure as Code(IaC) automation tools like Ansible, all that pain goes away. In this blog, we shall leverage the automation tool to create a MongoDB sharded cluster from scratch in just a few minutes, using <a target="_blank" href="https://github.com/123MwanjeMike/ansible-mongodb">this Ansible role</a>.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ol>
<li><p>Ansible installed on your control node</p>
</li>
<li><p>Seven (or more) target servers running any Ubuntu OS version</p>
</li>
<li><p>SSH access to the target servers</p>
</li>
</ol>
<h2 id="heading-getting-our-hands-dirty"><strong>Getting our hands dirty</strong></h2>
<h3 id="heading-install-the-ansible-role">Install the Ansible Role</h3>
<p>As I mentioned earlier, we shall be using an existing Ansible role and so we won't have to write all the playbooks by ourselves from scratch. However, to install the role, we shall need a file that has it's installation information. A <em>requirements.yml</em> file . Run the commands below to create the file and the directory, <em>ansible_mongodb,</em> which we shall be working.</p>
<pre><code class="lang-bash">mkdir ~/ansible_mongodb
<span class="hljs-built_in">echo</span> <span class="hljs-string">"-   name: 123mwanjemike.ansible_mongodb
    src: https://github.com/123MwanjeMike/ansible-mongodb
    version: v1.0.4"</span> &gt; ~/ansible_mongodb/requirements.yml
</code></pre>
<details><summary>Note on version v1.0.4</summary><div data-type="detailsContent"><em>The latest version of the role, 123mwanjemike.ansible_mongodb, at the time of writing this blog, was v1.0.4. There may however be more recent </em><a target="_blank" href="https://github.com/123MwanjeMike/ansible-mongodb/releases"><em>release versions</em></a><em> at the time you read this which are more recommended to use instead.</em></div></details>

<p>You can now install the role using the Ansible Galaxy command below.</p>
<pre><code class="lang-bash">ansible-galaxy install -r ~/ansible_mongodb/requirements.yml
</code></pre>
<p>Output:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705081849770/0bda5aeb-5031-40a0-9ef2-18d672b0514e.png" alt="Output from running the ansible-galaxy install command" class="image--center mx-auto" /></p>
<h3 id="heading-the-inventory-file">The Inventory File</h3>
<p>Create an inventory using the command below</p>
<pre><code class="lang-bash">mkdir ~/ansible_mongodb/inventory
touch ~/ansible_mongodb/inventory/hosts
</code></pre>
<p>Next, populate the inventory with the target servers grouped according to the respective replicaSets. That is to say, the Config servers replicaSet servers in their group, and the Shard servers in the corresponding group.</p>
<p>Also, make sure to use the Fully Qualified Domain Names(FQDN) of the servers when adding them to the inventory. Below is how my inventory looks like.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">all:</span>
  <span class="hljs-attr">children:</span>            
    <span class="hljs-attr">mongo_cluster:</span>
      <span class="hljs-attr">children:</span>
        <span class="hljs-attr">mongos_routers:</span>
          <span class="hljs-attr">hosts:</span>
            <span class="hljs-attr">mongos-router-0.europe-north1-b.c.oceanic-muse-408212.internal:</span>
        <span class="hljs-attr">config_servers:</span>
          <span class="hljs-attr">hosts:</span>
            <span class="hljs-attr">mongod-cfgsvr-0.europe-north1-b.c.oceanic-muse-408212.internal:</span>
            <span class="hljs-attr">mongod-cfgsvr-1.europe-north1-c.c.oceanic-muse-408212.internal:</span>
            <span class="hljs-attr">mongod-cfgsvr-2.europe-north1-a.c.oceanic-muse-408212.internal:</span>
        <span class="hljs-attr">shard_0:</span>
          <span class="hljs-attr">hosts:</span>
            <span class="hljs-attr">mongod-shard-0-0.europe-north1-b.c.oceanic-muse-408212.internal:</span>
            <span class="hljs-attr">mongod-shard-0-1.europe-north1-c.c.oceanic-muse-408212.internal:</span>
            <span class="hljs-attr">mongod-shard-0-2.europe-north1-a.c.oceanic-muse-408212.internal:</span>
</code></pre>
<h3 id="heading-the-variables">The Variables</h3>
<p>There will be a number of variables at play when using this role. Let's get them right.</p>
<p><strong>Host Variables</strong><br />Run the commands below to put them in place</p>
<ol>
<li><p>All hosts</p>
<pre><code class="lang-bash"> mkdir ~/ansible_mongodb/inventory/group_vars
 <span class="hljs-built_in">echo</span> <span class="hljs-string">"ansible_python_interpreter: /usr/bin/python3"</span> &gt; ~/ansible_mongodb/inventory/group_vars/all.yml
</code></pre>
</li>
<li><p>Router(s)</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"cluster_role: 'router'"</span> &gt; ~/ansible_mongodb/inventory/group_vars/mongos_routers.yml
</code></pre>
</li>
<li><p>Config Servers</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"cluster_role: 'config'

 replica_set:
   name: 'cfgsvr'
   group: 'config_servers'"</span> &gt; ~/ansible_mongodb/inventory/group_vars/config_servers.yml
</code></pre>
</li>
<li><p>Shard_0 Servers</p>
<pre><code class="lang-bash"> <span class="hljs-built_in">echo</span> <span class="hljs-string">"cluster_role: 'shard'

 replica_set:
   name: 'shard-0'
   group: 'shard_0'"</span> &gt; ~/ansible_mongodb/inventory/group_vars/shard_0.yml
</code></pre>
</li>
</ol>
<p><strong>Configuration Variables</strong></p>
<p>You can find <a target="_blank" href="https://github.com/123MwanjeMike/ansible-mongodb/blob/develop/vars/main.yaml">the role's preset configuration variables</a> in the <code>var/main.yml</code> file at the path where the role was installed. For example in my case, it was installed at <code>~/.ansible/roles/123mwanjemike.ansible_mongodb</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705087388438/29a4d481-d23b-464a-a219-9e558f8ebd5b.png" alt class="image--center mx-auto" /></p>
<p>You can change them as you wish, especially the <code>mongo_configs</code> versions and public keys given that they point to Ubuntu Jammy's MongoDB version 7.0 public keys.</p>
<p><strong>Cluster Variables</strong></p>
<p>As for the MongoDB cluster configuration variables, they have been documented on the role's README on GitHub <a target="_blank" href="https://github.com/123MwanjeMike/ansible-mongodb#flags-and-variables">here</a>.</p>
<h3 id="heading-a-playbook">A Playbook</h3>
<p>We're almost good to go. We now just need a playbook to install and configure the MongoDB sharded cluster. I build from the <a target="_blank" href="https://github.com/123MwanjeMike/ansible-mongodb#sample-playbook">sample playbook</a> provided in the README of the role on GitHub. Below is my <code>~/ansible_mongodb/sample_playbook.yml</code> file</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">mongo_cluster</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">adminUser:</span> <span class="hljs-string">"myAdminUser"</span>
    <span class="hljs-attr">adminPass:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># Secret password here</span>
    <span class="hljs-attr">new_shard:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">"shard-0"</span>
      <span class="hljs-attr">group:</span> <span class="hljs-string">"shard_0"</span>
    <span class="hljs-attr">tgt_db:</span> <span class="hljs-string">"myDatabase"</span>
    <span class="hljs-attr">roles:</span> [<span class="hljs-string">"readWrite"</span>, <span class="hljs-string">"userAdmin"</span>]
    <span class="hljs-attr">userName:</span> <span class="hljs-string">"targetUser"</span>
    <span class="hljs-attr">userPass:</span> <span class="hljs-string">""</span> <span class="hljs-comment"># Secret password here</span>
    <span class="hljs-attr">keyfile_src:</span> <span class="hljs-string">"./keyfile"</span>
    <span class="hljs-attr">cfg_server:</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">"cfgsvr"</span>
      <span class="hljs-attr">group:</span> <span class="hljs-string">"config_servers"</span>

  <span class="hljs-attr">pre_tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Generate</span> <span class="hljs-string">random</span> <span class="hljs-string">string</span> <span class="hljs-string">with</span> <span class="hljs-string">OpenSSL</span> <span class="hljs-string">on</span> <span class="hljs-string">ansible</span> <span class="hljs-string">controller</span>
      <span class="hljs-attr">shell:</span> <span class="hljs-string">openssl</span> <span class="hljs-string">rand</span> <span class="hljs-string">-base64</span> <span class="hljs-number">756</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">keyfile</span>
      <span class="hljs-attr">delegate_to:</span> <span class="hljs-string">localhost</span>
      <span class="hljs-attr">args:</span>
        <span class="hljs-attr">creates:</span> <span class="hljs-string">keyfile</span>

  <span class="hljs-attr">roles:</span>
    <span class="hljs-bullet">-</span> { <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>, <span class="hljs-attr">flags:</span> [<span class="hljs-string">"install_mongo"</span>] }
    <span class="hljs-bullet">-</span> { <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>, <span class="hljs-attr">flags:</span> [<span class="hljs-string">"configure_mongo"</span>] }
    <span class="hljs-bullet">-</span> { <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>, <span class="hljs-attr">flags:</span> [<span class="hljs-string">"clear_logs"</span>] }
    <span class="hljs-bullet">-</span> { <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>, <span class="hljs-attr">flags:</span> [<span class="hljs-string">"prepare_members"</span>] }
    <span class="hljs-bullet">-</span> { <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>, <span class="hljs-attr">flags:</span> [<span class="hljs-string">"start_mongo"</span>] }
    <span class="hljs-bullet">-</span> {
        <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>,
        <span class="hljs-attr">flags:</span> [<span class="hljs-string">"init_replica"</span>],
        <span class="hljs-attr">when:</span> <span class="hljs-string">cluster_role</span> <span class="hljs-type">!=</span> <span class="hljs-string">'router'</span>,
      }
    <span class="hljs-bullet">-</span> {
        <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>,
        <span class="hljs-attr">flags:</span> [<span class="hljs-string">"create_admin"</span>],
        <span class="hljs-attr">when:</span> <span class="hljs-string">cluster_role</span> <span class="hljs-type">!=</span> <span class="hljs-string">'router'</span>,
        <span class="hljs-attr">delegate_to:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ groups[replica_set.group] | first }}</span>"</span>,
      }
    <span class="hljs-bullet">-</span> {
        <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>,
        <span class="hljs-attr">flags:</span> [<span class="hljs-string">"add_shard"</span>],
        <span class="hljs-attr">when:</span> <span class="hljs-string">cluster_role</span> <span class="hljs-string">==</span> <span class="hljs-string">'router'</span>,
        <span class="hljs-attr">run_once:</span> <span class="hljs-literal">true</span>,
      }
    <span class="hljs-bullet">-</span> {
        <span class="hljs-attr">role:</span> <span class="hljs-string">123mwanjemike.ansible_mongodb</span>,
        <span class="hljs-attr">flags:</span> [<span class="hljs-string">"create_database"</span>],
        <span class="hljs-attr">when:</span> <span class="hljs-string">cluster_role</span> <span class="hljs-string">==</span> <span class="hljs-string">'router'</span>,
        <span class="hljs-attr">run_once:</span> <span class="hljs-literal">true</span>,
      }
</code></pre>
<hr />
<p>The moment of truth has now come. We shall be running the playbook with the command below and see what happens.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/ansible_mongodb/
ansible-playbook sample_playbook.yml -i inventory/hosts
</code></pre>
<p>Output:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705091039925/b72995d4-c256-4286-88b3-2afc4ade11e6.png" alt="Play recap" class="image--center mx-auto" /></p>
<p>Checking out the cluster 🎉💃🏽🕺🏽😎</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705132956920/4322c80f-a0ac-49e1-8bf6-a2ebd5bd9978.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-closing-remarks">Closing Remarks</h1>
<p>With this Ansible role, we've been able to create and configure a MongoDB sharded cluster in under 10 minutes without writing any single configuration for the servers by ourselves.</p>
<p>Incase you were wondering, I am indeed the author of the role we have just used to create the cluster. I am also working on <a target="_blank" href="https://github.com/123MwanjeMike/mongo-cluster-terraform">this Terraform project</a> that can be used to create the server instances for the sharded cluster used herein. Currently, I have only set up the provider for Google Cloud Platform(GCP) but will be adding Azure, AWS, and other service providers soon. I hope you enjoy them as much as I did when creating them.</p>
<p><em>Cheers!</em></p>
<h2 id="heading-additional-resources">Additional resources</h2>
<ol>
<li><p><a target="_blank" href="https://www.ansible.com/">Ansible</a></p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#roles">[Ansible] Roles</a></p>
</li>
<li><p><a target="_blank" href="https://www.ssl.com/faqs/what-is-a-fully-qualified-domain-name/">What Is a “Fully Qualified Domain Name”?</a></p>
</li>
<li><p><a target="_blank" href="https://www.mongodb.com/docs/manual/core/sharded-cluster-components/#sharded-cluster-components">[MongoDB] Sharded Cluster Components</a></p>
</li>
<li><p><a target="_blank" href="https://www.cloudflare.com/learning/cloud/what-is-a-virtual-private-cloud/">What is a virtual private cloud (VPC)?</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[My experience with GitOps. So far...]]></title><description><![CDATA[GitOps is a modern way to manage cloud-native systems that are powered by Kubernetes. It leverages a policy-as-code approach to define and manage every layer of the modern application stack - infrastructure, networking, application code, and the GitO...]]></description><link>https://blog.mwanje.me/my-experience-with-gitops-so-far</link><guid isPermaLink="true">https://blog.mwanje.me/my-experience-with-gitops-so-far</guid><category><![CDATA[gitops]]></category><category><![CDATA[ArgoCD]]></category><category><![CDATA[Devops]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[AirQo]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Sat, 26 Nov 2022 11:49:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/uRnWbdawNLo/upload/v1669353196078/ugz_NJafKT.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>GitOps is a modern way to manage cloud-native systems that are powered by Kubernetes. It leverages a policy-as-code approach to define and manage every layer of the modern application stack - infrastructure, networking, application code, and the GitOps pipeline itself.<br />-- <a target="_blank" href="https://www.weave.works/technologies/gitops/#what-is-gitops">Weaveworks</a></p>
</blockquote>
<p>GitOps as a concept was established in 2017 by Weaveworks and has since been widely adopted in software delivery with a growing number of CNCF tools being developed around it.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669442879412/u3k0a0Cf1.png" alt="image.png" /></td></tr>
</thead>
<tbody>
<tr>
<td><em>Source: <a target="_blank" href="https://www.weave.works/blog/gitops-high-velocity-cicd-for-kubernetes">GitOps: High velocity CICD for Kubernetes</a></em></td></tr>
</tbody>
</table>
</div><p>While at <a target="_blank" href="https://airqo.net/">AirQo</a>, I've had the opportunity of working in both traditional and GitOps environments and in this article, I share experience from a DevOps perspective after having made the shift in our delivery pipeline.</p>
<h3 id="heading-some-bit-of-context">Some bit of context</h3>
<p>AirQo is a cleantech startup whose vision is <strong>clean air for all African cities</strong>. A key duty in getting there is availing our users with Africa's air quality information and so we have a number software products ranging from mobile applications, web applications, and an API just for that. Our API relies on a couple of microservices in the backend to transform the Particulate Matter(PM) data collected by our air quality monitors into consumption-ready information. We run our microservices on some few Kubernetes(k8s) clusters.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><img src="https://raw.githubusercontent.com/airqo-platform/AirQo-api/staging/docs/img/architecture-diagram.png" alt="AirQo Architecture diagram" /></td></tr>
</thead>
<tbody>
<tr>
<td><em>Architectural diagram</em></td></tr>
</tbody>
</table>
</div><p>We use a Gitflow worflow in <a target="_blank" href="https://github.com/airqo-platform/AirQo-frontend">our mono-repository</a> with all the products and the corresponding k8s manifests in the same repo.</p>
<h3 id="heading-initial-setup">Initial setup</h3>
<p>In the early days, our continuous delivery pipeline had both the integration and deployment done by the same tool, GitHub Actions. All application images pointed to the "latest" tag in within k8s configurations thus it was the de facto for all applications restarts. GitHub Actions built and pushed the images to Google Container Registry(GCR), but was did not update the corresponding k8s manifest with the new image tag despite the fact that it was the uniquely identifiable image tag(e.g prod-docs-123abc). This meant that the deployed state was always divergent from the state on GitHub at the image tag level at least. Some of the challenges we faced include;</p>
<ol>
<li>Difficulty in telling the actual resource definition of applications on the cluster.<br />
There were times when quick/minor fixes were made to applications directly through kubectl without going through the git workflow. It was difficult for the rest of the team to know of such changes given that they were rarely communicated or committed to GitHub. Not to mention that the image tag was obviously one of such application properties we weren't sure about.</li>
<li>The development team lacked visibility into their applications.<br />
Most development team members weren't conversant with k8s and kubectl commands to for instance view live application logs whenever need arose. This made troubleshooting unnecessarily a step longer given since a DevOps engineer was needed to retrieve the logs and thus,</li>
<li>A larger workload for the operations team given that it was an operations-centric approach.</li>
<li>Operations engineers always had to have access to the clusters via a kubeconfig in order to carry out even very basic tasks.</li>
<li>Rolling back changes was more technical  than necessary. Given that we defaulted to the latest tag on GitHub. A DevOps engineer had to get the image tag of a previous application version from GCR, then run a <code>kubectl patch</code> command to update the application image tag with a previous one.</li>
</ol>
<h3 id="heading-how-we-got-there">How we got there</h3>
<p>A mono-repository setup can present some challenges and we faced one ourselves with the different products' delivery pipelines to production getting intertwined; deeming it impossible to deploy a specific product at a time. The CI/CD pipelines were all dependent on the git workflow and thus had the same trigger. Perhaps I can discuss how we went about in another article but during my search for an alternative autonomous trigger, is when I landed on ArgoCD. Up until then, I had miniature knowledge of GitOps but I found it interesting as I read about it. Fascinated by how the methodology would address many challenges we faced, I instantly <a target="_blank" href="https://github.com/airqo-platform/AirQo-api/tree/staging/k8s/argo-cd">installed ArgoCD</a> on one of our clusters. Due to it's multi-tenant nature, I was able to add setup applications in the different clusters and their respective projects under one roof: and subsequently on boarded the entire team onto it. 
We now use ArgoCD as our deploy operator but kept GitHub Actions as our CI tool and <a target="_blank" href="https://github.com/airqo-platform/AirQo-frontend/blob/17cbaacfcff96997a45c50ae095cec4d32ab1fbd/.github/workflows/deploy-frontends-to-staging.yml#L396">Config Updater</a> for image tags on Helm(which we defaulted to).</p>
<h3 id="heading-argocd-removed-pain-points">ArgoCD removed pain points</h3>
<ol>
<li>With ArgoCD, we are notified via slack whenever the state of the deployed application diverges from the state on GitHub. The states are also automatically synced to match the GitHub definition in line with the GitOps principle of Git as the source of truth. This ensures that changes are documented on GitHub as a bare minimum.</li>
<li>The development team is now able to self serve on anything to do with their applications. The ArgoCD UI is so intuitive that they can carry out tasks ranging from creating new applications to accessing logs of existing ones without assistance from the operations team. A more developer-centric approach.</li>
<li>Rollbacks are now much easier. It's now a matter of reverting changes on the GitHub repo and an older image tag will be deployed automatically.</li>
<li>As the DevOps engineer, I am now able to run basic operations on the clusters without using and scripting commands. For instance, I can view health statuses of all applications on the clusters at the comfort of my phone, without running any commands to switch cluster context.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1669317215701/NTtr6eQSN.png" alt="image.png" /></p>
<h3 id="heading-final-words">Final words</h3>
<p>Our adoption of GitOps is recent but I can definitely tell that there's a lot we have to benefit from it at AirQo as we continue to explore it. I thus generally believe that what I've shared here is only but the surface and trust that you and your organisation could discovered that this methodology solves challenges you had gotten accustomed when you try it.</p>
<p>Until next time... <em>Happy hacking</em></p>
<p><strong>Further reading</strong></p>
<ol>
<li><a target="_blank" href="https://www.weave.works/blog/the-history-of-gitops">The History of GitOps</a></li>
<li><a target="_blank" href="https://www.weave.works/blog/what-is-gitops-really">What is GitOps really</a></li>
<li><a target="_blank" href="https://www.weave.works/technologies/gitops/#key-benefits-of-gitops">Key benefits of GitOps</a></li>
<li><a target="_blank" href="https://www.gitops.tech/">gitops.tech</a> </li>
</ol>
]]></content:encoded></item><item><title><![CDATA[[Abstract] A way to boost open-source contribution to mono-repos]]></title><description><![CDATA[Open sourcing software products more often than not increases their success with software engineers all over the globe rapidly contributing to its development than the case is with proprietary software products. This has led to many organisations inc...]]></description><link>https://blog.mwanje.me/abstract-a-way-to-boost-open-source-contribution-to-mono-repos</link><guid isPermaLink="true">https://blog.mwanje.me/abstract-a-way-to-boost-open-source-contribution-to-mono-repos</guid><category><![CDATA[Open Source]]></category><category><![CDATA[monorepo]]></category><category><![CDATA[version control]]></category><category><![CDATA[contribution to open source]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Thu, 10 Nov 2022 19:37:03 GMT</pubDate><content:encoded><![CDATA[<p>Open sourcing software products more often than not increases their success with software engineers all over the globe rapidly contributing to its development than the case is with proprietary software products. This has led to many organisations including enterprises with multiple software products, with millions of lines of code, maintained in a mono-repository to open source their source code in order to benefit from what the open source community has to offer. Whereas mono-repositories can provide a couple of advantages for maintainers in such an organisation, they provide a real push back to external contributors most especially the one time and first time contributors both of which do not benefit from the mono-repository set-up in any way. This is of essence as the success of an open source software project greatly depends on the attitude of the contributors which can get negatively affected by the first time experience. A good solution should be able to maintain the benefits of the mono-repository whereas addressing the underlying issues of large repository clone size, inter-product dependencies, the bureaucratic processes that arise from a mono-repo. My proposal is to use a combination of tools to achieve this. These may include the use of git-subtrees and sophisticated automations that require minimal human intervention to seamlessly integrate external open source contributions.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668108441365/hCqv-G0r5.png" alt="spectrums applied to the real world" /></td></tr>
</thead>
<tbody>
<tr>
<td><em>Source: https://monorepo.tools/</em></td></tr>
</tbody>
</table>
</div>]]></content:encoded></item><item><title><![CDATA[For the month of October...]]></title><description><![CDATA[...I am taking a break from blogging. Why? Cause it's that time of the year again. It's the month when we get to celebrate open source. Its the Hacktoberfest!

Hacktoberfest is DigitalOcean’s annual event that encourages people to contribute to open ...]]></description><link>https://blog.mwanje.me/for-the-month-of-october</link><guid isPermaLink="true">https://blog.mwanje.me/for-the-month-of-october</guid><category><![CDATA[#hacktoberfest ]]></category><category><![CDATA[hacktoberfest2022]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Tue, 25 Oct 2022 19:00:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1666723252822/k3QlCaLiI.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>...I am taking a break from blogging. Why? Cause it's that time of the year again. It's the month when we get to celebrate open source. <strong><em>Its the Hacktoberfest!</em></strong></p>
<blockquote>
<p>Hacktoberfest is DigitalOcean’s annual event that encourages people to contribute to open source throughout October.</p>
</blockquote>
<p>Hacktoberfest is is open to anyone and everyone to make non-code, low-code, or code contributions to open source projects. You can contribute in which ever way(s) you are comfortable with.</p>
<p>Personally, my focus has been and will continue to be around local open source projects and awareness of the same during this year's Hacktoberfest. The interest in open source is generally still low in Uganda and I hope that my contributions can improve this in one way or another.
<a target="_blank" href="https://twitter.com/kayondoedward/status/1584646406211801089?s=20&amp;t=gSes0diWZdvyPMGb4Bylew">This twitter thread</a> has a couple of Ugandan open source projects you should consider contributing to.</p>
<p>Examples of non-code and low-code contributions to open-source projects include:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td></td><td>Low-Code</td><td>Non-Code</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Writing</strong></td><td>Technical documentation</td><td>Translating, Copy editing</td></tr>
<tr>
<td><strong>Design</strong></td><td>Testing</td><td>User experience testing, Graphic design</td></tr>
<tr>
<td><strong>Advocacy</strong></td><td>Talks, Technical blog posts</td><td>Social media, Blog posts</td></tr>
</tbody>
</table>
</div><p>If you haven't already, do checkout the official <a target="_blank" href="https://hacktoberfest.com/">Hacktoberfest website</a> to find out more or even signup. Psst, there's a prize for everyone.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1666723264829/j9_cngwnZ.jpg" alt="Hacktoberfest participants" /></td></tr>
</thead>
<tbody>
<tr>
<td><em>Hacktoberfest participants at an open source event at Outbox Hub</em></td></tr>
</tbody>
</table>
</div><p><em>From us to you... Happy Hacking!</em></p>
]]></content:encoded></item><item><title><![CDATA[Multitasking, something we copied from computers.]]></title><description><![CDATA[Multitasking is the concurrent execution of multiple tasks for a given time. This means that if a new task comes up amidst execution of an already running task, the new task is executed without having to wait for the first one to complete. The term o...]]></description><link>https://blog.mwanje.me/multitasking-something-we-copied-from-computers</link><guid isPermaLink="true">https://blog.mwanje.me/multitasking-something-we-copied-from-computers</guid><category><![CDATA[multitasking]]></category><category><![CDATA[multi-tasking]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Sat, 17 Sep 2022 09:44:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/0yaZcxwLCYw/upload/v1663407639160/iF6-uA8v1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Multitasking is the concurrent execution of multiple tasks for a given time. This means that if a new task comes up amidst execution of an already running task, the new task is executed without having to wait for the first one to complete. The term originated from computing in the mid 1960s and we since then adopted it to describe a similar ability in humans. Whereas this is humanly possible as with computers, I want us to briefly explore the concept and see why we might want to avoid it. </p>
<h2 id="heading-how-multitasking-is-done">How multitasking is done?</h2>
<p>Before we can go deeper into this exploration, let's first get a grasp of how multitasking is archived in both computers and humans. At the core of it all, multitasking is reached by switching between active tasks and we can perhaps rate how good or bad it is based on how fast the task switching occurs.  </p>
<h3 id="heading-in-computers">In computers</h3>
<p>Given that the vocabulary was picked from computing, it's only right for us to start with computers.
During multitasking, each task gets an allocated CPU time according to specific priority policies and this is in fractions of a second. The CPU switches so fast between these tasks that you may get the illusion that the computer is working on all these tasks at a single smallest instance of time. There's also a concept of parallelism in which large tasks into smaller, independent tasks that can be executed simultaneously by multiple processors of the CPU: The multitasking in this case is at the processor level.</p>
<p>To generally improve the multitasking ability of a computer, you just need to increase the computational resources of that single machine, that is CPU and RAM. The RAM comes in mainly during context switching.</p>
<h3 id="heading-in-humans">In humans</h3>
<p>Multitasking is archived by allocating brain power to a given set of tasks. It can also be done by task switching, and through use of the subconscious mind. 
Task switching in humans is costly as it the context switching in this case is quite different and requires brain power to execute. An example is how you take the first few minutes of switching to a different task trying to recall the last progress you made.</p>
<blockquote>
<p>‍Context switching is our tendency to shift from one unrelated task to another. </p>
</blockquote>
<p>You can also archive multitasking as a human by delegating repetitive tasks to the subconscious mind. These tasks are usually motor tasks. For example speaking on the phone while driving a car.</p>
<h2 id="heading-avoid-multitasking-because">Avoid multitasking because...</h2>
<p>We have seen above how computers are well suited and designed for multitasking unlike we humans. It makes us;</p>
<ul>
<li>Lose our ability to focus for longer</li>
<li>Prone to errors due to shorter attention spans</li>
<li>Waste time during context switching</li>
<li>Less productivity</li>
</ul>
<p>However, there are times you can use multitasking to your advantage as a life hack, for example if you suffer from short attention spans and/or are procrastinator. This helps you prevent yourself from getting easily bored.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Multitasking occurs because it is somewhat impossible for any single atomic faculty to execute more than one task at any single point in time. It is however possible to create an illusion of this over a time span with rapid task switching.</p>
]]></content:encoded></item><item><title><![CDATA[Creating a new website? I have Nuttertools for you.]]></title><description><![CDATA[Ever played Grand Theft Auto? If you have, then you might be familiar with "Nuttertools". Nuttertools is one of the many cheat codes in the video game which gives your character a set of weapons. In this article, I'll share some cheat codes of my own...]]></description><link>https://blog.mwanje.me/creating-a-new-website-i-have-nuttertools-for-you</link><guid isPermaLink="true">https://blog.mwanje.me/creating-a-new-website-i-have-nuttertools-for-you</guid><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[web]]></category><category><![CDATA[website]]></category><category><![CDATA[Developer Tools]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Fri, 02 Sep 2022 20:10:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/noOXRT9gfQ8/upload/v1662149241897/lYoWqx9JV.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever played Grand Theft Auto? If you have, then you might be familiar with "Nuttertools". Nuttertools is one of the many cheat codes in the video game which gives your character a set of weapons. In this article, I'll share some cheat codes of my own with you. A special group of websites that will make you stand apart from any ordinary web developer. I call these my Nuttertools.</p>
<h2 id="heading-1-httpswwwwebpagetestorg">1. https://www.webpagetest.org/</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662141710966/OvEEPZTg3.png" alt="image.png" />
With this website, you get to run performance, lighthouse, core web vitals, visual comparison, and trace route tests on your website. It can simulate a wide range of internet connections right from 2G to cable in various locations around the world. The tests are recorded and so can can playback to see how your website gets displayed on the browser and also share the recorded results if you wish to.</p>
<h2 id="heading-2-httpsvalidatorw3org">2. https://validator.w3.org/</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662145372605/z9lZU3yn2.png" alt="image.png" />
This W3C markup validator checks for areas of improvement and errors in your website HTML syntax and suggests how these can be addressed. As you can see in the image above, I have some of my own I can fix.</p>
<h2 id="heading-3-httpswavewebaimorg">3. https://wave.webaim.org/</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662146900427/7bAt1UmYq.png" alt="image.png" />
The internet has a very large number of users who are diverse in many of ways. This tool comes in handy if you wish to create a website that is accessible to a wide audience, including those with visual, audial, physical, and cognitive impairments. It helps you identify implementations that may not be favourable for impaired individuals and how these can be can corrected. An example is colour contrasts that may not be distinguishable by people with colour blindness as well as they would be for normal users. This way you can promote inclusivity even in your web solutions.</p>
<h2 id="heading-4-httpswwwwhatsmydnsnet">4. https://www.whatsmydns.net/</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662142900165/x6imoxLkG.png" alt="image.png" />
Let's say you've gotten a new domain and/or made a couple of changes. These changes can take a some good time, up to 72hours  to reflect worldwide on the all the Domain Name Servers. This website helps you check the DNS propagation.</p>
<h2 id="heading-5-httpswwwwebsitecarboncom">5. https://www.websitecarbon.com/</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662147385354/z0JE1rzak.png" alt="image.png" />
Last but most definitely not the least, is a tool that calculates how much carbon is released into the atmosphere whenever a user visits your website. You can then see how much your website contributes to global warming and also suggests how you can reduce the carbon footprint. If there's at least one take away from this article, I hope this be it.</p>
<p>Do you know of any other "Nuttertools" that aren't on this list, do share in the comments section. I'd love to learn from you too.</p>
<p><em>Cheers!</em></p>
]]></content:encoded></item><item><title><![CDATA[Set up Kubernetes role based access control]]></title><description><![CDATA[Kubernetes(K8s) role-based access control is a powerful tool in restricting access to resources within a Kubernetes cluster. In this post, we shall briefly discuss what role-based access control is, and how to set it up in Kubernetes. I promise, it's...]]></description><link>https://blog.mwanje.me/set-up-kubernetes-role-based-access-control</link><guid isPermaLink="true">https://blog.mwanje.me/set-up-kubernetes-role-based-access-control</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[k8s]]></category><category><![CDATA[rbac]]></category><category><![CDATA[role-based-access-control]]></category><category><![CDATA[clusterrolebindings]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Mon, 22 Aug 2022 16:15:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/7GfRwb78YWs/upload/v1661008450696/o_ngBHhdq.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Kubernetes(K8s) role-based access control is a powerful tool in restricting access to resources within a Kubernetes cluster. In this post, we shall briefly discuss what role-based access control is, and how to set it up in Kubernetes. I promise, it's not as long a process as you may think.</p>
<h2 id="heading-what-is-role-based-access-control">What is role-based access control?</h2>
<p>Role-based access control(RBAC) is a security model in which users are assigned roles, and can then be granted access to certain resources based on those roles. For example, an organisation can have a role-based access control system that allows DevOps engineers to perform privileged operations within the entire Kubernetes cluster, while restricting the application developers to only viewing information about their applications deployed in a given namespace.</p>
<h3 id="heading-definitions">Definitions</h3>
<p>Before setting up RBAC in Kubernetes, let's review some of the key concepts that we are going to be dealing with:</p>
<ul>
<li><p><strong>Service accounts</strong> are identities that can be used to delegate access to Kubernetes resources within a namespace. They can be used by users or services. We shall do some minor tweaks later on in the article using clusterrolebindings to enable us override the "namespacedness" limitation.</p>
</li>
<li><p><strong>Resources</strong> are objects within a Kubernetes cluster that we aim to implement role-based access control around. Examples include pods, deployments, replicasets, configmaps, and services. </p>
</li>
<li><p><strong>Roles</strong> are collections of permissions that are applied within a given namespace. In most cases, these consist of a set of permissions that can be granted to service accounts.</p>
</li>
<li><p><strong>Rolebindings</strong> are the association of a role with a service account. In other words, rolebindings are used to assign roles. Think of a rolebinding as a mechanism to plug a set of permissions(a <em>role</em>) to a service account.</p>
</li>
<li><p><strong>Clusterroles</strong> are roles that can be assigned to service accounts within a Kubernetes cluster. Whereas roles are namespaced, clusterroles apply to the entire cluster. Some default ones in a k8s cluster include view, edit, admin, and cluster-admin.</p>
</li>
<li><p><strong>Clusterrolebindings</strong> are associations of a clusterrole with a service account. As you might have guessed, clusterrolebindings enable us assign cluster wide permissions to a service accounts through clusterroles.</p>
</li>
</ul>
<p>Now let us see how we can set up RBAC in Kubernetes.</p>
<h2 id="heading-how-to-set-up-a-kubernetes-cluster-using-rbac">How to set up a Kubernetes cluster using RBAC</h2>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li><p>A working Kubernetes cluster. If you don't have one yet, you might find <a target="_blank" href="https://itnext.io/kubernetes-installation-methods-the-complete-guide-1036c860a2b3">this post</a> helpful. It lists a couple of ways you could setup a K8s cluster.</p>
</li>
<li><p>Kubectl. You can install kubectl following <a target="_blank" href="https://kubernetes.io/docs/tasks/tools/#kubectl">this guide</a>.</p>
</li>
<li><p>Kubernetes 1.20 or later. Earlier versions of Kubernetes are no longer actively maintained and RBAC in those versions is not as stable. <a target="_blank" href="https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-upgrade/">This documentation</a> can guide you through the process.</p>
</li>
<li><p><a target="_blank" href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/">The Kubernetes Dashboard</a> if you intend on using it in the <a class="post-section-overview" href="#heading-access-the-kubernetes-dashboard">Access the Kubernetes dashboard</a> section later on.</p>
</li>
</ul>
<h3 id="heading-setting-up-rbac-within-a-namespace">Setting up RBAC within a namespace</h3>
<p>First, let us create a namespace which we are going to be working with. Let's call it <strong><em>dev-env</em></strong>. </p>
<pre><code>kubectl <span class="hljs-keyword">create</span> namespace dev-env
</code></pre><p><em>Output:</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660247153561/cQe5gGH0H.png" alt="Output of  kubectl create command namespace" /></p>
<p>Next, create a service account called <strong><em>app-dev</em></strong> which we shall use all through out. </p>
<pre><code>kubectl create serviceaccount app<span class="hljs-operator">-</span>dev <span class="hljs-operator">-</span><span class="hljs-operator">-</span>namespace dev<span class="hljs-operator">-</span>env
</code></pre><p><em>Output:</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660247550731/NHgtbaLGv.png" alt="Output of  kubectl create command serviceaccount" /></p>
<p>We shall follow this up by creating an admin role that has all rights within the <code>dev-env</code> namespace. Below is its manifest, which is also available <a target="_blank" href="https://github.com/123MwanjeMike/k8s-rbac/blob/main/admin-role.yaml">on GitHub</a>.</p>
<pre><code><span class="hljs-attribute">kind</span>: Role
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: admin
  <span class="hljs-attribute">namespace</span>: dev-env
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">apiGroups</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">resources</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">verbs</span>: [<span class="hljs-string">"*"</span>]
</code></pre><p>Apply the file to create the <strong><em>admin</em></strong> role.</p>
<pre><code>kubectl apply -f https://raw.githubusercontent.com/<span class="hljs-number">123</span>MwanjeMike/k8s-rbac/main/<span class="hljs-keyword">admin</span>-<span class="hljs-keyword">role</span>.yaml
</code></pre><p><em>Output:</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660251942764/5Qpd7n8Tz.png" alt="Output after creating role" /></p>
<p><strong><em>Note:</em></strong> <em>We shall be creating the rest of the Kubernetes resources in this tutorial from the pre-written manifest files in <a target="_blank" href="https://github.com/123MwanjeMike/k8s-rbac">this repository</a> as we have done above. However, you may also edit these manifests if you wish to create K8s resources tailored to your needs.</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/123MwanjeMike/k8s-rbac">https://github.com/123MwanjeMike/k8s-rbac</a></div>
<p>Next,create a rolebinding, <strong><em>app-dev-rolebinding</em></strong>, of the <code>admin</code> role to the <code>app-dev</code> service account. Below is the manifest. </p>
<pre><code><span class="hljs-attribute">kind</span>: RoleBinding
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: app-dev-rolebinding
  <span class="hljs-attribute">namespace</span>: dev-env
<span class="hljs-attribute">subjects</span>:
- <span class="hljs-attribute">kind</span>: ServiceAccount
  <span class="hljs-attribute">name</span>: app-dev
  <span class="hljs-attribute">namespace</span>: dev-env
<span class="hljs-attribute">roleRef</span>:
  <span class="hljs-attribute">apiGroup</span>: rbac.authorization.k8s.io
  <span class="hljs-attribute">kind</span>: Role
  <span class="hljs-attribute">name</span>: admin
</code></pre><p>Run the command below to create it.</p>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/<span class="hljs-number">123</span>MwanjeMike/k<span class="hljs-number">8</span>s-rbac/main/rolebinding.yaml
</code></pre><p><em>Output:</em>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660333008852/wt0J1nH5e.png" alt="Output of creating a rolebinding" /></p>
<p>Our service account is now an admin within the <code>dev-env</code> namespace. We can even go further and define access rights for it across the entire cluster. Let's say, basing on our DevOps engineer and application developer example we had at the start, that we want our application developers to view any resource cluster wide and even to use the Kubernetes dashboard. How we can we do this?</p>
<h3 id="heading-setting-up-rbac-for-the-entire-cluster">Setting up RBAC for the entire cluster</h3>
<p>For the first part, we're in luck! Kubernetes has a default view clusterrole which can be used for viewing of all resources in a cluster; and so we shall just leverage this. We shall create a clusterrolebinding, <code>app-dev-view</code>, that does just that.</p>
<pre><code><span class="hljs-attribute">kind</span>: ClusterRoleBinding
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: app-dev-view
<span class="hljs-attribute">subjects</span>:
- <span class="hljs-attribute">kind</span>: ServiceAccount
  <span class="hljs-attribute">name</span>: app-dev
  <span class="hljs-attribute">namespace</span>: dev-env
<span class="hljs-attribute">roleRef</span>:
  <span class="hljs-attribute">apiGroup</span>: rbac.authorization.k8s.io
  <span class="hljs-attribute">kind</span>: ClusterRole
  <span class="hljs-attribute">name</span>: view
</code></pre><p>Create the clusterrolebinding</p>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/<span class="hljs-number">123</span>MwanjeMike/k<span class="hljs-number">8</span>s-rbac/main/clusterrolebindings/view.yaml
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660983996844/OYnYOQv_9.png" alt="image.png" /></p>
<p>Now, for <code>app-dev</code> to access the Kubernetes dashboard, we could easily just create a bind the <code>edit</code> default clusterrole to it since it has the access rights needed for this. However, this would also give the service account more permissions than it should have and so we want to use a more fine-grained clusterrole in accordance to the principle of least privileges.</p>
<p>So, we shall create a custom clusterrole with only the required permissions needed to access the K8s dashboard on top of those already held by the service account through default <code>view</code> clusterrole.</p>
<pre><code><span class="hljs-attribute">kind</span>: ClusterRole
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: view-addition-for-k8s-dashboard-access
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">apiGroups</span>: [<span class="hljs-string">""</span>]
  <span class="hljs-attribute">resources</span>: [<span class="hljs-string">"pods/attach"</span>, <span class="hljs-string">"pods/exec"</span>, <span class="hljs-string">"pods/portforward"</span>, <span class="hljs-string">"pods/proxy"</span>, <span class="hljs-string">"services/proxy"</span>]
  <span class="hljs-attribute">verbs</span>: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>]
- <span class="hljs-attribute">apiGroups</span>: [<span class="hljs-string">""</span>]
  <span class="hljs-attribute">resources</span>: [<span class="hljs-string">"services"</span>]
  <span class="hljs-attribute">verbs</span>: [<span class="hljs-string">"proxy"</span>]
</code></pre><p>Run the command below to create the <code>view-addition-for-k8s-dashboard-access</code> clusterrole with the above definition.</p>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/<span class="hljs-number">123</span>MwanjeMike/k<span class="hljs-number">8</span>s-rbac/main/custom-clusterrole.yaml
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660988651204/toageyghC.png" alt="image.png" /></p>
<p>We can now bind <code>view-addition-for-k8s-dashboard-access</code> clusterrole to the <code>app-dev</code> service account for it access the K8s dashboard. Let's have a look at the clusterrolebinding manifest for this.</p>
<pre><code>kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: app<span class="hljs-operator">-</span>dev<span class="hljs-operator">-</span>use<span class="hljs-operator">-</span>k8s<span class="hljs-operator">-</span>dashboard
subjects:
<span class="hljs-operator">-</span> kind: ServiceAccount
  name: app<span class="hljs-operator">-</span>dev
  namespace: dev<span class="hljs-operator">-</span>env
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: <span class="hljs-keyword">view</span><span class="hljs-operator">-</span>addition<span class="hljs-operator">-</span><span class="hljs-keyword">for</span><span class="hljs-operator">-</span>k8s<span class="hljs-operator">-</span>dashboard<span class="hljs-operator">-</span>access
</code></pre><p>Create the <code>app-dev-use-k8s-dashboard</code> clusterrolebinding</p>
<pre><code><span class="hljs-attribute">kubectl</span> apply -f https://raw.githubusercontent.com/<span class="hljs-number">123</span>MwanjeMike/k<span class="hljs-number">8</span>s-rbac/main/clusterrolebindings/use-k<span class="hljs-number">8</span>s-dashboard.yaml
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660989964648/FL4jrUjbc.png" alt="image.png" /></p>
<p>Our <code>app-dev</code> service account can now be used by our "application developers" to create applications in the <code>dev-env</code> namespace and conveniently access the k8s dashboard.</p>
<h2 id="heading-putting-it-to-use">Putting it to use</h2>
<h3 id="heading-create-a-kubectl-configuration">Create a kubectl configuration</h3>
<p>We are going to need a token for the <code>app-dev</code> service account and so to get it, run the commands below</p>
<pre><code>export NAMESPACE<span class="hljs-operator">=</span><span class="hljs-string">"dev-env"</span>
export SERVICE_ACCOUNT<span class="hljs-operator">=</span><span class="hljs-string">"app-dev"</span>
kubectl <span class="hljs-operator">-</span>n ${NAMESPACE} describe secret $(kubectl <span class="hljs-operator">-</span>n ${NAMESPACE} get secret <span class="hljs-operator">|</span> (grep ${SERVICE_ACCOUNT} <span class="hljs-operator">|</span><span class="hljs-operator">|</span> echo <span class="hljs-string">"$_"</span>) <span class="hljs-operator">|</span> awk <span class="hljs-string">'{print $1}'</span>) <span class="hljs-operator">|</span> grep token: <span class="hljs-operator">|</span> awk <span class="hljs-string">'{print $2}'</span>\n
</code></pre><p>Your output will be some long string. It should start with something similar to what I have below. Save that <strong>token</strong> somewhere temporarily.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661021009694/Vq5dbO3gW.png" alt="image.png" /></p>
<p>Next, get the certificate data by running the command below in the same terminal where you had defined the <em>NAMESPACE</em> and <em>SERVICE_ACCOUNT</em> variables.</p>
<pre><code>kubectl  <span class="hljs-operator">-</span>n ${NAMESPACE} get secret `kubectl <span class="hljs-operator">-</span>n ${NAMESPACE} get secret <span class="hljs-operator">|</span> (grep ${SERVICE_ACCOUNT} <span class="hljs-operator">|</span><span class="hljs-operator">|</span> echo <span class="hljs-string">"$_"</span>) <span class="hljs-operator">|</span> awk <span class="hljs-string">'{print $1}'</span>` <span class="hljs-operator">-</span>o <span class="hljs-string">"jsonpath={.data['ca\.crt']}"</span>
</code></pre><p>Your output will be another long string whose beginning is something similar to what I have below. Save that <strong>certificate data</strong> somewhere temporarily.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661021487335/MAfiYFNYJ.png" alt="image.png" /></p>
<p>With these two, we let's create our kubectl config file. You might already have a <em>.kube/config </em>which you might still need. So, we shall back it up as .kube/config.bak and create a new one for the <code>app-dev</code>. Run the commands below to do precisely that.</p>
<pre><code>cd
mv .kube/config .kube/config.bak
nano .kube/config
</code></pre><p>This will open the nano text editor. Fill in the template below paste in the editor. Make sure to substitute the <em>certificate-data</em>, <em>server-ip-address</em>, <em>cluster-name</em>, and <em>token</em> with the correct values before saving the changes. Remember to delete the token and certificate data from where you had temporarily saved them.</p>
<pre><code>apiVersion: v1
clusters:
<span class="hljs-operator">-</span> cluster:
    certificate<span class="hljs-operator">-</span>authority<span class="hljs-operator">-</span>data:  <span class="hljs-operator">&lt;</span>certificate<span class="hljs-operator">-</span>data<span class="hljs-operator">&gt;</span>
    server: https:<span class="hljs-comment">//&lt;server-ip-address&gt;:6443</span>
  name: <span class="hljs-operator">&lt;</span>cluster<span class="hljs-operator">-</span>name<span class="hljs-operator">&gt;</span>
contexts:
<span class="hljs-operator">-</span> context:
    cluster: <span class="hljs-operator">&lt;</span>cluster<span class="hljs-operator">-</span>name<span class="hljs-operator">&gt;</span>
    namespace: dev<span class="hljs-operator">-</span>env
    user: app<span class="hljs-operator">-</span>dev
  name: <span class="hljs-operator">&lt;</span>cluster<span class="hljs-operator">-</span>name<span class="hljs-operator">&gt;</span>
current<span class="hljs-operator">-</span>context: dev<span class="hljs-operator">-</span>env
kind: Config
preferences: {}
users:
<span class="hljs-operator">-</span> name: app<span class="hljs-operator">-</span>dev
  user:
    client<span class="hljs-operator">-</span>key<span class="hljs-operator">-</span>data:  <span class="hljs-operator">&lt;</span>certificate<span class="hljs-operator">-</span>data<span class="hljs-operator">&gt;</span>
    token:  <span class="hljs-operator">&lt;</span>token<span class="hljs-operator">&gt;</span>
</code></pre><h3 id="heading-access-the-kubernetes-dashboard">Access the Kubernetes dashboard</h3>
<ol>
<li>Run <code>kubectl proxy</code> and click <a target="_blank" href="http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/">here</a> to access the dashboard.</li>
<li>Sign in with the .kube/config we created in the previous step.</li>
<li>Attempt to create a user role in the dev-env namespace with the manifest below. All should go on well.</li>
</ol>
<pre><code><span class="hljs-attribute">kind</span>: Role
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: test-admin
  <span class="hljs-attribute">namespace</span>: dev-env
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">apiGroups</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">resources</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">verbs</span>: [<span class="hljs-string">"*"</span>]
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661025164815/2Rl9dZStI.png" alt="test-admin role creation successful in dev-env namespace" /></p>
<ol>
<li>Attempt to create the user role in the kube-public namespace with the manifest below.</li>
</ol>
<pre><code><span class="hljs-attribute">kind</span>: Role
<span class="hljs-attribute">apiVersion</span>: rbac.authorization.k8s.io/v1
<span class="hljs-attribute">metadata</span>:
  <span class="hljs-attribute">name</span>: test-admin
  <span class="hljs-attribute">namespace</span>: kube-public
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">apiGroups</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">resources</span>: [<span class="hljs-string">"*"</span>]
  <span class="hljs-attribute">verbs</span>: [<span class="hljs-string">"*"</span>]
</code></pre><p>Tadaa!
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1661023906267/ynJGj_q5q.png" alt="test-admin role creation failed in kube-public namespace" /></p>
<p>We did it. <code>app-dev</code> has no such rights outside the <code>dev-env</code> namespace. All application developers can now use that .kube/config file to gain controlled access to the cluster</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this post, we looked at how we can implement RBAC not only at the namespace level, but also at the cluster level. We specifically used a service account given that it is managed by Kubernetes taking away this task from us. It is also important to note that Kubernetes also user accounts as much as it does service accounts. One key difference between the two is that user accounts represent actual Kubernetes users unlike the service accounts.</p>
<p>I hope you enjoyed this article and that you'll show support of some sort as I'd greatly appreciate that. And if you have any thoughts or questions in relation to this, feel free to drop them via the comments section. I'd love to hear from you.</p>
<p><em>Happy hacking</em></p>
<h3 id="heading-additional-resources">Additional resources</h3>
<ol>
<li><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Using RBAC Authorization</a></li>
<li><a target="_blank" href="https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml">Kubernetes default clusterroles definitions</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Principle_of_least_privilege">The principle of least privileges</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[What is best platform for tech blogging and building a developer community?]]></title><description><![CDATA[As a technology professional, I have always been passionate about sharing knowledge with others. With the rise of blogs and social media, I realized that there are many other benefits to blogging besides just sharing knowledge. For example, by bloggi...]]></description><link>https://blog.mwanje.me/what-is-best-platform-for-tech-blogging-and-building-a-developer-community</link><guid isPermaLink="true">https://blog.mwanje.me/what-is-best-platform-for-tech-blogging-and-building-a-developer-community</guid><category><![CDATA[Blogging]]></category><category><![CDATA[Technical writing ]]></category><category><![CDATA[writing]]></category><category><![CDATA[Hashnode]]></category><category><![CDATA[medium]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Thu, 21 Jul 2022 20:35:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/YK0HPwWDJ1I/upload/v1658435472880/tExbmoypa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a technology professional, I have always been passionate about sharing knowledge with others. With the rise of blogs and social media, I realized that there are many other benefits to blogging besides just sharing knowledge. For example, by blogging, I am able to build visibility and grow an audience. Additionally, documenting my work and experiences can be a valuable resource for myself in the future.</p>
<p>Given that I am committed to blogging monthly, it was important for me to find the best platform for this. I wanted to take advantage of everything that blogging has to offer and this platform had to be feature rich and user-friendly. So I decided to carry out a research on different platforms that could assist me in achieving my goals. The platforms that I evaluated were Medium, DEV(Dev.to), and tech Twitter. You might be wondering about Hashnode but I will get to this in a bit.</p>
<h3 id="heading-if-you-were-to-choose-one-platform-to-blog-on-which-one-would-it-be-and-why">If you were to choose one platform to blog on, which one would it be and why?</h3>
<p>I picked thoughts from people both <a target="_blank" href="https://twitter.com/Mwanje_Mike_/status/1540716014622543874?s=20&amp;t=0j7VVSl8qdhm32tMnMC8Bw">on Twitter</a> and a couple of developer communities, and here is what I found...</p>
<h4 id="heading-dev">DEV</h4>
<p>DEV is a great place to start out as a technical blogger. It has a large technical audience and a supportive developer community. Articles from junior and senior developers get the same level of visibility. This makes it an ideal place for enthusiastic learners as well as seasoned professionals to share their learnings and expertise with a broader audience. DEV Markdown also makes it easy to write code snippets and have them formatted correctly.</p>
<h4 id="heading-medium">Medium</h4>
<p>Medium provides a greater visibility for content than DEV with a much more diverse audience. While it is not as popular with developers, Medium has a growing number of tech bloggers who use the platform to share their work and insights with a wider audience. Medium is however best to use if you already have an established blog audience and want to amplify your content as new bloggers may not get as much visibility. Medium is a better place for sharing experiences and ideas rather than just technical content. It is also a great place to create a thought leadership presence for yourself and build a brand.</p>
<h4 id="heading-hashnode">Hashnode</h4>
<p>Hashnode is relatively new to the market compared to other blogging platforms. However, it has risen quickly to become popular with developers because it offers all the features that tech bloggers require. I had initially not considered Hashnode as it is somewhat similar to DEV. Both are tailored specifically for the developer community and also have a similar markdown support (although Hashnode’s offerings are far superior to those available on DEV). Hashnode however has smaller audiences than DEV and Medium and as a result, you may not get as much attention.</p>
<h4 id="heading-tech-twitter">Tech Twitter</h4>
<p>Tech Twitter is where all the developers hang out. If you have not heard of it before, then you must be living under a rock! Tech Twitter is a micro blogging platform where developers share their latest discoveries, thoughts and insights on various tech topics. It is a noisy place where you need to be constantly talking to get noticed. However, it is a great place to build relationships with other developers and get your content read by the right people.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Choosing the right blog platform is not easy. It depends on your objective and how involved you want to be with the platform. However, if you are starting out and are looking to build an organic audience, then I would suggest DEV. If you are looking for a platform that provides greater visibility and a wider audience as a thought leader, then Medium the best choice for you. While if you want to take charge of your blogging process and build a more niche audience, then Hashnode would be the best platform for you. Finally, if you are looking for a platform to constantly keep your audience engaged, then Tech Twitter is the place to be.
The good news is that you can always use canonical urls to republish your blogs to leverage what the other platforms have to offer.</p>
<p>Do you have any thoughts on what I just shared? Let me know in the comments below! I would also love to hear from you on what your favourite blogging platform and why?</p>
<p>Happy blogging!</p>
]]></content:encoded></item><item><title><![CDATA[Plan your quarter and the week ahead.]]></title><description><![CDATA[Zealous. Psyched up. Determined to do the extraordinary. It’s a fresh start and we are ready to tap into the potential that comes with a new year. We set goals for it but before you know it, we’re halfway through the year and there’s nothing to show ...]]></description><link>https://blog.mwanje.me/plan-your-quarter-and-the-week-ahead-d57f1cf566e9</link><guid isPermaLink="true">https://blog.mwanje.me/plan-your-quarter-and-the-week-ahead-d57f1cf566e9</guid><category><![CDATA[planning]]></category><category><![CDATA[Career]]></category><category><![CDATA[personal development]]></category><category><![CDATA[Career Coach]]></category><category><![CDATA[Soft Skills]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Sat, 09 Jul 2022 15:10:46 GMT</pubDate><content:encoded><![CDATA[<p>Zealous. Psyched up. Determined to do the extraordinary. It’s a fresh start and we are ready to tap into the potential that comes with a new year. We set goals for it but before you know it, we’re halfway through the year and there’s nothing to show for it. Why is this the case though? There is a couple of factors that can lead to this but I will share about one in particular herein: Planning.</p>
<blockquote>
<p>A goal without a plan is only a dream. — Brain Tracy</p>
</blockquote>
<p>Planning is a fundamental cognitive skill that forms part of our executive functions. It keeps us in line with our goals and focused on the bigger picture.<br />It’s funny how for instance we can have quarterly goals at the organizations we subscribe to and how we plan the execution of our responsibilities with reference to these goals and yet may have none for ourselves; it is something we can replicate in our individual lives. It can be toward improving health, building a relationship, or even learning a new skill for example. The most relatable perhaps is what we envision for our careers and how we plan on getting there. A Career Development Plan(CDP).</p>
<p>A CDP is a carefully thought through plan we set for our career growth. It is meant to help us reach our short, mid, and long term goals. Even though we may all have varying perceptions of time, the ideal understanding is that short term is a period with in 3 months, mid term 6 to 12 months, and long term 5 or more years. When creating a CDP, you basically group your career goals by the amount of time it takes to achieve them. It is best to have these goals not contradict themselves. The smaller duration goals should actually be a contribution to the larger duration goals.</p>
<p>A quarter represents 3 months in the year thus your quarterly goals are the same as the short term goals in the CDP.<br />For each of the goals in your CDP, detail how you will meet the goal, what resources you will need, a time breakdown, and the definition of success. You do this planning at the end of every quarter and evaluate yourself based off the success definition you set 3 months back.</p>
<p>With a clearer vision of what you want and how much you need to invest to get there, you then move onto planning the week.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657653468454/LgQfc2liu.jpeg" alt="FWf5-AQUcAA_hrz.jpeg" />
<a target="_blank" href="https://twitter.com/strongminded101/status/1542478214232023042?s=20&amp;t=SHvJ88KJfR9lfqlzOvi-sA">Image source</a></p>
<p>The week’s plan is perhaps the most important of all the plans as it is where we plan the miniature executions towards the grand goal. It is then that we determine how many steps we shall be making towards our quarterly goals while also taking into consideration potential blockers that are outside our control. It is important to plan the week even before it starts; I do mine on Saturday afternoons for instance. While at it, you need to accord the process enough attention and time to fully visualize how each day can be spent to reach your end goal. You should however desist from the temptation of creating a fancy timed To-do list in the guise of a plan.</p>
<blockquote>
<p>There seems to be implicit cultural recognition of the week as a single, complete unit of time. Business, education, and many other facets of society operate within the framework of the week, designating certain days for focused investment and others for relaxation or inspiration. — Stephen Covey</p>
</blockquote>
<p>As for me, I have just finished planning my quarter and next week.<br />For the first time in 6 months, I feel a great sense of comfort and relief having my tasks clearly laid out instead of them jumbled up in my head as to-dos. I explicitly defined for myself the what, when, and how for each of the tasks. I also specified how long each of the tasks would take. This perhaps explains why I already feel like next week is going to be a success; I have already been there through my mind’s eye and I am mentally prepared for it.</p>
<p>Previously, I had fallen into the vice merely creating a fancy to do list and had no ownership or control over my own time, on my own calendar. I was being irresponsible and reactive rather with my life. The price for not being proactive was the feeling of helplessness and I didn’t enjoy paying that at all.</p>
<h3 id="heading-final-thoughts">Final thoughts</h3>
<p>One trick to achieving your goals is committing to your plan and holding yourself accountable. However, it is also possible that you plan and things don’t go as planned because of factors out of your control. It is okay if this happens. Sometimes things just don’t work out but it is better to plan and be intentional with your goals and life than sitting back and leaving it all to fate.</p>
<p>If you liked this post, be sure to leave a like or comment. It indicates that this was helpful and encourages me to share more of such.</p>
<p><em>Cheers!</em></p>
]]></content:encoded></item><item><title><![CDATA[How to run API automated tests with Newman on CircleCI.]]></title><description><![CDATA[Postman is a great tool not only for building, but also for testing APIs. In this post, we shall look at how to use Newman, Postman’s command-line collection runner, to run automated tests for an API in a CI/CD pipeline running on CicleCI. Whereas th...]]></description><link>https://blog.mwanje.me/how-to-run-api-automated-tests-with-newman-on-circleci</link><guid isPermaLink="true">https://blog.mwanje.me/how-to-run-api-automated-tests-with-newman-on-circleci</guid><category><![CDATA[Postman]]></category><category><![CDATA[Newman]]></category><category><![CDATA[APIs]]></category><category><![CDATA[Testing]]></category><category><![CDATA[CircleCI]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Sat, 15 Jan 2022 17:10:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654633684/-fbJsaH2U.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Postman is a great tool not only for building, but also for testing APIs. In this post, we shall look at how to use Newman, Postman’s command-line collection runner, to run automated tests for an API in a CI/CD pipeline running on CicleCI. Whereas there may be various tools and libraries to test APIs built in a specific language, automation testing with Newman cuts across and thus the steps followed herein can be followed to test an API developed in any programming language. That stated, we shall use a Node.js API in this post.</p>
<hr />
<h2 id="heading-tutorial">Tutorial</h2>
<ol>
<li><a class="post-section-overview" href="#set-up">Setting up</a></li>
<li><a class="post-section-overview" href="#import-collection">Importing collection and environment</a></li>
<li><a class="post-section-overview" href="#intergrate-circleci">Integrating with CircleCI</a></li>
</ol>
<h3 id="heading-setting-up">Setting up</h3>
<p><a></a></p>
<p>Before we start, make sure you have the newman cli installed on your computer. If you do not have it yet, install it globally by simply running</p>
<pre><code>$ <span class="hljs-built_in">npm</span> install -g newman
</code></pre><p>Fork and clone <a target="_blank" href="https://github.com/123MwanjeMike/node-express-realworld-example-app">this repository</a> which has the API we shall be using and change directory into the created folder. Run the commands below to do so.to install it globally.</p>
<pre><code>$ git clone https:<span class="hljs-comment">//github.com/123MwanjeMike/node-express-realworld-example-app.git</span>
$ cd node<span class="hljs-operator">-</span>express<span class="hljs-operator">-</span>realworld<span class="hljs-operator">-</span>example<span class="hljs-operator">-</span>app<span class="hljs-operator">/</span>
</code></pre><p>Switch to the <code>00--tutorialStart</code> branch.</p>
<pre><code>$ git checkout 00<span class="hljs-operator">-</span><span class="hljs-operator">-</span>tutorialStart
</code></pre><p>Install the dependencies with a package manager(npm, yarn) of your choice. In my case, I’ll be using yarn all throughout.</p>
<pre><code>$ yarn install
</code></pre><p>Run the start script to confirm that everything is working fine.
Ps: You need to have MongoDB installed on your machine. If you don’t have it, head over <a target="_blank" href="https://docs.mongodb.com/guides/server/install/">here</a> to install it or jump right to <a class="post-section-overview" href="#import-collection">Importing collection and environment</a>.</p>
<pre><code>$ yarn <span class="hljs-keyword">start</span>
</code></pre><p>Open another terminal and run the test script</p>
<pre><code>$ yarn <span class="hljs-built_in">test</span>
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654600648/uU4Sc320b.png" alt="All tests passing" /></p>
<h3 id="heading-importing-collection-and-environment">Importing collection and environment</h3>
<p><a></a></p>
<p>Now let’s head over to Postman to get our hands dirty. We need to have our collection in Postman and its environment as well. So, open your postman Workspace and click the <strong>Import</strong> button. You can either import from the test folder of your cloned repository or from your forked repository(<code>00--tutorialStart</code> branch) on GitHub.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654602947/Y1164XPMd.gif" alt="figcaption" /></p>
<p>With the import complete, set the environment to <code>Conduit API Tests — Environment</code>. We can add one more test of our own. So head over to <code>Auth/Login and Remember Token</code> in the <em>Conduit API Tests</em> collection, select the <strong>Tests</strong> tab, and add a test to check that the response status code is 200 from the <strong>SNIPPETS</strong> on the right. Send the request.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654605618/m3TNkHoUI.gif" alt="Adding a status code test from snippets" /></p>
<h3 id="heading-integrating-with-circleci">Integrating with CircleCI</h3>
<p><a></a></p>
<p>We shall now integrate our API in Postman with CircleCI. This way, we shall also be able to trigger and view test builds from within Postman. So, head over to <strong>APIs</strong> and click ‘+’ to create a new API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654607844/OdMJ5fTZq.png" alt="Conduit API, version 1.0.0 created" /></p>
<p>Head over to the <strong>Test</strong> tab and click <strong>Add Test Suite</strong>. Select <strong>Add existing test</strong>, then select <code>Conduit API Tests</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654609758/orWNESEmo.png" alt="Test suite added" /></p>
<p>Still at the same page, under <strong>Connect to CI/CD Builds</strong>, select CircleCI. You’ll be presented with a screen such as below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654611971/Ep_J_wH67.png" alt="Connect to CircleCI pop up empty" /></p>
<p>We need to generate an API key for this integration on CircleCI. So click <a target="_blank" href="https://app.circleci.com/settings/user/tokens?return-to=https%3A%2F%2Fapp.circleci.com%2Fpipelines%2Fgithub%2FBSE21-13">here</a> to go to your CircleCI <strong>User Settings</strong> page, <strong>Personal Access Tokens</strong> section from where you’ll create one. Copy the API token you have added and paste it in the <strong>API Key</strong> input on the Postman screen above.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654614187/do8r_gvKm.png" alt="Creating a CircleCI API token for Postman integration" /></p>
<p>Enter a <strong>Nickname</strong> and select the <strong>CI Project</strong> to run the tests. It’s very likely your forked project is not on that list yet. You want to go back to your CircleCI dashboard and <strong>Set Up Project</strong> for it to reflect on Postman. It’s okay for the first build to fail.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654615963/91jpENsO2.png" alt="Connect to CircleCI pop up filled in" /></p>
<p>Next we shall generate a Newman configuration that we shall place in our <code>.circleci/config.yml</code> file in the project repository. Click <strong>Configure Newman</strong> in the top right corner.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654617919/RxSRZh_Ly.png" alt="Generate Newman Configuration screen" /></p>
<p>For our environment, we need to create a mock server so that we don’t run our requests against any real data. So click <strong>Mock Servers</strong> on the Postman left menu and click ‘<strong>+</strong>’ to create one. Choose the <strong>Select an existing collection</strong> option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654619826/uJMPtoVPP.png" alt="Creating a Mock Server on Postman" /></p>
<p>Select the <code>Conduit API Tests</code> collection. You’ll proceed to the Configuration screen as below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654622079/ln27DHTIa.png" alt="Configuration stage for the Mock Server creation process" /></p>
<p>Click the <strong>Create Mock Server</strong> button and copy the Mock URL provided.
Go to <strong>Environments</strong> to update the <code>apiUrl</code> variable for the <code>Conduit API Tests — Environment</code>. Set its <strong>INITIAL VALUE</strong> to the Mock URL you copied and save the changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654624255/H4MchbQIr.png" alt="Conduit APT Tests -Environment variables" /></p>
<p>With everything now set, we can head back to generate our Newman configuration. Set the collection to <code>Conduit APT Tests</code> and environment to <code>Conduit APT Tests -Environment</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654626029/1Oy8BweW3.png" alt="Newman configuration for CircleCI generated" /></p>
<p>Copy the generated configuration. We shall add it to <code>.circleci/config.yml</code> in our code base. So create the file in the project directory with the commands below.</p>
<pre><code>$ mkdir .circleci
$ touch .circleci/config.yml
</code></pre><p>Paste the copied configuration in the created file. Create a workflow at the bottom to run your job. The workflow should be similar to that below.</p>
<pre><code>workflows:
  test<span class="hljs-operator">-</span>workflow:
    jobs:
      <span class="hljs-operator">-</span> newman<span class="hljs-operator">-</span>collection<span class="hljs-operator">-</span>run
</code></pre><p>Your entire <code>.circleci/config.yml</code> file should look like that below. Take special care of the indention <code>newman/newman-run</code> step(lines 13 –15) to avoid indention related errors.</p>
<p>{% gist https://gist.github.com/123MwanjeMike/b796ba84486c114706889ff14cb76dbc file=config.yml %}</p>
<p>We are almost done. However, as you might have noticed, we have two instances of <code>$POSTMAN_API_KEY</code> (lines 14 and 15) in our <code>.circleci/config.yml</code> file and these must be substituted with an actual value on CircleCI. To create a Postman API Key, head over <a target="_blank" href="https://go.postman.co/settings/me/api-keys">here</a> and click <strong>Generate API Key</strong>. Copy the key and go to the project settings on CircleCI. Add an environment variable called <code>POSTMAN_API_KEY</code> and paste the Postman API Key in the value box.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654628025/JEathyw21.png" alt="POSTMAN_API_KEY variable added" /></p>
<p>While still on CircleCI, go to the <strong>Organization Settings</strong> &gt; <strong>Security</strong> &gt; <strong>Orb Security Settings</strong>, and select <strong>Yes</strong> to allow use of third-party orbs. This is because in our CircleCI configuration, we use <code>newman: postman/newman@0.0.2</code> on line 5 which is not a default CircleCI orb.
Commit and push the changes to GitHub</p>
<pre><code>$ git add .circleci/config.yml
$ git commit <span class="hljs-operator">-</span>m <span class="hljs-string">"add circleci config"</span>
$ git push origin
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654629953/iZf4fSi18.png" alt="Newman report from CircleCI" /></p>
<p>Yeeey!
All our tests passed. We can also view our build history on Postman.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654631817/9-DDebPCC.png" alt="Build history on Postman" /></p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>As you too have seen, testing your API with Postman is quite basic and you do not need to install any other testing packages into your project like mocha, jest, chai-http for automated testing. All you need is some basic understanding of JavaScript to write test cases on Postman.</p>
<p>I hope you enjoyed this post and wouldn't hesitate to leave me a heart or comment.</p>
<p><em>Happy coding!</em></p>
<h3 id="heading-further-reading">Further reading</h3>
<ol>
<li><a target="_blank" href="https://learning.postman.com/docs/writing-scripts/test-scripts/">Writing Postman tests</a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/newman">Newman</a></li>
<li><a target="_blank" href="https://learning.postman.com/docs/integrations/ci-integrations/">Postman CI Integrations</a></li>
</ol>
]]></content:encoded></item><item><title><![CDATA[AutoIdle cuts your Heroku bill by auto-putting your staging and review apps to sleep]]></title><description><![CDATA[From simple to complex applications, Heroku stands out as a deployment choice for many developers. This is because with Heroku, getting an application up and running is a very simple procedure that abstracts the underlying infrastructure and its scal...]]></description><link>https://blog.mwanje.me/autoidle-cuts-your-heroku-bill-by-auto-putting-your-staging-and-review-apps-to-sleep</link><guid isPermaLink="true">https://blog.mwanje.me/autoidle-cuts-your-heroku-bill-by-auto-putting-your-staging-and-review-apps-to-sleep</guid><category><![CDATA[Heroku]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Devops]]></category><category><![CDATA[addon]]></category><category><![CDATA[autoidle]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Wed, 27 Oct 2021 21:56:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654685248/RMY2liOv3C.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>From simple to complex applications, Heroku stands out as a deployment choice for many developers. This is because with Heroku, getting an application up and running is a very simple procedure that abstracts the underlying infrastructure and its scaling needs. However, with more applications running on Heroku is a growing bill even when no traffic is being served. <a target="_blank" href="https://autoidle.com/">AutoIdle</a> is a Heroku add-on  that helps cut your Heroku bill by automatically putting your staging and review apps to sleep when you don't need them. In this article, we shall see how we can install AutoIdle on a Heroku app and review apps in a pipeline and observe how much we save.</p>
<h2 id="heading-how-does-autoidle-work">How does AutoIdle work?</h2>
<p><a target="_blank" href="https://autoidle.com/">AutoIdle</a> typically puts your apps to sleep after 30mins of inactivity and gets them up and running in under 10seconds of a new request. This way, you are not charged for the time the your apps are up but idle. With that stated…</p>
<h3 id="heading-lets-get-our-hands-dirty">…let's get our hands dirty.</h3>
<p>Let's create an app called <em>autoidle-saving</em> in a  Heroku pipeline. Starting from the <em>start</em> branch of <a target="_blank" href="https://github.com/123MwanjeMike/autoidle-saving">this repository</a>, we shall incrementally build our application to the state in the <em>main</em> branch; so fork the repository and go <a target="_blank" href="https://dashboard.heroku.com/pipelines/new">here</a> to create a Heroku pipeline. You will be presented with the screen below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654638319/_RwlnL1ch.png" alt="The newly created Heroku pipeline. It's name is saving0with-autoidle" /></p>
<p>Now select the <em>Settings</em> tab and under the <em>Connect to GitHub</em> option, search for the forked repository and connect it to the pipeline.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654640704/5k6hDtPF5.png" alt="Connecting the pipeline to a GitHub repository" /></p>
<p>After connecting the repository, the <em>Review apps</em> option should now be available. Click <em>Enable</em> to have review apps for Pull Requests.
<strong>Note:</strong> Check the <em>Wait for CI to pass</em> option if you want HerokuCI to run your tests before deploying to the review app. There is a detailed article on Heroku CI and how to use it <a target="_blank" href="https://blog.mikemwanje.dev/build-a-cicd-pipeline-with-heroku-ci">here</a> that you can check out if you want to learn more about HerokuCI specifically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654642316/8tjxfqHO7.png" alt="Enabling review apps for the pipeline" /></p>
<p>Return to the pipeline tab now and for each of staging and production sections, click <strong><em>Add app</em></strong>, and click <strong><em>Create new app…</em></strong> to create a staging and production app respectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654644320/zjqgAC7Yd.png" alt="Creating the staging app for autoidle-saving" /></p>
<p>Your pipeline now looks like so</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654645971/78jOBvxQZ.png" alt="The pipeline after adding a staging and production app" /></p>
<p>Let us make one additional tweak on our two apps. We want the staging app to automatically deploy code in the develop branch and the production app that in main branch on every push to the respective branches of the connected GitHub repository.  So, starting with the staging app, click the <em>Configure automatic deploys…</em> option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654647664/UwtNUdNcBj.png" alt="autoidle-saving-staging app options" /></p>
<p>You will be presented with a screen such as below. Select the develop branch and click <strong><em>Enable Automatic Deploys</em></strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654649124/GNLhSkvKh.png" alt="Enabling automatic deploys on the staging app" /></p>
<p>The same goes for the production application, only that for it we shall deploy the main branch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654651326/rWhKZUxnEP.png" alt="Enabling automatic deploys on the production app" /></p>
<p>The pipeline is now ready and we can make an addition to our app so as to set everything in motion.
Run the commands below to clone the forked repository and install the dependencies of the application.</p>
<pre><code>$ git clone <span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/github.com/</span><span class="hljs-number">123</span>MwanjeMike/autoidle-saving.git
$ cd autoidle-saving
$ npm install
</code></pre><p>Now to confirm that all is okay, run <strong>npm start</strong> in the terminal and you should have the output below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654656933/KtnkB-FV_.png" alt="Terminal output after running npm start" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654658369/uC4U_wzqZ.png" alt="Accessing our application through the browser at 127.0.0.1:3000" /></p>
<p>Now let us deploy to our staging app. So back to the staging section of the pipeline, and  under the app options, click <strong><em>Deploy a branch…</em></strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654660407/3JOh1J0QK.png" alt="staging app options" /></p>
<p>…and deploy the develop branch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654662268/NfmN-0v7Q.png" alt="Manually deploying the develop branch to staging app" /></p>
<p>Let us try to access our staging app in the browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654663827/o4K4x0N80.png" alt="Develop branch successfully deployed" /></p>
<p>We are now going to install AutoIdle on the staging app so that it is automatically put to sleep. So if you already <a target="_blank" href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install">downloaded and installed</a> Heroku CLI on your computer, go to your terminal and login.</p>
<pre><code>$ heroku <span class="hljs-keyword">login</span>
</code></pre><p>Next, we shall add our staging app's git repository to the local repository. To get a Heroku app's git URL, select the app and go to its Settings tab. You should see the <strong><em>Heroku git URL</em></strong> listed under the app's information.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654665531/Nw7Jb-P6s.png" alt="Getting the git url of a  Heroku app" /></p>
<p>Copy the link and add it as a remote repository</p>
<pre><code>$ git remote add staging-app <span class="hljs-symbol">https:</span>/<span class="hljs-regexp">/git.heroku.com/autoidle</span>-saving-staging.git
$ git remote -v
</code></pre><p>You should see a list of your remote repositories in the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654667229/CriGvgXjr.png" alt="Output after running git remote -v" /></p>
<p>Now we shall add AutoIdle to our application.</p>
<pre><code>$ heroku addons:<span class="hljs-keyword">create</span> autoidle
</code></pre><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654668885/gLgqrjs5O.png" alt="AutoIdle successfully added to the staging app" /></p>
<p>We shall also add AutoIdle to our review apps. So, create a new branch <em>heroku-config</em> and add  an <strong><em>app.json</em></strong> file, a manifest format for describing Heroku  web apps.</p>
<pre><code>$ git checkout <span class="hljs-operator">-</span>b heroku<span class="hljs-operator">-</span>config
</code></pre><p>the <em>app.json</em> has the content below</p>
<pre><code>{
   <span class="hljs-attr">"environments"</span>: {
      <span class="hljs-attr">"review"</span>: {
         <span class="hljs-attr">"addons"</span>: [<span class="hljs-string">"autoidle:hobby"</span>]
      }
   }
}
</code></pre><p>Now commit and push your changes to the GitHub repository.</p>
<pre><code>$ git add app.json
$ git commit <span class="hljs-operator">-</span>m <span class="hljs-string">"AutoIdle configs for review apps"</span>
$ git push <span class="hljs-operator">-</span>u origin heroku<span class="hljs-operator">-</span>config
</code></pre><p>Open a PR for the <em>heroku-config</em> branch against develop and merge it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654670491/zRD0MoUpu.png" alt="Our pipeline after merging our PR" /></p>
<p>Now lets make a final addition to our app to have a review app and we compare the saving with AutoIdle. The endpoint is for the 'Hello World, happy saving!' message.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

<span class="hljs-keyword">const</span> app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">`Welcome!, let's save with AutoIdle 🎊`</span>});
});

app.get(<span class="hljs-string">'/salutation/:name'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">`Happy saving <span class="hljs-subst">${req.params.name}</span>.`</span>});
});

app.get(<span class="hljs-string">'/world'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Hello World, happy saving!'</span>});
});

app.get(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.status(<span class="hljs-number">404</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Sorry, resource not found.'</span> });
});

<span class="hljs-keyword">const</span> port = process.env.PORT || <span class="hljs-number">3000</span>;
app.listen(port, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Listening on port <span class="hljs-subst">${port}</span>`</span>));
</code></pre>
<p>Commit and push your changes.</p>
<pre><code>$ git commit <span class="hljs-operator">-</span>am <span class="hljs-string">"hello-world"</span>
$ git push <span class="hljs-operator">-</span>u origin hello<span class="hljs-operator">-</span>world
</code></pre><p>Open a PR for hello-world against develop and we should have a new review app for that.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654676528/DYdbITSHU.png" alt="Hello-world review app running" /></p>
<p>Now let's observe the saving on the <a target="_blank" href="https://app.autoidle.com/">AutoIdle dashboard</a>. In your terminal, run</p>
<pre><code>$ heroku addons:<span class="hljs-keyword">open</span> autoidle
</code></pre><p>You will be taken to the browser and presented with the screen below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654680675/cMtwSG4mg.png" alt="AutoIdle dashboard with the staging and review apps running" /></p>
<p>We can see that all our apps are running and we  don't have any available saving data, so shall check again after some time has passed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657654683186/YOa3iyujj.png" alt="AutoIdle dashboard with available apps stopped" /></p>
<p>We can see that both our staging and review applications were automatically stopped without us having to click any button.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With cloud computing, every extra second your application is running counts and you want to be as lean as possible. In this tutorial, we only used AutoIdle on two applications, but imagine how much you could save with a large application with multiple contributors and new PRs created by the minute, each with a new review app. The cost of having apps running while not actively in use can be unnecessarily large and overwhelming. I hope <a target="_blank" href="https://autoidle.com/">AutoIdle</a> is a tool you and your organization can leverage to reduce costs.</p>
<p>Till next time,
<em>Happy saving!</em></p>
]]></content:encoded></item><item><title><![CDATA[Build a CI/CD pipeline with Heroku CI.]]></title><description><![CDATA[Heroku CI automatically runs your app’s test suite with every push to your app’s GitHub repository, enabling you to easily review test results before merging or deploying changes to your codebase.

Besides strong Dev/prod parity, using Heroku CI come...]]></description><link>https://blog.mwanje.me/build-a-cicd-pipeline-with-heroku-ci</link><guid isPermaLink="true">https://blog.mwanje.me/build-a-cicd-pipeline-with-heroku-ci</guid><category><![CDATA[Heroku]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Devops]]></category><category><![CDATA[agile]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Fri, 01 Oct 2021 20:56:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655705520/2oY2_CI21.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Heroku CI automatically runs your app’s test suite with every push to your app’s GitHub repository, enabling you to easily review test results before merging or deploying changes to your codebase.</p>
</blockquote>
<p>Besides strong Dev/prod parity, using Heroku CI comes with many benefits key of which include;</p>
<ul>
<li>Parallel Test Runs.</li>
<li>Browser tests and User Acceptance Tests</li>
<li>Seamless integration with Heroku pipelines</li>
<li>Easy customization of test dependencies in the test environment</li>
<li>A Simple Integrated Solution (integration, hosting and deployment)</li>
<li>Simple, prescriptive developer experience in modern CI/CD solutions</li>
<li>Management support</li>
<li>Many supported languages</li>
</ul>
<p>In this article, we shall build a Heroku Flow CI/CD pipeline that utilizes Heroku CI.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655673965/46cjevUqD.gif" alt="Heroku Flow illustration" /></p>
<h3 id="heading-part-1-app-init">Part 1: App init.</h3>
<p>We are going to build a simple node.js application that calculates the factorial of a number. We shall however start with its api and then add the factorial functionality later. This is so we can see Heroku CI in action.
To start, fork the repository <a target="_blank" href="https://github.com/123MwanjeMike/cicd-with-herokuci.git">here</a> and then clone your forked repository to your computer.</p>
<pre><code>$ git <span class="hljs-keyword">clone</span> https:<span class="hljs-comment">//github.com/123MwanjeMike/cicd-with-herokuci.git</span>
</code></pre><p>Next, change to the created directory and switch to the start branch.</p>
<pre><code>$ cd cicd<span class="hljs-operator">-</span>with<span class="hljs-operator">-</span>herokuci<span class="hljs-operator">/</span>
$ git checkout start
</code></pre><p>Initialize the project with <em>npm init</em> and then install express</p>
<pre><code>$ <span class="hljs-built_in">npm</span> init -y
$ <span class="hljs-built_in">npm</span> install express
</code></pre><p>You should have a new file <em>package.json</em> in your directory. Open it and add the start script <em>node index.js</em>.</p>
<pre><code>"scripts": {
  "test": "echo \"Error: <span class="hljs-keyword">no</span> test specified\" &amp;&amp; exit 1",
  "start": "node index.js"
},
</code></pre><p>Now create a simple api for the app in <em>index.js</em> file with the code below.</p>
<pre><code>$ touch <span class="hljs-keyword">index</span>.js
</code></pre><pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);

<span class="hljs-keyword">const</span> app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Welcome to the Factorial calculator 🎊'</span> });
});

app.get(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.status(<span class="hljs-number">404</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Resource not found.'</span> });
});

<span class="hljs-keyword">const</span> port = process.env.PORT || <span class="hljs-number">3000</span>;
app.listen(port, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Listening on port <span class="hljs-subst">${port}</span>`</span>));
</code></pre>
<p>Running <em>npm start</em> should give a similar output as below
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655675963/SbUhXI6dO.png" alt="Output after running npm start" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655678082/-o63WiNDP.png" alt="When base url is accessed in browser" /></p>
<p>We now have a basic setup for our application.</p>
<h3 id="heading-part-2-the-pipeline">Part 2: The pipeline.</h3>
<p>We shall be adding our application to a Heroku pipeline.
First we need to tell Heroku which services to run and how to run these. To do this, we shall add a <em>Procfile</em> file at the root of our directory by running;</p>
<pre><code>$ echo <span class="hljs-string">"web: node index.js"</span> <span class="hljs-operator">&gt;</span><span class="hljs-operator">&gt;</span> Procfile
</code></pre><p>Commit and push your changes to the remote repository.</p>
<pre><code>$ git <span class="hljs-keyword">add</span> .
$ git <span class="hljs-keyword">commit</span> -m "&lt;enter your message here&gt;"
$ git push origin <span class="hljs-keyword">start</span>
</code></pre><p>Now move <a target="_blank" href="https://dashboard.heroku.com/pipelines/new">here</a> if you have a Heroku account and create a new Heroku pipeline. Remember to connect your forked repository as you create the pipeline. You should end up with a screen like below.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655679634/syDiquSbl.png" alt />
Click <strong>Enable Review Apps</strong> to have deploy previews for the pull requests(PRs) before they are merged.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655681289/sUu_-nYWG.png" alt />
Next, under the staging section of the pipeline, click <strong>Add app</strong> and create a new app. Its common practice to have the staging app deployed from the develop branch and the production app from the main branch.
In this case, my factorial-app is being deployed from my default branch(main).
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655683309/EtePD76_d.png" alt />
Click settings and scroll to the Heroku CI section. Click Enable Heroku CI.
<strong>Note:</strong> Heroku gives 1000 free dyno hours per month to its verified users and these can be used to access the Heroku CI service for free until they are depleted — in which case you start paying per extra dyno minutes used.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655685003/-C4G6elKu.png" alt="Before enabling Heroku CI" />
Our pipeline is now set up and we are ready for action.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655686837/oQpHz2gZr.png" alt /></p>
<h3 id="heading-part-3-the-applications-factorials-service">Part 3: The application’s factorials service</h3>
<p>We are now going to add a service to our application. We want to enable it calculate the factorial of a number. This will be through a get request with path parameters. We shall use the test driven development approach while at it, so install the test runner mocha and the assertion library chai.</p>
<pre><code>$ <span class="hljs-built_in">npm</span> install -D mocha chai
</code></pre><p>Open the <em>package.json</em> file and find the test key under scripts and change its value to have <em>mocha *.test.js || true</em>. Your scripts now look like so.</p>
<pre><code><span class="hljs-string">"scripts"</span>: {
  <span class="hljs-string">"test"</span>: <span class="hljs-string">"mocha *.test.js || true"</span>,
  <span class="hljs-string">"start"</span>: <span class="hljs-string">"node index.js"</span>
},
</code></pre><p>We are basically telling mocha to execute <em>.test.js</em> files without displaying errors when we run <em>npm test</em>.
Let us now create our test file <em>factorial.test.js</em> that will have our tests which will be automated later on.</p>
<pre><code>$ touch factorial.test.js
</code></pre><pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { assert } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'chai'</span>);
<span class="hljs-keyword">const</span> factorial = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./factorial'</span>);

describe(<span class="hljs-string">'Factorial test'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'Factorial(0) = 1'</span>, <span class="hljs-function">() =&gt;</span> {
    assert.equal(factorial(<span class="hljs-number">0</span>), <span class="hljs-number">1</span>);
  });

  it(<span class="hljs-string">'Factorial(1) = 1'</span>, <span class="hljs-function">() =&gt;</span> {
    assert.equal(factorial(<span class="hljs-number">1</span>), <span class="hljs-number">1</span>);
  });

  it(<span class="hljs-string">'Factorial(5) = 120'</span>, <span class="hljs-function">() =&gt;</span> {
    assert.equal(factorial(<span class="hljs-number">5</span>), <span class="hljs-number">120</span>);
  });

  it(<span class="hljs-string">'Factorial(171) = Infinity'</span>, <span class="hljs-function">() =&gt;</span> {
    assert.equal(factorial(<span class="hljs-number">171</span>), <span class="hljs-literal">Infinity</span>);
  });
});
</code></pre>
<p>Next, create the file <em>factorial.js</em> to which we shall write our function that calculates the factorial of a number.</p>
<pre><code>$ touch factorial.js
</code></pre><pre><code class="lang-javascript"><span class="hljs-keyword">const</span> factorial = <span class="hljs-function">(<span class="hljs-params">number</span>) =&gt;</span> {
  <span class="hljs-keyword">let</span> result = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">if</span> (number === <span class="hljs-number">0</span> || number === <span class="hljs-number">1</span>) {
    <span class="hljs-keyword">return</span> result;
  }
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = number; i &gt;= <span class="hljs-number">1</span>; i -= <span class="hljs-number">1</span>) {
    result *= i;
  }
  <span class="hljs-keyword">return</span> result;
};

<span class="hljs-built_in">module</span>.exports = factorial;
</code></pre>
<p>We can run our test suite with <em>npm test</em> and get the output as below
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655688470/UZJr9KHkD.png" alt="npm test output" />
Now let us update our <em>index.js</em> too with an end point that can serve requests for the factorials of numbers.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> factorial = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./factorial'</span>);

<span class="hljs-keyword">const</span> app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { host } = req.headers;
  res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Welcome to the Factorial calculator 🎊'</span>, <span class="hljs-attr">docs</span>: <span class="hljs-string">`http://<span class="hljs-subst">${host}</span>/docs`</span> });
});

app.get(<span class="hljs-string">'/docs'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { host } = req.headers;
  res.status(<span class="hljs-number">200</span>).json({
    <span class="hljs-attr">message</span>: <span class="hljs-string">'Documentation'</span>,
    <span class="hljs-attr">request</span>: <span class="hljs-string">`http://<span class="hljs-subst">${host}</span>/factorial/&lt;number&gt;`</span>,
    <span class="hljs-attr">response</span>: <span class="hljs-string">'The factorial of &lt;number&gt; is &lt;result&gt;`'</span>,
    <span class="hljs-attr">example</span>: {
      <span class="hljs-attr">request</span>: <span class="hljs-string">`http://<span class="hljs-subst">${host}</span>/factorial/5`</span>,
      <span class="hljs-attr">response</span>: <span class="hljs-string">'The factorial of 5 is 120`'</span>,
    },
  });
});

app.get(<span class="hljs-string">'/factorial/:number'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { number } = req.params;
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(number)) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">`'<span class="hljs-subst">${req.params.number}</span>' is not a number.`</span> });
  <span class="hljs-keyword">if</span> (number &gt; <span class="hljs-number">200</span>) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">`The factorial of <span class="hljs-subst">${number}</span> is Infinity`</span> });
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">`The factorial of <span class="hljs-subst">${number}</span> is <span class="hljs-subst">${factorial(number)}</span>`</span> });
});

app.get(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { host } = req.headers;
  res.status(<span class="hljs-number">404</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'Resource not found.'</span>, <span class="hljs-attr">docs</span>: <span class="hljs-string">`http://<span class="hljs-subst">${host}</span>/docs`</span> });
});

<span class="hljs-keyword">const</span> port = process.env.PORT || <span class="hljs-number">3000</span>;
app.listen(port, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Listening on port <span class="hljs-subst">${port}</span>`</span>));
</code></pre>
<p>I added a docs endpoint to provide documentation for our service request.</p>
<h3 id="heading-part-4-the-magic">Part 4: The magic</h3>
<p>The moment of truth has come. Now we shall push our changes again to GitHub, open a pull request then observe how it all works.</p>
<pre><code>$ git <span class="hljs-keyword">add</span> .
$ git <span class="hljs-keyword">commit</span> -m "Factorial service"
$ git push origin <span class="hljs-keyword">start</span>
</code></pre><p>After opening the pull request, we see that Heroku CI tests passed and that the branch was successfully deployed. We can also go ahead to checkout the deployment.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655690062/4EdCSUyNo.png" alt="Open pull request on GitHub" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655691773/ceTyHJXBn.png" alt="Deploy preview for our pull request" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655693387/1D2LTYGLU.png" alt="All our tests passed on Heroku CI" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655695119/WG8Cl9kKh.png" alt="Our Heroku pipeline" />
Finally, lets enable automatic deploys for our factorial-app still under the staging section of our pipeline.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655696931/RchURlZzS.png" alt />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655698686/sAk7GVPhM.png" alt /></p>
<p>From now on, any code push to our main branch will automatically be deployed to the staging environment of our application.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655700531/TM791R9cy.png" alt="Our changes on main branch deployed to staging" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655702188/r5IjZvL8B.png" alt="Factorial app running" />
Since everything is working fine, we can then move/promote the app to production.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655703872/k2_56wOHv.png" alt /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As you have seen, setting up a CI/CD pipeline with Heroku CI is quite easy and Heroku Flow is a very intuitive workflow for visualizing your code delivery process. Heroku is ideal for small and medium business applications even though large business application with a microservices architecture can still leverage it. With Heroku CI, you can move fast without breaking things.</p>
<p>For reference, you can find the entire project code in this <a target="_blank" href="https://github.com/123MwanjeMike/cicd-with-herokuci">repository</a> with branches start, part-1, part-2, and part-3 that have the resultant code version of each respective part above and the final application code in the main branch.</p>
<p>This post was sponsored by <a target="_blank" href="https://autoidle.com/">AutoIdle</a>. AutoIdle is an add-on that cuts your Heroku bill by automatically putting your staging and review apps to sleep when you don't need them.</p>
<p><em>Happy hacking.</em></p>
]]></content:encoded></item><item><title><![CDATA[Project management with Github Issues.]]></title><description><![CDATA[Photo by Roman Synkevych on Unsplash
Software projects might be different from other types of projects in terms of visibility, complexity, conformity, and flexibility as Fred Brooks points out; but one thing in common with all projects is change. Ess...]]></description><link>https://blog.mwanje.me/project-management-with-github-issues-b5cc08ab2401</link><guid isPermaLink="true">https://blog.mwanje.me/project-management-with-github-issues-b5cc08ab2401</guid><category><![CDATA[GitHub]]></category><category><![CDATA[project management]]></category><category><![CDATA[projects]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Tue, 07 Sep 2021 05:29:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651194560/yR_74aVgt.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photo by <a target="_blank" href="https://unsplash.com/@synkevych?utm_source=medium&amp;utm_medium=referral">Roman Synkevych</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<p>Software projects might be different from other types of projects in terms of visibility, complexity, conformity, and flexibility as Fred Brooks points out; but one thing in common with all projects is change. Essentially, project management is change management and this is what project management tools help us with. Tools like Trello, ClickUp, Jira are built to enable us best plan and track our work — herein, you’ll learn to do the same with Github Issues.</p>
<h3 id="heading-but-first-why-github-issues">But first, why Github Issues?</h3>
<p>Given that Github is a leading software development platform, most project management tools support integration with it as a way to bridge management and development teams. However, the underlying problem still remains: this is not where the code is and developers have to leave the platform to update their work item status on these project management tools. In addition, automation on these is not as easy and the pricing of these tools is maybe high.</p>
<h3 id="heading-what-are-github-issues">What are Github Issues.</h3>
<p><a target="_blank" href="https://docs.github.com/en/issues">Github Issues</a> is a collection of Github products that include labels and milestones, issues, project boards, and projects(beta). All these products can be used together to easily plan and track your progress on your software project. The scope of this article will be limited to issues and Github project boards and that’s what will be covered in the tutorial section.</p>
<h3 id="heading-github-project-boards">Github project boards</h3>
<p><em>Project boards on GitHub help you organize and prioritize your work. You can create project boards for specific feature work, comprehensive roadmaps, or even release checklists. With project boards, you have the flexibility to create customized workflows that suit your needs.</em></p>
<p>Depending on your needs, you can either create repository, user-owned, or organization-wide project boards all with different scope. A card on a project board represents either an issue, a pull request or a note. These cards contain metadata about the issue or pull request the like labels, assignee(s), the status, and who opened it. Github project boards also provides you with templates to create a new board some of which come with built in automation. They include basic kanban, automated kanban, automated kanban with review, and bug triage. Now, let’s see how this works.</p>
<h3 id="heading-tutorial">Tutorial</h3>
<p><strong>Step 1:</strong> Create the project board.First, <a target="_blank" href="https://docs.github.com/en/issues/organizing-your-work-with-project-boards/managing-project-boards/creating-a-project-board#creating-a-repository-project-board">create a repository project board</a> from the Automated Kanban template under the <strong>project template</strong> drop-down. I’ll call my board User App.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651187123/JvOYFihtE.png" alt /></p>
<p>You can rename the <em>To do</em> Column to <em>Product Backlog</em> and add a column called <em>Sprint Backlog</em> with the To do preset, and click the <strong>Reopened</strong> checkbox under <strong>Move issues here when…</strong> and move it after the <em>Product Backlog</em> column.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651188926/dDrr8IJDCi.png" alt /></p>
<p><strong>Step 2:</strong> Add notes to the project board<br />Next, we will <a target="_blank" href="https://docs.github.com/en/issues/organizing-your-work-with-project-boards/tracking-work-with-project-boards/adding-notes-to-a-project-board#adding-notes-to-a-project-board">add notes</a> to our project board under the <em>Product Backlog</em> list and after <a target="_blank" href="https://docs.github.com/en/issues/organizing-your-work-with-project-boards/tracking-work-with-project-boards/adding-notes-to-a-project-board#converting-a-note-to-an-issue">convert them to issues</a>. These are features we plan to work on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651190786/I6grqdDFl.png" alt /></p>
<p><strong>Step 3:</strong> Assign issues.<br />Now that we have a good understanding of what we plan to do, we shall start embarking on the issues. So say, for the first sprint we are working on <em>Signup with email</em> and <em>Login with email and password</em>. We shall move these cards to the <em>Sprint Backlog</em> column and <a target="_blank" href="https://docs.github.com/en/issues/tracking-your-work-with-issues/assigning-issues-and-pull-requests-to-other-github-users#assigning-an-individual-issue-or-pull-request">assign each issue</a> to an individual.</p>
<p><strong>Step 4:</strong> Update <em>In Progress</em> column automation.<br />We will now <a target="_blank" href="https://docs.github.com/en/issues/organizing-your-work-with-project-boards/managing-project-boards/configuring-automation-for-project-boards">update automation</a> for the <em>In Progress</em> column move pull requests here when newly added, reopened, and pending approval by reviewer. This will enable us know what tasks are currently being worked upon.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651192742/VkBCjS4PN.png" alt /></p>
<p><strong>Moment of truth  
</strong>With the above setup complete, we are now good to go. When an assignee opens a PR(Pull Request) and <a target="_blank" href="https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#manually-linking-a-pull-request-to-an-issue">links</a> it to the assigned issue, we shall see a new card under the <em>In Progress</em> column that corresponds to the PR, and when the PR is merged, both cards (for the PR and the linked issue) will automatically be moved to the <em>Done</em> column.</p>
<iframe src="https://www.youtube.com/embed/djJI9dJtqZw?feature=oembed" width="700" height="393"></iframe>

<p>Github Project Board: <a target="_blank" href="https://github.com/123MwanjeMike/user-app/projects/1">https://github.com/123MwanjeMike/user-app/projects/1</a></p>
<h3 id="heading-final-thoughts">Final thoughts.</h3>
<p>This is only the tip of the iceberg that can be done with Github Issues and with good mastery of the Github project management products, you are spoilt for choice.<br />One only challenge I’ve found with Github Issues is that clients and management may have little or totally no knowledge of how to use these products. In such cases, you want to rethink the solution to get a workaround.</p>
]]></content:encoded></item><item><title><![CDATA[How different is CommonJs require from ES6 import?]]></title><description><![CDATA[In JavaScript, you can use either ECMAScript 6(ES6) modules or CommonJs modules in your project and there are a few differences between these that do affect how your program modules are loaded. In this article, I explore how each works and how it may...]]></description><link>https://blog.mwanje.me/how-different-is-commonjs-require-from-es6-import</link><guid isPermaLink="true">https://blog.mwanje.me/how-different-is-commonjs-require-from-es6-import</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><category><![CDATA[commonjs]]></category><category><![CDATA[require]]></category><category><![CDATA[Import]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Sat, 28 Aug 2021 09:28:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657655579463/tJ3qiRcPt.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In JavaScript, you can use either ECMAScript 6(ES6) modules or CommonJs modules in your project and there are a few differences between these that do affect how your program modules are loaded. In this article, I explore how each works and how it may affect your program execution.</p>
<h3 id="heading-commonjs-modules">CommonJs modules.</h3>
<p>CommonJs is the original and default module system of Node.js which uses require and module.exports. Below is an example.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Importing modules</span>
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> fileDelete = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./fileDeleter'</span>);
<span class="hljs-keyword">const</span> fileName = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./fileNamer'</span>);

<span class="hljs-keyword">const</span> writeFile = <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> fs.writeFileSync(fileName, data);
}

<span class="hljs-comment">// Exporting writeFile module</span>
modules.exports = writeFile;
</code></pre>
<p>With require, you can’t selectively load only the modules you need. This means even the fileDelete module from the example above will be imported even if it is not needed or used anywhere. Additionally, importing of the modules is synchronous which means that fileName module can’t be imported before fs and fileDelete modules are imported, and a failure to import fileDelete will cause run-time errors even if it is not used anywhere in our program. CommonJS modules are the choice for the node.js server.</p>
<h3 id="heading-ecmascript-modules">ECMAScript modules</h3>
<p>ECMAScript modules are relatively newer and use import and export. Below is the transformation of our CommonJs example from above to ESM.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Importing modules</span>
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>;
<span class="hljs-keyword">import</span> fileDelete <span class="hljs-keyword">from</span> <span class="hljs-string">'./fileDeleter'</span>;
<span class="hljs-keyword">import</span> fileName <span class="hljs-keyword">from</span> <span class="hljs-string">'./fileNamer'</span>;

<span class="hljs-keyword">const</span> writeFile = <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> fs.writeFileSync(fileName, data);
}

<span class="hljs-comment">// Exporting writeFile module</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">writeFile</span>;</span>
</code></pre>
<p>With import, you load only the modules you need. For example, the fileDelete module from the above will not be imported since it is not used anywhere. Additionally, the importing of the modules is asynchronous which means that both fs and fileName are imported at the same time. You generally want to use ESM for your new projects.</p>
<p><strong>…how about .cjs and .mjs?</strong>
.cjs is a file extension for CommonJS modules while .mjs is a file extension for ECMAScript module. Node.js by default treats .js files as CommonJS modules. You can change this by adding "type": "module"to your package.json file so you can use ECMAScript modules (in your .mjs files) within a Node.js environment. This is what Google Chrome <a target="_blank" href="https://v8.dev/">V8</a> recommends.</p>
<p>I hope this was helpful to you and for further reading, do checkout <a target="_blank" href="https://v8.dev/features/modules">JavaScript modules</a>.</p>
<p><em>Happy coding!</em></p>
]]></content:encoded></item><item><title><![CDATA[Why is everyone shifting to the cloud?]]></title><description><![CDATA[Photo by Sigmund on Unsplash
Over the past decade, cloud computing saw a rapid growth and you must have made use of it several times whether you were aware or not. You might be wondering though why there is so much excitement about moving to the clou...]]></description><link>https://blog.mwanje.me/why-is-everyone-shifting-to-the-cloud-29a09ed287bf</link><guid isPermaLink="true">https://blog.mwanje.me/why-is-everyone-shifting-to-the-cloud-29a09ed287bf</guid><category><![CDATA[Cloud Computing]]></category><dc:creator><![CDATA[Mike Mwanje]]></dc:creator><pubDate>Thu, 19 Aug 2021 14:03:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1657651178934/WDFjn5Jue.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photo by <a target="_blank" href="https://unsplash.com/@sigmund?utm_source=medium&amp;utm_medium=referral">Sigmund</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<p>Over the past decade, cloud computing saw a rapid growth and you must have made use of it several times whether you were aware or not. You might be wondering though why there is so much excitement about moving to the cloud and how best you can leverage it. You may also not be sure if moving to the cloud is a good decision given your scenario. Well, this article is just for you.</p>
<p>First off, the <em>Cloud is a means of storing and accessing data and programs over the internet instead of your local server or personal computer.</em> You probably know Software as a service(SaaS) examples Facebook, Gmail, Netflix, or Platform as a service(PaaS) examples Heroku, Google App Engine, Infrastructure as a service(IaaS) examples Google Cloud, or AWS. I will not go deeper into each <a target="_blank" href="https://blog.hubspot.com/service/iaas-paas-saas"><em>cloud services</em></a> category, but that is how diverse cloud computing goes.</p>
<h3 id="heading-what-you-get-when-you-shift-to-the-cloud">What you get when you shift to the cloud.</h3>
<ol>
<li>Efficiency<br />With cloud computing, you only pay for services that use and for the duration used. This means you are not billed for any redundant resources. A higher billing only comes with additional client usage which means even more revenue for you, so its basically a win-win situation.</li>
<li>Scalability<br />With containerization, your application is automatically scaled up and down depending on the number of requests it processes. There is no additional configuration needed or waiting time latency with higher request traffic.</li>
<li>Security<br />Whilst you may have cyber security experts, moving to the cloud only means you even have a larger team. This also means that your cloud service provider is liable in case of any security attacks.<br />In addition, it’s so much easier to recover from security attacks while on the cloud as compared to on-premise.</li>
<li>Reliability<br />Hosting your services with a cloud service provider ensures the availability of these to your end users much more than your on-premise alternative may ever guarantee.</li>
<li>Cost reduction<br />If you are a startup, this is where cloud computing comes in handy. You don’t need to purchase a server computer, or hire extra personnel to manage the latter.</li>
</ol>
<p>I’ve only covered some of what the cloud has to offer even though you need technical expertise to best configure for some of the benefits.</p>
<h3 id="heading-considerations">Considerations</h3>
<p>Before moving to the cloud, you might want to first confirm if this is a decision you are ready to make.</p>
<ol>
<li>Your application generates huge files. Repeatedly moving huge files up and down the cloud can be very costly. You might want to remain on-premise.</li>
<li>You have security problems on-premises. If there exist security exploits while still on-premise, chances are moving to cloud will not fix these. You need to fix risks before exposing the same to 7.8 billion potential attackers.</li>
<li>Your organization culture is not ready for a move to cloud. As an IT leader, you need to support personnel at your organization to deal with the “as is” and the “to be” states of your cloud application before making the shift.</li>
</ol>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Cloud computing is an evolving potential with even newer technologies that are meant to enable us do what we did yesterday better and faster today and tomorrow.</p>
]]></content:encoded></item></channel></rss>