From 240977c15bae66083efaa0c2311a3d09cada9504 Mon Sep 17 00:00:00 2001 From: Hehe Tan <443016215@qq.com> Date: Mon, 17 Dec 2018 22:17:44 +0800 Subject: [PATCH] fix generated invalid trigger role or policy names (#109) * fix generated invalid trigger role or policy names * update package version --- lib/deploy/deploy-by-tpl.js | 14 +-- lib/deploy/deploy-support.js | 97 ++++++++------- lib/ram.js | 7 +- package-lock.json | 8 +- package.json | 2 +- test/deploy/deploy-support.test.js | 187 ++++++++++++++++++++++++++++- 6 files changed, 249 insertions(+), 66 deletions(-) diff --git a/lib/deploy/deploy-by-tpl.js b/lib/deploy/deploy-by-tpl.js index 50a255a0b..b6a6d0784 100644 --- a/lib/deploy/deploy-by-tpl.js +++ b/lib/deploy/deploy-by-tpl.js @@ -33,7 +33,8 @@ let { let { makeRole, attachPolicyToRole, - makeAndAttachPolicy + makeAndAttachPolicy, + normalizeRoleOrPoliceName } = require('../ram'); function extractFcRole(role) { @@ -42,10 +43,6 @@ function extractFcRole(role) { return roleName; } -function nomalizeRoleOrPoliceName(roleName) { - return roleName.replace(/_/g, '-'); -} - async function deployFunction(baseDir, serviceName, functionName, functionDefinition) { const properties = functionDefinition.Properties || {}; @@ -107,12 +104,11 @@ async function deployPolicy(serviceName, roleName, policy, curCount) { const profile = await getProfile(); - const policyName = nomalizeRoleOrPoliceName(`AliyunFcGeneratedServicePolicy-${profile.defaultRegion}-${serviceName}${curCount}`); + const policyName = normalizeRoleOrPoliceName(`AliyunFcGeneratedServicePolicy-${profile.defaultRegion}-${serviceName}${curCount}`); await makeAndAttachPolicy(policyName, policy, roleName); return curCount + 1; - } async function deployPolicies(serviceName, roleName, policies) { @@ -150,7 +146,7 @@ async function deployFcService(baseDir, serviceName, serviceDefinition) { createRoleIfNotExist = false; } else { roleName = `aliyunfcgeneratedrole-${profile.defaultRegion}-${serviceName}`; - roleName = nomalizeRoleOrPoliceName(roleName); + roleName = normalizeRoleOrPoliceName(roleName); createRoleIfNotExist = true; } @@ -166,7 +162,7 @@ async function deployFcService(baseDir, serviceName, serviceDefinition) { if (logConfig.Logstore && logConfig.Project) { if (!roleArn) { - const logPolicyName = nomalizeRoleOrPoliceName(`AliyunFcGeneratedLogPolicy-${profile.defaultRegion}-${serviceName}`); + const logPolicyName = normalizeRoleOrPoliceName(`AliyunFcGeneratedLogPolicy-${profile.defaultRegion}-${serviceName}`); await makeAndAttachPolicy(logPolicyName, { 'Version': '1', 'Statement': [{ diff --git a/lib/deploy/deploy-support.js b/lib/deploy/deploy-support.js index 5d588f2fe..e50991d7c 100644 --- a/lib/deploy/deploy-support.js +++ b/lib/deploy/deploy-support.js @@ -22,11 +22,7 @@ const promiseRetry = require('../retry'); const funignore = require('../package/ignore'); -let { - makeRole, - attachPolicyToRole, - makePolicy -} = require('../ram'); +const ram = require('../ram'); const readFile = util.promisify(fs.readFile); @@ -505,9 +501,10 @@ function getTriggerConfig(triggerType, triggerProperties) { async function makeInvocationRole(serviceName, functionName, triggerType) { if (triggerType === 'Log') { - const invocationRoleName = `AliyunFcGeneratedInvocationRole-${serviceName}-${functionName}`; - const invocationRole = await makeRole(invocationRoleName, true, 'Used for fc invocation', { + const invocationRoleName = ram.normalizeRoleOrPoliceName(`AliyunFcGeneratedInvocationRole-${serviceName}-${functionName}`); + + const invocationRole = await ram.makeRole(invocationRoleName, true, 'Used for fc invocation', { 'Statement': [{ 'Action': 'sts:AssumeRole', 'Effect': 'Allow', @@ -520,48 +517,49 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { 'Version': '1' }); - const policyName = `AliyunFcGeneratedInvocationPolicy-${serviceName}-${functionName}`; + const policyName = ram.normalizeRoleOrPoliceName(`AliyunFcGeneratedInvocationPolicy-${serviceName}-${functionName}`); - await makePolicy(policyName, { + await ram.makePolicy(policyName, { 'Version': '1', 'Statement': [{ - 'Action': [ - 'fc:InvokeFunction' - ], - 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`, - 'Effect': 'Allow' - }, - { - 'Action': [ - 'log:Get*', - 'log:List*', - 'log:PostLogStoreLogs', - 'log:CreateConsumerGroup', - 'log:UpdateConsumerGroup', - 'log:DeleteConsumerGroup', - 'log:ListConsumerGroup', - 'log:ConsumerGroupUpdateCheckPoint', - 'log:ConsumerGroupHeartBeat', - 'log:GetConsumerGroupCheckPoint' - ], - 'Resource': '*', - 'Effect': 'Allow' - } + 'Action': [ + 'fc:InvokeFunction' + ], + 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`, + 'Effect': 'Allow' + }, + { + 'Action': [ + 'log:Get*', + 'log:List*', + 'log:PostLogStoreLogs', + 'log:CreateConsumerGroup', + 'log:UpdateConsumerGroup', + 'log:DeleteConsumerGroup', + 'log:ListConsumerGroup', + 'log:ConsumerGroupUpdateCheckPoint', + 'log:ConsumerGroupHeartBeat', + 'log:GetConsumerGroupCheckPoint' + ], + 'Resource': '*', + 'Effect': 'Allow' + } ] }); - await attachPolicyToRole(policyName, invocationRoleName, 'Custom'); + await ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom'); return invocationRole.Role; } else if (triggerType === 'RDS' || triggerType === 'MNSTopic') { - const invocationRoleName = `FunCreateRole-${serviceName}-${functionName}`; + + const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`); var tMap = { 'RDS': 'rds', 'MNSTopic': 'mns' }; var principalService = util.format('%s.aliyuncs.com', tMap[triggerType]); - const invocationRole = await makeRole(invocationRoleName, true, 'Used for fc invocation', { + const invocationRole = await ram.makeRole(invocationRoleName, true, 'Used for fc invocation', { 'Statement': [{ 'Action': 'sts:AssumeRole', 'Effect': 'Allow', @@ -574,9 +572,9 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { 'Version': '1' }); - const policyName = `FunCreatePolicy-${serviceName}-${functionName}`; + const policyName = ram.normalizeRoleOrPoliceName(`FunCreatePolicy-${serviceName}-${functionName}`); - await makePolicy(policyName, { + await ram.makePolicy(policyName, { 'Version': '1', 'Statement': [{ 'Action': [ @@ -587,14 +585,14 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { }] }); - await attachPolicyToRole(policyName, invocationRoleName, 'Custom'); + await ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom'); return invocationRole.Role; } else if (triggerType === 'TableStore') { - const invocationRoleName = `FunCreateRole-${serviceName}-${functionName}`; + const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`); - const invocationRole = await makeRole(invocationRoleName, true, 'Used for fc invocation', { + const invocationRole = await ram.makeRole(invocationRoleName, true, 'Used for fc invocation', { 'Statement': [{ 'Action': 'sts:AssumeRole', 'Effect': 'Allow', @@ -607,9 +605,9 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { 'Version': '1' }); - const invkPolicyName = `FunCreateInvkPolicy-${serviceName}-${functionName}`; + const invkPolicyName = ram.normalizeRoleOrPoliceName(`FunCreateInvkPolicy-${serviceName}-${functionName}`); - await makePolicy(invkPolicyName, { + await ram.makePolicy(invkPolicyName, { 'Version': '1', 'Statement': [{ 'Action': [ @@ -620,11 +618,11 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { }] }); - await attachPolicyToRole(invkPolicyName, invocationRoleName, 'Custom'); + await ram.attachPolicyToRole(invkPolicyName, invocationRoleName, 'Custom'); - const otsReadPolicyName = `FunCreateOtsReadPolicy-${serviceName}-${functionName}`; + const otsReadPolicyName = ram.normalizeRoleOrPoliceName(`FunCreateOtsReadPolicy-${serviceName}-${functionName}`); - await makePolicy(otsReadPolicyName, { + await ram.makePolicy(otsReadPolicyName, { 'Version': '1', 'Statement': [{ 'Action': [ @@ -638,7 +636,7 @@ async function makeInvocationRole(serviceName, functionName, triggerType) { }] }); - await attachPolicyToRole(otsReadPolicyName, invocationRoleName, 'Custom'); + await ram.attachPolicyToRole(otsReadPolicyName, invocationRoleName, 'Custom'); return invocationRole.Role; } @@ -971,7 +969,7 @@ async function makeApiTrigger({ restApiId }) { if (!restApiId) { - const role = await makeRole('apigatewayAccessFC'); + const role = await ram.makeRole('apigatewayAccessFC'); debug('%j', role); const apiGroup = await makeGroup({ @@ -1018,8 +1016,8 @@ async function makeOtsInstance(instanceName, clusterType, description) { ClusterType: clusterType, Description: description }, { - method: 'POST' - }); + method: 'POST' + }); } catch (ex) { if (ex.code === 'InvalidParameter' || ex.code === 'QuotaExhausted') { console.error(red(ex.message)); @@ -1092,5 +1090,6 @@ module.exports = { makeLogstore, makeLogstoreIndex, makeCustomDomain, - makeMnsTopic + makeMnsTopic, + makeInvocationRole }; \ No newline at end of file diff --git a/lib/ram.js b/lib/ram.js index 416587f57..024abdbfe 100644 --- a/lib/ram.js +++ b/lib/ram.js @@ -19,6 +19,9 @@ const getRamClient = async () => { }); }; +function normalizeRoleOrPoliceName(roleName) { + return roleName.replace(/_/g, '-'); +} async function deletePolicyNotDefaultVersion(ram, policyName) { const listResponse = await ram.listPolicyVersions({ @@ -180,5 +183,7 @@ async function makeAndAttachPolicy(policyName, policyDocument, roleName) { } module.exports = { - makeRole, makePolicy, attachPolicyToRole, makeAndAttachPolicy + makeRole, makePolicy, + attachPolicyToRole, makeAndAttachPolicy, + normalizeRoleOrPoliceName }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a0dbd5068..0e824b0a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@alicloud/fun", - "version": "2.7.0", + "version": "2.7.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -138,7 +138,7 @@ }, "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, "requires": { @@ -1729,7 +1729,7 @@ }, "git-ignore-parser": { "version": "0.0.2", - "resolved": "http://registry.npm.taobao.org/git-ignore-parser/download/git-ignore-parser-0.0.2.tgz", + "resolved": "https://registry.npmjs.org/git-ignore-parser/-/git-ignore-parser-0.0.2.tgz", "integrity": "sha1-fykXBKrv9mlvmHePzLJ2AbtzOTI=" }, "glob": { @@ -2753,7 +2753,7 @@ "dependencies": { "commander": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "dev": true, "requires": { diff --git a/package.json b/package.json index d8efd3c48..cc72f66b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@alicloud/fun", - "version": "2.7.0", + "version": "2.7.1", "description": "(have)Fun with Serverless", "engines": { "node": ">=8.0.0" diff --git a/test/deploy/deploy-support.test.js b/test/deploy/deploy-support.test.js index dedbc21fa..ff48ab9e4 100644 --- a/test/deploy/deploy-support.test.js +++ b/test/deploy/deploy-support.test.js @@ -2,10 +2,19 @@ var nock = require('nock'); -const deploySupport = require('../../lib/deploy/deploy-support'); +let deploySupport = require('../../lib/deploy/deploy-support'); + +const ram = require('../../lib/ram'); const { setProcess } = require('../test-utils'); +const proxyquire = require('proxyquire'); +const sinon = require('sinon'); +const sandbox = sinon.createSandbox(); +const assert = sinon.assert; + +const expect = require('expect.js'); + describe('make', () => { let restoreProcess; @@ -280,7 +289,7 @@ describe('make', () => { nock('http://apigateway.cn-shanghai.aliyuncs.com:80', { 'encodedQueryParams': true }) .get('/') .query(actualQueryObject => actualQueryObject.Action === 'ModifyApi') - .reply(200, { 'RequestId': '776A0731-DEDF-4F71-80B5-979B17F7034E' }, ); + .reply(200, { 'RequestId': '776A0731-DEDF-4F71-80B5-979B17F7034E' }); nock('http://apigateway.cn-shanghai.aliyuncs.com:80', { 'encodedQueryParams': true }) .get('/') @@ -297,4 +306,178 @@ describe('make', () => { }); }); +}); + +describe('make invocation role', () => { + + let restoreProcess; + + beforeEach(async () => { + + sandbox.stub(ram, 'makeRole').resolves({ + Role: 'generated-role-name' + }); + sandbox.stub(ram, 'makePolicy').resolves({}); + sandbox.stub(ram, 'attachPolicyToRole').resolves({}); + + deploySupport = await proxyquire('../../lib/deploy/deploy-support', { + '../../lib/ram': ram + }); + + restoreProcess = setProcess({ + ACCOUNT_ID: 'testAccountId', + ACCESS_KEY_ID: 'testKeyId', + ACCESS_KEY_SECRET: 'testKeySecret', + }); + }); + + afterEach(() => { + sandbox.restore(); + restoreProcess(); + }); + + it('makeInvocationRole of log', async () => { + const role = await deploySupport.makeInvocationRole('test-service_name', 'test-function_name', 'Log'); + + assert.calledOnce(ram.makeRole); + assert.calledWith(ram.makeRole, + 'AliyunFcGeneratedInvocationRole-test-service-name-test-function-name', + true, + 'Used for fc invocation', + { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: ['log.aliyuncs.com'] } + }], + Version: '1' + }); + + assert.calledOnce(ram.makePolicy); + assert.calledWith(ram.makePolicy, 'AliyunFcGeneratedInvocationPolicy-test-service-name-test-function-name', + { + Statement: [{ + Action: ['fc:InvokeFunction'], + Effect: 'Allow', + Resource: 'acs:fc:*:*:services/test-service_name/functions/*' + }, { + Action: ['log:Get*', 'log:List*', 'log:PostLogStoreLogs', 'log:CreateConsumerGroup', 'log:UpdateConsumerGroup', 'log:DeleteConsumerGroup', 'log:ListConsumerGroup', 'log:ConsumerGroupUpdateCheckPoint', 'log:ConsumerGroupHeartBeat', 'log:GetConsumerGroupCheckPoint'], + Effect: 'Allow', + Resource: '*' + }], + Version: '1' + }); + + assert.calledOnce(ram.attachPolicyToRole); + assert.calledWith(ram.attachPolicyToRole, 'AliyunFcGeneratedInvocationPolicy-test-service-name-test-function-name', 'AliyunFcGeneratedInvocationRole-test-service-name-test-function-name', 'Custom'); + + expect(role).to.be('generated-role-name'); + }); + + it('makeInvocationRole of rds', async () => { + const role = await deploySupport.makeInvocationRole('test-service_name', 'test-function_name', 'RDS'); + + assert.calledOnce(ram.makeRole); + assert.calledWith(ram.makeRole, + 'FunCreateRole-test-service-name-test-function-name', + true, + 'Used for fc invocation', + { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: ['rds.aliyuncs.com'] } + }], + Version: '1' + }); + + assert.calledOnce(ram.makePolicy); + assert.calledWith(ram.makePolicy, 'FunCreatePolicy-test-service-name-test-function-name', + { + Statement: [{ + Action: ['fc:InvokeFunction'], + Effect: 'Allow', + Resource: 'acs:fc:*:*:services/test-service_name/functions/*' + }], + Version: '1' + }); + + assert.calledOnce(ram.attachPolicyToRole); + assert.calledWith(ram.attachPolicyToRole, 'FunCreatePolicy-test-service-name-test-function-name', 'FunCreateRole-test-service-name-test-function-name', 'Custom'); + + expect(role).to.be('generated-role-name'); + }); + + it('makeInvocationRole of mns topic', async () => { + const role = await deploySupport.makeInvocationRole('test-service_name', 'test-function_name', 'MNSTopic'); + + assert.calledOnce(ram.makeRole); + assert.calledWith(ram.makeRole, + 'FunCreateRole-test-service-name-test-function-name', + true, + 'Used for fc invocation', + { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { Service: ['mns.aliyuncs.com'] } + }], + Version: '1' + }); + + assert.calledOnce(ram.makePolicy); + assert.calledWith(ram.makePolicy, 'FunCreatePolicy-test-service-name-test-function-name', + { + Statement: [{ + Action: ['fc:InvokeFunction'], + Effect: 'Allow', + Resource: 'acs:fc:*:*:services/test-service_name/functions/*' + }], + Version: '1' + }); + + assert.calledOnce(ram.attachPolicyToRole); + assert.calledWith(ram.attachPolicyToRole, 'FunCreatePolicy-test-service-name-test-function-name', 'FunCreateRole-test-service-name-test-function-name', 'Custom'); + + expect(role).to.be('generated-role-name'); + }); + + it('makeInvocationRole of table store', async () => { + const role = await deploySupport.makeInvocationRole('test-service_name', 'test-function_name', 'TableStore'); + + assert.calledOnce(ram.makeRole); + assert.calledWith(ram.makeRole, + 'FunCreateRole-test-service-name-test-function-name', + true, + 'Used for fc invocation', + { + Statement: [{ + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { RAM: ['acs:ram::1604337383174619:root'] } }], + Version: '1' + }); + + assert.calledTwice(ram.makePolicy); + assert.calledWith(ram.makePolicy.firstCall, 'FunCreateInvkPolicy-test-service-name-test-function-name', + { + Statement: [{ Action: ['fc:InvokeFunction'], Effect: 'Allow', Resource: '*' }], + Version: '1' + }); + assert.calledWith(ram.makePolicy.secondCall, 'FunCreateOtsReadPolicy-test-service-name-test-function-name', + { + Statement: [{ + Action: ['ots:BatchGet*', 'ots:Describe*', 'ots:Get*', 'ots:List*'], + Effect: 'Allow', + Resource: '*' + }], + Version: '1' + }); + + assert.calledTwice(ram.attachPolicyToRole); + assert.calledWith(ram.attachPolicyToRole.firstCall, 'FunCreateInvkPolicy-test-service-name-test-function-name', 'FunCreateRole-test-service-name-test-function-name', 'Custom'); + assert.calledWith(ram.attachPolicyToRole.secondCall, 'FunCreateOtsReadPolicy-test-service-name-test-function-name', 'FunCreateRole-test-service-name-test-function-name', 'Custom'); + + expect(role).to.be('generated-role-name'); + }); }); \ No newline at end of file