diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 97c7cc82f1..7876d7bbcd 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -2,48 +2,63 @@ name: run-e2e-tests on: workflow_dispatch: {} jobs: - run: - ######################### - # Force Github action to run only a single job at a time (based on the group name) - # This is to prevent "race-condition" in building e2e tests infrastructure - ######################### - concurrency: - group: e2e-tests + example-and-package-check: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: "Checkout" + uses: actions/checkout@v3 + - name: "Use NodeJS 14" + uses: actions/setup-node@v3 + with: + # Always use version 14 as we use TypeScript target es2020 + node-version: 14 + - name: "Install npm@8.x" + run: npm i -g npm@next-8 + - name: "Install monorepo packages" + # This installs all the dependencies of ./packages/* + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts + - name: "Install example packages" + # Since we are not managing the cdk examples with npm workspaces we install + # the dependencies in a separate step + working-directory: ./examples/cdk + run: npm ci + - name: "Test packaging" + run: | + npm run lerna-package + cd examples/cdk + npm install ../../packages/**/dist/aws-lambda-powertools-* + npm run test + e2e-tests: runs-on: ubuntu-latest permissions: id-token: write # needed to interact with GitHub's OIDC Token endpoint. contents: read + strategy: + matrix: + version: [12, 14] + package: [logger, metrics, tracing] steps: - - name: "Checkout" - uses: actions/checkout@v3 - ######################### - # Release new version - ######################### - - name: "Use NodeJS 14" - uses: actions/setup-node@v3 - with: - node-version: '14' - - name: Install npm@8.x - run: npm i -g npm@next-8 - - name: Install monorepo packages - # This installs all the dependencies of ./packages/* - # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts - run: npm ci --foreground-scripts - - name: Install example packages - # Since we are not managing the cdk examples with npm workspaces we install - # the dependencies in a separate step - working-directory: ./examples/cdk - run: npm ci - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1.6.1 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} - aws-region: eu-west-1 - - name: Run integration tests - run: npm run lerna-test:e2e - - name: Test packaging - run: | - npm run lerna-package - cd examples/cdk - npm install ../../packages/**/dist/aws-lambda-powertools-* - npm run test \ No newline at end of file + - name: "Checkout" + uses: actions/checkout@v3 + - name: "Use NodeJS 14" + uses: actions/setup-node@v3 + with: + # Always use version 14 as we use TypeScript target es2020 + node-version: 14 + - name: "Install npm@8.x" + run: npm i -g npm@next-8 + - name: "Install monorepo packages" + # This installs all the dependencies of ./packages/* + # See https://github.com/npm/cli/issues/4475 to see why --foreground-scripts + run: npm ci --foreground-scripts + - name: "Configure AWS credentials" + uses: aws-actions/configure-aws-credentials@v1.6.1 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN_TO_ASSUME }} + aws-region: eu-west-1 + - name: "Run integration tests" + run: | + RUNTIME=nodejs${{ matrix.version }}x npm run test:e2e -w packages/${{ matrix.package }} diff --git a/package-lock.json b/package-lock.json index 991f2431e1..70e9ff32c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6380,61 +6380,6 @@ "typedarray": "^0.0.6" } }, - "node_modules/concurrently": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.0.0.tgz", - "integrity": "sha512-WKM7PUsI8wyXpF80H+zjHP32fsgsHNQfPLw/e70Z5dYkV7hF+rf8q3D+ScWJIEr57CpkO3OWBko6hwhQLPR8Pw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "rxjs": "^6.6.3", - "spawn-command": "^0.0.2-1", - "supports-color": "^8.1.0", - "tree-kill": "^1.2.2", - "yargs": "^16.2.0" - }, - "bin": { - "concurrently": "dist/bin/concurrently.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.0 || >=16.0.0" - } - }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/concurrently/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -6770,19 +6715,6 @@ "node": ">=10" } }, - "node_modules/date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", - "dev": true, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -13936,12 +13868,6 @@ "source-map": "^0.6.0" } }, - "node_modules/spawn-command": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", - "dev": true - }, "node_modules/spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -14532,15 +14458,6 @@ "node": ">=8" } }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, "node_modules/trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", @@ -15577,8 +15494,7 @@ "devDependencies": { "@types/lodash.clonedeep": "^4.5.6", "@types/lodash.merge": "^4.6.6", - "@types/lodash.pickby": "^4.6.6", - "concurrently": "^7.0.0" + "@types/lodash.pickby": "^4.6.6" } }, "packages/metrics": { @@ -15813,7 +15729,6 @@ "@types/lodash.clonedeep": "^4.5.6", "@types/lodash.merge": "^4.6.6", "@types/lodash.pickby": "^4.6.6", - "concurrently": "^7.0.0", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0" @@ -20822,48 +20737,6 @@ "typedarray": "^0.0.6" } }, - "concurrently": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.0.0.tgz", - "integrity": "sha512-WKM7PUsI8wyXpF80H+zjHP32fsgsHNQfPLw/e70Z5dYkV7hF+rf8q3D+ScWJIEr57CpkO3OWBko6hwhQLPR8Pw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "date-fns": "^2.16.1", - "lodash": "^4.17.21", - "rxjs": "^6.6.3", - "spawn-command": "^0.0.2-1", - "supports-color": "^8.1.0", - "tree-kill": "^1.2.2", - "yargs": "^16.2.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } - } - }, "config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -21125,12 +20998,6 @@ "whatwg-url": "^8.0.0" } }, - "date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", - "dev": true - }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -26580,12 +26447,6 @@ "source-map": "^0.6.0" } }, - "spawn-command": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", - "dev": true - }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -27041,12 +26902,6 @@ "punycode": "^2.1.1" } }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, "trim-newlines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", diff --git a/packages/commons/tests/utils/e2eUtils.ts b/packages/commons/tests/utils/e2eUtils.ts index eafaf3c95c..caced6e568 100644 --- a/packages/commons/tests/utils/e2eUtils.ts +++ b/packages/commons/tests/utils/e2eUtils.ts @@ -57,7 +57,7 @@ export const createStackWithLambdaFunction = (params: StackWithLambdaFunctionOpt }; export const generateUniqueName = (name_prefix: string, uuid: string, runtime: string, testName: string): string => - `${name_prefix}-${runtime}-${testName}-${uuid}`.substring(0, 64); + `${name_prefix}-${runtime}-${uuid.substring(0,5)}-${testName}`.substring(0, 64); export const invokeFunction = async (functionName: string, times: number = 1, invocationMode: 'PARALLEL' | 'SEQUENTIAL' = 'PARALLEL', payload: FunctionPayload = {}): Promise => { const invocationLogs: InvocationLogs[] = []; diff --git a/packages/logger/package.json b/packages/logger/package.json index d657a0abfc..9ea15814ab 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -15,7 +15,7 @@ "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "test:e2e:nodejs12x": "RUNTIME=nodejs12x jest --group=e2e", "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", - "test:e2e": "concurrently \"npm:test:e2e:nodejs12x\" \"npm:test:e2e:nodejs14x\"", + "test:e2e": "jest --group=e2e", "watch": "jest --watch --group=unit", "build": "tsc", "lint": "eslint --ext .ts --fix --no-error-on-unmatched-pattern src tests", @@ -47,8 +47,7 @@ "devDependencies": { "@types/lodash.clonedeep": "^4.5.6", "@types/lodash.merge": "^4.6.6", - "@types/lodash.pickby": "^4.6.6", - "concurrently": "^7.0.0" + "@types/lodash.pickby": "^4.6.6" }, "files": [ "lib" diff --git a/packages/logger/tests/e2e/constants.ts b/packages/logger/tests/e2e/constants.ts index d99be1f441..79eb64e5aa 100644 --- a/packages/logger/tests/e2e/constants.ts +++ b/packages/logger/tests/e2e/constants.ts @@ -1,5 +1,6 @@ export const RESOURCE_NAME_PREFIX = 'Logger-E2E'; -export const TEST_CASE_TIMEOUT = 20_000; // 20 seconds -export const SETUP_TIMEOUT = 300_000; // 300 seconds -export const TEARDOWN_TIMEOUT = 200_000; +export const ONE_MINUTE = 60 * 1000; +export const TEST_CASE_TIMEOUT = ONE_MINUTE; +export const SETUP_TIMEOUT = 5 * ONE_MINUTE; +export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; export const STACK_OUTPUT_LOG_GROUP = 'LogGroupName'; \ No newline at end of file diff --git a/packages/metrics/package.json b/packages/metrics/package.json index 8b41dc0769..5b59b5910f 100644 --- a/packages/metrics/package.json +++ b/packages/metrics/package.json @@ -15,7 +15,7 @@ "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "test:e2e:nodejs12x": "RUNTIME=nodejs12x jest --group=e2e", "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", - "test:e2e": "concurrently \"npm:test:e2e:nodejs12x\" \"npm:test:e2e:nodejs14x\"", + "test:e2e": "jest --group=e2e", "watch": "jest --group=unit --watch ", "build": "tsc", "lint": "eslint --ext .ts --fix --no-error-on-unmatched-pattern src tests", diff --git a/packages/metrics/tests/e2e/constants.ts b/packages/metrics/tests/e2e/constants.ts index c89fdfad20..a950bb7f0d 100644 --- a/packages/metrics/tests/e2e/constants.ts +++ b/packages/metrics/tests/e2e/constants.ts @@ -1,5 +1,5 @@ export const RESOURCE_NAME_PREFIX = 'Metrics-E2E'; -export const ONE_MINUTE = 60 * 10_00; -export const TEST_CASE_TIMEOUT = 90_000; // 90 seconds -export const SETUP_TIMEOUT = 300_000; // 300 seconds -export const TEARDOWN_TIMEOUT = 200_000; \ No newline at end of file +export const ONE_MINUTE = 60 * 1000; +export const TEST_CASE_TIMEOUT = 3 * ONE_MINUTE; +export const SETUP_TIMEOUT = 5 * ONE_MINUTE; +export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; \ No newline at end of file diff --git a/packages/tracing/package.json b/packages/tracing/package.json index 3fa4ccac07..6cf33c023f 100644 --- a/packages/tracing/package.json +++ b/packages/tracing/package.json @@ -15,7 +15,7 @@ "test:unit": "jest --group=unit --detectOpenHandles --coverage --verbose", "test:e2e:nodejs12x": "RUNTIME=nodejs12x jest --group=e2e", "test:e2e:nodejs14x": "RUNTIME=nodejs14x jest --group=e2e", - "test:e2e": "concurrently \"npm:test:e2e:nodejs12x\" \"npm:test:e2e:nodejs14x\"", + "test:e2e": "jest --group=e2e", "watch": "jest --watch", "build": "tsc", "lint": "eslint --ext .ts --fix --no-error-on-unmatched-pattern src tests", diff --git a/packages/tracing/tests/e2e/constants.ts b/packages/tracing/tests/e2e/constants.ts index 22ebcc06d0..6d738a3d36 100644 --- a/packages/tracing/tests/e2e/constants.ts +++ b/packages/tracing/tests/e2e/constants.ts @@ -1,8 +1,8 @@ export const RESOURCE_NAME_PREFIX = 'Tracer-E2E'; export const ONE_MINUTE = 60 * 1_000; -export const TEST_CASE_TIMEOUT = ONE_MINUTE * 3; -export const SETUP_TIMEOUT = ONE_MINUTE * 5; -export const TEARDOWN_TIMEOUT = ONE_MINUTE * 3; +export const TEST_CASE_TIMEOUT = 5 * ONE_MINUTE; +export const SETUP_TIMEOUT = 5 * ONE_MINUTE; +export const TEARDOWN_TIMEOUT = 5 * ONE_MINUTE; export const expectedCustomAnnotationKey = 'myAnnotation'; export const expectedCustomAnnotationValue = 'myValue'; diff --git a/packages/tracing/tests/helpers/FunctionSegmentNotDefinedError.ts b/packages/tracing/tests/helpers/FunctionSegmentNotDefinedError.ts new file mode 100644 index 0000000000..8408b3c57b --- /dev/null +++ b/packages/tracing/tests/helpers/FunctionSegmentNotDefinedError.ts @@ -0,0 +1,6 @@ +export class FunctionSegmentNotDefinedError extends Error { + public constructor(msg: string) { + super(msg); + Object.setPrototypeOf(this, FunctionSegmentNotDefinedError.prototype); + } +} diff --git a/packages/tracing/tests/helpers/tracesUtils.ts b/packages/tracing/tests/helpers/tracesUtils.ts index 467b50b9f8..8895d9e351 100644 --- a/packages/tracing/tests/helpers/tracesUtils.ts +++ b/packages/tracing/tests/helpers/tracesUtils.ts @@ -14,11 +14,16 @@ import { expectedCustomResponseValue, expectedCustomErrorMessage, } from '../e2e/constants'; +import { FunctionSegmentNotDefinedError } from './FunctionSegmentNotDefinedError'; interface ParsedDocument { name: string id: string start_time: number end_time: number + // This flag may be set if the segment hasn't been fully processed + // The trace may have already appeared in the `getTraceSummaries` response + // but a segment may still be in_progress + in_progress?: boolean aws?: { request_id: string } @@ -117,6 +122,28 @@ const getTraces = async (xrayClient: XRay, startTime: Date, resourceArn: string, })).sort((a, b) => a.Document.start_time - b.Document.start_time) as ParsedSegment[], })).sort((a, b) => a.Segments[0].Document.start_time - b.Segments[0].Document.start_time); + // Verify that all trace has fully loaded invocation subsegments. + // The subsegments may be not available yet or still in progress. + for (const trace of sortedTraces) { + let retryFlag = false; + + let invocationSubsegment; + try { + invocationSubsegment = getInvocationSubsegment(trace); + } catch (error) { + if (error instanceof FunctionSegmentNotDefinedError){ + retry(new Error(`There is no Function subsegment (AWS::Lambda::Function) yet. Retry.`)); + } else { + throw error; + } + } + + retryFlag = retryFlag || (!!invocationSubsegment.in_progress); + if (retryFlag) { + retry(new Error(`There is at least an invocation subsegment that hasn't been fully processed yet. The "in_progress" flag is still "true" in the document.`)); + } + } + if (sortedTraces === undefined) { throw new Error(`Traces are undefined for ${resourceArn}`); } @@ -139,7 +166,7 @@ const getFunctionSegment = (trace: ParsedTrace): ParsedSegment => { const functionSegment = trace.Segments.find((segment) => segment.Document.origin === 'AWS::Lambda::Function'); if (functionSegment === undefined) { - throw new Error('Function segment is undefined'); + throw new FunctionSegmentNotDefinedError('Function segment is undefined. This can be either due to eventual consistency or a bug in Tracer'); } return functionSegment;