<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Oliver</title>
    <description>The latest articles on DEV Community by Oliver (@mxoliver).</description>
    <link>https://dev.to/mxoliver</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F158298%2F4bcc9b45-ad67-4a7f-8a4f-98d92e448de5.jpg</url>
      <title>DEV Community: Oliver</title>
      <link>https://dev.to/mxoliver</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mxoliver"/>
    <language>en</language>
    <item>
      <title>Learning GitOps with Helm Charts + Flux</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Fri, 31 Jan 2020 03:11:18 +0000</pubDate>
      <link>https://dev.to/mxoliver/learning-gitops-with-helm-charts-flux-163m</link>
      <guid>https://dev.to/mxoliver/learning-gitops-with-helm-charts-flux-163m</guid>
      <description>&lt;p&gt;Earlier this month I built and deployed my first ever GitOps Continuous Delivery pipeline using kubernetes and flux. Throughout the process I found myself wading through quite a bit of outdated and confusing documentation, so having made it to the other side I wanted to write up a walk-through of the steps I took in the hopes that other developers can have an easier time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stack:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/eks/"&gt;AWS EKS&lt;/a&gt; for the kubernetes cluster&lt;br&gt;
HelmOperator by &lt;a href="https://fluxcd.io/"&gt;Flux&lt;/a&gt; for GitOps&lt;br&gt;
&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; + &lt;a href="https://aws.amazon.com/cloudformation/"&gt;Cloudformation&lt;/a&gt; for infrastructure &lt;/p&gt;

&lt;p&gt;The application I deployed to the cluster was a GraphQL API that lives in a docker image on AWS ECR, but this can be adapted to any docker container or other containerized build. &lt;/p&gt;

&lt;p&gt;Since there are a lot of good resources on creating a kubernetes cluster specific to whatever service you are using (AWS, Google GKE, etc) I am going to start this walk-through with the assumption that you have an existing cluster that you want to deploy an image to. &lt;/p&gt;
&lt;h1&gt;
  
  
  Step 1: GitHub
&lt;/h1&gt;

&lt;p&gt;You will need a dedicated GitHub repository for this process. This is where you will commit all of the following configuration (your helm charts, helm releases, etc), and it should be separate from whatever repository you are using for building the application that you are deploying. &lt;/p&gt;
&lt;h1&gt;
  
  
  Step 2: Setting up Fluxcd
&lt;/h1&gt;

&lt;p&gt;Follow the instructions for &lt;a href="https://helm.sh/docs/intro/install/"&gt;installing Helm&lt;/a&gt; and &lt;a href="https://docs.fluxcd.io/en/1.17.1/references/fluxctl.html"&gt;Fluxctl&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, use helm to install the fluxcd charts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add fluxcd https://charts.fluxcd.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a namespace on your cluster for flux-related pods to live:&lt;/p&gt;

&lt;p&gt;I used 'fluxcd' but you can choose whatever namespace makes sense for you&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create ns fluxcd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your flux pod (the GitHub repo + branch will be from Step 1):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;-i&lt;/span&gt; flux fluxcd/flux &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; fluxcd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; git.url&lt;span class="o"&gt;=&lt;/span&gt;git@github.com:&amp;lt;GITHUB REPO&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; git.branch&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;BRANCH&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your repository is private you will also need to add the knownhosts flag:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--set-file ssh.known_hosts=$known_hosts_path&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;pointing to the path where your knownhosts are, usually in the ~/.ssh directory. &lt;/p&gt;

&lt;h1&gt;
  
  
  Step 3: The Helm Operator
&lt;/h1&gt;

&lt;p&gt;The helm operator is a controller that works in tandem with flux and allows you to create HelmReleases that will deploy an image, as well as update the image to the latest version whenever Flux detects a change.&lt;/p&gt;

&lt;p&gt;Apply the Helm Operator to your cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/fluxcd/helm-operator/master/deploy/flux-helm-release-crd.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the helmRelease pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade &lt;span class="nt"&gt;-i&lt;/span&gt; helm-operator fluxcd/helm-operator &lt;span class="nt"&gt;--wait&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; fluxcd &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; helm.versions&lt;span class="o"&gt;=&lt;/span&gt;v3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the SSH Key:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fluxctl identity --k8s-fwd-ns fluxcd&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;and add it to your github repository under deploy keys. &lt;/p&gt;

&lt;p&gt;You will want to give this key write access so that flux can update your helmReleases when it detects a new version of your image.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 4: HelmReleases
&lt;/h1&gt;

&lt;p&gt;Now that flux is all set up, it's time to get to the good stuff. As I mentioned previously the HelmReleases are yaml files that flux will use to deploy an image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;helm.fluxcd.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HelmRelease&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;app name -- name of the application you're deploying&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;flux.weave.works/automated&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;flux.weave.works/tag.chart-image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;glob:dev-*&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;releaseName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;app name&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;helmVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v3&lt;/span&gt;
  &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;github repo from step 1&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;the directory where your charts live -- ex. charts/app-name &amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;github branch set in step 2&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;image endpoint of your application&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;hpa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;extraEnvs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;## This is where you can add any public env variables&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oliver_2020&lt;/span&gt;
      &lt;span class="na"&gt;dbServer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;11.0.1.213&lt;/span&gt; 
    &lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;## The Secret Name for private env variables (more on that later)&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each HelmRelease you have will need a corresponding Helm Chart. Helm has some great &lt;a href="https://helm.sh/docs/chart_template_guide/getting_started/"&gt;documentation&lt;/a&gt; on building custom Helm Charts + Templating, but I'll add the basics.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 5: Helm Charts
&lt;/h1&gt;

&lt;p&gt;If kubernetes is the pilot, Helm is the steering wheel. Most Helm Charts contain the following components: &lt;/p&gt;

&lt;p&gt;The Chart.yaml file is almost like the 'title page' of a helm chart. It consists of the chart name and version information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Super Awesome GraphQL API&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;graphql&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The values.yaml file is where you will define all the variables you will need for your chart.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;image endpoint of your application&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;replicaCount&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;hpa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;extraEnvs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oliver_2020&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These variables are then consumed by the various template files. &lt;/p&gt;

&lt;p&gt;A common template file is the deployment template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;template "app.name" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;template "app.chart" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;heritage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Service&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.replicaCount&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;template "app.name" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;template "app.name" .&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
        &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Release.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;prometheus.io/scrape&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Chart.Name&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.image&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.imagePullPolicy&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
          &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publicEnvKey&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.extraEnv.key | quote&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secretEnvKey&lt;/span&gt;
              &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbPassword&lt;/span&gt;
                  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecrets&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;toYaml .Values.resources | indent 12&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- with .Values.nodeSelector&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;nodeSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;toYaml . | indent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- with .Values.affinity&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;affinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;toYaml . | indent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- with .Values.tolerations&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;tolerations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;toYaml . | indent 8&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
        &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have access to variables defined throughout your chart using the templating syntax&lt;/p&gt;

&lt;p&gt;.Values&lt;br&gt;
.Release&lt;br&gt;
.Chart&lt;/p&gt;

&lt;p&gt;However, if you need to change the structure of a value, for example to remove or add a hyphen, you can add a helper.tpl file. Template helpers are also useful for generating values like timestamps, where it wouldn't make sense to put it in your values.yaml file because it's not really integral to the chart.  &lt;/p&gt;
&lt;h1&gt;
  
  
  Step 6: Template Helpers
&lt;/h1&gt;

&lt;p&gt;Variables are defined in a helper file with the &lt;code&gt;define&lt;/code&gt; action&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- define "mychart.labels"&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;generator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;helm&lt;/span&gt;
    &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;now | htmlDate&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then call this value in any of your template files with the &lt;code&gt;template&lt;/code&gt; action&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{{ template "mychart.labels" }}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;which the template engine will read as the current date. &lt;/p&gt;

&lt;p&gt;For a thorough reference on declaring custom values see the helm &lt;a href="https://helm.sh/docs/chart_template_guide/named_templates/"&gt;named templates guide&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Step 7: Sealed Secrets
&lt;/h1&gt;

&lt;p&gt;If you have any private env variables you want to use, it's good practice to store them in a secret. Kubernetes has a native way to store secrets but the values are only base64 encoded which isn't the safest strategy so I've been using a tool called &lt;a href="https://github.com/bitnami-labs/sealed-secrets"&gt;sealed-secrets&lt;/a&gt; which allows you to create SealedSecrets resources that you can then store in GitHub because they can only be decrypted by the controller that created them. &lt;/p&gt;

&lt;p&gt;A SealedSecret resource looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bitnami.com/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SealedSecret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecrets&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;encryptedData&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;dbPassword&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq.....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and can be referenced in your helmRelease by the secret name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;envFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysecrets&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;To begin, install the Stable repository:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helm repo add stable https://kubernetes-charts.storage.googleapis.com/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Update your Helm repositories:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helm repo update&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and then install sealed-secrets from the stable repository:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helm install --namespace kube-system stable/sealed-secrets --generate-name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once it's installed you can apply the sealed-secrets controller to you cluster:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.9.6/controller.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and then fetch the generated public key (this can be committed to GitHub) using the generated sealed secrets pod name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeseal &lt;span class="nt"&gt;--fetch-cert&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--controller-namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--controller-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sealed-secrets-230493143 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; pub-cert.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can grab the pod name with &lt;code&gt;kubectl get pods -n kube-system&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 8: Creating a SealedSecret Resource
&lt;/h1&gt;

&lt;p&gt;To create a Secret you will first generate it with kubectl and then ecrypt it with kubeseal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;env &lt;/span&gt;namespace&amp;gt; create secret generic &amp;lt;secretname&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;key&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;value&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;key&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;value&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--dry-run&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-o&lt;/span&gt; json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &amp;lt;secretname&amp;gt;.json

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

&lt;/div&gt;



&lt;p&gt;the dry run flag will create the json file with base64 encrypted values, but will not create an actual kubernetes secret on your cluster. &lt;/p&gt;

&lt;p&gt;To encrypt it with kubeseal you will pass in the location of the public key from the previous step, the name of the json file you just created, and the filename you want to use for your secret&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeseal &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yaml &lt;span class="nt"&gt;--cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pub-cert.pem &amp;lt; secretname.json &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secretname.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process will generate a custom SealedSecret resource that contains encrypted credentials that can only be unsealed by the controller that created it. &lt;/p&gt;

&lt;p&gt;Once you have your SealedSecret resource you can delete or .gitignore the generated json file. When you commit the pub-cert file and the new SealedSecrets resource to GitHub flux will apply the secret to your cluster and then the controller will unseal it into a native kubernetes secret to be read by your cluster. &lt;/p&gt;

&lt;h1&gt;
  
  
  Step 9: Push To Git
&lt;/h1&gt;

&lt;p&gt;Once your helm releases and corresponding helm charts are all set you can go ahead and push your commits to the GitHub repository from step 1. &lt;/p&gt;

&lt;p&gt;Once your commits are merged or pushed to the branch you specified in step 2 flux will pick up the changes and deploy your first helm release. &lt;/p&gt;

&lt;p&gt;Flux generally takes a minute or two to sync, but you can force a sync manually with &lt;/p&gt;

&lt;p&gt;&lt;code&gt;fluxctl sync --k8s-fwd-ns fluxcd&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;replacing &lt;code&gt;fluxcd&lt;/code&gt; with whatever namespace you chose for your flux pod in step 2.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 10. TroubleShooting
&lt;/h1&gt;

&lt;p&gt;If you pushed your commits, waited for flux to sync, and still aren't seeing the pod with the app-name from your helm release there are a few ways you can troubleshoot. &lt;/p&gt;

&lt;p&gt;Get a list of all pods with &lt;code&gt;kubectl get pods --all-namespaces&lt;/code&gt; and copy the name of the flux pod. Then run &lt;code&gt;kubectl logs &amp;lt;fluxpodname&amp;gt; -n fluxcd&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;You should be able to see the logs of flux trying to clone the repo, and polling for updates, and can check for any errors. &lt;/p&gt;

&lt;p&gt;Another strategy is &lt;code&gt;kubectl get hr&lt;/code&gt; which will get all helm release instances, and will often show where the release is in the deployment process and any errors that came up. &lt;/p&gt;

&lt;p&gt;Lastly, I always find it helpful to describe the resources and make sure they are configured as expected. You can describe any kubernetes resource with &lt;code&gt;kubectl describe &amp;lt;resourcetype&amp;gt; &amp;lt;resourcename&amp;gt;&lt;/code&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This is only one of many ways to configure a continuous delivery pipeline for your kubernetes pipeline. Flux is a great tool because it integrates well with Helm, and is a relatively straight forward way to automate the deployment process. Helm recently updated to version 3 and flux support for Helm v3 is still in beta mode, thus the somewhat confusing/outdated documentation, but the team at Weaveworks has been very responsive on their slack channel at &lt;a href="https://cloud-native.slack.com/"&gt;https://cloud-native.slack.com/&lt;/a&gt; and hopefully this walkthrough can help navigate some of the complexity as well.&lt;/p&gt;

&lt;p&gt;Have questions, feedback, pictures of your pets? Don't hesitate to comment down below and happy coding! &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>flux</category>
      <category>helm</category>
      <category>gitops</category>
    </item>
    <item>
      <title>How I got hired as a Bootcamp Grad (without a relevant degree)</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 25 Jul 2019 14:46:44 +0000</pubDate>
      <link>https://dev.to/mxoliver/how-i-got-hired-as-a-bootcamp-grad-without-a-relevant-degree-2dcb</link>
      <guid>https://dev.to/mxoliver/how-i-got-hired-as-a-bootcamp-grad-without-a-relevant-degree-2dcb</guid>
      <description>&lt;p&gt;Back in September of 2018 I enrolled in Bloc.io, an online bootcamp designed to teach full-stack development (ReactJS on the front-end, and a choice of either Node or Rails on the backend). If you want to read about my experiences from during the program you can check out my previous posts, for this post I want to skip ahead and focus on that transition period between finishing bootcamp and getting a job as a developer. &lt;/p&gt;

&lt;p&gt;I started my job search in March 2018, I officially graduated from Bloc in May, and I started my first job as a developer in June. Just to give a brief overview, between March and June I applied to 68 jobs, and interviewed with 11 different companies. I don't know if those numbers are average, and on a whole I would encourage you not to worry too much about numbers, I only mention it to show that it is perfectly normal to not interview with or even hear back from the majority of places that you apply to. Not hearing back is not a sign that you are never going to get a job, or that you aren't hire-able. It can be a stressful and disheartening process, but I hope that by sharing my strategy it might be a little easier. &lt;/p&gt;

&lt;p&gt;Tip 1: Start early and get your application out there. &lt;br&gt;
Start applying before you graduate, I suggest about two months before you finish but it depends on how long or short your program is. Regardless of whether or not you feel ready, get your resume written and just start applying. You could hear back, but more realistically this will serve as practice for some important skills -- quickly drafting up compelling cover letters, tailoring your resume based on the job requirements, finding relevant jobs, and getting used to putting yourself out there. &lt;/p&gt;

&lt;p&gt;Quick note on cover letters: Early in my job search I was told not to bother with them, but in my experience these are essential for someone like me without a traditional tech background. If you have a CS degree having a stellar resume may be enough to get you considered, but if not, give yourself a chance by telling your story and making your own case for why you are worth hiring.&lt;/p&gt;

&lt;p&gt;Tip 2: Track everything related to your job search. My bootcamp gave us a spreadsheet for this and it was honestly a life saver. Every time you apply somewhere, write down the date, company name, position title, company website, location, job posting url, your enthusiasm level about the position, how you found the job (career website, networking contact, cold outreach, personal connection), and your status (researching, applied, interviewing, rejected). You also want to track your interviews, questions asked, how you think you did, the name of who you interviewed with, etc. Lastly, I found it helpful to track my networking efforts as well. So, keeping track of meetups and who I met there, any informational interviews I went on, any cold outreach efforts I made and whether I heard back. &lt;/p&gt;

&lt;p&gt;Tip 3: Networking. More specifically, making connections with experienced developers in your area. This is a big one so I'm going to split it into subsections: meetups, informational interviews, and getting your foot in the door &lt;/p&gt;

&lt;p&gt;Meetups: &lt;/p&gt;

&lt;p&gt;I started by going to a meetup called Code and Coffee in my area. Code and Coffee is a bi-monthly Saturday morning meetup with coffee and donuts where we just sit and work on personal projects or what ever other coding things we want to do. &lt;/p&gt;

&lt;p&gt;Having connections to your local developer community is helpful not only for hearing about job opportunities, but for keeping up to date on new frameworks or strategies, keeping your skills sharp by working on personal projects, meeting people involved in open-source projects that you can help with (this will give you experience programming as a team), and just spending time in a space where people are excited about what they do. &lt;/p&gt;

&lt;p&gt;You will also get practice talking about code, and being able to explain what you are doing, which is a super important skill for interviews but also just for working as a developer in general. &lt;/p&gt;

&lt;p&gt;Informational Interviews: &lt;/p&gt;

&lt;p&gt;If you haven't heard this term before, an informational interview is a semi-informal chat with the goal of learning more about the person and their experience in your field. This is a great place to learn from more experienced developers and ask lots of questions about how they keep their skills up to date, how they went about getting a job, the challenges of their work and how they get through them, etc etc. You don't want to talk too much about yourself here, this is about learning about them and their experiences. If you are part of a meetup already, ask someone there if they'd like to grab a coffee that week and share their experience. If not, you can reach out on twitter or linkedIn and send a message to a developer in your area. Looking for someone with some kind of connection to you is your best bet here, so look for someone who went to the same college as you (even if it was years apart), someone with a mutual acquaintance (even if it's super distant), or someone who also graduated from a coding bootcamp. &lt;/p&gt;

&lt;p&gt;When you land an informational interview, make sure to look into their background and prepare some questions ahead of time for what you'd like to learn about them but try to keep it organic rather than a rapid-fire interrogation. &lt;/p&gt;

&lt;p&gt;At the end, ALWAYS always try to leave with at least one name of someone else you can talk to. Something like, "Thanks so much for taking the time to meet with me, I really enjoyed hearing about ______, do you know of anyone else that would be open to chatting with me or that you think I should meet?" The more you grow your network, the more amazing people you get to meet and learn from, but also the higher the chance will be that you will know someone who knows someone who works at the company you are looking at applying to. &lt;/p&gt;

&lt;p&gt;Getting your foot in the door: &lt;/p&gt;

&lt;p&gt;Because of the high volume of applications, your number one best bet to scoring an interview is to connect with someone who already works there. This is where that awesome network you've been working on comes into play. Every time I found a job I was interested in I would go to LinkedIn look at the company and all of the employees and try to find any kind of connection between someone I know and someone who works there. Once I found it, I would reach out, mention our mutual connection "Hey, I noticed that you also went to Bloc" or "Hey, I noticed that you know _&lt;strong&gt;&lt;em&gt;, I met them _&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;" then, "I have been looking at the opening for a junior rails developer at _&lt;/em&gt;__ and I was wondering if you'd be open to chatting about your experience working there. Maybe we could set up a phone call or grab coffee". &lt;/p&gt;

&lt;p&gt;When you meet with them, ask about what their day-to-day is like, how they feel about working there, what the company culture like, etc etc. This is your chance to get a look behind the scenes. Feel free to ask them about themselves and how they got into tech as well, this is also a great chance to ask what the interview processes looks like at this company.  Towards the end of the conversation, bring up the position you are looking at. Briefly pitch why you are interested in the position/company and why you'd be a good fit, and ask if they know anyone who works on that team that you could speak with, or if they have any advice on how you might stand out as a candidate. &lt;/p&gt;

&lt;p&gt;The key with networking is that you shouldn't go into a conversation with the goal of walking out with a job. Instead, it's an opportunity to learn from someone who has been where you are and who knows what it's like to actually work there. Networking is also a two way street, it benefits both the job seeker and the hiring team. Interviewing someone who already has the endorsement of someone who works for you can feel like less of a risk than interviewing someone from a pile of faceless applications. &lt;/p&gt;

&lt;p&gt;Tip 4: Continue expanding your skills. Teach yourself stuff that wasn't covered in your bootcamp. Build things you are passionate about. As I mentioned before, my bootcamp had an option of node or rails for the backend. I chose node because I was already into JavaScript, but after graduating I taught myself ruby and rails as well and built a mini reddit-clone in rails just to get the hang of it. I also learned Redux for state management in React, D3 for data visualizations, and some other fun stuff. The point is, keep learning and keep building things. &lt;/p&gt;

&lt;p&gt;As a developer things will always be changing, there will never be a point in your career where you know everything and can just coast. You need to be able to learn on your own, and know how to get yourself out of sticky situations, and practicing is the best way to do that. Then, when you are at interviews talk about what you've been working on that wasn't covered in your bootcamp, talk about how you got past that bug or what you've been learning on your own.  &lt;/p&gt;

&lt;p&gt;If you can, try to get some pair programming experience as well. Contributing to a local open source project is great for this, you can also ask someone in your meetup who is learning as well if they want to practice pair programming. I like to practice pairing on sites like CodeWars because it's low stakes, doesn't take any project set up, and is good practice for technical interviews.&lt;/p&gt;

&lt;p&gt;Tip 5: Frame your lack of a formal CS degree as a positive. In undergrad I majored in fine arts at a liberal arts school, after graduation I went back to school to study education and spent some time working as a teacher. None of my formal degrees or work experiences are even tangentially related to computer science, but in my experience my background has made me a better developer and that is something I always expand on whenever I make my elevator pitch. Highlight your transferable skills and how your unique experiences have helped your work as a programmer. Let your background work for you instead of against you.   &lt;/p&gt;

&lt;p&gt;That's all for now, let me know if this was helpful and feel free to comment below or tweet me with any questions or thoughts at @mxoliverleigh    &lt;/p&gt;

</description>
      <category>bootcamp</category>
      <category>codenewbie</category>
      <category>jobsearch</category>
      <category>tips</category>
    </item>
    <item>
      <title>Redux within Full Stack JavaScript</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 16 May 2019 17:11:44 +0000</pubDate>
      <link>https://dev.to/mxoliver/redux-within-full-stack-javascript-4p7j</link>
      <guid>https://dev.to/mxoliver/redux-within-full-stack-javascript-4p7j</guid>
      <description>&lt;p&gt;About a month ago I started the first sprint of my first ever full-stack JavaScript application. Previously I had worked with React in single-page applications, and I had built server-side applications with Node.js and Express, using EJS Templates for the view, but aside from having a theoretical understanding of the process, I had never actually experienced how the client and server connect and communicate. Enter Pocket Therabuddy, a full-stack NERD (node, express, react, database) application and mental health tool built with the intention of putting better data in the hands of users and the awareness to take control of how their actions can effect their moods in both positive and negative ways.&lt;/p&gt;

&lt;p&gt;Pocket Therabuddy began as the capstone project for my nine month long bootcamp. The only stipulations for our capstone were that we had to follow the agile methodology throughout our planning and building process, otherwise we had complete free range over our tech stack, what we wanted to build, and how we wanted to build it. It has been a few weeks since I officially graduated, but this project is something I plan to continue for a long time. In my time as a teacher and now as a developer I have always emphasized project-centric learning because it allows you to be exposed to problems and topics you would have never thought to learn about otherwise. Which brings me to the crux of this article, one of the things I learned in building this project was the role of Redux in a full-stack JavaScript application.&lt;/p&gt;

&lt;p&gt;Redux is in short a state management tool, but in keeping track of the state it can take charge of quite a lot of important logically processes including communicating with the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight hack"&gt;&lt;code&gt;&lt;span class="nx"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'../helpers/authHeader'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;export&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;habitService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;addHabit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;getHistory&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addHabit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;habit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;requestOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;habit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;habit&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`${process.env.REACT_APP_API_URL}/api/habittracker/add_habit`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'habits'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;requestOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`${process.env.REACT_APP_API_URL}/api/habittracker/${user.id}/history`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="no"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;handleResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is a snippet of code with a GET request and POST request to the server. All in all pretty straight forward, the fun part is how redux handles making these requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="nx"&gt;habitService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;habits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;habits&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
            &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alertActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;habitConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HABIT_HISTORY_REQUEST&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;habits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;habitConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HABIT_HISTORY_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;habits&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;habitConstants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HABIT_HISTORY_FAILURE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;p&gt;This is the action creator for the GET request above, this action which can be called in any react component with &lt;code&gt;habitActions.getHistory(id)&lt;/code&gt; will dispatch the request, then call the &lt;code&gt;habitService.getHistory&lt;/code&gt; function which is the GET request from before. If the request is successful, it will pass the JSON response from the server into &lt;code&gt;dispatch(success(habits))&lt;/code&gt; which can be accessed in the component as this.props.habits . On failure, it will dispatch the error.&lt;/p&gt;

&lt;p&gt;The last part in this process is how you access your redux action creators and responses within the component. Redux has both a &lt;code&gt;mapStateToProps&lt;/code&gt; method and a &lt;code&gt;dispatchState&lt;/code&gt; method, though I have found personally that &lt;code&gt;mapStateToProps&lt;/code&gt; can be used for pretty much everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;habits&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchHabits&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;habits&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;p&gt;This function here will get the currently logged in user (authentication, and fetchHabits are the names of the redux reducers that handle these states) and whichever habits are returned from the server request. This function should be located outside of your React class, either above or bellow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;        
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           
   &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;habitActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;p&gt;Here is the dispatch call that will trigger the action creator and service request we looked at above, which will set &lt;code&gt;this.props.habits&lt;/code&gt; to the data returned from the server.&lt;/p&gt;

&lt;p&gt;The last crucial but very simple part is the npm package &lt;code&gt;react-redux&lt;/code&gt; which will give you access to the connect function&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import { connect } from 'react-redux'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then at the bottom of your component connect your redux &lt;code&gt;mapStateToProps&lt;/code&gt; function to the name of your class Component and export it as usual.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HabitHistory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;ConnectedHabitHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;HabitHistory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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



&lt;p&gt;If you’d like to check out Pocket Therabuddy for yourself it is deployed &lt;a href="https://pocket-therabuddy.herokuapp.com"&gt;here&lt;/a&gt; and you can find the GitHub repo &lt;a href="https://github.com/MxOliver/Pocket-Therabuddy"&gt;here&lt;/a&gt; with more information about running it on your local server and upcoming features. Stay tuned for an article about d3 and using data visualization with React (another thing I learned in making Pocket Therabuddy).&lt;/p&gt;

&lt;p&gt;— MxOliver&lt;/p&gt;

</description>
      <category>redux</category>
      <category>react</category>
      <category>node</category>
      <category>restfulapis</category>
    </item>
    <item>
      <title>Role-based Authorization and the View</title>
      <dc:creator>Oliver</dc:creator>
      <pubDate>Thu, 21 Mar 2019 14:26:57 +0000</pubDate>
      <link>https://dev.to/mxoliver/role-based-authorization-and-the-view-1mnj</link>
      <guid>https://dev.to/mxoliver/role-based-authorization-and-the-view-1mnj</guid>
      <description>&lt;p&gt;Over the past few weeks I have been working on building a client-side wikipedia-esque web application using Node called Wikology. In Wikology, all standard members can create, edit, and read public wikis. Users can also upgrade to a premium account (using the Stripe API client for payment) and create private wikis which can only be read and edited by their collaborators.&lt;/p&gt;

&lt;p&gt;In the past, when handling role based authorization and which CRUD (create, read, update, delete) functions user role’s are authorized for, all users, whether they were signed-in or not, had read access to everything. This meant that the majority of the authorization took place between the controllers and the model. I could easily pass in a policy class to my query files and check if a user was authorized before allowing them to continue with a CRUD action, and if they weren’t, I could flash an unauthorized notice and redirect them. With Wikology, the introduction of private wikis meant that read access was not a given for all users, and thus, role authorization had to be implemented not just between the model and controller, but also in the view.&lt;/p&gt;

&lt;p&gt;So how does one implement role based authorization for CRUD methods in the view part of an application? If you are a seasoned developer this may seem like an obvious answer, but this was my second ever client-side application and I was about to reacquaint myself with the MVC framework on a whole other level.&lt;/p&gt;

&lt;p&gt;The key to working with the View side of the Model-View-Controller framework is to think about how you are passing information to the view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;wikiQueries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getWiki&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wiki&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/wikis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;

                    &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wikis/show&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;collaborator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

               &lt;span class="nx"&gt;collaboratorQueries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collaboratorAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;collaborator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;collaborator&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                        &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You do not have permission
                        to view this wiki.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/wikis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                        &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wikis/show&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wiki&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;collaborator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;collaborators&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This is the show method which handles the view for each individual wiki. Let’s break this down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Get the wiki — in order to do anything we need to retrieve the specific instance that matches the given wikiId.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check if the wiki is private and if so, check if we are the owner — if the wiki is public, or if we wrote the wiki, we have guaranteed read access and can go ahead and render the view. Here we are passing the wiki from step 1 in as {wiki: wiki } and the collaborators as null because they aren’t relevant to the show method if we are the owner or if the wiki is public.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it is private and we are not the owner, we need to retrieve the collaborators for this wiki — each Collaborator instance has a one-to-one relationship with User, and a one-to-many relationship with Wiki (eg. each collaborator instance only has one user and one wiki, but a wiki can have many collaborator instances). The specific query here is happening on my collaboratorQuery file (shown below) but the concept is fairly straightforward. I pass in the wikiId from step 1, as well as req.user.id which is the id of the user making the request. Notice I’m not trying to retrieve all of the collaborators for this wiki, I only need to know if the current user is a collaborator in order to determine their read access.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="nx"&gt;collaboratorAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wikiId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;Collaborator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;wikiId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wikiId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;collaborator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;collaborator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;



&lt;p&gt;If there is an error, or if a collaborator instance doesn’t exist between the current user and the wiki, the user is redirected and sent a message letting them know that they are not authorized to view that wiki. &lt;/p&gt;

&lt;p&gt;However, if everything succeeds we can go ahead and call on the view to render the wiki. This time, we pass in &lt;code&gt;{wiki: wiki, collaborator: collaborator}&lt;/code&gt; which not only passes the wiki to the view, but passes the collaborator instance that links the current user as a collaborator with the wiki. By passing the collaborator instance to the view I can make sure the current user has access to view the private wiki before it renders. Ideally the user would be redirected before they even reach the view, but if they were to enter the route manually (eg. a former collaborator who no longer has read access but knows the url route) this would double-check their authorization before rendering.&lt;/p&gt;

&lt;p&gt;This same strategy can be used for the rest of the CRUD methods, passing in authorized users as collaborator instances in order to determine what should be rendered in the view. Other examples include, determining whether the edit and delete buttons should be visible to a given user, or which private wiki titles should appear on a user’s index page (eg. the ones that they own or collaborate on).&lt;/p&gt;

&lt;p&gt;Whenever I am stuck on a problem, the most effective question I can ask myself is “Who knows what and how?” Essentially — Does this component have the information it needs to do what I am asking it to do? Where is that information coming from and how is it accessing that knowledge?&lt;br&gt;
and remember if you’ve been staring at the same problem and getting nowhere — take a step back so you can see the forest for it’s trees.&lt;/p&gt;

&lt;p&gt;— MxOliver&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>javascript</category>
      <category>sequelize</category>
      <category>node</category>
    </item>
  </channel>
</rss>
