Code:
#!/usr/bin/env bash
# Exit immediately if command returns bad exit code
set -e
ENABLED_SLACK_REFS=(develop qa main prod)
export CI_COMMIT_AUTHOR=$(git log --format="%an" -n1)
export CI_COMMIT_MESSAGE=$(git log --format="%B" -n1)
export CI_COMMIT_URL="${CI_PROJECT_URL}/commit/${CI_COMMIT_SHA}"
main() {
if fn_exists $1; then
${1}
else
echo "Function $1 doesn't exist" && exit 1
fi
}
#------------------------------------------
# Job Tasks
#------------------------------------------
function build_docker_for_pipeline() {
# Pull latest version of image for branch / tag, for caching purposes (speed up the build)
# docker pull ${CI_REGISTRY_IMAGE}:${CI_BRANCH_SLUG} || true
# Build and tag with this pipeline ID so we can use it later, in test & release & deploy
docker build --build-arg CI=${CI} -t ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} .
# docker push ${CI_REGISTRY_IMAGE}
}
function lint() {
# docker pull ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID}
docker run --rm -e CI=${CI} ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} lint
}
function test() {
# Pull image built within this pipeline previously
docker pull ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID}
# Run npm test, via entrypoint.sh test action and generate coverage
mkdir coverage && docker run --rm -e CI=${CI} \
--mount type=bind,source="$(pwd)"/coverage,target=/app/coverage \
${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} test --coverage
}
function security() {
# Pull image built within this pipeline previously
docker pull ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID}
# Audit our dependencies for security vulnerabilities
docker run --rm -e CI=${CI} ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} yarn audit
}
function release_docker_for_branch() {
# Pull image built in this pipeline
docker pull ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID}
# Tag as latest image for branch / tag
docker tag ${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} ${CI_REGISTRY_IMAGE}:${CI_BRANCH_SLUG}
# Push to GitLab Container Registry
docker push ${CI_REGISTRY_IMAGE}
}
function deploy() {
# Run npm build, setting REACT_APP_* env variables
export REACT_APP_ENVIRONMENT=${CI_ENVIRONMENT_NAME:-dev}
export REACT_APP_SENTRY_VERSION="${CI_PROJECT_NAME}#$(jq -r '.version' package.json)-${CI_JOB_ID}"
echo $REACT_APP_SENTRY_VERSION
echo "Building app via Docker, injecting following environment variables:"
get_react_env_vars_raw_names
docker run --rm -v source=/home/halo-solutions-ltd/actions-runner/_work/halo-web/halo-web/,target=/app \
$(get_react_env_vars_for_docker) \
${CI_REGISTRY_IMAGE}:${CI_PIPELINE_ID} build
# create_sentry_release
# Set up required environment variables for deployment
export_deploy_ci_variables
aws configure set default.s3.max_concurrent_requests 20
# Sync build output with what is currently deployed
# This also deletes files in bucket that aren't present in build output (cleans old releases)
# Set a blanket cache policy to cache for up to 12hrs - used for versioned static assets
aws s3 sync build s3://${CI_DEPLOY_BUCKET} --delete --cache-control "public,max-age=43200" --exclude "*.map"
# Disable caching for specific files. In particular, ones that reference versioned assets
# This means we can cache bust our versioned assets!
export CACHE_DISABLED_PARAMS="--metadata-directive REPLACE --cache-control max-age=0,no-cache,no-store,must-revalidate --acl public-read"
aws s3 cp s3://${CI_DEPLOY_BUCKET}/service-worker.js s3://${CI_DEPLOY_BUCKET}/service-worker.js --content-type application/javascript ${CACHE_DISABLED_PARAMS}
aws s3 cp s3://${CI_DEPLOY_BUCKET}/index.html s3://${CI_DEPLOY_BUCKET}/index.html --content-type text/html ${CACHE_DISABLED_PARAMS}
aws s3 cp s3://${CI_DEPLOY_BUCKET}/manifest.json s3://${CI_DEPLOY_BUCKET}/manifest.json --content-type application/json ${CACHE_DISABLED_PARAMS}
aws s3 cp s3://${CI_DEPLOY_BUCKET}/asset-manifest.json s3://${CI_DEPLOY_BUCKET}/asset-manifest.json --content-type application/json ${CACHE_DISABLED_PARAMS}
# finalize_sentry_release
}
function deploy_qa_if_updated() {
last_qa_release=$(AWS_DEFAULT_REGION=eu-west-1 AWS_ACCESS_KEY_ID=AKIAJ6355LQD3QU4IK3A AWS_SECRET_ACCESS_KEY=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 \
aws dynamodb get-item --table-name gitlab-react-qa-releases --key "{\"ProjectId\": {\"N\": \"${CI_PROJECT_ID}\"}}" \
--output text --query "Item.[LastReleaseSha.S,LastReleaseTimestamp.N]"
)
echo "Last QA Release: ${last_qa_release}"
# If first QA release
if [ "${last_qa_release}" = "None" ]; then
deploy && update_last_qa_release && deploy_success_slack
return 0
fi
last_qa_release_sha=$(echo "${last_qa_release}" | awk '{print $1}')
last_qa_release_timestamp=$(echo "${last_qa_release}" | awk '{print $2}')
# If current commit is same as last release, or last release isn't in history (not sure how)
if [ "${CI_COMMIT_SHA}" = "${last_qa_release_sha}" ] || ! git merge-base --is-ancestor ${last_qa_release_sha} HEAD; then
no_qa_updates_slack ${last_qa_release_sha} ${last_qa_release_timestamp}
return 0
fi
# If last release occurred previously in branch
deploy && update_last_qa_release && deploy_success_slack
return 0
}
function update_last_qa_release() {
AWS_DEFAULT_REGION=eu-west-1 AWS_ACCESS_KEY_ID=AKIAJ6355LQD3QU4IK3A AWS_SECRET_ACCESS_KEY=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 \
aws dynamodb put-item --table-name gitlab-react-qa-releases --item \
"{\
\"ProjectId\": {\"N\": \"${CI_PROJECT_ID}\"}, \
\"LastReleaseSha\": {\"S\": \"${CI_COMMIT_SHA}\"}, \
\"LastReleaseTimestamp\": {\"N\": \"$(date +%s)\"} \
}"
}
#------------------------------------------
# Slack functions
#------------------------------------------
function slack_enabled_for_ref() {
local ref
ref=${CI_COMMIT_REF_NAME:-none}
echo "${ENABLED_SLACK_REFS[*]}" | grep -F -q -w "$ref";
}
function deploy_success_slack() {
if [ ! -z ${CI_SLACK_WEBHOOK_URL+x} ] && slack_enabled_for_ref; then
local text attachments
export_deploy_ci_variables
text="Successful deployment of <${CI_PIPELINE_URL}|${CI_PROJECT_NAME}> to <${CF_URL}|${CI_ENVIRONMENT_NAME}>"
attachments="{\"fallback\":\"${text}\",\"color\":\"good\", \"text\": \"${text}\",\
\"fields\": [\
{\"title\": \"Git Commit\", \"value\": \"${CI_COMMIT_MESSAGE}\"},\
{\"title\": \"Git Author\", \"value\": \"${CI_COMMIT_AUTHOR}\"}\
],\
\"actions\": [\
{\"type\": \"button\", \"text\": \"View Pipeline\", \"style\": \"primary\", \"url\": \"${CI_PIPELINE_URL}\"}\
]\
}"
curl -s -X POST --data-urlencode \
"payload={\"channel\": \"${CI_SLACK_CHANNEL}\", \"username\": \"React GitLab CICD\",\
\"attachments\": [${attachments}], \"icon_emoji\": \":reactjs:\" }" \
"${CI_SLACK_WEBHOOK_URL}"
fi
}
function deploy_failure_slack() {
if [ ! -z ${CI_SLACK_WEBHOOK_URL+x} ] && slack_enabled_for_ref; then
local text attachments
text="Failed to deploy <${CI_PIPELINE_URL}|${CI_PROJECT_NAME}> to ${CI_ENVIRONMENT_NAME}"
attachments="{\"fallback\":\"${text}\",\"color\":\"danger\", \"text\": \"${text}\",\
\"fields\": [\
{\"title\": \"Git Commit\", \"value\": \"${CI_COMMIT_MESSAGE}\"},\
{\"title\": \"Git Author\", \"value\": \"${CI_COMMIT_AUTHOR}\"}\
],\
\"actions\": [\
{\"type\": \"button\", \"text\": \"View Pipeline\", \"style\": \"primary\", \"url\": \"${CI_PIPELINE_URL}\"}\
]\
}"
curl -s -X POST --data-urlencode \
"payload={\"channel\": \"${CI_SLACK_CHANNEL}\", \"username\": \"React GitLab CICD\",\
\"attachments\": [${attachments}], \"icon_emoji\": \":reactjs:\" }" \
"${CI_SLACK_WEBHOOK_URL}"
fi
}
function job_failure_slack() {
if [ ! -z ${CI_SLACK_WEBHOOK_URL+x} ] && slack_enabled_for_ref; then
local text attachments
text="The ${CI_JOB_NAME} job failed in the <${CI_PIPELINE_URL}|${CI_PROJECT_NAME}> pipeline."
attachments="{\"fallback\":\"${text}\",\"color\":\"danger\", \"text\": \"${text}\",\
\"fields\": [\
{\"title\": \"Git Author\", \"value\": \"${CI_COMMIT_AUTHOR}\", \"short\": true},\
{\"title\": \"Git Branch\", \"value\": \"${CI_COMMIT_REF_NAME}\", \"short\": true}\
],\
\"actions\": [\
{\"type\": \"button\", \"text\": \"View Pipeline\", \"style\": \"primary\", \"url\": \"${CI_PIPELINE_URL}\"}\
]\
}"
curl -s -X POST --data-urlencode \
"payload={\"channel\": \"${CI_SLACK_CHANNEL}\", \"username\": \"React GitLab CICD\",\
\"attachments\": [${attachments}], \"icon_emoji\": \":reactjs:\" }" \
"${CI_SLACK_WEBHOOK_URL}"
fi
}
function no_qa_updates_slack() {
# $1 = Last Release Sha, $2 = Last Release Timestamp
if [ ! -z ${CI_SLACK_WEBHOOK_URL+x} ] && slack_enabled_for_ref; then
local text attachments
text="No unreleased changes found to make a scheduled QA release. The last release was at $(date --date="#$2" "+%a %d %b %T UTC")"
attachments="{\"fallback\":\"${text}\",\"color\":\"good\", \"text\": \"${text}\",\
\"fields\": [\
{\"title\": \"Last Commit Message\", \"value\": \"$(git show -s --format=%B $1)\"},\
{\"title\": \"Last Commit Hash\", \"value\": \"<${CI_PROJECT_URL}/commit/$1|$(git rev-parse --short $1)>\"}\
]\
}"
curl -s -X POST --data-urlencode \
"payload={\"channel\": \"${CI_SLACK_CHANNEL}\", \"username\": \"React GitLab CICD\",\
\"attachments\": [${attachments}], \"icon_emoji\": \":reactjs:\" }" \
"${CI_SLACK_WEBHOOK_URL}"
fi
}
#------------------------------------------
# utils
#------------------------------------------
# Create a Sentry release and upload source maps
function create_sentry_release() {
export SENTRY_URL="${SENTRY_URL:-https://sentry.io}"
export SENTRY_AUTH_TOKEN="${SENTRY_AUTH_TOKEN}"
export SENTRY_ORG="${SENTRY_ORG:-halo-solutions-ltd}"
export SENTRY_PROJECT="${SENTRY_PROJECT:-$CI_PROJECT_NAME}"
export SENTRY_DISABLE_UPDATE_CHECK="true"
export SENTRY_LOG_LEVEL="info"
sentry-cli releases new $REACT_APP_SENTRY_VERSION
#TODO: sentry-cli releases set-commits --auto $REACT_APP_SENTRY_VERSION
sentry-cli releases files $REACT_APP_SENTRY_VERSION upload-sourcemaps -x .js -x .map --validate --rewrite --url-prefix '~/static/js/' ./build/static/js/
}
# Finalize a Sentry release (call once deployed)
function finalize_sentry_release() {
sentry-cli releases finalize $REACT_APP_SENTRY_VERSION
}
# Get the REACT_APP env vars for this build.
# That is, env vars beginning with REACT_APP_ or <ENV>_REACT_APP_
function get_react_env_vars() {
env | egrep "(^REACT_APP_)|(^${CI_ENVIRONMENT_NAME}_REACT_APP_)"
}
# Get the raw names of REACT_APP env vars we will be injecting into build
# That is, names of all env vars starting with REACT_APP_ or <ENV>_REACT_APP_
function get_react_env_vars_raw_names() {
get_react_env_vars | egrep -oh "^(.*=)*" | cut -d '=' -f 1
}
# Get react env vars in format ready to pass to Docker as env variables
# all <ENV>_REACT_APP_ vars renamed to REACT_APP_ format
# Prepended with -e so it can be passed to Docker build as env vars
function get_react_env_vars_for_docker() {
get_react_env_vars | sed "s/^${CI_ENVIRONMENT_NAME}_REACT_APP/REACT_APP/" \
| sed "s/^/-e /"
}
# Export CI variables needed for deployment
function export_deploy_ci_variables() {
export AWS_ACCESS_KEY_ID=$(A=${CI_ENVIRONMENT_NAME}_CI_DEPLOY_ACCESS_KEY_ID; echo ${!A})
export AWS_SECRET_ACCESS_KEY=$(A=${CI_ENVIRONMENT_NAME}_CI_DEPLOY_SECRET_ACCESS_KEY; echo ${!A})
export AWS_DEFAULT_REGION=$(A=${CI_ENVIRONMENT_NAME}_CI_DEPLOY_REGION; echo ${!A})
export CI_DEPLOY_BUCKET=$(S3=${CI_ENVIRONMENT_NAME}_CI_DEPLOY_BUCKET; echo ${!S3})
export CI_CF_DISTRIBUTION=$(CFD=${CI_ENVIRONMENT_NAME}_CI_CF_DISTRIBUTION; echo ${!CFD})
export CF_URL=$(aws cloudfront get-distribution --id ${CI_CF_DISTRIBUTION} \
--query "Distribution.[DomainName, DistributionConfig.Aliases.Items[0]]" \
--output text | awk '{if($2 == "None"){print "http://"$1} else {print "http://"$2}}')
}
function fn_exists() {
# appended double quote is an ugly trick to make sure we do get a string -- if $1 is not a known command, type does not output anything
[ `type -t $1`"" == 'function' ]
}
main "$#"
Now here is the working flow of github:
name: halo solutions web-admin deployment
# Modified for Halo post HHL handover 21/05/22 ML
# develop -> dev
# qa -> qa
# main -> uat
# prod -> prod/live
on:
workflow_dispatch:
push:
branches:
- feature/*
- issue/*
- develop
- qa
- main
- prod
pull_request:
branches:
- develop
- qa
- main
- prod
env:
PROJECT: halo-web
CI_REGISTRY_IMAGE: halo-solutions-ltd/halo-web
CI_ENVIRONMENT_NAME: DEV
CI_SLACK_CHANNEL: ${{ secrets.CI_SLACK_CHANNEL }}
CI_SLACK_WEBHOOK_URL: ${{ secrets.CI_SLACK_WEBHOOK_URL }}
CI_JOB_ID: ${{ github.run_id }}
CI_PIPELINE_ID: ${{ github.run_id }}
jobs:
lint_test_build_release:
runs-on: [ubuntu-latest]
container:
image: ghcr.io/halo-solutions-ltd/docker-aws:latest
steps:
- name: Checkout repo content
uses: actions/checkout#v2
- name: Build
uses: ./.github/common/build
with:
command: "./ci build_docker_for_pipeline || { ./ci job_failure_slack; exit 1; }"
# TODO : Need to implement composite action so we can avoid repeating the code
- name: Dev deploy
if: github.ref == 'refs/heads/develop'
run: |
export CI_COMMIT_REF_NAME=$(echo ${GITHUB_REF#refs/heads/} | sed -e 's/[^A-Za-z0-9._-]/_/g')
export CI_ENVIRONMENT_NAME=DEV
export DEV_REACT_APP_ENV_NAME=${{ secrets.DEV_REACT_APP_ENV_NAME }}
export DEV_REACT_APP_JS_KEY=${{ secrets.DEV_REACT_APP_JS_KEY }}
export DEV_REACT_APP_KEY=${{ secrets.DEV_REACT_APP_KEY }}
export DEV_REACT_APP_PARSE_SERVER=${{ secrets.DEV_REACT_APP_PARSE_SERVER }}
export DEV_REACT_APP_SERVER=${{ secrets.DEV_REACT_APP_SERVER }}
export DEV_CI_DEPLOY_ACCESS_KEY_ID=${{ secrets.DEV_CI_DEPLOY_ACCESS_KEY_ID }}
export DEV_CI_DEPLOY_SECRET_ACCESS_KEY=${{ secrets.DEV_CI_DEPLOY_SECRET_ACCESS_KEY }}
export DEV_CI_DEPLOY_REGION=${{ secrets.DEV_CI_DEPLOY_REGION }}
export DEV_CI_DEPLOY_BUCKET=${{ secrets.DEV_CI_DEPLOY_BUCKET }}
export DEV_CI_CF_DISTRIBUTION=${{ secrets.DEV_CI_CF_DISTRIBUTION }}
./ci deploy && ./ci deploy_success_slack || { ./ci deploy_failure_slack; exit 1; }
- name: QA deploy
if: github.ref == 'refs/heads/qa'
run: |
export CI_COMMIT_REF_NAME=$(echo ${GITHUB_REF#refs/heads/} | sed -e 's/[^A-Za-z0-9._-]/_/g')
export CI_ENVIRONMENT_NAME=QA
export QA_REACT_APP_ENV_NAME=${{ secrets.QA_REACT_APP_ENV_NAME }}
export QA_REACT_APP_JS_KEY=${{ secrets.QA_REACT_APP_JS_KEY }}
export QA_REACT_APP_KEY=${{ secrets.QA_REACT_APP_KEY }}
export QA_REACT_APP_PARSE_SERVER=${{ secrets.QA_REACT_APP_PARSE_SERVER }}
export QA_REACT_APP_SERVER=${{ secrets.QA_REACT_APP_SERVER }}
export QA_CI_DEPLOY_ACCESS_KEY_ID=${{ secrets.QA_CI_DEPLOY_ACCESS_KEY_ID }}
export QA_CI_DEPLOY_SECRET_ACCESS_KEY=${{ secrets.QA_CI_DEPLOY_SECRET_ACCESS_KEY }}
export QA_CI_DEPLOY_REGION=${{ secrets.QA_CI_DEPLOY_REGION }}
export QA_CI_DEPLOY_BUCKET=${{ secrets.QA_CI_DEPLOY_BUCKET }}
export QA_CI_CF_DISTRIBUTION=${{ secrets.QA_CI_CF_DISTRIBUTION }}
./ci deploy && ./ci deploy_success_slack || { ./ci deploy_failure_slack; exit 1; }
- name: UAT deploy
if: github.ref == 'refs/heads/main'
run: |
export CI_COMMIT_REF_NAME=$(echo ${GITHUB_REF#refs/heads/} | sed -e 's/[^A-Za-z0-9._-]/_/g')
export CI_ENVIRONMENT_NAME=UAT
export UAT_REACT_APP_ENV_NAME=${{ secrets.UAT_REACT_APP_ENV_NAME }}
export UAT_REACT_APP_JS_KEY=${{ secrets.UAT_REACT_APP_JS_KEY }}
export UAT_REACT_APP_KEY=${{ secrets.UAT_REACT_APP_KEY }}
export UAT_REACT_APP_PARSE_SERVER=${{ secrets.UAT_REACT_APP_PARSE_SERVER }}
export UAT_REACT_APP_SERVER=${{ secrets.UAT_REACT_APP_SERVER }}
export UAT_CI_DEPLOY_ACCESS_KEY_ID=${{ secrets.UAT_CI_DEPLOY_ACCESS_KEY_ID }}
export UAT_CI_DEPLOY_SECRET_ACCESS_KEY=${{ secrets.UAT_CI_DEPLOY_SECRET_ACCESS_KEY }}
export UAT_CI_DEPLOY_REGION=${{ secrets.UAT_CI_DEPLOY_REGION }}
export UAT_CI_DEPLOY_BUCKET=${{ secrets.UAT_CI_DEPLOY_BUCKET }}
export UAT_CI_CF_DISTRIBUTION=${{ secrets.UAT_CI_CF_DISTRIBUTION }}
./ci deploy && ./ci deploy_success_slack || { ./ci deploy_failure_slack; exit 1; }
- name: LIVE deploy
if: github.ref == 'refs/heads/prod'
run: |
export CI_COMMIT_REF_NAME=$(echo ${GITHUB_REF#refs/heads/} | sed -e 's/[^A-Za-z0-9._-]/_/g')
export CI_ENVIRONMENT_NAME=LIVE
export LIVE_REACT_APP_ENV_NAME=${{ secrets.LIVE_REACT_APP_ENV_NAME }}
export LIVE_REACT_APP_KEY=${{ secrets.LIVE_REACT_APP_KEY }}
export LIVE_REACT_APP_PARSE_SERVER=${{ secrets.LIVE_REACT_APP_PARSE_SERVER }}
export LIVE_REACT_APP_SERVER=${{ secrets.LIVE_REACT_APP_SERVER }}
export LIVE_CI_DEPLOY_ACCESS_KEY_ID=${{ secrets.LIVE_CI_DEPLOY_ACCESS_KEY_ID }}
export LIVE_CI_DEPLOY_SECRET_ACCESS_KEY=${{ secrets.LIVE_CI_DEPLOY_SECRET_ACCESS_KEY }}
export LIVE_CI_DEPLOY_REGION=${{ secrets.LIVE_CI_DEPLOY_REGION }}
export LIVE_CI_DEPLOY_BUCKET=${{ secrets.LIVE_CI_DEPLOY_BUCKET }}
export LIVE_CI_CF_DISTRIBUTION=${{ secrets.LIVE_CI_CF_DISTRIBUTION }}
./ci deploy && ./ci deploy_success_slack || { ./ci deploy_failure_slack; exit 1; }
These are errors:
Done in 86.82s.
The user-provided path build does not exist.
fatal: unsafe repository ('/__w/halo-web/halo-web' is owned by someone else)
To add an exception for this directory, call:
git config --global --add safe.directory /__w/halo-web/halo-web
fatal: unsafe repository ('/__w/halo-web/halo-web' is owned by someone else)
To add an exception for this directory, call:
git config --global --add safe.directory /__w/halo-web/halo-web
Error: Process completed with exit code 3.
This is the thing which is very all ok but providing some type of errors and make some errors. Have any suggestions related to this..?
Related
I am using AWS lambda function with below code
'use strict';
constchildProcess= require("child_process");
constpath= require("path");
const backupDatabase = () => {
const scriptFilePath =path.resolve(__dirname, "./backup.sh");
return newPromise((resolve, reject) => {
childProcess.execFile(scriptFilePath, (error) => {
if (error) {
console.error(error);
resolve(false);
}
resolve(true);
});
});
};
module.exports.handler = async (event) => {
const isBackupSuccessful = await backupDatabase();
if (isBackupSuccessful) {
return {
status: "success",
message: "Database backup completed successfully!"
};
}
return {
status: "failed",
message: "Failed to backup the database! Check out the logs for more details"
};
};
The code above run's with in the docker container, tries to run the below backup script
#!/bin/bash
#
# Author: Bruno Coimbra <bbcoimbra#gmail.com>
#
# Backups database located in DB_HOST, DB_PORT, DB_NAME
# and can be accessed using DB_USER. Password should be
# located in $HOME/.pgpass and this file should be
# chmod 0600[1].
#
# Target bucket should be set in BACKUP_BUCKET variable.
#
# AWS credentials should be available as needed by aws-cli[2].
#
# Dependencies:
#
# * pg_dump executable (can be found in postgresql-client-<version> package)
# * aws-cli (with python environment configured execute 'pip install awscli')
#
#
# References
# [1] - http://www.postgresql.org/docs/9.3/static/libpq-pgpass.html
# [2] - http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html
#
#
###############
### Variables
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
DB_HOST=
DB_PORT="5432"
DB_USER="postgres"
BACKUP_BUCKET=
###############
#
# **RISK ZONE** DON'T TOUCH below this line unless you know
# exactly what you are doing.
#
###############
set -e
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
### Variables
S3_BACKUP_BUCKET=${BACKUP_BUCKET:-test-db-backup-bucket}
TEMPFILE_PREFIX="db-$DB_NAME-backup"
TEMPFILE="$(mktemp -t $TEMPFILE_PREFIX-XXXXXXXX)"
DATE="$(date +%Y-%m-%d)"
TIMESTAMP="$(date +%s)"
BACKUPFILE="backup-$DB_NAME-$TIMESTAMP.sql.gz"
LOGTAG="DB $DB_NAME Backup"
### Validations
if [[ ! -r "$HOME/.pgpass" ]]; then
logger -t "$LOGTAG" "$0: Can't find database credentials. $HOME/.pgpass file isn't readable. Aborted."
exit 1
fi
if ! which pg_dump > /dev/null; then
logger -t "$LOGTAG" "$0: Can't find 'pg_dump' executable. Aborted."
exit 1
fi
if ! which aws > /dev/null; then
logger -t "$LOGTAG" "$0: Can't find 'aws cli' executable. Aborted."
exit 1
fi
logger -t "$LOGTAG" "$0: remove any previous dirty backup file"
rm -f /tmp/$TEMPFILE_PREFIX*
### Generate dump and compress it
logger -t "$LOGTAG" "Dumping Database..."
pg_dump -O -x -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -w "$DB_NAME" > "$TEMPFILE"
logger -t "$LOGTAG" "Dumped."
logger -t "$LOGTAG" "Compressing file..."
nice gzip -9 "$TEMPFILE"
logger -t "$LOGTAG" "Compressed."
mv "$TEMPFILE.gz" "$BACKUPFILE"
### Upload it to S3 Bucket and cleanup
logger -t "$LOGTAG" "Uploading '$BACKUPFILE' to S3..."
aws s3 cp "$BACKUPFILE" "s3://$S3_BACKUP_BUCKET/$DATE/$BACKUPFILE"
logger -t "$LOGTAG" "Uploaded."
logger -t "$LOGTAG" "Clean-up..."
rm -f $TEMPFILE
rm -f $BACKUPFILE
rm -f /tmp/$TEMPFILE_PREFIX*
logger -t "$LOGTAG" "Finished."
if [ $? -eq 0 ]; then
echo "script passed"
exit 0
else
echo "script failed"
exit 1
fi
I created a docker image with above app.js content and bakup.sh with the below docker file
ARG FUNCTION_DIR="/function"
FROM node:14-buster
RUN apt-get update && \
apt install -y \
g++ \
make \
cmake \
autoconf \
libtool \
wget \
openssh-client \
gnupg2
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list && \
apt-get update && apt-get -y install postgresql-client-12
ARG FUNCTION_DIR
RUN mkdir -p ${FUNCTION_DIR} && chmod -R 755 ${FUNCTION_DIR}
WORKDIR ${FUNCTION_DIR}
COPY package.json .
RUN npm install
COPY backup.sh .
RUN chmod +x backup.sh
COPY app.js .
ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]
CMD ["app.handler"]
I am running the docker container created with the image created from the above docker file
docker run -v ~/aws:/aws -it --rm -p 9000:8080 --entrypoint /aws/aws-lambda-rie backup-db:v1 /usr/local/bin/npx aws-lambda-ric app.handler
And trying to hit that docker container with below curl command
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
when I run curl command I am seeing the below error
29 Nov 2021 10:57:30,838 [INFO] (rapid) extensionsDisabledByLayer(/opt/disable-extensions-jwigqn8j) -> stat /opt/disable-extensions-jwigqn8j: no such file or directory
29 Nov 2021 10:57:30,838 [WARNING] (rapid) Cannot list external agents error=open /opt/extensions: no such file or directory
START RequestId: 053246ef-4687-438d-aade-a6794b917b79 Version: $LATEST
2021-11-29T10:57:30.912Z undefined INFO Executing 'app.handler' in function directory '/function'
2021-11-29T10:57:30.919Z undefined ERROR constchildProcess is not defined
29 Nov 2021 10:57:30,926 [WARNING] (rapid) First fatal error stored in appctx: Runtime.ExitError
29 Nov 2021 10:57:30,927 [WARNING] (rapid) Process 53(npx) exited: Runtime exited with error: exit status 1
29 Nov 2021 10:57:30,927 [ERROR] (rapid) Init failed error=Runtime exited with error: exit status 1 InvokeID=
29 Nov 2021 10:57:30,927 [WARNING] (rapid) Reset initiated: ReserveFail
29 Nov 2021 10:57:30,927 [WARNING] (rapid) Cannot list external agents error=open /opt/extensions: no such file or directory
Could someone help me with fixing the error ? My expected output is the message as described in the function, but am seeing the errors.
Thank you
Because they both do not exist. There is a typo on your first 2 lines:
constchildProcess= require("child_process");
constpath= require("path");
Should be:
const childProcess= require("child_process");
const path= require("path");
I am using AWS lambda function with below code
'use strict';
var newPromise = require('es6-promise').Promise;
const childProcess= require("child_process");
const path= require("path");
const backupDatabase = () => {
const scriptFilePath =path.resolve(__dirname, "./backup.sh");
return newPromise((resolve, reject) => {
childProcess.execFile(scriptFilePath, (error) => {
if (error) {
console.error(error);
resolve(false);
}
resolve(true);
});
});
};
module.exports.handler = async (event) => {
const isBackupSuccessful = await backupDatabase();
if (isBackupSuccessful) {
return {
status: "success",
message: "Database backup completed successfully!"
};
}
return {
status: "failed",
message: "Failed to backup the database! Check out the logs for more details"
};
};
The code above run's with in the docker container, tries to run the below backup script
#!/bin/bash
#
# Author: Bruno Coimbra <bbcoimbra#gmail.com>
#
# Backups database located in DB_HOST, DB_PORT, DB_NAME
# and can be accessed using DB_USER. Password should be
# located in $HOME/.pgpass and this file should be
# chmod 0600[1].
#
# Target bucket should be set in BACKUP_BUCKET variable.
#
# AWS credentials should be available as needed by aws-cli[2].
#
# Dependencies:
#
# * pg_dump executable (can be found in postgresql-client-<version> package)
# * aws-cli (with python environment configured execute 'pip install awscli')
#
#
# References
# [1] - http://www.postgresql.org/docs/9.3/static/libpq-pgpass.html
# [2] - http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html
#
#
###############
### Variables
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
DB_HOST=
DB_PORT="5432"
DB_USER="postgres"
BACKUP_BUCKET=
###############
#
# **RISK ZONE** DON'T TOUCH below this line unless you know
# exactly what you are doing.
#
###############
set -e
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
### Variables
S3_BACKUP_BUCKET=${BACKUP_BUCKET:-test-db-backup-bucket}
TEMPFILE_PREFIX="db-$DB_NAME-backup"
TEMPFILE="$(mktemp -t $TEMPFILE_PREFIX-XXXXXXXX)"
DATE="$(date +%Y-%m-%d)"
TIMESTAMP="$(date +%s)"
BACKUPFILE="backup-$DB_NAME-$TIMESTAMP.sql.gz"
LOGTAG="DB $DB_NAME Backup"
### Validations
if [[ ! -r "$HOME/.pgpass" ]]; then
logger -t "$LOGTAG" "$0: Can't find database credentials. $HOME/.pgpass file isn't readable. Aborted."
exit 1
fi
if ! which pg_dump > /dev/null; then
logger -t "$LOGTAG" "$0: Can't find 'pg_dump' executable. Aborted."
exit 1
fi
if ! which aws > /dev/null; then
logger -t "$LOGTAG" "$0: Can't find 'aws cli' executable. Aborted."
exit 1
fi
logger -t "$LOGTAG" "$0: remove any previous dirty backup file"
rm -f /tmp/$TEMPFILE_PREFIX*
### Generate dump and compress it
logger -t "$LOGTAG" "Dumping Database..."
pg_dump -O -x -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -w "$DB_NAME" > "$TEMPFILE"
logger -t "$LOGTAG" "Dumped."
logger -t "$LOGTAG" "Compressing file..."
nice gzip -9 "$TEMPFILE"
logger -t "$LOGTAG" "Compressed."
mv "$TEMPFILE.gz" "$BACKUPFILE"
### Upload it to S3 Bucket and cleanup
logger -t "$LOGTAG" "Uploading '$BACKUPFILE' to S3..."
aws s3 cp "$BACKUPFILE" "s3://$S3_BACKUP_BUCKET/$DATE/$BACKUPFILE"
logger -t "$LOGTAG" "Uploaded."
logger -t "$LOGTAG" "Clean-up..."
rm -f $TEMPFILE
rm -f $BACKUPFILE
rm -f /tmp/$TEMPFILE_PREFIX*
logger -t "$LOGTAG" "Finished."
if [ $? -eq 0 ]; then
echo "script passed"
exit 0
else
echo "script failed"
exit 1
fi
I created a docker image with above app.js content and bakup.sh with the below docker file
ARG FUNCTION_DIR="/function"
FROM node:14-buster
RUN apt-get update && \
apt install -y \
g++ \
make \
cmake \
autoconf \
libtool \
wget \
openssh-client \
gnupg2
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - && \
echo "deb http://apt.postgresql.org/pub/repos/apt/ buster-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list && \
apt-get update && apt-get -y install postgresql-client-12
ARG FUNCTION_DIR
RUN mkdir -p ${FUNCTION_DIR} && chmod -R 755 ${FUNCTION_DIR}
WORKDIR ${FUNCTION_DIR}
COPY package.json .
RUN npm install
COPY backup.sh .
RUN chmod +x backup.sh
COPY app.js .
ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]
CMD ["app.handler"]
I am running the docker container created with the image created from the above docker file
docker run -v ~/aws:/aws -it --rm -p 9000:8080 --entrypoint /aws/aws-lambda-rie backup-db:v1 /usr/local/bin/npx aws-lambda-ric app.handler
And trying to hit that docker container with below curl command
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'
when I run curl command I am seeing the below error
An error I see is :"newPromise is not defined","trace":["ReferenceError: newPromise is not defined"," at backupDatabase (/function/app.js:9:3)","
Tried adding the variable var newPromise = require('es6-promise').Promise;, but that gave a new error "Cannot set property 'scqfkjngu7o' of undefined","trace"
Could someone help me with fixing the error ? My expected output is the message as described in the function, but am seeing the errors.
Thank you
Node 14 supports promises natively. You should do:
return new Promise((resolve, reject) => {
childProcess.execFile(scriptFilePath, (error) => {
if (error) {
console.error(error);
resolve(false);
}
resolve(true);
});
Note the space between new and Promise. Promise is the object and you are using a constructor. There is no need to import any module.
My pipeline builds, plans and applies just fine for my dev branch. When I push to my master branch, I get "Error refreshing state: HTTP remote state endpoint requires auth". See pipeline logs: (and since someone will ask, the token "$onbaord_cloud_account_into_monitoring" has complete read/write access to the scoped project API.)...
Running with gitlab-runner 13.4.1 (REDACTED)
on runner-gitlab-runner-REDACTED-REDACTED REDACTED
Resolving secrets
00:00
Preparing the "kubernetes" executor
00:00
Using Kubernetes namespace: gitlab-managed-apps
Using Kubernetes executor with image my-gitlab.io:5005/team-cloud-platform-team/terraform-modules/root-module-deployment/python-terraform14 ...
Preparing environment
00:03
Waiting for pod gitlab-managed-apps/runner-REDACTED-project-REDACTED-concurrent-REDACTED to be running, status is Pending
Running on runner-REDACTED-project-REDACTED-concurrent-REDACTED via runner-gitlab-runner-REDACTED-REDACTED...
Getting source from Git repository
00:02
Fetching changes with git depth set to 50...
Initialized empty Git repository in /builds/team-cloud-platform-team/teamcloudv2/workers/onboard-cloud-account-into-monitoring/.git/
Created fresh repository.
Checking out REDACTED as master...
Skipping Git submodules setup
Restoring cache
00:00
Checking cache for REDACTEDREDACTEDREDACTEDREDACTED...
No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted.
Successfully extracted cache
Executing "step_script" stage of the job script
00:05
$ git config --global credential.helper cache
$ git fetch
From https://my-gitlab.io/team-cloud-platform-team/teamcloudv2/workers/onboard-cloud-account-into-monitoring
* [new branch] dev -> origin/dev
$ if [ "$CI_COMMIT_REF_NAME" == "master" ]; then TF_ACCOUNT="REDACTED";fi
$ if [ "$CI_COMMIT_REF_NAME" == "dev" ]; then TF_ACCOUNT="REDACTED";fi
$ if [ "$CI_COMMIT_REF_NAME" == "master" ]; then ORG_ACCOUNT="REDACTED";fi
$ if [ "$CI_COMMIT_REF_NAME" == "dev" ]; then ORG_ACCOUNT="REDACTED";fi
$ echo ${TF_ACCOUNT}
REDACTED
$ echo ${TF_ADDRESS}
https://my-gitlab.io/api/v4/projects/10849/terraform/state/onboard-cloud-account-into-monitoring-master
$ echo TF_HTTP_ADDRESS=${TF_ADDRESS}
TF_HTTP_ADDRESS=https://my-gitlab.io/api/v4/projects/10849/terraform/state/onboard-cloud-account-into-monitoring-master
$ echo TF_HTTP_LOCK_ADDRESS=${TF_LOCK}
TF_HTTP_LOCK_ADDRESS=https://my-gitlab.io/api/v4/projects/10849/terraform/state/onboard-cloud-account-into-monitoring-master/lock
$ echo TF_HTTP_UNLOCK_ADDRESS=${TF_UNLOCK}
TF_HTTP_UNLOCK_ADDRESS=https://my-gitlab.io/api/v4/projects/10849/terraform/state/onboard-cloud-account-into-monitoring-master/lock
$ echo TF_ADDRESS=${TF_ADDRESS}
TF_ADDRESS=https://my-gitlab.io/api/v4/projects/10849/terraform/state/onboard-cloud-account-into-monitoring-master
$ export TF_HTTP_ADDRESS=${TF_ADDRESS}
$ export TF_HTTP_LOCK_ADDRESS=${TF_LOCK}
$ export TF_HTTP_UNLOCK_ADDRESS=${TF_UNLOCK}
$ export TF_HTTP_LOCK_METHOD="POST"
$ export TF_HTTP_UNLOCK_METHOD="DELETE"
$ export TF_HTTP_RETRY_WAIT_MIN='5'
$ export TF_HTTP_USERNAME='JOHN.DOE'
$ export TF_HTTP_PASSWORD=${onbaord_cloud_account_into_monitoring}
$ export TF_VAR_ACCOUNT=${TF_ACCOUNT}
$ export TF_VAR_ORG_ACCOUNT=${ORG_ACCOUNT}
$ export AWS_DEFAULT_REGION='us-east-1'
$ terraform init
Initializing modules...
Downloading git::https://my-gitlab.io/team-cloud-platform-team/teamcloudv2/terraform/lambda-modules.git?ref=dev for lambda...
- lambda in .terraform/modules/lambda
Downloading git::https://my-gitlab.io/team-cloud-platform-team/teamcloudv2/terraform/iam-modules/lambda-iam.git?ref=dev for lambda_iam...
- lambda_iam in .terraform/modules/lambda_iam
Initializing the backend...
Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
Error refreshing state: HTTP remote state endpoint requires auth
Cleaning up file based variables
00:00
ERROR: Job failed: command terminated with exit code 1
A peek at my gitlab-ci.yml:
image:
name: my-gitlab.io:5005/team-cloud-platform-team/terraform-modules/root-module-deployment/python-terraform14
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
stages:
- build
- plan
- apply
variables:
PLAN: plan.tfplan
JSON_PLAN_FILE: tfplan.json
WORKSPACE: "dev"
TF_IN_AUTOMATION: "true"
ZIP_FILE: "lambda_package.zip"
STATE_FILE: ${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}
TF_ACCOUNT: ""
ORG_ACCOUNT: ""
TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}
TF_LOCK: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}/lock
TF_UNLOCK: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}-${CI_COMMIT_BRANCH}/lock
cache:
key: "$CI_COMMIT_SHA"
paths:
- .terraform
before_script:
- git config --global credential.helper cache
- git fetch
- if [ "$CI_COMMIT_REF_NAME" == "master" ]; then TF_ACCOUNT="REDACTED";fi
- if [ "$CI_COMMIT_REF_NAME" == "dev" ]; then TF_ACCOUNT="REDACTED";fi
- if [ "$CI_COMMIT_REF_NAME" == "master" ]; then ORG_ACCOUNT="REDACTED";fi
- if [ "$CI_COMMIT_REF_NAME" == "dev" ]; then ORG_ACCOUNT="REDACTED";fi
- echo ${TF_ACCOUNT}
- echo ${TF_ADDRESS}
- echo TF_HTTP_ADDRESS=${TF_ADDRESS}
- echo TF_HTTP_LOCK_ADDRESS=${TF_LOCK}
- echo TF_HTTP_UNLOCK_ADDRESS=${TF_UNLOCK}
- echo TF_ADDRESS=${TF_ADDRESS}
- export TF_HTTP_ADDRESS=${TF_ADDRESS}
- export TF_HTTP_LOCK_ADDRESS=${TF_LOCK}
- export TF_HTTP_UNLOCK_ADDRESS=${TF_UNLOCK}
- export TF_HTTP_LOCK_METHOD="POST"
- export TF_HTTP_UNLOCK_METHOD="DELETE"
- export TF_HTTP_RETRY_WAIT_MIN='5'
- export TF_HTTP_USERNAME='MATTHEW.FETHEROLF'
- export TF_HTTP_PASSWORD=${onbaord_cloud_account_into_monitoring}
- export TF_VAR_ACCOUNT=${TF_ACCOUNT}
- export TF_VAR_ORG_ACCOUNT=${ORG_ACCOUNT}
- export AWS_DEFAULT_REGION='us-east-1'
- terraform init
# -----BUILD-----
# 1 build job per lambda function
buildLambda:
stage: build
tags:
- cluster
script:
- echo "Beginning Build"
- cd lambda_code/
- echo "Switched into Lambda dir"
- pip3 install -r requirements.txt --target .
- echo "Requirements installed"
- echo $ZIP_FILE
- zip -r $ZIP_FILE *
- echo "Zip file packaged up"
artifacts:
paths:
- lambda_code/
only:
- dev
- master
- merge_requests
# -----PLAN-----
planMerge:
stage: plan
tags:
- cluster
script:
- terraform plan
dependencies:
- buildLambda
only:
- merge_requests
planCommit:
stage: plan
tags:
- cluster
script:
- terraform plan
dependencies:
- buildLambda
# -----APPLY-----
# dev branch will auto deploy
applyDev:
stage: apply
tags:
- cluster
script:
- echo "Running Terraform Apply"
- terraform apply -auto-approve
dependencies:
- buildLambda
only:
- dev
#when: manual
# prod branch will require manual deployment approval
applyProd:
stage: apply
tags:
- cluster
script:
- echo "Running Terraform Apply"
- terraform apply -auto-approve
when: manual
dependencies:
- buildLambda
only:
- master
backend.tf:
terraform {
backend "http" {
}
}
main.tf:
locals {
common_tags = {
SVC_ACCOUNT_ID = "REDACTED",
CLOUD_PLATFORM_PROD_ID = "REDACTED"
}
}
module "lambda" {
source = "git::https://my-gitlab.io/team-cloud-platform-team/teamcloudv2/terraform/lambda-modules.git?ref=dev"
lambda_name = var.name
lambda_role = "arn:aws:iam::${var.ACCOUNT}:role/${var.lambda_role}"
lambda_handler = var.handler
lambda_runtime = var.runtime
default_lambda_timeout = var.timeout
env = local.common_tags
ACCOUNT = var.ACCOUNT
}
module "lambda_iam" {
source = "git::https://my-gitlab.io/team-cloud-platform-team/teamcloudv2/terraform/iam-modules/lambda-iam.git?ref=dev"
lambda_policy = var.lambda_policy
ACCOUNT = var.ACCOUNT
lambda_role = var.lambda_role
}
inputs.tf:
variable "handler" {
type = string
default = "handler.lambda_handler"
}
variable "runtime" {
type = string
default = "python3.8"
}
variable "name" {
type = string
default = "onboard-cloud-account-into-monitoring"
}
variable "timeout"{
type = string
default = "120"
}
variable "lambda_role" {
type = string
default = "cloud-platform-onboard-to-monitoring"
}
variable "ACCOUNT" {
type = string
}
variable "ORG_ACCOUNT" {
type = string
}
variable "lambda_policy" {
default = "{\"Version\": \"2012-10-17\",\"Statement\": [{\"Sid\": \"VisualEditor0\",\"Effect\": \"Allow\",\"Action\": [\"logs:CreateLogStream\",\"logs:CreateLogGroup\"],\"Resource\": \"*\"},{\"Sid\": \"VisualEditor1\",\"Effect\": \"Allow\",\"Action\": \"logs:PutLogEvents\",\"Resource\": \"*\"},{\"Sid\": \"VisualEditor2\",\"Effect\": \"Allow\",\"Action\": \"sts:AssumeRole\",\"Resource\": \"*\"},{\"Sid\": \"VisualEditor3\",\"Effect\": \"Allow\",\"Action\": \"secretsmanager:GetSecretValue\",\"Resource\": \"*\"}]}"
}
provider.tf:
provider "aws" {
region = "us-east-1"
assume_role {
role_arn ="arn:aws:iam::${var.ACCOUNT}:role/team-platform-onboard-to-monitoring"
}
default_tags {
tags = {
owner = "REDACTED#REDACTED.com"
altowner = "REDACTED#REDACTED.com"
blc = "REDACTED"
costcenter = "REDACTED"
itemid = "PlatformAutomation"
}
}
}
Perhaps I also need to share the terraform modules being invoked?
We found the problem:
The pipeline referenced a protected environment variable. The master branch was not a protected branch. The solution was to unprotect the environment variable or protect the branch. Instantly solved the problem.
I am using latest Jenkins in my Linux box.
I would like to implement a Test stage like below:
pipeline {
agent any
stages {
stage('Test') {
HTTP_CODE = sh (
script: 'echo $(curl --write-out \\"%{http_code}\\" --silent --output /dev/null http://localhost/)',
returnStdout: true
).trim()
}
}
}
In this stage, I want to execute a bash script to check whether the web server is up or not. HTTP_CODE variable will have the value 200 if everything is fine, and if there is any other value, then it can be treated as error.
How can I implement this logic as a testing stage in my Jenkins pipeline?
Thanks.
You should update your pipeline as follow:
pipeline {
agent any
stages {
stage('Test') {
HTTP_CODE = sh (
script: 'echo $(curl --write-out \\"%{http_code}\\" --silent --output /dev/null http://localhost/)',
returnStdout: true
).trim()
if ('200' != HTTP_CODE) {
currentBuild.result = "FAILURE"
error('Test stage failed!)
}
}
}
}
Regards.
Hoping someone can help. I am experiencing difficulty making a curl POST request with JSON data from a gitlab CI job.
The curl request works fine in a local terminal session, (N.B I did not use double quotes in terminal session). If I do not escape double quotes in the gitlab CI yaml I get the error curl: (3) [globbing] nested brace in column 112
If I escape the double quotes in the GitLab CI job, as shown below I get the error:
curl: (3) [globbing] unmatched brace in column 1
In all cases I get the error /bin/bash: line 134: warning: here-document at line 134 delimited by end-of-file (wanted `EOF')
Is it possible to POST JSON data using here-documents from a GitLab CI job?
.gitlab-ci.yml job extract
release:
image: node:12-stretch-slim
stage: release
before_script:
- apt-get update && apt-get install -y curl git jq
script:
- version=$(git describe --tags | cut -d'-' -f 1 | sed 's/^v*//')
- echo "generating release for version ${version}"
- npm pack
# - >
# url=$(curl
# --header "Private-Token: $API_TOKEN"
# -F "file=#${CI_PROJECT_DIR}/objectdetection-plugin-${version}.tgz" "https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/uploads"
# |
# jq '.url')
- url="http://www.example.com"
- echo "Retrieved url from file upload as ${url}"
- echo "The full url would be ${CI_PROJECT_URL}/${url}"
- >
curl -X POST https://requestbin.io/1f84by61
--header 'Content-Type: application/json; charset=utf-8'
--data-binary #- << EOF
{
\"name\": \"Release v${version}\",
\"tag_name\": \"v${version}\",
\"ref\": \"v${version}\",
\"description\": \"Test\",
\"assets\": {
\"links\": [
{
\"name\": \"objectdetection-plugin-source\",
\"url\": \"CI_PROJECT_URL/${url}\",
\"filepath\": \"${url}\",
\"link_type\": \"other\"
}
]
}
}
EOF
when: manual
only:
- /^release-.*$/
Solved it after reading this
Using |- preserves newlines within the command and does not append a newline at the end of the command string. Used this principle to save the JSON data to a variable and then referenced the variable in the subsequent curl command.
Below I have included the script:
release:
image: node:12-stretch-slim
stage: release
before_script:
- apt-get update && apt-get install -y curl git jq
script:
- git fetch --prune --unshallow
- version=$(git describe --tags | cut -d'-' -f 1 | sed 's/^v*//')
- npm pack
- >
url=$(curl --silent --show-error
--request POST
--header "Private-Token: $API_TOKEN"
-F "file=#${CI_PROJECT_DIR}/objectdetection-plugin-${version}.tgz" "https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/uploads"
|
jq --raw-output --monochrome-output '.url')
- |-
PAYLOAD=$(cat << JSON
{
"name": "Release v$version",
"tag_name": "v$version",
"ref": "v$version",
"description": "$(sed -zE 's/\r\n|\n/\\n/g' < CHANGELOG.md)",
"assets": {
"links": [
{
"name": "objectdetection-plugin-source",
"url": "$CI_PROJECT_URL$url",
"filepath": "$url",
"link_type": "other"
}
]
}
}
JSON
)
- echo "$PAYLOAD"
- >
http_response=$(curl --silent --show-error --write-out "%{http_code}" -o response.txt
--request POST "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/releases"
--header 'Content-Type: application/json'
--header 'Accept: application/json'
--header "Private-Token: ${API_TOKEN}"
--data-binary "${PAYLOAD}")
- |-
if [ $http_response != "201" ]; then
exit 1
else
echo "Server returned:"
cat response.txt
fi
when: manual
allow_failure: false
only:
- /^release-.*$/
I was trying to send a message to my Google Chat, WebHook URL, and the above solution didn't work.
So I used this instead
'curl --silent --show-error --location --request POST ${GCHAT_DEVOPS_WEBHOOK} --header ''Content-Type: application/json; charset=UTF-8'' --data-raw ''{"text":"Hey <users/all>: A new build has been deployed to *''${APP_ENVIRONMENT}''* via ''${CI_JOB_NAME}'' - *''${APP_BRANCH}''*"}'''