WD401level7-Sai Mallik Rameshwaram

                                                                                                WD401-level7



1)Steps were taken to containerize the node-js application:

->step-1: Create a Docker file

     I created a file named Docker file in your project directory and wrote the                     following instructions:

   

In above Docker file:

  ->The base image is defined as an official Node.js runtime based on the Alpine Linux distribution. The working directory inside the container is set to /app.

  ->The package.json file is copied from the local machine to the root directory in the container.The rest of the application code is copied to the /app directory in the container.

->Port 7000 is exposed for communication with the application.

->Two stages are defined: production and dev.

        1) In the production stage, the environment is set to production, dependencies are installed, and the         command to run the application (node index.js) is specified.

        2)In the development stage, the environment is set to development, nodemon is installed globally,         project dependencies are installed, and the command to run the application in development mode            (npm run start) is specified

->step-2Create docker-compose.yml

Created a file named docker-compose.yml in my project directory and wrote the following instructions. 

version:'3.8'
services:
  app:
    build:
      context: .
      target: dev
    image: online-voting:development
    volumes:
      - .:/app
    ports:
      - 7000:7000
    env_file:
      - .env
    depends_on:
      - db
  db:
    image: postgres:15
    volumes:
      - pg-dev-data:/var/lib/postgresql/data
    env_file:
      - .env
    environment:
      POSTGRES_USER: $DEV_USERNAME
      POSTGRES_DB: $DEV_DATABASE
      POSTGRES_PASSWORD: $DEV_PASSWORD

volumes:
  pg-dev-data:

1)The Docker Compose configuration defines two services: app and db, where app represents the application container and db represents the PostgreSQL database container.

2)The app service builds an image using the Dockerfile in the current directory targeting the dev stage, mounts the current directory as a volume for live code updates, exposes port 7000 for communication, and loads environment variables from a .env file.

->step 3: Create docker-compose-prod.yml

I created a file named docker-compose-prod.yml in my project directory and wrote the following instructions:

version: '3.8'
services:
  app:
    build:
      context: .
      target: prod
    image: online-voting:production
    volumes:
      - .:/app
    ports:
      -7000:7000
    env_file:
      - .env
    depends_on:
      - db
  db:
    image: postgres:15
    volumes:
      -pg-prod-data:/var/lib/postgresql/data
    env_file:
      - .env
    environment:
      POSTGRES_USER: $PROD_USERNAME
      POSTGRES_DB: $PROD_DATABASE
      POSTGRES_PASSWORD: $PROD_PASSWORD

volumes:
  pg-prod-data:

1) The Docker Compose configuration sets up two services: app and db, where app is the container for the application and db is the PostgreSQL database container.

2) The app service builds an image targeting the prod stage from the Dockerfile, mounts the current directory as a volume for live code updates, exposes port 7000, and loads environment variables from a .env file. It also depends on the db service.


->step 4: Run Docker Compose

Now, I ran below Docker Compose command to build and run your containers:

For development:

docker-compose up -f docker-compose.yml

For production:

docker-compose up -f docker-compose-prod.yml


2)Environment Variable Configuration Documentation within the Docker container

PORT=7000
###### production ######
NODE_ENV=production
PROD_USERNAME=postgres
PROD_PASSWORD=mallik
PROD_DATABASE=vote_dev2
PROD_HOST=db
PROD_DIALECT=postgres
DATABASE_URL=postgres://postgres:mallik@db:5432/vote_dev2


###### development ######
DEV_USERNAME=postgres
DEV_PASSWORD=mallik
DEV_DATABASE=vote_dev2
DEV_HOST=db
DEV_DIALECT=postgres

Port Configuration
   ->PORT:
    Specifying the port number 7000 on which the application will listen for incoming requests.
       
Production Environment Configuration:
      -> Node Environment:
          Specifying the environment mode in production.
      ->Database Connection:
        Username: PROD_USERNAME
        Password: PROD_PASSWORD
        Database Name: PROD_DATABASE
        Host: PROD_HOST
        Dialect: PROD_DIALECT
        URL: DATABASE_URL
Specifying the configuration parameters for connecting to the production database.
example as per our code:
Username: postgres
Password: ******
Database: vote_dev2
Host: db
Dialect: postgres
URL: postgres://postgres:mallik@db:5432/vote_dev2

Development Environment Configuration: 
->Node Environment:
  Specifying the environment mode in development.
->Database Connection:
    Username: DEV_USERNAME
    Password: DEV_PASSWORD
    Database Name: DEV_DATABASE
    Host: DEV_HOST
    Dialect: DEV_DIALECT
 Specifying the configuration parameters for connecting to the development database.
Example as per our code:
Copy code
Username: postgres
Password: *****
Database: vote_dev2
Host: db
Dialect: postgres


3) Docker Compose Configuration Documentation

Docker Compose is a tool for defining and running multi-container Docker applications. Below is the configuration from our application code for setting up services for both the Node.js application and the database using Docker Compose.

 Application Service

  Service Name: app
    Image: Specifies the Docker image for the Node.js application.
    Example: my-node-app
    Environment Variables:
        PORT: Specifies the port number on which the Node.js application will listen for incoming                                 requests.
        Example: 7000
        NODE_ENV: Specifies the environment mode for the Node.js application.
        Example: production or development
        DATABASE_URL: Specifies the URL for connecting to the database.
        Example: postgres://user:password@db_host:5432/database_name
 
Ports: Specifies the ports to expose for accessing the Node.js application externally.
Example: 7000:7000

Volumes: Specifies any volumes to mount for persisting data or sharing files between the host and the container.

Depends On: Specifies any dependencies that the service relies on, such as other services.
    Example: db


Database Service

    Service Name: db
        Image: Specifies the Docker image for the database.
        Example: postgres:latest
    Environment Variables:
        POSTGRES_USER: Specifies the username for accessing the database.
        Example: postgres
        POSTGRES_PASSWORD: Specifies the password for accessing the database.
        Example: mallik
        POSTGRES_DB: Specifies the name of the database.
        Example: vote_dev2

    Volumes: Specifies any volumes to mount for persisting data or sharing files between the host and         the container.
    Ports: Specifies the ports to expose for accessing the database externally.
    Depends On: Specifies any dependencies that the service relies on, such as other services.

The below is the docker-compose.yml file from my application:

version: '3.8'
services:
  app:
    build:
      context: .
      target: dev
    image: online-voting:development
    volumes:
      - .:/app
    ports:
      - 7000:7000
    env_file:
      - .env
    depends_on:
      - db
  db:
    image: postgres:15
    volumes:
      - pg-dev-data:/var/lib/postgresql/data
    env_file:
      - .env
    environment:
      POSTGRES_USER: $DEV_USERNAME
      POSTGRES_DB: $DEV_DATABASE
      POSTGRES_PASSWORD: $DEV_PASSWORD

volumes:
  pg-dev-data:

4) CI/CD Pipeline Setup Documentation:


name: Auto tests For OnlineVotingApplication
on:
  push:
    branches:
      - master
env:
  PG_DATABASE: online_voting_DB
  PG_USER: postgres
  PG_PASSWORD: ${{ secrets.password_DB }}
jobs:
  #Code validation
  lint:
    name: Validation code
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "14"

      - name: Install Dependencies
        run: npm ci

      - name: Run ESLint
        run: npx lint-staged

  run-tests:
    # Containers must run in Linux based operating
    runs-on: ubuntu-latest

    # Service containers to run with `container-job`
    services:
      # Label used to access the service container
      postgres:
        # Docker Hub image
        image: postgres:11.7
        # Provide the password for postgres
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: ${{secrets.password_DB }}
          POSTGRES_DB: online_voting_DB
        # Set health checks to wait until postgres has started
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      # Downloads a copy of the code in your repository before running CI tests
      - name: Check out repository code
        uses: actions/checkout@v3

      # Performs a clean installation of all dependencies in the `package.json` file
      # For more information, see https://docs.npmjs.com/cli/ci.html
      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm test
      - name: Run the app
        id: run-app
        run: |
          npm install
          npx sequelize-cli db:drop
          npx sequelize-cli db:create
          npx sequelize-cli db:migrate
          PORT=7000 npm start &
          sleep 5
      - name: Run integration tests
        run: |
          npm install cypress cypress-json-results
          npx cypress run

  build:
    name: Dockerization
    needs: [run-tests]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Login to Docker desktop
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_ACCESS_TOKEN }}

      - name: Docker Set up
        uses: docker/setup-buildx-action@v3

      - name: Building and updating to hub
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/megabyte:latest

  deploy:
    name: Automaic Deployment
    needs: [build] #before deployment run
    runs-on: ubuntu-latest

    steps:
      - name: Backend application deployment in Render
        uses: johnbeynon/render-deploy-action@v0.0.8
        with:
          service-id: ${{ secrets.RENDER_SERVICEID}}
          api-key: ${{ secrets.API_TOKEN }}

  notifications-tests:
    name: Test Notifications
    needs: [run-tests] ## should run tests
    runs-on: ubuntu-latest
    if: ${{ always() }} ## runs or independent on previous jobs. i.e always show notifications
    steps:
      - name: Discord Notifications for Test Results
        env:
          DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
        run: |
          if [[ ${{ needs.run-tests.result }} == 'success' ]]; then
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Tes
t cases* completed successfully. \nCheck the logs for details: https://github.com/${{
github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
          else
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Tes
t cases* failed. \nCheck the logs for details: https://github.com/${{ github.repository
}}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
          fi

  notifications-codevalidation:
    name: Code validation Notifications
    needs: [lint] ## should run
    runs-on: ubuntu-latest
    if: ${{ always() }} ## runs or independent on previous jobs. i.e always show notifications
    steps:
      - name: Discord Code validation for Results
        env:
          DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
        run: |
          if [[ ${{ needs.lint.result }} == 'success' ]]; then
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Co
de validation* completed successfully , No staged files found. \nCheck the logs for de
tails: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}
' $DISCORD_WEBHOOKURL
          else
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Cod
e validation* failed. \nCheck the logs for details: https://github.com/${{ github.repo
sitory }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
          fi

  notifications-dockerization:
    name: Dockerization Notifications
    needs: [build] ## should deployment
    runs-on: ubuntu-latest
    if: ${{ always() }} ## runs or independent on previous jobs. i.e  notifications
    steps:
      - name: Dockerization Notifications for Dockerization Results
        env:
          DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
        run: |
          if [[ ${{ needs.build.result }} == 'success' ]]; then
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Doc
kerization of application* completed successfully. \nCheck the logs for details: https
://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_
WEBHOOKURL
          else
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Doc
kerization of the application* failed. \nCheck the logs for details: https://github.
com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
          fi

  notifications-deploy:
    name: Deployment Notifications
    needs: [deploy] ## should deployment
    runs-on: ubuntu-latest
    if: ${{ always() }} ## runs or independent on previous jobs. i.e  notifications
    steps:
      - name: Discord Notifications for Deployment Results
        env:
          DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
        run: |
          if [[ ${{ needs.deploy.result }} == 'success' ]]; then
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *De
ployment of application* completed successfully. \nCheck the logs for details: https:
//github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_
WEBHOOKURL
          else
            curl -X POST -H 'Content-type: application/json' --data '{"content":" *Dep
loyment of the application* failed. \nCheck the logs for details: https://github.com/$
{{ github.repository }}/actions/runs/${{ github.run_id }}"}' $DISCORD_WEBHOOKURL
          fi

  push-notification:
    name: Push Notification
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    steps:
      - name: Discord Notifications for Push Event
        env:
          DISCORD_WEBHOOKURL: ${{ secrets.DISCORD_WEBHOOKURL }}
        run: |
          curl -X POST -H 'Content-type: application/json' --data '{"content":"A new
push event has been triggered on the master branch of the repository. Check it out at:
https://github.com/${{ github.repository }}/commit/${{ github.sha }}"}' $DISCORD_WEBH
OOKURL

This CI/CD pipeline is set up to automate the testing, building, and deployment process for my application called OnlineVotingApplication. It uses GitHub Actions for automation and integrates various stages to ensure code quality, test coverage, Dockerization, and deployment.

Pipeline Structure

The pipeline consists of several stages, each responsible for a specific job:

    1) Validation Code (using lint-staged): Validates the codebase using ESLint to ensure consistent code style and formatting.

   2)Run Tests: Installs dependencies and runs unit tests using npm test. Runs the application, performs database setup (drop, create, migrate), and runs integration tests using Cypress.

    3)Dockerization: Builds the Docker image for the application. And Pushes the image to Docker Hub.

    4)Deployment: Deploys the application to a hosting platform. And Uses Render Deploy Action to         deploy my application.

Environment Variables:

    ->Database Credentials:

        PG_DATABASE: Online Voting Database name.

        PG_USER: PostgreSQL username.

        PG_PASSWORD: Encrypted secret for PostgreSQL password.

Error Handling Mechanisms:

->Discord webhooks are used to send notifications to the development team. Notifications are sent for each stage of the pipeline (code validation, tests, Dockerization, deployment). 

->Different messages are sent based on the success or failure of each stage.

Overview on workflow:

This pipeline basically triggers on every push event to the master branch. Upon triggering, it starts with code validation, followed by tests, Dockerization, and deployment. Notifications are sent at each stage to inform the status about the pipeline's progress and outcome.
































Comments

Popular posts from this blog

WD401-level8-Sai Mallik Rameshwaram

The cold Boy