Wade Stern Projects

Staging

CD Staging Image

Input: For the staging part of the pipeline to function, it requires images for the frontend and backend in the docker hub. It also needs Azure to have a configured resource group to put the Kubernetes in. It also needs a DNS zone and a network watcher so that it can create DNS records.

Output: The output of this segment of the pipeline is the application having been deployed to Azure and tested to ensure that the frontend, backend, and database are all able to connect and function properly. DNS records should be created that make the application accessible at staging.wadestern.com.

Steps

Create_kubernetes_cluster
create_kubernetes_cluster:
    docker:
      - image: dudesm00thie/wadeazcli
        user: root
    steps:
      - checkout
      - run:
          name: Login to Azure
          command: |
            az login --service-principal --username "${AZURE_CLIENT_ID}" --password "${AZURE_CLIENT_SECRET}" --tenant "${AZURE_TENANT_ID}"  
      - run:
          name: Configure SSH Key
          command: |
            echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
            chmod 600 ~/.ssh/id_rsa
            echo "$SSH_PUBLIC_KEY" > ~/.ssh/id_rsa.pub
            chmod 600 ~/.ssh/id_rsa.pub
      - run:
          name: Create Kubernetes Cluster
          command: |
            az aks create -g buypotatoResourceGroup -n buypotatoStaging --node-count 2 --ssh-key-value ~/.ssh/id_rsa.pub
            az aks get-credentials --resource-group buypotatoResourceGroup --name buypotatoStaging
      - persist_to_workspace:
          root: /root
          paths:
            - .kube/config

This step is responsible for generating the Kubernetes cluster. First, it logs in to Azure using my credentials which are stored as a CircleCI secret. Then it generates an SSH key to be used in the Kubernetes cluster creation step. It then creates the cluster inside of the buypotatoResourceGroup. After creating the Kubernetes cluster, it gets the credentials for it and persists them to the workspace so that other steps can easily access the cluster.

Setup_dns_and_deploy
setup_dns_and_deploy:
    docker:
      - image: dudesm00thie/kubeaz:latest
        user: root
    steps:
      - checkout
      - attach_workspace:
          at: /root
      - run:
          name: Login to Azure
          command: |
            az login --service-principal --username "${AZURE_CLIENT_ID}" --password "${AZURE_CLIENT_SECRET}" --tenant "${AZURE_TENANT_ID}"
      - run:
          name: Set Vars
          command: |
            PRINCIPAL_ID=$(az aks show --resource-group buypotatoResourceGroup --name buypotatoStaging \
            --query "identityProfile.kubeletidentity.objectId" --output tsv)
            AZURE_DNS_ZONE="staging.wadestern.com"
            AZURE_DNS_ZONE_RESOURCE_GROUP="buypotatoResourceGroup"
            DNS_ID=/subscriptions/a24f845d-8972-45b6-ab3d-9dd9e598dbeb/resourceGroups/buypotatoresourcegroup/providers/Microsoft.Network/dnszones/staging.wadestern.com
            echo $PRINCIPAL_ID
            echo $AZURE_DNS_ZONE
            echo $AZURE_DNS_ZONE_RESOURCE_GROUP
            echo $DNS_ID
            az role assignment create --role "DNS Zone Contributor" --assignee "${PRINCIPAL_ID}" --scope "${DNS_ID}"
      - run:
          name: Create kubernetes secret and namespace
          command: |
            kubectl --kubeconfig=/root/.kube/config create secret generic azure-config-file --namespace "default" --from-file /root/project/deploy/azure.json
            kubectl --kubeconfig=/root/.kube/config create --namespace "default" --filename /root/project/deploy/azure-external-dns.yaml
      - run:
          name: Deploy backend to Kubernetes
          command: kubectl --kubeconfig=/root/.kube/config apply -f /root/project/deploy/staging-backend-deploy.yaml
      - run:
          name: Deploy frontend to Kubernetes
          command: kubectl --kubeconfig=/root/.kube/config apply -f /root/project/deploy/staging-frontend-deploy.yaml
      - run:
          name: Wait for external-dns pod to be running
          command: |
            end=$((SECONDS+180))
              while [[ $SECONDS -lt $end ]]; do
                if kubectl --kubeconfig=/root/.kube/config get pods -n buypotatostaging | grep -q "0/"; then
                  sleep 10
                else
                  break
                fi
              done

This step is responsible for setting up the tool that will create DNS records when it sees the frontend and backend get deployed to the Kubernetes cluster. It also deploys the frontend and backend then it has a wait step to help the next step in the pipeline.

Puppeteer_testing
puppeteer_testing:
    working_directory: ~/repo/testing
    docker:
      - image: mudbone67/node-pup-chr
    environment:
      prodorstaging: staging
    steps:
      - checkout:
          path: ~/repo
      - run:
          name: Update NPM
          command: npm install -g npm
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules  
      - run:
          name: Wait
          command: sleep 120
      - run:
          name: List Environment Variables
          command: printenv
      - run:
          name: Run tests
          command: npm test -- --forceExit

The last step for the staging part of the pipeline is to run the puppeteer tests to ensure that all parts of the application are running and that they are able to communicate with each other. This job involves setting up the container and installing all of the necessary dependencies. There is a 2-minute pause in this step to allow the pods in the Kubernetes that were set up in the previous step to be ready and for the DNS record to be updated. The pipeline then runs the puppeteer test which tries to log in to the application and checks if it was successful. Logging in requires the backend to get the data from the database, the database to send the data to the backend, and the frontend to send the request to the backend. If the login works, it demonstrates that all of the parts of the application are functioning and able to communicate with each other.