Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial migration of OIL tests to use deployer-platform #1001

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 257 additions & 0 deletions .github/workflows/nonregression-dp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
name: Non Regression Testing (dp)

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 Test Dependencies
working-directory: .github/workflows/scripts/dp
run: npm install

- name: Run Test
id: runDeployerPlatformTest
working-directory: .github/workflows/scripts/dp
run: |
node main.js
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
SQS_URL: ${{ secrets.SQS_URL }}
DYNAMO_TABLE: ${{ secrets.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/dp/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const fs = require('fs')
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++) {

try {
const response = await dynamodb.send(queryForDeploymentStatus(deploymentId))
for (let i = 0; i < response.Items.length; i++) {
const item = unmarshall(response.Items[i])
if (item.completed) {
if (item.status === 'FAILED') {
console.error(`::error:: Deployment failed: ${item.message}`)
return false
}

return true
}
}

console.log(`Deployment pending, sleeping ${waitSeconds} seconds...`)
await sleep(waitSeconds * 1000)

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

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

async function main() {
let messageId
let configJson = fs.readFileSync(`${process.env.GITHUB_WORKSPACE}/${JSON.parse(process.env.MATRIX).testDefinitionFile}`)
try {
const command = new SendMessageCommand({
QueueUrl: SQS_URL,
MessageBody: configJson,
})
data = await sqs.send(command);
messageId = data.MessageId
console.log(`Message sent: ${messageId}`)
} catch (err) {
console.log(err.message)
throw new Error('Failed sending message to SQS queue');
}

// Initial sleep since fargate takes time to spin up deployer
await sleep(120 * 1000)

// Execute the query with retries/sleeps
let RETRIES = 50, WAIT_SECONDS = 30
const success = await isDeploymentSuccessful(messageId, RETRIES, WAIT_SECONDS)
if (!success) {
throw new Error(`Deployment failed for ${messageId} after ${RETRIES} retries`);
}

console.log(`Successfully install New Relic for instanceId ${messageId}!`)
}

if (require.main === module) {
main().then(() => {
console.log('::set-output name=exit_status::0')
}).catch((err) => {
console.error(`::error::${err}`)
console.error('::set-output name=exit_status::1')
})
}
Loading
Loading