Skip to content

Instantly share code, notes, and snippets.

@ahmetgeymen
Last active October 19, 2024 10:12
Show Gist options
  • Save ahmetgeymen/f176f11ab0c81c2496cedac963e8e8ae to your computer and use it in GitHub Desktop.
Save ahmetgeymen/f176f11ab0c81c2496cedac963e8e8ae to your computer and use it in GitHub Desktop.
Bitbucket Pipelines: Build Docker Image + GCR Image Push + GKE Deploy
image: openjdk:11-jdk-slim
definitions:
caches:
gradlewrapper: ~/.gradle/wrapper
gke-kubectl-pipe: &pipe atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables: &pipe-variables
KEY_FILE: $GKE_API_KEYFILE
PROJECT: $GCP_PROJECT_ID
COMPUTE_ZONE: $GKE_COMPUTE_ZONE
CLUSTER_NAME: $GKE_CLUSTER_NAME
KUBECTL_COMMAND: "set image deployment/$K8S_DEPLOYMENT_NAME $K8S_CONTAINER_NAME=$IMAGE_NAME --namespace=$K8S_NAMESPACE"
steps:
- step: &lint
name: Lint
caches:
- gradle
- gradlewrapper
script:
- ./gradlew lintKotlin
- step: &test
name: Test
caches:
- gradle
- gradlewrapper
script:
- ./gradlew test
- step: &assemble
name: Assemble
caches:
- gradle
- gradlewrapper
script:
- ./gradlew assemble
artifacts:
- build/libs/*.jar
- step: &build-image
name: Build Docker image
services:
- docker
caches:
- docker
script:
- export IMAGE_NAME=$GCP_IMAGE_NAME
- docker build -t $IMAGE_NAME --build-arg JAR_FILE=build/libs/app.jar .
- docker save --output docker.tar $IMAGE_NAME
artifacts:
- docker.tar
- step: &push-image-to-gcr
name: Push image
image: google/cloud-sdk:alpine
services:
- docker
caches:
- docker
script:
# backup docker image
- docker load --input docker.tar
# Authenticating with the service account key file
- echo $GCP_API_KEYFILE | base64 -d > ./gcloud-api-key.json
- gcloud info --run-diagnostics
- gcloud auth activate-service-account --key-file gcloud-api-key.json
- gcloud config set project $GCP_PROJECT_ID
# Login to google docker hub
- cat ./gcloud-api-key.json | docker login -u _json_key --password-stdin https://$GCP_REGISTRY_HOSTNAME
# Prepare image name and shorten commit id
- export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$GCP_PROJECT_ID/$GCP_IMAGE_NAME
- export BITBUCKET_COMMIT_SHORT="${BITBUCKET_COMMIT::7}"
# If any image does not exist with commit id, then
- if [ "$(gcloud container images list-tags ${IMAGE_NAME} | grep ${BITBUCKET_COMMIT_SHORT})" == '' ]; then
# Tag docker image with commit id and push
- docker tag $GCP_IMAGE_NAME $IMAGE_NAME:$BITBUCKET_COMMIT_SHORT
- docker push $IMAGE_NAME:$BITBUCKET_COMMIT_SHORT
# Tag docker image with :latest tag and push
- docker tag $GCP_IMAGE_NAME ${IMAGE_NAME}
- docker push ${IMAGE_NAME}
# Tag docker image with commit tag and push (if pipeline triggered with tag)
- if [ "${BITBUCKET_TAG}" != '' ]; then
- docker tag $GCP_IMAGE_NAME $IMAGE_NAME:$BITBUCKET_TAG
- docker push $IMAGE_NAME:$BITBUCKET_TAG
- fi
# Else image exists with commit id
- else
# Add commit tag into existing image with commit id (if pipeline triggered with tag)
- if [ "${BITBUCKET_TAG}" != '' ]; then
- gcloud container images add-tag $IMAGE_NAME:$BITBUCKET_COMMIT_SHORT $IMAGE_NAME:$BITBUCKET_TAG --quiet
- fi
# End if
- fi
- step: &deploy-to-gke-staging
name: Deploy to Staging
deployment: staging
script:
- export BITBUCKET_COMMIT_SHORT="${BITBUCKET_COMMIT::7}"
- export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$GCP_PROJECT_ID/$GCP_IMAGE_NAME:$BITBUCKET_COMMIT_SHORT
- pipe: *pipe
variables: *pipe-variables
- step: &deploy-to-gke-production
name: Deploy to Production
deployment: production
# Added for manual promotion
trigger: manual
script:
- export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$GCP_PROJECT_ID/$GCP_IMAGE_NAME:$BITBUCKET_TAG
- pipe: *pipe
variables: *pipe-variables
pipelines:
pull-requests:
'**':
- parallel:
- step: *lint
- step: *test
tags:
'*':
- parallel:
- step: *lint
- step: *test
- step: *assemble
- step: *build-image
- step: *push-image-to-gcr
- step: *deploy-to-gke-staging
- step: *deploy-to-gke-production
branches:
master:
- parallel:
- step: *lint
- step: *test
- step: *assemble
- step: *build-image
- step: *push-image-to-gcr
- step: *deploy-to-gke-staging
@ahmetgeymen
Copy link
Author

Environment variables:

GCP_API_KEYFILE: GCP service account api key json file as base64 encoded format with Storage Admin role
GCP_REGISTRY_HOSTNAME: GCP Container Registry hostname
GCP_PROJECT_ID: GCP project id
GCP_IMAGE_NAME: image name

GKE_API_KEYFILE: GKE service account api key with Kubernetes Engine Developer role
GKE_COMPUTE_ZONE: GKE cluster zone
GKE_CLUSTER_NAME: GKE cluster name

K8S_DEPLOYMENT_NAME: Kubernetes deployment name
K8S_CONTAINER_NAME: Container name
K8S_NAMESPACE: Kubernetes namespace (it is given in Deployments, as an environment variable)

@abdulrbutt1010
Copy link

abdulrbutt1010 commented Oct 11, 2024

Hi there Im taking help from you pipelines file
Below is the error Im getting in this step &step-push-app

+ gcloud auth activate-service-account --key-file gcloud-api-key.json
ERROR: (gcloud.auth.activate-service-account) Could not read json file gcloud-api-key.json: Expecting value: line 1 column 1 (char 0)

Below is my bitbucket-pipelines.yml file
image: atlassian/pipelines-awscli:latest

definitions:
    gke-kubectl-pipe: &pipe atlassian/google-gke-kubectl-run:1.3.1
    gke-kubectl-pipe-variables: &pipe-variables
        KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
        PROJECT: $PROJECT_NAME
        COMPUTE_ZONE: $GCP_REGION
        CLUSTER_NAME: $GCP_CLUSTER_NAME
        # KUBECTL_COMMAND: "set image deployment/$K8S_DEPLOYMENT_NAME $K8S_CONTAINER_NAME=$IMAGE_NAME --namespace=$K8S_NAMESPACE"
        
    steps:

    - step: &step-env-app
        image: atlassian/pipelines-awscli:latest
        name: Generating ENV file...
        deployment: dev
        oidc: true
        script:
          - apk update && apk add jq
          - touch .env
          - echo "ENV=${ENV}" >> .env
          - export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
          - echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
          - export NODE_ENV=$(aws ssm get-parameter --name /${ENV}/project-name/node-env --with-decryption | jq --raw-output '.Parameter.Value') || echo "Failed to fetch NODE_ENV"
          - echo "NODE_ENV=${NODE_ENV}" >> .env
          - export JWT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/jwt-secret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "JWT_SECRET=${JWT_SECRET}" >> .env
          - export DATABASE=$(aws ssm get-parameter --name /${ENV}/project-name/database --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE=${DATABASE}" >> .env
          - export DATABASE_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/database-password --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE_PASSWORD=${DATABASE_PASSWORD}" >> .env
          - export REPAIRSHOPR_API_KEY=$(aws ssm get-parameter --name /${ENV}/project-name/repairshoper-apikey --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "REPAIRSHOPR_API_KEY=${REPAIRSHOPR_API_KEY}" >> .env
          - export CLIENTID=$(aws ssm get-parameter --name /${ENV}/project-name/client-id --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CLIENTID=${CLIENTID}" >> .env
          - export CLIENTSECRET=$(aws ssm get-parameter --name /${ENV}/project-name/client-secret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CLIENTSECRET=${CLIENTSECRET}" >> .env
          - export REDIRECTURI=$(aws ssm get-parameter --name /${ENV}/project-name/redirect-uri --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "REDIRECTURI=${REDIRECTURI}" >> .env
          - export REFRESHTOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/refresh-token --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "REFRESHTOKEN=${REFRESHTOKEN}" >> .env
          - export DEVOPS_CLIENTID=$(aws ssm get-parameter --name /${ENV}/project-name/devops-clientid --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DEVOPS_CLIENTID=${DEVOPS_CLIENTID}" >> .env
          - export DEVOPS_CLIENTSECRET=$(aws ssm get-parameter --name /${ENV}/project-name/devops-clientsecret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DEVOPS_CLIENTSECRET=${DEVOPS_CLIENTSECRET}" >> .env
          - export DEVOPS_REFRESHTOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/devops-refreshtoken --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DEVOPS_REFRESHTOKEN=${DEVOPS_REFRESHTOKEN}" >> .env
          - export DONOTREPLY_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/donotreply-password --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DONOTREPLY_PASSWORD=${DONOTREPLY_PASSWORD}" >> .env
          - export DO_NOT_REPLY_CLIENT_ID=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyclientid --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DO_NOT_REPLY_CLIENT_ID=${DO_NOT_REPLY_CLIENT_ID}" >> .env
          - export DO_NOT_REPLY_CLIENT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyclientsecret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DO_NOT_REPLY_CLIENT_SECRET=${DO_NOT_REPLY_CLIENT_SECRET}" >> .env
          - export DO_NOT_REPLY_REFRESH_TOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyrefreshtoken --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DO_NOT_REPLY_REFRESH_TOKEN=${DO_NOT_REPLY_REFRESH_TOKEN}" >> .env
          - export OTPAUTH_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/otp-authsecret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "OTPAUTH_SECRET=${OTPAUTH_SECRET}" >> .env
          - export CODI_EMAIL=$(aws ssm get-parameter --name /${ENV}/project-name/codi-email --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CODI_EMAIL=${CODI_EMAIL}" >> .env
          - export CODI_EMAIL_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/codi-emailpassword --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CODI_EMAIL_PASSWORD=${CODI_EMAIL_PASSWORD}" >> .env
          - export CODI_CLIENT=$(aws ssm get-parameter --name /${ENV}/project-name/codi-client --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CODI_CLIENT=${CODI_CLIENT}" >> .env
          - export FEDEX_CLIENT_ID=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-clientid --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "FEDEX_CLIENT_ID=${FEDEX_CLIENT_ID}" >> .env
          - export FEDEX_CLIENT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-clientsecret --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "FEDEX_CLIENT_SECRET=${FEDEX_CLIENT_SECRET}" >> .env
          - export FEDEX_ACCOUNT_NUMBER=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-accountnumber --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "FEDEX_ACCOUNT_NUMBER=${FEDEX_ACCOUNT_NUMBER}" >> .env
          - export TCS_AUTH_TOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/tcs-authtoken --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "TCS_AUTH_TOKEN=${TCS_AUTH_TOKEN}" >> .env
          - export MAILGUN_API_KEY=$(aws ssm get-parameter --name /${ENV}/project-name/mailgun-apikey --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "MAILGUN_API_KEY=${MAILGUN_API_KEY}" >> .env
          - export CSVPARSER_V2=$(aws ssm get-parameter --name /${ENV}/project-name/csvparser-v2 --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "CSVPARSER_V2=${CSVPARSER_V2}" >> .env

          # Codifix Cron
          - touch CODiFixcron/.env
          - echo "ENV=${ENV}" >> CODiFixcron/.env
          - export BASTION_HOST=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-host --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "BASTION_HOST=${BASTION_HOST}" >> CODiFixcron/.env
          - export BASTION_PORT=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-port --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "BASTION_PORT=${BASTION_PORT}" >> CODiFixcron/.env
          - export BASTION_USERNAME=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-username --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "BASTION_USERNAME=${BASTION_USERNAME}" >> CODiFixcron/.env
          - export BASTION_PRIVATE_KEY_PATH=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-privarekeypath --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "BASTION_PRIVATE_KEY_PATH=${BASTION_PRIVATE_KEY_PATH}" >> CODiFixcron/.env
          - export DATABASE_MYSQL_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqlurl --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE_MYSQL_URL=${DATABASE_MYSQL_URL}" >> CODiFixcron/.env
          - export DATABASE_MYSQL_USER=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqluser --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE_MYSQL_USER=${DATABASE_MYSQL_USER}" >> CODiFixcron/.env
          - export DATABASE_MYSQL_PASSWORD=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqlpassword --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE_MYSQL_PASSWORD=${DATABASE_MYSQL_PASSWORD}" >> CODiFixcron/.env
          - export DATABASE_MYSQL=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysql --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "DATABASE_MYSQL=${DATABASE_MYSQL}" >> CODiFixcron/.env
          - export GRAPHQL_API_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/graphql-apiurl --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "GRAPHQL_API_URL=${GRAPHQL_API_URL}" >> CODiFixcron/.env
          - export API_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/api-url --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "API_URL=${API_URL}" >> CODiFixcron/.env
          - export API_KEY=$(aws ssm get-parameter --name /${ENV}/codifixcron/api-key --with-decryption | jq --raw-output '.Parameter.Value')
          - echo "API_KEY=${API_KEY}" >> CODiFixcron/.env
          
        artifacts:
          - .env
          - CODiFixcron/.env

    # Evstar-Ev-Api
    - step: &step-build-app
        name: Building Application...
        services:
          - docker
        caches:
          - docker
        script:
          - export $(cat .env | xargs)
          - echo "Building Docker image for Application"
          - export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$PROJECT_NAME/$GLOBAL_REPO/evstar-ev-api/$ENV
          - docker build -t $IMAGE_NAME .
          - docker save --output docker_app.tar $IMAGE_NAME
        artifacts:
          - docker_app.tar

    # Evstar-Ev-Api
    - step: &step-push-app
        image: google/cloud-sdk:alpine
        name: Pushing Application...
        services:
          - docker
        caches:
          - docker
        script:
          # backup docker image
          - docker load --input docker_app.tar
          # Authenticating with the service account key file
          - echo $GCP_SERVICE_ACCOUNT_KEY | base64 -d > ./gcloud-api-key.json
          - gcloud info --run-diagnostics
          - gcloud auth activate-service-account --key-file gcloud-api-key.json
          - gcloud config set project $PROJECT_NAME
          # Login to google docker hub
          - cat ./gcloud-api-key.json | docker login -u _json_key --password-stdin https://$GCP_REGISTRY_HOSTNAME
          # Prepare image name and shorten commit id
          - export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$PROJECT_NAME/$GLOBAL_REPO/evstar-ev-api/$ENV
          # Tag docker image with commit id and push
          - docker tag $IMAGE_NAME $IMAGE_NAME:latest
          - docker push $IMAGE_NAME:latest
          
        artifacts:
            - docker_app.tar

    # Evstar-Ev-Api
    - step: &step-build-cron
        name: Building CronJob...
        services:
          - docker
        caches:
          - docker
        script:
          - export $(cat .env | xargs)
          - echo "Building Docker image for Application"
          - export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$PROJECT_NAME/$GLOBAL_REPO/evstar-api-fix-cron-${ENV}/$ENV
          - docker build -t $IMAGE_NAME -f CODiFixcron/Dockerfile .
          - docker save --output docker_cron.tar $IMAGE_NAME

        artifacts:
          - docker_cron.tar

    # Evstar-Ev-Api
    - step: &step-push-cron
        image: google/cloud-sdk:alpine
        name: Pushing CronJob...
        services:
          - docker
        caches:
          - docker
        script:
          # backup docker image
          - docker load --input docker_cron.tar
          # Authenticating with the service account key file
          - echo $GCP_SERVICE_ACCOUNT_KEY | base64 -d > ./gcloud-api-key.json
          - gcloud info --run-diagnostics
          - gcloud auth activate-service-account --key-file gcloud-api-key.json
          - gcloud config set project $PROJECT_NAME
          # Login to google docker hub
          - cat ./gcloud-api-key.json | docker login -u _json_key --password-stdin https://$GCP_REGISTRY_HOSTNAME
          # Prepare image name and shorten commit id
          - export IMAGE_NAME=$GCP_REGISTRY_HOSTNAME/$PROJECT_NAME/$GLOBAL_REPO/evstar-api-fix-cron-${ENV}/$ENV
          # Tag docker image with commit id and push
          - docker tag $IMAGE_NAME $IMAGE_NAME:latest
          - docker push $IMAGE_NAME:latest

        artifacts:
            - docker_cron.tar

    # Evstar-Ev-Api
    - step: &step-apply-app
        name: kubectl apply...
        script:
          # Export environment variables from the .env file
          - export $(cat .env | xargs)
          
          # Set necessary variables from the second block
          - export KEY_FILE=${KEY_FILE}
          - export PROJECT=pipes-alexk-serverless-deploy
          - export COMPUTE_ZONE=us-east1
          - export CLUSTER_NAME="hello-cluster"
          - export KUBECTL_COMMAND=apply
          - export SPEC_FILE=helloweb-deployment.yaml
          - export KUBECTL_ARGS="--dry-run"

          # Installing Kustomize
          - echo "Installing Kustomize"

          - pipe: atlassian/google-gke-kubectl-run:0.1.1
          - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
          - sudo mv ./kustomize /usr/local/bin/
          - kustomize version

          # Deploying to GKE test cluster
          - echo "Deploying to GKE test cluster"
          - gcloud container clusters get-credentials ${GCP_CLUSTER_NAME} --zone ${GCP_ZONE} --project ${GCP_PROJECT_ID}

          # Kustomize build and kubectl apply
          - cd k8s/${ENV}
          - kustomize build . | kubectl ${KUBECTL_COMMAND} -f - ${KUBECTL_ARGS}

    # Codi-Fix-Cron-Job
    - step: &step-apply-cron
        name: kubectl apply...
        image: atlassian/pipelines-kubectl:latest
        script:
          - export $(cat .env | xargs)
          - echo "Installing Kustomize"
          - curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
          - sudo mv ./kustomize /usr/local/bin/
          - kustomize version
          - echo "Deploying to GKE test cluster"
          - gcloud container clusters get-credentials ${GCP_CLUSTER_NAME} --zone ${GCP_ZONE} --project ${GCP_PROJECT_ID}
          - cd CODiFixcron/k8s/${ENV}
          - kustomize build . | kubectl apply -f -

    - step: &step-message
        name: Deleting .env files
        image: alpine:latest
        script:
          - echo "Cleaning .env and CODifixcron/.env files from the artifact"
          - rm -f .env CODifixcron/.env

pipelines:
  branches:
    abdul-test:
      - step:
          <<: *step-env-app
      - parallel:
        - step: *step-build-app
        - step: *step-build-cron
      - parallel:
        - step: *step-push-app
        - step: *step-push-cron
      - parallel:
        - step: *step-apply-app
        - step: *step-apply-cron
      - step: *step-message
        
    test:
      - step:
          <<: *step-env-app
          deployment: test
      - parallel:
        - step: *step-build-app
        - step: *step-build-cron
      - parallel:
        - step: *step-push-app
        - step: *step-push-cron
      - parallel:
        - step: *step-apply-app
        - step: *step-apply-cron
      - step: *step-message

    master:
      - step:
          <<: *step-env-app
          deployment: prod
      - step: *step-env-app
      - parallel:
        - step: *step-build-app
        - step: *step-build-cron
      - parallel:
        - step: *step-push-app
        - step: *step-push-cron
      - parallel:
        - step: *step-apply-app
        - step: *step-apply-cron
      - step: *step-message

I have already configured GCP_SERVICE_ACCOUNT_KEY in workspace variables and the service key I have create in GCP with all required IAM roles attached to it.
Can you please help me in this

@abdulrbutt1010
Copy link

Fixed it I was putting the data from .json file to workspace variables without converting them into base64. Fixed it by converting the files into base64 and then update the variables with the values that I get to Workspace variables on Bitbucket below is the command I used to convert it into base64
base64 my-service-account-key.json | tr -d '\n'

@ahmetgeymen
Copy link
Author

Fixed it I was putting the data from .json file to workspace variables without converting them into base64. Fixed it by converting the files into base64 and then update the variables with the values that I get to Workspace variables on Bitbucket below is the command I used to convert it into base64 base64 my-service-account-key.json | tr -d '\n'

I'm glad it worked.

@abdulrbutt1010
Copy link

abdulrbutt1010 commented Oct 17, 2024

Thank you so much @ahmetgeymen for sharing this I'm able to create complete deployment pipeline below is my pipeline if someone maybe this could help other in the use-case I have
`image: atlassian/pipelines-awscli:latest

definitions:
###############################################
# Test Environment
###############################################
# App Test
gke-kubectl-pipe-app-test: &pipe-app-test atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-app-test: &pipe-variables-app-test
KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
PROJECT: $PROJECT_NAME
COMPUTE_ZONE: $GCP_ZONE
CLUSTER_NAME: $GCP_CLUSTER_NAME
# If there is a change in k8s manifest enable below line and disable after that line
# KUBECTL_COMMAND: "apply -f k8s/test/deployment.yml --namespace=default"
KUBECTL_COMMAND: "rollout restart deployment/evstar-ev-api-test --namespace=default"

# Caddy Test
gke-kubectl-pipe-caddy-test: &pipe-caddy-test atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-caddy-test: &pipe-variables-caddy-test
    KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
    PROJECT: $PROJECT_NAME
    COMPUTE_ZONE: $GCP_ZONE
    CLUSTER_NAME: $GCP_CLUSTER_NAME
    # If there is a change in k8s manifest enable below line and disable after that line
    # KUBECTL_COMMAND: "apply -f k8s/test/caddy.yml --namespace=default"
    KUBECTL_COMMAND: "rollout restart deployment/evstar-ev-api-test-caddy --namespace=default"

# Cron Test
gke-kubectl-pipe-cron-test: &pipe-cron-test atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-cron-test: &pipe-variables-cron-test
    KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
    PROJECT: $PROJECT_NAME
    COMPUTE_ZONE: $GCP_ZONE
    CLUSTER_NAME: $GCP_CLUSTER_NAME
    # If there is a change in k8s manifest enable below line and disable after that line
    # KUBECTL_COMMAND: "apply -f CODiFixcron/k8s/test/deployment.yml --namespace=default"
    KUBECTL_COMMAND: "rollout restart deployment/evstar-api-fix-cron-test --namespace=default"

###############################################
# Production Environment
###############################################
# App Prod
gke-kubectl-pipe-app-prod: &pipe-app-prod atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-app-prod: &pipe-variables-app-prod
    KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
    PROJECT: $PROJECT_NAME
    COMPUTE_ZONE: $GCP_ZONE
    CLUSTER_NAME: $GCP_CLUSTER_NAME
    # If there is a change in k8s manifest enable below line and disable after that line
    # KUBECTL_COMMAND: "apply -f k8s/prod/deployment.yml --namespace=default"
    KUBECTL_COMMAND: "rollout restart deployment/evstar-ev-api-prod --namespace=default"

# Caddy Prod
gke-kubectl-pipe-caddy-prod: &pipe-caddy-prod atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-caddy-prod: &pipe-variables-caddy-prod
    KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
    PROJECT: $PROJECT_NAME
    COMPUTE_ZONE: $GCP_ZONE
    CLUSTER_NAME: $GCP_CLUSTER_NAME
    # If there is a change in k8s manifest enable below line and disable after that line
    # KUBECTL_COMMAND: "apply -f k8s/prod/caddy.yml --namespace=default"
    KUBECTL_COMMAND: "rollout restart deployment/evstar-ev-api-prod-caddy --namespace=default"

# Cron Prod
gke-kubectl-pipe-cron-prod: &pipe-cron-prod atlassian/google-gke-kubectl-run:1.3.1
gke-kubectl-pipe-variables-cron-prod: &pipe-variables-cron-prod
    KEY_FILE: $GCP_SERVICE_ACCOUNT_KEY
    PROJECT: $PROJECT_NAME
    COMPUTE_ZONE: $GCP_ZONE
    CLUSTER_NAME: $GCP_CLUSTER_NAME
    # If there is a change in k8s manifest enable below line and disable after that line
    # KUBECTL_COMMAND: "apply -f CODiFixcron/k8s/prod/deployment.yml --namespace=default"
    KUBECTL_COMMAND: "rollout restart deployment/evstar-api-fix-cron-prod --namespace=default"

steps:
- step: &step-env-app
    image: atlassian/pipelines-awscli:latest
    name: Generating ENV file...
    deployment: dev
    oidc: true
    script:
      - apk update && apk add jq
      - touch .env
      - echo "ENV=${ENV}" >> .env
      - export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
      - echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
      - export NODE_ENV=$(aws ssm get-parameter --name /${ENV}/project-name/node-env --with-decryption | jq --raw-output '.Parameter.Value') || echo "Failed to fetch NODE_ENV"
      - echo "NODE_ENV=${NODE_ENV}" >> .env
      - export JWT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/jwt-secret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "JWT_SECRET=${JWT_SECRET}" >> .env
      - export DATABASE=$(aws ssm get-parameter --name /${ENV}/project-name/database --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE=${DATABASE}" >> .env
      - export DATABASE_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/database-password --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_PASSWORD=${DATABASE_PASSWORD}" >> .env
      - export REPAIRSHOPR_API_KEY=$(aws ssm get-parameter --name /${ENV}/project-name/repairshoper-apikey --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "REPAIRSHOPR_API_KEY=${REPAIRSHOPR_API_KEY}" >> .env
      - export CLIENTID=$(aws ssm get-parameter --name /${ENV}/project-name/client-id --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CLIENTID=${CLIENTID}" >> .env
      - export CLIENTSECRET=$(aws ssm get-parameter --name /${ENV}/project-name/client-secret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CLIENTSECRET=${CLIENTSECRET}" >> .env
      - export REDIRECTURI=$(aws ssm get-parameter --name /${ENV}/project-name/redirect-uri --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "REDIRECTURI=${REDIRECTURI}" >> .env
      - export REFRESHTOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/refresh-token --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "REFRESHTOKEN=${REFRESHTOKEN}" >> .env
      - export DEVOPS_CLIENTID=$(aws ssm get-parameter --name /${ENV}/project-name/devops-clientid --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DEVOPS_CLIENTID=${DEVOPS_CLIENTID}" >> .env
      - export DEVOPS_CLIENTSECRET=$(aws ssm get-parameter --name /${ENV}/project-name/devops-clientsecret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DEVOPS_CLIENTSECRET=${DEVOPS_CLIENTSECRET}" >> .env
      - export DEVOPS_REFRESHTOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/devops-refreshtoken --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DEVOPS_REFRESHTOKEN=${DEVOPS_REFRESHTOKEN}" >> .env
      - export DONOTREPLY_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/donotreply-password --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DONOTREPLY_PASSWORD=${DONOTREPLY_PASSWORD}" >> .env
      - export DO_NOT_REPLY_CLIENT_ID=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyclientid --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DO_NOT_REPLY_CLIENT_ID=${DO_NOT_REPLY_CLIENT_ID}" >> .env
      - export DO_NOT_REPLY_CLIENT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyclientsecret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DO_NOT_REPLY_CLIENT_SECRET=${DO_NOT_REPLY_CLIENT_SECRET}" >> .env
      - export DO_NOT_REPLY_REFRESH_TOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/donot-replyrefreshtoken --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DO_NOT_REPLY_REFRESH_TOKEN=${DO_NOT_REPLY_REFRESH_TOKEN}" >> .env
      - export OTPAUTH_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/otp-authsecret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "OTPAUTH_SECRET=${OTPAUTH_SECRET}" >> .env
      - export CODI_EMAIL=$(aws ssm get-parameter --name /${ENV}/project-name/codi-email --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CODI_EMAIL=${CODI_EMAIL}" >> .env
      - export CODI_EMAIL_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/codi-emailpassword --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CODI_EMAIL_PASSWORD=${CODI_EMAIL_PASSWORD}" >> .env
      - export CODI_CLIENT=$(aws ssm get-parameter --name /${ENV}/project-name/codi-client --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CODI_CLIENT=${CODI_CLIENT}" >> .env
      - export FEDEX_CLIENT_ID=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-clientid --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "FEDEX_CLIENT_ID=${FEDEX_CLIENT_ID}" >> .env
      - export FEDEX_CLIENT_SECRET=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-clientsecret --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "FEDEX_CLIENT_SECRET=${FEDEX_CLIENT_SECRET}" >> .env
      - export FEDEX_ACCOUNT_NUMBER=$(aws ssm get-parameter --name /${ENV}/project-name/fedex-accountnumber --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "FEDEX_ACCOUNT_NUMBER=${FEDEX_ACCOUNT_NUMBER}" >> .env
      - export TCS_AUTH_TOKEN=$(aws ssm get-parameter --name /${ENV}/project-name/tcs-authtoken --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "TCS_AUTH_TOKEN=${TCS_AUTH_TOKEN}" >> .env
      - export MAILGUN_API_KEY=$(aws ssm get-parameter --name /${ENV}/project-name/mailgun-apikey --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "MAILGUN_API_KEY=${MAILGUN_API_KEY}" >> .env
      - export CSVPARSER_V2=$(aws ssm get-parameter --name /${ENV}/project-name/csvparser-v2 --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "CSVPARSER_V2=${CSVPARSER_V2}" >> .env

      # Codifix Cron
      - touch CODiFixcron/.env
      - echo "ENV=${ENV}" >> CODiFixcron/.env
      - export BASTION_HOST=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-host --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "BASTION_HOST=${BASTION_HOST}" >> CODiFixcron/.env
      - export BASTION_PORT=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-port --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "BASTION_PORT=${BASTION_PORT}" >> CODiFixcron/.env
      - export BASTION_USERNAME=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-username --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "BASTION_USERNAME=${BASTION_USERNAME}" >> CODiFixcron/.env
      - export BASTION_PRIVATE_KEY_PATH=$(aws ssm get-parameter --name /${ENV}/codifixcron/bastion-privarekeypath --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "BASTION_PRIVATE_KEY_PATH=${BASTION_PRIVATE_KEY_PATH}" >> CODiFixcron/.env
      - export DATABASE_MYSQL_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqlurl --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_MYSQL_URL=${DATABASE_MYSQL_URL}" >> CODiFixcron/.env
      - export DATABASE_MYSQL_USER=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqluser --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_MYSQL_USER=${DATABASE_MYSQL_USER}" >> CODiFixcron/.env
      - export DATABASE_MYSQL_PASSWORD=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysqlpassword --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_MYSQL_PASSWORD=${DATABASE_MYSQL_PASSWORD}" >> CODiFixcron/.env
      - export DATABASE_MYSQL=$(aws ssm get-parameter --name /${ENV}/codifixcron/database-mysql --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_MYSQL=${DATABASE_MYSQL}" >> CODiFixcron/.env
      - export GRAPHQL_API_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/graphql-apiurl --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "GRAPHQL_API_URL=${GRAPHQL_API_URL}" >> CODiFixcron/.env
      - export API_URL=$(aws ssm get-parameter --name /${ENV}/codifixcron/api-url --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "API_URL=${API_URL}" >> CODiFixcron/.env
      - export API_KEY=$(aws ssm get-parameter --name /${ENV}/codifixcron/api-key --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "API_KEY=${API_KEY}" >> CODiFixcron/.env
      - export DATABASE=$(aws ssm get-parameter --name /${ENV}/project-name/database --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE=${DATABASE}" >> .env
      - export DATABASE_PASSWORD=$(aws ssm get-parameter --name /${ENV}/project-name/database-password --with-decryption | jq --raw-output '.Parameter.Value')
      - echo "DATABASE_PASSWORD=${DATABASE_PASSWORD}" >> .env
      
    artifacts:
      - .env
      - CODiFixcron/.env

- step: &step-build-app
    name: Building Application...
    services:
      - docker
    caches:
      - docker
    script:
      - export $(cat .env | xargs)
      - echo "Building Docker image for Application"
      - docker build -t evstar-ev-api-${ENV} .
      - docker save --output docker_app.tar evstar-ev-api-${ENV}:latest
    artifacts:
      - docker_app.tar

- step: &step-build-cron
    name: Building CronJob...
    services:
      - docker
    caches:
      - docker
    script:
      - export $(cat .env | xargs)
      - echo "Building Docker image for Application"
      - docker build -t evstar-api-fix-cron-${ENV} -f CODiFixcron/Dockerfile .
      - docker save --output docker_cron.tar evstar-api-fix-cron-${ENV}:latest

    artifacts:
      - docker_cron.tar

- step: &step-push-app
    image: google/cloud-sdk:alpine
    name: Pushing Application To GCR...
    services:
      - docker
    caches:
      - docker
    script:
      - export $(cat .env | xargs)
      # backup docker image
      - docker load --input docker_app.tar
      # Authenticating with the service account key file
      - echo $GCP_SERVICE_ACCOUNT_KEY | base64 -d > ./gcloud-api-key.json
      - gcloud info --run-diagnostics
      - gcloud auth activate-service-account --key-file gcloud-api-key.json
      - gcloud config set project $PROJECT_NAME
      # Login to google docker hub
      - cat ./gcloud-api-key.json | docker login -u _json_key --password-stdin https://${GCP_REGISTRY_HOSTNAME}
      # Prepare image name and shorten commit id
      - export IMAGE_NAME_APP=evstar-ev-api-${ENV}:latest
      - export GCR_IMAGE_NAME_APP=${GCP_REGISTRY_HOSTNAME}/${PROJECT_NAME}/${GLOBAL_REPO}/evstar-ev-api/${ENV}
      - echo "$GCR_IMAGE_NAME_APP"
      # Tag docker image with commit id and push
      - docker tag $IMAGE_NAME_APP $GCR_IMAGE_NAME_APP:latest
      - docker push $GCR_IMAGE_NAME_APP:latest

    artifacts:
        - docker_app.tar

- step: &step-push-cron
    image: google/cloud-sdk:alpine
    name: Pushing CronJob To GCR...
    services:
      - docker
    caches:
      - docker
    script:
      - export $(cat .env | xargs)
      # backup docker image
      - docker load --input docker_cron.tar
      # Authenticating with the service account key file
      - echo $GCP_SERVICE_ACCOUNT_KEY | base64 -d > ./gcloud-api-key.json
      - gcloud info --run-diagnostics
      - gcloud auth activate-service-account --key-file gcloud-api-key.json
      - gcloud config set project $PROJECT_NAME
      # Login to google docker hub
      - cat ./gcloud-api-key.json | docker login -u _json_key --password-stdin https://${GCP_REGISTRY_HOSTNAME}
      # Prepare image name and shorten commit id
      - export IMAGE_NAME_CRON=evstar-api-fix-cron-${ENV}:latest
      - export GCR_IMAGE_NAME_CRON=${GCP_REGISTRY_HOSTNAME}/${PROJECT_NAME}/${GLOBAL_REPO}/evstar-api-fix-cron-${ENV}/${ENV}
      - echo "$GCR_IMAGE_NAME_CRON"
      # Tag docker image with commit id and push
      - docker tag $IMAGE_NAME_CRON $GCR_IMAGE_NAME_CRON:latest
      - echo "$GCR_IMAGE_NAME_CRON"
      - docker push $GCR_IMAGE_NAME_CRON:latest

    artifacts:
        - docker_cron.tar

- step: &step-deploy-app-test
    name: Deploy App/Caddy To GKE...
    script:
      - export $(cat .env | xargs)
      - echo "Deploying evstar-ev-api to test"
      - pipe: *pipe-app-test
        variables: *pipe-variables-app-test

      - echo "Deploying Caddy to test"
      - pipe: *pipe-caddy-test
        variables: *pipe-variables-caddy-test

- step: &step-deploy-cron-test
    name: Deploy CronJob To GKE...
    script:
      - export $(cat .env | xargs)
      - echo "Deploying CODiFixcron to test"
      - pipe: *pipe-cron-test
        variables: *pipe-variables-cron-test

- step: &step-deploy-app-prod
    name: Deploy to Prod Environment...
    script:
      - export $(cat .env | xargs)
      - echo "Deploying evstar-ev-api to Prod"
      - pipe: *pipe-app-prod
        variables: *pipe-variables-app-prod

      - echo "Deploying Caddy to Prod"
      - pipe: *pipe-caddy-prod
        variables: *pipe-variables-caddy-prod

- step: &step-deploy-cron-prod
    name: Deploy CronJob to Prod Environment...
    script:
      - export $(cat .env | xargs)
      - echo "Deploying CODiFixcron to Prod"
      - pipe: *pipe-cron-prod
        variables: *pipe-variables-cron-prod

- step: &step-message
    name: Deleting .env files
    image: alpine:latest
    script:
      - echo "Cleaning .env and CODifixcron/.env files from the artifact"
      - rm -f .env CODifixcron/.env

pipelines:
branches:

abdul-test:
  - step:
      <<: *step-env-app
  - parallel:
    - step: *step-build-app
    - step: *step-build-cron
  - parallel:
    - step: *step-push-app
    - step: *step-push-cron
  - parallel:
    - step: *step-deploy-app-test
    - step: *step-deploy-cron-test
  - step: *step-message
    
test:
  - step:
      <<: *step-env-app
      deployment: test
  - parallel:
    - step: *step-build-app
    - step: *step-build-cron
  - parallel:
    - step: *step-push-app
    - step: *step-push-cron
  - parallel:
    - step: *step-deploy-app-test
    - step: *step-deploy-cron-test
  - step: *step-message

master:
  - step:
      <<: *step-env-app
      deployment: prod
  - step: *step-env-app
  - parallel:
    - step: *step-build-app
    - step: *step-build-cron
  - parallel:
    - step: *step-push-app
    - step: *step-push-cron
  - parallel:
    - step: *step-deploy-app-prod
    - step: *step-deploy-cron-prod
  - step: *step-message

`

@ahmetgeymen
Copy link
Author

I think it would be better if you share yours as a gist and post its link as a comment. Thank you for sharing your experience. 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment