Skip to content

Commit

Permalink
feat(regression-us): added manually dispatched GHA for deployer-platf…
Browse files Browse the repository at this point in the history
…orm US regression tests
  • Loading branch information
mbruzina committed Mar 27, 2024
1 parent b8508d3 commit 7cf994e
Show file tree
Hide file tree
Showing 3 changed files with 390 additions and 0 deletions.
261 changes: 261 additions & 0 deletions .github/workflows/nonregression-dp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
name: Non Regression Testing (US) (inactive)

on:
workflow_dispatch:
# schedule:
# - cron: "0 9 * * 1-5"
# push:
# branches: [ main ]

jobs:
log-context:
runs-on: ubuntu-latest
steps:
# Dump all contexts
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
- name: Dump steps context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: echo "$STEPS_CONTEXT"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Dump strategy context
env:
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
run: echo "$STRATEGY_CONTEXT"
- name: Dump matrix context
env:
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"
get-test-definition-files:
name: Get Test Definition Files
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.get-test-definition-files.outputs.result }}
steps:
- name: Checkout Repo
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get Test Definition Files
id: get-test-definition-files
uses: actions/github-script@v3
with:
script: |
const fs = require("fs");
const fsp = fs.promises;
const path = require("path");
const { isOHIValidationTimeout } = require("${{ github.workspace }}/.github/workflows/scripts/ohiValidationTimeout");
// readdir recursive directory search
const { readdir } = fsp;
async function getFiles(dir) {
const dirents = await readdir(dir, { withFileTypes: true });
const files = await Promise.all(
dirents.map((dirent) => {
const res = path.join(dir, dirent.name);
return dirent.isDirectory() ? getFiles(res) : res;
})
);
return Array.prototype.concat(...files);
}
const definitionsDir = "test/definitions";
const testDefinitions = await getFiles(definitionsDir);
const outputTestFilesMap = testDefinitions
.filter((testDefinitionFile) => !isOHIValidationTimeout(testDefinitionFile))
.map((testDefinitionFile) => {
return {
testDefinitionFile,
testDisplayName: testDefinitionFile.replace(`${definitionsDir}/`, ""),
};
});
const output = {
include: outputTestFilesMap,
};
console.log(output);
return output;
test-deploy-recipe:
name: ${{ matrix.testDisplayName }}
needs: [get-test-definition-files]
if: ${{ fromJSON(needs.get-test-definition-files.outputs.matrix).include[0] }} # Avoids empty matrix validation error
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.get-test-definition-files.outputs.matrix) }}
fail-fast: false
env:
MATRIX: ${{ toJSON(matrix) }}
steps:
- name: Checkout Repo
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Update Test Definition Files URLs
id: get-test-definition-files
env:
TEST_DEFINITION_FILE: ${{ matrix.testDefinitionFile }}
uses: actions/github-script@v3
with:
script: |
const fs = require('fs');
const fsp = fs.promises;
const path = require('path');
// before returning, we need to edit the deploy config files in-place so they
// use the right URLs from the branch
async function getDeployConfigFile(file, outputDir) {
const data = await fsp.readFile(path.join(outputDir, file));
return JSON.parse(data);
}
// Get testDefinitonFile from MATRIX env var
const testDefinitionFile = process.env.TEST_DEFINITION_FILE;
console.log(`Detected Deploy Config: ${JSON.stringify(testDefinitionFile, null, 2)}`)
// Update URLs to use branch this PR is opened with
const data = await getDeployConfigFile(testDefinitionFile, process.env.GITHUB_WORKSPACE);
// Update github source URLs with branch name
let jsonContent = JSON.stringify(data, null, 2);
const branchName = process.env.GITHUB_HEAD_REF ? process.env.GITHUB_HEAD_REF : process.env.GITHUB_REF_NAME;
const replacementString = `$1$2-b ${branchName} $3$4`;
const sourceRepositoryRegex = /(.*)(\")(https:\/\/github.com\/newrelic\/open-install-library)(.*)/gi;
jsonContent = jsonContent.replace(sourceRepositoryRegex, replacementString);
console.log(`Detected Deploy Config: ${JSON.stringify(jsonContent, null, 2)}`)
// Update raw URLs with branch name
const replacementString2 = `$1${branchName}$3`;
const sourceRepositoryRegex2 = /(raw.githubusercontent.com\/newrelic\/open-install-library\/)(main)(\/newrelic\/recipes\/)*/gi;
jsonContent = jsonContent.replace(sourceRepositoryRegex2, replacementString2);
console.log(`Detected Deploy Config: ${JSON.stringify(jsonContent, null, 2)}`)
// Write file back to workspace
const outputPath = `${process.env.GITHUB_WORKSPACE}/${testDefinitionFile}`;
fs.writeFileSync(outputPath, jsonContent);
return testDefinitionFile;
- name: Install npm dependencies for deployer test runner
working-directory: .github/workflows/scripts/deployer-platform
run: npm install

- name: Execute test
id: runDeployerPlatformTest
working-directory: .github/workflows/scripts/deployer-platform
run: |
node main.js
env:
TEST_DEFINITION_FILE: ${{ matrix.testDefinitionFile }}
AWS_ACCESS_KEY_ID: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_REGION }}
SQS_URL: ${{ secrets.DEPLOYER_PLATFORM_US_SQS_URL }}
DYNAMO_TABLE: ${{ secrets.DEPLOYER_PLATFORM_US_DYNAMO_TABLE }}

- name: Report any error
if: steps.runDeployerPlatformTest.outputs.exit_status != 0
run: exit 1

slack-notify:
runs-on: ubuntu-latest
needs: [test-deploy-recipe]
if: always()
steps:
- name: Build Result Slack Notification
uses: 8398a7/action-slack@v3
with:
author_name: GitHub Actions
status: custom
fields: commit,repo,ref,author,eventName,message,workflow
custom_payload: |
{
username: "GitHub Actions",
icon_emoji: ":octocat:",
attachments: [{
color: ${{
needs.test-deploy-recipe.result == 'success'
}} === true ? '#43cc11' : '#e05d44',
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Build for ${process.env.AS_REPO}`
}
},
{
type: "section",
fields: [
{
type: "mrkdwn",
text: `*Commit:*\n${process.env.AS_COMMIT}`
},
{
type: "mrkdwn",
text: `*Author:*\n${process.env.AS_AUTHOR}`
},
{
type: "mrkdwn",
text: `*Branch:*\n${process.env.AS_REF}`
},
{
type: "mrkdwn",
text: `*Message:*\n${process.env.AS_MESSAGE}`
},
{
type: "mrkdwn",
text: `*Type:*\n${process.env.AS_EVENT_NAME}`
},
{
type: "mrkdwn",
text: "*PR:*\n${{ github.event.pull_request.html_url }}"
},
{
type: "mrkdwn",
text: `*Workflow:*\n${ process.env.AS_WORKFLOW }`
}
]
},
{
type: "section",
text: {
type: "mrkdwn",
text: [
"*Result:*",
`• ${ ${{ needs.test-deploy-recipe.result == 'success' }} === true ? '✅' : '❌' } Non-regression testing of all recipes: ${{ needs.test-deploy-recipe.result }}`
].join('\n')
}
},
{
type: "context",
elements: [
{
type: "image",
image_url: "https://avatars2.githubusercontent.com/in/15368",
alt_text: "Github Actions"
},
{
type: "mrkdwn",
text: "This message was created automatically by GitHub Actions."
}
]
}
]
}]
}
env:
GITHUB_TOKEN: ${{ github.token }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
110 changes: 110 additions & 0 deletions .github/workflows/scripts/deployer-platform/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const fs = require('fs');
const fsp = fs.promises;
const {SQSClient, SendMessageCommand} = require('@aws-sdk/client-sqs')
const {DynamoDBClient, QueryCommand} = require('@aws-sdk/client-dynamodb')
const {unmarshall} = require('@aws-sdk/util-dynamodb')

const AWS_REGION = process.env.AWS_REGION
const SQS_URL = process.env.SQS_URL
const DYNAMO_TABLE = process.env.DYNAMO_TABLE
const AWS_CREDS = {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
};

const sqs = new SQSClient({region: AWS_REGION, credentials: AWS_CREDS})
const dynamodb = new DynamoDBClient({region: AWS_REGION, credentials: AWS_CREDS})


function queryForDeploymentStatus(messageId) {
const query_params = {
TableName: DYNAMO_TABLE,
KeyConditionExpression: 'id = :id',
FilterExpression: 'completed = :completed',
ExpressionAttributeNames: {
'#id': 'id',
'#completed': 'completed',
'#status': 'status',
'#message': 'message',
},
ExpressionAttributeValues: {
':id': {
S: messageId,
},
':completed': {
BOOL: true,
},
},
ProjectionExpression: '#id, #completed, #status, #message',
ScanIndexForward: false, //returns items by descending timestamp
}
return new QueryCommand(query_params)
}

async function isDeploymentSuccessful(deploymentId, retries, waitSeconds) {
for (let i = 0; i < retries; i++) {
console.log(`Deployment pending, sleeping ${waitSeconds} seconds...`)
await sleep(waitSeconds * 1000)

try {
const response = await dynamodb.send(queryForDeploymentStatus(deploymentId))
console.log(`Query succeeded. Items found: ${response.Items.length}`)

for (let i = 0; i < response.Items.length; i++) {
const item = unmarshall(response.Items[i])
if (item.completed) {
console.log(`Completed: ${item.id} - ${item.message} - ${item.completed} - ${item.status}`)
if (item.status === 'FAILED') {
console.error(`::error:: Deployment failed: ${item.message}`)
return false
}

return true
}
}
} catch (err) {
console.log(`Error querying table: ${err}`)
}
}
return false
}

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}

async function getDeployConfigFile(file) {
const data = await fsp.readFile(file);
return JSON.parse(data);
}

function main() {
getDeployConfigFile(`${process.env.GITHUB_WORKSPACE}/${process.env.TEST_DEFINITION_FILE}`)
.then(async (json) => {
let messageId
try {
const command = new SendMessageCommand({
QueueUrl: SQS_URL,
MessageBody: JSON.stringify(json),
})
data = await sqs.send(command)
messageId = data.MessageId
console.log(`Message sent: ${messageId}`)
} catch (err) {
console.error(`Error sending message: ${err}`)
}

// Execute the query with retries/sleeps
let RETRIES = 200, WAIT_SECONDS = 15
const success = await isDeploymentSuccessful(messageId, RETRIES, WAIT_SECONDS)
if (!success) {
process.exit(1)
}
}).catch((error) =>
console.log(`Error reading deploy config ${process.env.TEST_DEFINITION_FILE}: ${error}`)
)
}

if (require.main === module) {
main()
}
19 changes: 19 additions & 0 deletions .github/workflows/scripts/deployer-platform/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "dp",
"version": "1.0.0",
"description": "Runs OIL tests using deployer-platform to provision target hosts",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"@actions/core": "1.2.6",
"@aws-sdk/client-sqs": "3.27.0",
"@aws-sdk/client-dynamodb": "3.27.0",
"@aws-sdk/util-dynamodb": "3.27.0",
"@aws-sdk/credential-providers": "3.451.0"
},
"keywords": [],
"author": "",
"license": "ISC"
}

0 comments on commit 7cf994e

Please sign in to comment.