Skip to content

Commit 7cf994e

Browse files
committed
feat(regression-us): added manually dispatched GHA for deployer-platform US regression tests
1 parent b8508d3 commit 7cf994e

File tree

3 files changed

+390
-0
lines changed

3 files changed

+390
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
name: Non Regression Testing (US) (inactive)
2+
3+
on:
4+
workflow_dispatch:
5+
# schedule:
6+
# - cron: "0 9 * * 1-5"
7+
# push:
8+
# branches: [ main ]
9+
10+
jobs:
11+
log-context:
12+
runs-on: ubuntu-latest
13+
steps:
14+
# Dump all contexts
15+
- name: Dump GitHub context
16+
env:
17+
GITHUB_CONTEXT: ${{ toJson(github) }}
18+
run: echo "$GITHUB_CONTEXT"
19+
- name: Dump job context
20+
env:
21+
JOB_CONTEXT: ${{ toJson(job) }}
22+
run: echo "$JOB_CONTEXT"
23+
- name: Dump steps context
24+
env:
25+
STEPS_CONTEXT: ${{ toJson(steps) }}
26+
run: echo "$STEPS_CONTEXT"
27+
- name: Dump runner context
28+
env:
29+
RUNNER_CONTEXT: ${{ toJson(runner) }}
30+
run: echo "$RUNNER_CONTEXT"
31+
- name: Dump strategy context
32+
env:
33+
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
34+
run: echo "$STRATEGY_CONTEXT"
35+
- name: Dump matrix context
36+
env:
37+
MATRIX_CONTEXT: ${{ toJson(matrix) }}
38+
run: echo "$MATRIX_CONTEXT"
39+
get-test-definition-files:
40+
name: Get Test Definition Files
41+
runs-on: ubuntu-latest
42+
outputs:
43+
matrix: ${{ steps.get-test-definition-files.outputs.result }}
44+
steps:
45+
- name: Checkout Repo
46+
uses: actions/checkout@v2
47+
with:
48+
fetch-depth: 0
49+
- name: Get Test Definition Files
50+
id: get-test-definition-files
51+
uses: actions/github-script@v3
52+
with:
53+
script: |
54+
const fs = require("fs");
55+
const fsp = fs.promises;
56+
const path = require("path");
57+
58+
const { isOHIValidationTimeout } = require("${{ github.workspace }}/.github/workflows/scripts/ohiValidationTimeout");
59+
60+
// readdir recursive directory search
61+
const { readdir } = fsp;
62+
async function getFiles(dir) {
63+
const dirents = await readdir(dir, { withFileTypes: true });
64+
const files = await Promise.all(
65+
dirents.map((dirent) => {
66+
const res = path.join(dir, dirent.name);
67+
return dirent.isDirectory() ? getFiles(res) : res;
68+
})
69+
);
70+
return Array.prototype.concat(...files);
71+
}
72+
73+
const definitionsDir = "test/definitions";
74+
const testDefinitions = await getFiles(definitionsDir);
75+
76+
const outputTestFilesMap = testDefinitions
77+
.filter((testDefinitionFile) => !isOHIValidationTimeout(testDefinitionFile))
78+
.map((testDefinitionFile) => {
79+
return {
80+
testDefinitionFile,
81+
testDisplayName: testDefinitionFile.replace(`${definitionsDir}/`, ""),
82+
};
83+
});
84+
const output = {
85+
include: outputTestFilesMap,
86+
};
87+
console.log(output);
88+
return output;
89+
90+
test-deploy-recipe:
91+
name: ${{ matrix.testDisplayName }}
92+
needs: [get-test-definition-files]
93+
if: ${{ fromJSON(needs.get-test-definition-files.outputs.matrix).include[0] }} # Avoids empty matrix validation error
94+
runs-on: ubuntu-latest
95+
strategy:
96+
matrix: ${{ fromJSON(needs.get-test-definition-files.outputs.matrix) }}
97+
fail-fast: false
98+
env:
99+
MATRIX: ${{ toJSON(matrix) }}
100+
steps:
101+
- name: Checkout Repo
102+
uses: actions/checkout@v2
103+
with:
104+
fetch-depth: 0
105+
106+
- name: Update Test Definition Files URLs
107+
id: get-test-definition-files
108+
env:
109+
TEST_DEFINITION_FILE: ${{ matrix.testDefinitionFile }}
110+
uses: actions/github-script@v3
111+
with:
112+
script: |
113+
const fs = require('fs');
114+
const fsp = fs.promises;
115+
const path = require('path');
116+
117+
// before returning, we need to edit the deploy config files in-place so they
118+
// use the right URLs from the branch
119+
async function getDeployConfigFile(file, outputDir) {
120+
const data = await fsp.readFile(path.join(outputDir, file));
121+
return JSON.parse(data);
122+
}
123+
124+
// Get testDefinitonFile from MATRIX env var
125+
const testDefinitionFile = process.env.TEST_DEFINITION_FILE;
126+
console.log(`Detected Deploy Config: ${JSON.stringify(testDefinitionFile, null, 2)}`)
127+
128+
// Update URLs to use branch this PR is opened with
129+
const data = await getDeployConfigFile(testDefinitionFile, process.env.GITHUB_WORKSPACE);
130+
131+
// Update github source URLs with branch name
132+
let jsonContent = JSON.stringify(data, null, 2);
133+
const branchName = process.env.GITHUB_HEAD_REF ? process.env.GITHUB_HEAD_REF : process.env.GITHUB_REF_NAME;
134+
const replacementString = `$1$2-b ${branchName} $3$4`;
135+
const sourceRepositoryRegex = /(.*)(\")(https:\/\/github.com\/newrelic\/open-install-library)(.*)/gi;
136+
jsonContent = jsonContent.replace(sourceRepositoryRegex, replacementString);
137+
console.log(`Detected Deploy Config: ${JSON.stringify(jsonContent, null, 2)}`)
138+
139+
// Update raw URLs with branch name
140+
const replacementString2 = `$1${branchName}$3`;
141+
const sourceRepositoryRegex2 = /(raw.githubusercontent.com\/newrelic\/open-install-library\/)(main)(\/newrelic\/recipes\/)*/gi;
142+
jsonContent = jsonContent.replace(sourceRepositoryRegex2, replacementString2);
143+
console.log(`Detected Deploy Config: ${JSON.stringify(jsonContent, null, 2)}`)
144+
145+
// Write file back to workspace
146+
const outputPath = `${process.env.GITHUB_WORKSPACE}/${testDefinitionFile}`;
147+
fs.writeFileSync(outputPath, jsonContent);
148+
149+
return testDefinitionFile;
150+
151+
- name: Install npm dependencies for deployer test runner
152+
working-directory: .github/workflows/scripts/deployer-platform
153+
run: npm install
154+
155+
- name: Execute test
156+
id: runDeployerPlatformTest
157+
working-directory: .github/workflows/scripts/deployer-platform
158+
run: |
159+
node main.js
160+
env:
161+
TEST_DEFINITION_FILE: ${{ matrix.testDefinitionFile }}
162+
AWS_ACCESS_KEY_ID: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_ACCESS_KEY_ID }}
163+
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_SECRET_ACCESS_KEY }}
164+
AWS_REGION: ${{ secrets.DEPLOYER_PLATFORM_US_AWS_REGION }}
165+
SQS_URL: ${{ secrets.DEPLOYER_PLATFORM_US_SQS_URL }}
166+
DYNAMO_TABLE: ${{ secrets.DEPLOYER_PLATFORM_US_DYNAMO_TABLE }}
167+
168+
- name: Report any error
169+
if: steps.runDeployerPlatformTest.outputs.exit_status != 0
170+
run: exit 1
171+
172+
slack-notify:
173+
runs-on: ubuntu-latest
174+
needs: [test-deploy-recipe]
175+
if: always()
176+
steps:
177+
- name: Build Result Slack Notification
178+
uses: 8398a7/action-slack@v3
179+
with:
180+
author_name: GitHub Actions
181+
status: custom
182+
fields: commit,repo,ref,author,eventName,message,workflow
183+
custom_payload: |
184+
{
185+
username: "GitHub Actions",
186+
icon_emoji: ":octocat:",
187+
attachments: [{
188+
color: ${{
189+
needs.test-deploy-recipe.result == 'success'
190+
}} === true ? '#43cc11' : '#e05d44',
191+
blocks: [
192+
{
193+
type: "section",
194+
text: {
195+
type: "mrkdwn",
196+
text: `Build for ${process.env.AS_REPO}`
197+
}
198+
},
199+
{
200+
type: "section",
201+
fields: [
202+
{
203+
type: "mrkdwn",
204+
text: `*Commit:*\n${process.env.AS_COMMIT}`
205+
},
206+
{
207+
type: "mrkdwn",
208+
text: `*Author:*\n${process.env.AS_AUTHOR}`
209+
},
210+
{
211+
type: "mrkdwn",
212+
text: `*Branch:*\n${process.env.AS_REF}`
213+
},
214+
{
215+
type: "mrkdwn",
216+
text: `*Message:*\n${process.env.AS_MESSAGE}`
217+
},
218+
{
219+
type: "mrkdwn",
220+
text: `*Type:*\n${process.env.AS_EVENT_NAME}`
221+
},
222+
{
223+
type: "mrkdwn",
224+
text: "*PR:*\n${{ github.event.pull_request.html_url }}"
225+
},
226+
{
227+
type: "mrkdwn",
228+
text: `*Workflow:*\n${ process.env.AS_WORKFLOW }`
229+
}
230+
]
231+
},
232+
{
233+
type: "section",
234+
text: {
235+
type: "mrkdwn",
236+
text: [
237+
"*Result:*",
238+
`• ${ ${{ needs.test-deploy-recipe.result == 'success' }} === true ? '✅' : '❌' } Non-regression testing of all recipes: ${{ needs.test-deploy-recipe.result }}`
239+
].join('\n')
240+
}
241+
},
242+
{
243+
type: "context",
244+
elements: [
245+
{
246+
type: "image",
247+
image_url: "https://avatars2.githubusercontent.com/in/15368",
248+
alt_text: "Github Actions"
249+
},
250+
{
251+
type: "mrkdwn",
252+
text: "This message was created automatically by GitHub Actions."
253+
}
254+
]
255+
}
256+
]
257+
}]
258+
}
259+
env:
260+
GITHUB_TOKEN: ${{ github.token }}
261+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const fs = require('fs');
2+
const fsp = fs.promises;
3+
const {SQSClient, SendMessageCommand} = require('@aws-sdk/client-sqs')
4+
const {DynamoDBClient, QueryCommand} = require('@aws-sdk/client-dynamodb')
5+
const {unmarshall} = require('@aws-sdk/util-dynamodb')
6+
7+
const AWS_REGION = process.env.AWS_REGION
8+
const SQS_URL = process.env.SQS_URL
9+
const DYNAMO_TABLE = process.env.DYNAMO_TABLE
10+
const AWS_CREDS = {
11+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
12+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
13+
};
14+
15+
const sqs = new SQSClient({region: AWS_REGION, credentials: AWS_CREDS})
16+
const dynamodb = new DynamoDBClient({region: AWS_REGION, credentials: AWS_CREDS})
17+
18+
19+
function queryForDeploymentStatus(messageId) {
20+
const query_params = {
21+
TableName: DYNAMO_TABLE,
22+
KeyConditionExpression: 'id = :id',
23+
FilterExpression: 'completed = :completed',
24+
ExpressionAttributeNames: {
25+
'#id': 'id',
26+
'#completed': 'completed',
27+
'#status': 'status',
28+
'#message': 'message',
29+
},
30+
ExpressionAttributeValues: {
31+
':id': {
32+
S: messageId,
33+
},
34+
':completed': {
35+
BOOL: true,
36+
},
37+
},
38+
ProjectionExpression: '#id, #completed, #status, #message',
39+
ScanIndexForward: false, //returns items by descending timestamp
40+
}
41+
return new QueryCommand(query_params)
42+
}
43+
44+
async function isDeploymentSuccessful(deploymentId, retries, waitSeconds) {
45+
for (let i = 0; i < retries; i++) {
46+
console.log(`Deployment pending, sleeping ${waitSeconds} seconds...`)
47+
await sleep(waitSeconds * 1000)
48+
49+
try {
50+
const response = await dynamodb.send(queryForDeploymentStatus(deploymentId))
51+
console.log(`Query succeeded. Items found: ${response.Items.length}`)
52+
53+
for (let i = 0; i < response.Items.length; i++) {
54+
const item = unmarshall(response.Items[i])
55+
if (item.completed) {
56+
console.log(`Completed: ${item.id} - ${item.message} - ${item.completed} - ${item.status}`)
57+
if (item.status === 'FAILED') {
58+
console.error(`::error:: Deployment failed: ${item.message}`)
59+
return false
60+
}
61+
62+
return true
63+
}
64+
}
65+
} catch (err) {
66+
console.log(`Error querying table: ${err}`)
67+
}
68+
}
69+
return false
70+
}
71+
72+
function sleep(ms) {
73+
return new Promise((resolve) => setTimeout(resolve, ms))
74+
}
75+
76+
async function getDeployConfigFile(file) {
77+
const data = await fsp.readFile(file);
78+
return JSON.parse(data);
79+
}
80+
81+
function main() {
82+
getDeployConfigFile(`${process.env.GITHUB_WORKSPACE}/${process.env.TEST_DEFINITION_FILE}`)
83+
.then(async (json) => {
84+
let messageId
85+
try {
86+
const command = new SendMessageCommand({
87+
QueueUrl: SQS_URL,
88+
MessageBody: JSON.stringify(json),
89+
})
90+
data = await sqs.send(command)
91+
messageId = data.MessageId
92+
console.log(`Message sent: ${messageId}`)
93+
} catch (err) {
94+
console.error(`Error sending message: ${err}`)
95+
}
96+
97+
// Execute the query with retries/sleeps
98+
let RETRIES = 200, WAIT_SECONDS = 15
99+
const success = await isDeploymentSuccessful(messageId, RETRIES, WAIT_SECONDS)
100+
if (!success) {
101+
process.exit(1)
102+
}
103+
}).catch((error) =>
104+
console.log(`Error reading deploy config ${process.env.TEST_DEFINITION_FILE}: ${error}`)
105+
)
106+
}
107+
108+
if (require.main === module) {
109+
main()
110+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "dp",
3+
"version": "1.0.0",
4+
"description": "Runs OIL tests using deployer-platform to provision target hosts",
5+
"main": "main.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"dependencies": {
10+
"@actions/core": "1.2.6",
11+
"@aws-sdk/client-sqs": "3.27.0",
12+
"@aws-sdk/client-dynamodb": "3.27.0",
13+
"@aws-sdk/util-dynamodb": "3.27.0",
14+
"@aws-sdk/credential-providers": "3.451.0"
15+
},
16+
"keywords": [],
17+
"author": "",
18+
"license": "ISC"
19+
}

0 commit comments

Comments
 (0)