Skip to content

Commit

Permalink
feat(Telemetry): Add projectId to payload
Browse files Browse the repository at this point in the history
  • Loading branch information
pgrzesik committed Nov 3, 2021
1 parent 7bb2520 commit cc7d7e4
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 16 deletions.
9 changes: 9 additions & 0 deletions lib/plugins/aws/deploy/index.js
Expand Up @@ -80,6 +80,15 @@ class AwsDeploy {
.getStage()} ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
);
log.info(); // Ensure gap between verbose logging

// This is used to ensure that for `deploy` command, the `accountId` will be resolved and available
// for `generatePayload` telemetry logic
this.provider.getAccountId().then(
(accountId) => (this.provider.accountId = accountId),
() => {
/* pass on all errors */
}
);
}
},

Expand Down
32 changes: 18 additions & 14 deletions lib/plugins/aws/provider.js
Expand Up @@ -1437,6 +1437,9 @@ class AwsProvider {
// Store credentials in this variable to avoid creating them several times (messes up MFA).
this.cachedCredentials = null;

// Store accountId to be used in `generateTelemetry` logic
this.accountId = null;

Object.assign(this.naming, naming);
}

Expand Down Expand Up @@ -1725,20 +1728,6 @@ class AwsProvider {
return stageSourceValue.value || defaultStage;
}

getAccountInfo() {
return this.request('STS', 'getCallerIdentity', {}).then((result) => {
const arn = result.Arn;
const accountId = result.Account;
const partition = arn.split(':')[1]; // ex: arn:aws:iam:acctId:user/xyz
return {
accountId,
partition,
arn: result.Arn,
userId: result.UserId,
};
});
}

/**
* Get API Gateway Rest API ID from serverless config
*/
Expand Down Expand Up @@ -1858,6 +1847,21 @@ class AwsProvider {
Object.defineProperties(
AwsProvider.prototype,
memoizeeMethods({
getAccountInfo: d(
async function () {
const result = await this.request('STS', 'getCallerIdentity', {});
const arn = result.Arn;
const accountId = result.Account;
const partition = arn.split(':')[1]; // ex: arn:aws:iam:acctId:user/xyz
return {
accountId,
partition,
arn: result.Arn,
userId: result.UserId,
};
},
{ promise: true }
),
getAccountId: d(
async function () {
const result = await this.getAccountInfo();
Expand Down
14 changes: 14 additions & 0 deletions lib/utils/telemetry/generatePayload.js
Expand Up @@ -3,6 +3,7 @@
const path = require('path');
const os = require('os');
const fs = require('fs');
const crypto = require('crypto');
const resolveSync = require('ncjsm/resolve/sync');
const _ = require('lodash');
const isPlainObject = require('type/plain-object/is');
Expand Down Expand Up @@ -298,6 +299,19 @@ module.exports = ({
payload.config = getServiceConfig({ configuration, options, variableSources });
payload.isConfigValid = getConfigurationValidationResult(configuration);
payload.dashboard.orgUid = serverless && serverless.service.orgUid;

if (serverless && command === 'deploy' && isAwsProvider) {
const serviceName = isObject(configuration.service)
? configuration.service.name
: configuration.service;
const accountId = serverless.getProvider('aws').accountId;
if (serviceName && accountId) {
payload.projectId = crypto
.createHash('sha256')
.update(`${serviceName}-${accountId}`)
.digest('base64');
}
}
}

if (commandUsage) {
Expand Down
10 changes: 10 additions & 0 deletions test/unit/lib/plugins/aws/deploy/lib/extendedValidate.test.js
Expand Up @@ -191,6 +191,16 @@ describe('test/unit/lib/plugins/aws/deploy/lib/extendedValidate.test.js', () =>
},
},
},
awsRequestStubMap: {
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '1234567890',
Arn: 'arn:aws:iam::1234567890:user/test',
},
},
},
command: 'deploy',
lastLifecycleHookName: 'before:deploy:deploy',
});
Expand Down
2 changes: 2 additions & 0 deletions test/unit/lib/plugins/aws/deployFunction.test.js
Expand Up @@ -128,6 +128,8 @@ describe('AwsDeployFunction', () => {
let getRoleStub;

beforeEach(() => {
// Ensure that memoized function will be properly stubbed
awsDeployFunction.provider.getAccountInfo;
getAccountInfoStub = sinon
.stub(awsDeployFunction.provider, 'getAccountInfo')
.resolves({ accountId: '123456789012', partition: 'aws' });
Expand Down
Expand Up @@ -33,6 +33,8 @@ describe('#updateStage()', () => {
options = { stage: 'dev', region: 'us-east-1' };
awsProvider = new AwsProvider(serverless, options);
serverless.setProvider('aws', awsProvider);
// Ensure that memoized function will be properly stubbed
awsProvider.getAccountInfo;
providerGetAccountInfoStub = sinon.stub(awsProvider, 'getAccountInfo').resolves({
accountId: '123456',
partition: 'aws',
Expand Down
Expand Up @@ -147,10 +147,20 @@ describe('#generateCoreTemplate()', () => {
deploymentBucket: bucketName,
},
},
awsRequestStubMap: {
S3: { getBucketLocation: { LocationConstraint: '' } },
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '1234567890',
Arn: 'arn:aws:iam::1234567890:user/test',
},
},
},
command: 'deploy',
options: { 'aws-s3-accelerate': true },
lastLifecycleHookName: 'before:deploy:deploy',
awsRequestStubMap: { S3: { getBucketLocation: { LocationConstraint: '' } } },
})
).to.eventually.be.rejected.and.have.property(
'code',
Expand Down Expand Up @@ -185,6 +195,16 @@ describe('#generateCoreTemplate()', () => {
command: 'deploy',
options: { 'aws-s3-accelerate': true },
lastLifecycleHookName: 'before:deploy:deploy',
awsRequestStubMap: {
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '1234567890',
Arn: 'arn:aws:iam::1234567890:user/test',
},
},
},
}).then(({ cfTemplate: template }) => {
expect(template.Outputs.ServerlessDeploymentBucketAccelerated).to.not.equal(null);
expect(template.Outputs.ServerlessDeploymentBucketAccelerated.Value).to.equal(true);
Expand All @@ -196,6 +216,16 @@ describe('#generateCoreTemplate()', () => {
command: 'deploy',
options: { 'aws-s3-accelerate': false },
lastLifecycleHookName: 'before:deploy:deploy',
awsRequestStubMap: {
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '1234567890',
Arn: 'arn:aws:iam::1234567890:user/test',
},
},
},
}).then(({ cfTemplate: template }) => {
expect(template.Resources.ServerlessDeploymentBucket).to.be.deep.equal({
Type: 'AWS::S3::Bucket',
Expand All @@ -220,6 +250,16 @@ describe('#generateCoreTemplate()', () => {
runServerless({
config: { service: 'irrelevant', provider: { name: 'aws', region: 'us-gov-west-1' } },
command: 'deploy',
awsRequestStubMap: {
STS: {
getCallerIdentity: {
ResponseMetadata: { RequestId: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
UserId: 'XXXXXXXXXXXXXXXXXXXXX',
Account: '1234567890',
Arn: 'arn:aws:iam::1234567890:user/test',
},
},
},
options: { 'aws-s3-accelerate': false },
lastLifecycleHookName: 'before:deploy:deploy',
}).then(({ cfTemplate: template }) => {
Expand Down
Expand Up @@ -11,7 +11,7 @@ describe('test/unit/lib/plugins/aws/package/lib/stripNullPropsFromTemplateResour
before(async () => {
const result = await runServerless({
fixture: 'aws',
command: 'deploy',
command: 'package',
lastLifecycleHookName: 'package:finalize',
configExt: {
resources: {
Expand Down
21 changes: 21 additions & 0 deletions test/unit/lib/utils/telemetry/generatePayload.test.js
Expand Up @@ -659,4 +659,25 @@ describe('test/unit/lib/utils/telemetry/generatePayload.test.js', () => {

expect(payload.config.variableSources).to.deep.equal(['ssm', 'opt']);
});

it('Should correctly resolve projectId property', async () => {
const { serverless } = await runServerless({
fixture: 'httpApi',
command: 'print',
configExt: {
service: 'to-ensure-unique-serivce-name',
},
});
serverless.getProvider('aws').accountId = '1234567890';
const payload = generatePayload({
command: 'deploy',
options: {},
commandSchema: commandsSchema.get('deploy'),
serviceDir: serverless.serviceDir,
configuration: serverless.configurationInput,
serverless,
});

expect(payload.projectId).to.deep.equal('35dsFwCaexwLHppAP4uDsjKW4ci54q1AKcN5JTNaDtw=');
});
});

0 comments on commit cc7d7e4

Please sign in to comment.