diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 0000000..7440068 --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,272 @@ +# This is a basic workflow to help you get started with Actions +name: Build & Deploy - Dev + +# Controls when the action will run. Invokes the workflow on push events but only for the main branch +on: + push: + branches: [5165-cicd] + pull_request: + branches: [5165-cicd] + +env: + ENVIRONMENT: development + ECR_REPOSITORY: test # set this to your Amazon ECR repository name + ECS_SERVICE: geonetwork4-service #MY_ECS_SERVICE # set this to your Amazon ECS service name + ECS_CLUSTER: aodn-imos-v2 # set this to your Amazon ECS cluster name + ECS_TASK_DEFINITION: ./geonetwork4-td.json #MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition + CONTAINER_NAME: geonetwork4-container # set this to the name of the container in the + CA_DOMAIN: test + CA_DOMAIN_OWNER: test + CA_REPO: test + CA_PACKAGE: test + CA_NAMESPACE: test + + +# Permission can be added at job level or workflow level +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + build-deploy: + runs-on: ubuntu-latest + environment: development + steps: + - name: Git clone the repository + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.ROLE_ARN }} + role-session-name: GitHub_to_AWS_via_FederatedOIDC + aws-region: ${{ vars.AWS_REGION }} + + # Hello from AWS: WhoAmI + - name: Sts GetCallerIdentity + run: | + aws sts get-caller-identity + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Retrieve Parameters - ssm parameter store + id: getParameters + run: | + # Replace '--path' with your specific path from Parameter Store + parameters=$(aws ssm get-parameters-by-path --path "/core/geonetwork4/dev_ecr_ecs_config/" --recursive --query 'Parameters[*].[Name,Value]' --output json) + echo "$parameters" > parameters.json + echo "::set-output name=parameters_json::$parameters" + + - name: Process Parameters - ssm parameter store + run: | + parameters=$(cat parameters.json) + # Loop through the JSON array of parameters using jq + for row in $(echo "${parameters}" | jq -r '.[] | @base64'); do + _jq() { + echo "${row}" | base64 --decode | jq -r "${1}" + } + name=$(_jq '.[0]') + value=$(_jq '.[1]') + + echo "Name: $name, Value: $value" + + # Perform actions using parameter values here + # For example, set environment variables + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ecr_repo" ]; then + echo "ECR_REPOSITORY=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ecs_cluster" ]; then + echo "ECS_CLUSTER=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ecs_service" ]; then + echo "ECS_SERVICE=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/container_name" ]; then + echo "CONTAINER_NAME=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ca_domain" ]; then + echo "CA_DOMAIN=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ca_domain_owner" ]; then + echo "CA_DOMAIN_OWNER=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ca_repo" ]; then + echo "CA_REPO=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ca_package" ]; then + echo "CA_PACKAGE=$value" >> "$GITHUB_ENV" + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ca_namespace" ]; then + echo "CA_NAMESPACE=$value" >> "$GITHUB_ENV" + fi + done + + - name: Prepare Build-ID + id: prep + run: | + BRANCH=${GITHUB_REF##*/} + TS=$(date +%F%H%M%S | sed 's/-//g') + echo "TimeStamp=$TS" + REVISION=${GITHUB_SHA::8} + BUILD_ID="${BRANCH}-${REVISION}-${TS}" + LATEST_ID=canary + if [[ $GITHUB_REF == refs/tags/* ]]; then + BUILD_ID=${GITHUB_REF/refs\/tags\//} + LATEST_ID=latest + fi + echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" + echo "LATEST_ID=$LATEST_ID" + echo "BUILD_ID=$BUILD_ID" >> $GITHUB_OUTPUT + + - name: Get and calculate latest package version - AWS CodeArtifact + id: ca-getversion + env: + BUILD_ID: ${{ steps.prep.outputs.BUILD_ID }} + run: | + FLAG_INITIAL=false + + CURRENT_VERSION=$(aws codeartifact list-package-versions --domain $CA_DOMAIN --repository $CA_REPO --format generic --package $CA_PACKAGE --namespace $CA_NAMESPACE --query defaultDisplayVersion | jq -r ".") + echo "current version: $CURRENT_VERSION" + + if [ -z "$CURRENT_VERSION" ] || [ "$CURRENT_VERSION" == "null" ]; then + CURRENT_VERSION="1.0.0" + FLAG_INITIAL=true + fi + IFS='.' read -ra version_parts <<< "$CURRENT_VERSION" + MAJOR=${version_parts[0]} + MINOR=${version_parts[1]} + NEW_MINOR=$((MINOR + 1)) + if [ "$FLAG_INITIAL" == "true" ]; then + NEW_MINOR="0" + fi + + #version format[major.minor.build_number] + #build_number format{BRANCH}-${REVISION}-${TS} + echo "latest_version=v$MAJOR.$NEW_MINOR.${{ env.BUILD_ID }}" >> $GITHUB_OUTPUT + + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + cache: 'maven' + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Build and tag image + id: build-image + env: + IMAGE_TAG: ${{ steps.ca-getversion.outputs.latest_version }} + run: | + # Build a docker container and + # be deployed to ECS. + # docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + # echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + # aws ecr get-login-password --region ap-southeast-2 | docker login --username AWS --password-stdin $ACCOUNT_ID + docker build -t $ECR_REPOSITORY:$IMAGE_TAG . + echo "image=$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Run Trivy vulnerability scanner in docker mode + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ steps.build-image.outputs.image }} + format: 'table' + severity: 'HIGH,CRITICAL' + vuln-type: 'os,library' + exit-code: 1 + ignore-unfixed: true + continue-on-error: true + + - name: Push image to Amazon ECR + id: push-image + env: + IMAGE_TAG: ${{ steps.ca-getversion.outputs.latest_version }} + run: | + # Build a docker container and + # be deployed to ECS. + # docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + # echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + # aws ecr get-login-password --region ap-southeast-2 | docker login --username AWS --password-stdin $ACCOUNT_ID + docker push $ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.push-image.outputs.image }} + environment-variables: | + IMAGE=${{ steps.push-image.outputs.image }} + ES_HOST=${{ vars.ES_HOST }} + ES_PROTOCOL=${{ vars.ES_PROTOCOL }} + ES_PORT=${{ vars.ES_PORT }} + ES_USERNAME=${{ vars.ES_USERNAME }} + ES_PASSWORD=${{ secrets.ES_PASSWORD }} + GEONETWORK_DB_PASSWORD=${{ secrets.GEONETWORK_DB_PASSWORD }} + + GEONETWORK_DB_TYPE=${{ vars.GEONETWORK_DB_TYPE }} + GEONETWORK_DB_HOST=${{ vars.GEONETWORK_DB_HOST }} + GEONETWORK_DB_PORT=${{ vars.GEONETWORK_DB_PORT }} + GEONETWORK_DB_NAME=${{ vars.GEONETWORK_DB_NAME }} + GEONETWORK_DB_USERNAME=${{ vars.GEONETWORK_DB_USERNAME }} + + INDEXER_HOST=${{ vars.INDEXER_HOST }} + INDEXER_PORT=${{ vars.INDEXER_PORT }} + INDEXER_APIKEY=${{ secrets.INDEXER_APIKEY }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + id: ecs-deploy + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true + + - name: Check if deployment was successful + id: check-deployment + run: | + CURRENT_TASK_DEF_ARN=$(aws ecs describe-services --cluster ${{ env.ECS_CLUSTER }} --services ${{ env.ECS_SERVICE }} --query services[0].deployments[0].taskDefinition | jq -r ".") + NEW_TASK_DEF_ARN=${{ steps.ecs-deploy.outputs.task-definition-arn }} + REVISION=${GITHUB_SHA::8} + echo "Current task arn: $CURRENT_TASK_DEF_ARN" + echo "New task arn: $NEW_TASK_DEF_ARN" + echo "Latest revision: $REVISION" + if [ "$CURRENT_TASK_DEF_ARN" != "$NEW_TASK_DEF_ARN" ]; then + echo "Deployment failed with latest code revision." + exit 1 + else + echo "Deployment successfull." + fi + + - name: Publish JAR file - AWS CodeArtifact + id: ca-deploy + env: + CA_VERSION: ${{ steps.ca-getversion.outputs.latest_version }} + run: | + export ASSET_SHA256=$(sha256sum ${{ vars.CA_SOURCE_PATH }} | awk '{print $1;}') + #ASSET_SHA256:- This value is used as an integrity check to verify that the assetContent has not changed after it was originally sent or published. + + aws codeartifact publish-package-version \ + --repository $CA_REPO \ + --domain $CA_DOMAIN \ + --domain-owner $CA_DOMAIN_OWNER \ + --format generic \ + --package $CA_PACKAGE \ + --asset-content ${{ vars.CA_SOURCE_PATH }} \ + --package-version ${{ env.CA_VERSION }} \ + --asset-name $CA_PACKAGE \ + --asset-sha256 $ASSET_SHA256 \ + --namespace $CA_NAMESPACE \ + --output text \ No newline at end of file diff --git a/.github/workflows/rollback-workflow.yml b/.github/workflows/rollback-workflow.yml new file mode 100644 index 0000000..492f03d --- /dev/null +++ b/.github/workflows/rollback-workflow.yml @@ -0,0 +1,132 @@ +name: Rollback to particular version + +on: + push: + branches: [5195-cicd-test] +# on: +# workflow_dispatch: # Manual trigger for rollback +# inputs: +# DEPLOY_VERSION: +# description: 'Stable Version Image Id' +# required: true +env: + ECR_REPOSITORY: test # set this to your Amazon ECR repository name + ECS_SERVICE: test # set this to your Amazon ECS service name + ECS_CLUSTER: test # set this to your Amazon ECS cluster name + ECS_TASK_DEFINITION: ./geonetwork4-td.json #MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition + +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + rollback: + runs-on: ubuntu-latest + environment: development + steps: + - name: Git clone the repository + uses: actions/checkout@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.ROLE_ARN }} + role-session-name: GitHub_to_AWS_via_FederatedOIDC + aws-region: ${{ vars.AWS_REGION }} + + # Hello from AWS: WhoAmI + - name: Sts GetCallerIdentity + run: | + aws sts get-caller-identity + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Retrieve Parameters - ssm parameter store + id: getParameters + run: | + # Replace '--path' with your specific path from Parameter Store + parameters=$(aws ssm get-parameters-by-path --path "/core/geonetwork4/dev_ecr_ecs_config/" --recursive --query 'Parameters[*].[Name,Value]' --output json) + echo "$parameters" > parameters.json + echo "::set-output name=parameters_json::$parameters" + + - name: Process Parameters - ssm parameter store + id: process-parameters + run: | + parameters=$(cat parameters.json) + # Loop through the JSON array of parameters using jq + for row in $(echo "${parameters}" | jq -r '.[] | @base64'); do + _jq() { + echo "${row}" | base64 --decode | jq -r "${1}" + } + name=$(_jq '.[0]') + value=$(_jq '.[1]') + + echo "Name: $name, Value: $value" + + # Perform actions using parameter values here + # For example, set environment variables + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ecs_cluster" ]; then + echo "ECS_CLUSTER=$value" >> "$GITHUB_ENV" + echo "ECS_CLUSTER=$value" >> $GITHUB_OUTPUT + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/ecs_service" ]; then + echo "ECS_SERVICE=$value" >> "$GITHUB_ENV" + echo "ECS_SERVICE=$value" >> $GITHUB_OUTPUT + fi + if [ "$name" = "/core/geonetwork4/dev_ecr_ecs_config/container_name" ]; then + echo "CONTAINER_NAME=$value" >> "$GITHUB_ENV" + echo "CONTAINER_NAME=$value" >> $GITHUB_OUTPUT + fi + done + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ steps.process-parameters.outputs.CONTAINER_NAME }} + image: ${{ vars.DEPLOY_VERSION }} # TODO: this var will be replaced by manaul input from user ${{ inputs.DEPLOY_VERSION }} + environment-variables: | + IMAGE=${{ vars.DEPLOY_VERSION }} + ES_HOST=${{ vars.ES_HOST }} + ES_PROTOCOL=${{ vars.ES_PROTOCOL }} + ES_PORT=${{ vars.ES_PORT }} + ES_USERNAME=${{ vars.ES_USERNAME }} + ES_PASSWORD=${{ secrets.ES_PASSWORD }} + GEONETWORK_DB_PASSWORD=${{ secrets.GEONETWORK_DB_PASSWORD }} + + GEONETWORK_DB_TYPE=${{ vars.GEONETWORK_DB_TYPE }} + GEONETWORK_DB_HOST=${{ vars.GEONETWORK_DB_HOST }} + GEONETWORK_DB_PORT=${{ vars.GEONETWORK_DB_PORT }} + GEONETWORK_DB_NAME=${{ vars.GEONETWORK_DB_NAME }} + GEONETWORK_DB_USERNAME=${{ vars.GEONETWORK_DB_USERNAME }} + + INDEXER_HOST=${{ vars.INDEXER_HOST }} + INDEXER_PORT=${{ vars.INDEXER_PORT }} + INDEXER_APIKEY=${{ secrets.INDEXER_APIKEY }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + id: ecs-deploy + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ steps.process-parameters.outputs.ECS_SERVICE }} + cluster: ${{ steps.process-parameters.outputs.ECS_CLUSTER }} + wait-for-service-stability: true + + - name: Check if deployment was successful + id: check-deployment + run: | + CURRENT_TASK_DEF_ARN=$(aws ecs describe-services --cluster ${{ steps.process-parameters.outputs.ECS_CLUSTER }} --services ${{ steps.process-parameters.outputs.ECS_SERVICE }} --query services[0].deployments[0].taskDefinition | jq -r ".") + NEW_TASK_DEF_ARN=${{ steps.ecs-deploy.outputs.task-definition-arn }} + REVISION=${GITHUB_SHA::8} + echo "Current task arn: $CURRENT_TASK_DEF_ARN" + echo "New task arn: $NEW_TASK_DEF_ARN" + echo "Latest revision: $REVISION" + if [ "$CURRENT_TASK_DEF_ARN" != "$NEW_TASK_DEF_ARN" ]; then + echo "Deployment failed with latest code revision." + exit 1 + else + echo "Deployment successfull." + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index 91b83ad..54599c2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,6 @@ replay_pid* # Ingore local generated folders elasticdata/ gn4_data/ -.env \ No newline at end of file +.env + +**/target/**/* \ No newline at end of file diff --git a/geonetwork4-td.json b/geonetwork4-td.json new file mode 100644 index 0000000..439bb3a --- /dev/null +++ b/geonetwork4-td.json @@ -0,0 +1,49 @@ +{ + "containerDefinitions": [ + { + "name": "geonetwork4-container", + "image": "", + "cpu": 0, + "portMappings": [ + { + "name": "geonetwork4-container-80-tcp", + "containerPort": 8080, + "hostPort": 8080, + "protocol": "tcp", + "appProtocol": "http" + } + ], + "essential": true, + "environment": [], + "environmentFiles": [], + "mountPoints": [], + "volumesFrom": [], + "ulimits": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/aodn-geonetwork4", + "awslogs-region": "ap-southeast-2", + "awslogs-stream-prefix": "ecs" + }, + "secretOptions": [] + } + } + ], + "family": "aodn-geo4-td", + "executionRoleArn": "arn:aws:iam::704910415367:role/ecsTaskExecutionRole", + "networkMode": "awsvpc", + "volumes": [], + "placementConstraints": [], + "requiresCompatibilities": [ + "FARGATE" + ], + "cpu": "1024", + "memory": "3072", + "runtimePlatform": { + "cpuArchitecture": "X86_64", + "operatingSystemFamily": "LINUX" + }, + "tags": [] +} \ No newline at end of file