1 | 'use strict';
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | return new (P || (P = Promise))(function (resolve, reject) {
|
4 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
5 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
6 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
7 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
8 | });
|
9 | };
|
10 | const getProfile = require('./profile').getProfile;
|
11 | const { getFcClient } = require('./client');
|
12 | const debug = require('debug')('fun:trigger');
|
13 | const _ = require('lodash');
|
14 | const ram = require('./ram');
|
15 | const util = require('util');
|
16 | const { red, yellow } = require('colors');
|
17 | const triggerTypeMapping = {
|
18 | 'Datahub': 'datahub',
|
19 | 'Timer': 'timer',
|
20 | 'HTTP': 'http',
|
21 | 'Log': 'log',
|
22 | 'OSS': 'oss',
|
23 | 'RDS': 'rds',
|
24 | 'MNSTopic': 'mns_topic',
|
25 | 'TableStore': 'tablestore',
|
26 | 'CDN': 'cdn_events'
|
27 | };
|
28 | function getSourceArn(triggerType, triggerProperties) {
|
29 | return __awaiter(this, void 0, void 0, function* () {
|
30 | const profile = yield getProfile();
|
31 | if (triggerType === 'Log') {
|
32 | return `acs:log:${profile.defaultRegion}:${profile.accountId}:project/${triggerProperties.LogConfig.Project}`;
|
33 | }
|
34 | else if (triggerType === 'RDS') {
|
35 | return `acs:rds:${profile.defaultRegion}:${profile.accountId}:dbinstance/${triggerProperties.InstanceId}`;
|
36 | }
|
37 | else if (triggerType === 'MNSTopic') {
|
38 | if (triggerProperties.Region !== undefined) {
|
39 | return `acs:mns:${triggerProperties.Region}:${profile.accountId}:/topics/${triggerProperties.TopicName}`;
|
40 | }
|
41 | return `acs:mns:${profile.defaultRegion}:${profile.accountId}:/topics/${triggerProperties.TopicName}`;
|
42 | }
|
43 | else if (triggerType === 'TableStore') {
|
44 | return `acs:ots:${profile.defaultRegion}:${profile.accountId}:instance/${triggerProperties.InstanceName}/table/${triggerProperties.TableName}`;
|
45 | }
|
46 | else if (triggerType === 'OSS') {
|
47 | return `acs:oss:${profile.defaultRegion}:${profile.accountId}:${triggerProperties.BucketName || triggerProperties.bucketName}`;
|
48 | }
|
49 | else if (triggerType === 'CDN') {
|
50 | return `acs:cdn:*:${profile.accountId}`;
|
51 | }
|
52 | return;
|
53 | });
|
54 | }
|
55 | function getTriggerNameList({ serviceName, functionName }) {
|
56 | return __awaiter(this, void 0, void 0, function* () {
|
57 | const fc = yield getFcClient();
|
58 | var listTriggerResponse = yield fc.listTriggers(serviceName, functionName);
|
59 | var triggerNameArray = [];
|
60 | if (listTriggerResponse && listTriggerResponse.data.triggers) {
|
61 | triggerNameArray = listTriggerResponse.data.triggers.map(p => p.triggerName);
|
62 | }
|
63 | return triggerNameArray;
|
64 | });
|
65 | }
|
66 | function getTriggerConfig(triggerType, triggerProperties) {
|
67 | if (triggerType === 'Timer') {
|
68 | return {
|
69 | payload: triggerProperties.Payload,
|
70 | cronExpression: triggerProperties.CronExpression,
|
71 | enable: triggerProperties.Enable
|
72 | };
|
73 | }
|
74 | else if (triggerType === 'HTTP') {
|
75 | return {
|
76 | authType: (triggerProperties.AuthType).toLowerCase(),
|
77 | methods: triggerProperties.Methods
|
78 | };
|
79 | }
|
80 | else if (triggerType === 'Log') {
|
81 | const logConfig = triggerProperties.LogConfig;
|
82 | const jobConfig = triggerProperties.JobConfig;
|
83 | const sourceConfig = triggerProperties.SourceConfig;
|
84 | return {
|
85 | sourceConfig: {
|
86 | logstore: sourceConfig.Logstore
|
87 | },
|
88 | jobConfig: {
|
89 | maxRetryTime: jobConfig.MaxRetryTime,
|
90 | triggerInterval: jobConfig.TriggerInterval
|
91 | },
|
92 | logConfig: {
|
93 | project: logConfig.Project,
|
94 | logstore: logConfig.Logstore
|
95 | },
|
96 | functionParameter: triggerProperties.FunctionParameter || {},
|
97 | Enable: !(triggerProperties.Enable === false)
|
98 | };
|
99 | }
|
100 | else if (triggerType === 'RDS') {
|
101 | return {
|
102 | subscriptionObjects: triggerProperties.SubscriptionObjects,
|
103 | retry: triggerProperties.Retry,
|
104 | concurrency: triggerProperties.Concurrency,
|
105 | eventFormat: triggerProperties.EventFormat
|
106 | };
|
107 | }
|
108 | else if (triggerType === 'MNSTopic') {
|
109 | var notifyContentFormat = 'STREAM';
|
110 | if (triggerProperties.NotifyContentFormat !== undefined) {
|
111 | notifyContentFormat = triggerProperties.NotifyContentFormat;
|
112 | }
|
113 | var notifyStrategy = 'BACKOFF_RETRY';
|
114 | if (triggerProperties.NotifyStrategy !== undefined) {
|
115 | notifyStrategy = triggerProperties.NotifyStrategy;
|
116 | }
|
117 | var triggerCfg = {
|
118 | NotifyContentFormat: notifyContentFormat,
|
119 | NotifyStrategy: notifyStrategy
|
120 | };
|
121 | if (triggerProperties.FilterTag !== undefined) {
|
122 | triggerCfg.FilterTag = triggerProperties.FilterTag;
|
123 | }
|
124 | return triggerCfg;
|
125 | }
|
126 | else if (triggerType === 'TableStore') {
|
127 | return {};
|
128 | }
|
129 | else if (triggerType === 'OSS') {
|
130 | return {
|
131 | events: triggerProperties.Events || triggerProperties.events,
|
132 | filter: triggerProperties.Filter || triggerProperties.filter
|
133 | };
|
134 | }
|
135 | else if (triggerType === 'CDN') {
|
136 | return {
|
137 | eventName: triggerProperties.EventName,
|
138 | eventVersion: triggerProperties.EventVersion,
|
139 | notes: triggerProperties.Notes,
|
140 | filter: _.mapKeys(triggerProperties.Filter, (value, key) => {
|
141 | return _.lowerFirst(key);
|
142 | })
|
143 | };
|
144 | }
|
145 | console.error(`trigger type is ${triggerType} not supported.`);
|
146 | }
|
147 | function makeInvocationRole(serviceName, functionName, triggerType) {
|
148 | return __awaiter(this, void 0, void 0, function* () {
|
149 | if (triggerType === 'Log') {
|
150 | const invocationRoleName = ram.normalizeRoleOrPoliceName(`AliyunFcGeneratedInvocationRole-${serviceName}-${functionName}`);
|
151 | const invocationRole = yield ram.makeRole(invocationRoleName, true, 'Used for fc invocation', {
|
152 | 'Statement': [{
|
153 | 'Action': 'sts:AssumeRole',
|
154 | 'Effect': 'Allow',
|
155 | 'Principal': {
|
156 | 'Service': [
|
157 | 'log.aliyuncs.com'
|
158 | ]
|
159 | }
|
160 | }],
|
161 | 'Version': '1'
|
162 | });
|
163 | const policyName = ram.normalizeRoleOrPoliceName(`AliyunFcGeneratedInvocationPolicy-${serviceName}-${functionName}`);
|
164 | yield ram.makePolicy(policyName, {
|
165 | 'Version': '1',
|
166 | 'Statement': [{
|
167 | 'Action': [
|
168 | 'fc:InvokeFunction'
|
169 | ],
|
170 | 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`,
|
171 | 'Effect': 'Allow'
|
172 | },
|
173 | {
|
174 | 'Action': [
|
175 | 'log:Get*',
|
176 | 'log:List*',
|
177 | 'log:PostLogStoreLogs',
|
178 | 'log:CreateConsumerGroup',
|
179 | 'log:UpdateConsumerGroup',
|
180 | 'log:DeleteConsumerGroup',
|
181 | 'log:ListConsumerGroup',
|
182 | 'log:ConsumerGroupUpdateCheckPoint',
|
183 | 'log:ConsumerGroupHeartBeat',
|
184 | 'log:GetConsumerGroupCheckPoint'
|
185 | ],
|
186 | 'Resource': '*',
|
187 | 'Effect': 'Allow'
|
188 | }
|
189 | ]
|
190 | });
|
191 | yield ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom');
|
192 | return invocationRole.Role;
|
193 | }
|
194 | else if (triggerType === 'RDS' || triggerType === 'MNSTopic') {
|
195 | const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`);
|
196 | var tMap = {
|
197 | 'RDS': 'rds',
|
198 | 'MNSTopic': 'mns'
|
199 | };
|
200 | var principalService = util.format('%s.aliyuncs.com', tMap[triggerType]);
|
201 | const invocationRole = yield ram.makeRole(invocationRoleName, true, 'Used for fc invocation', {
|
202 | 'Statement': [{
|
203 | 'Action': 'sts:AssumeRole',
|
204 | 'Effect': 'Allow',
|
205 | 'Principal': {
|
206 | 'Service': [
|
207 | principalService
|
208 | ]
|
209 | }
|
210 | }],
|
211 | 'Version': '1'
|
212 | });
|
213 | const policyName = ram.normalizeRoleOrPoliceName(`FunCreatePolicy-${serviceName}-${functionName}`);
|
214 | yield ram.makePolicy(policyName, {
|
215 | 'Version': '1',
|
216 | 'Statement': [{
|
217 | 'Action': [
|
218 | 'fc:InvokeFunction'
|
219 | ],
|
220 | 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`,
|
221 | 'Effect': 'Allow'
|
222 | }]
|
223 | });
|
224 | yield ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom');
|
225 | return invocationRole.Role;
|
226 | }
|
227 | else if (triggerType === 'TableStore') {
|
228 | const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`);
|
229 | const invocationRole = yield ram.makeRole(invocationRoleName, true, 'Used for fc invocation', {
|
230 | 'Statement': [{
|
231 | 'Action': 'sts:AssumeRole',
|
232 | 'Effect': 'Allow',
|
233 | 'Principal': {
|
234 | 'RAM': [
|
235 | 'acs:ram::1604337383174619:root'
|
236 | ]
|
237 | }
|
238 | }],
|
239 | 'Version': '1'
|
240 | });
|
241 | const invkPolicyName = ram.normalizeRoleOrPoliceName(`FunCreateInvkPolicy-${serviceName}-${functionName}`);
|
242 | yield ram.makePolicy(invkPolicyName, {
|
243 | 'Version': '1',
|
244 | 'Statement': [{
|
245 | 'Action': [
|
246 | 'fc:InvokeFunction'
|
247 | ],
|
248 | 'Resource': '*',
|
249 | 'Effect': 'Allow'
|
250 | }]
|
251 | });
|
252 | yield ram.attachPolicyToRole(invkPolicyName, invocationRoleName, 'Custom');
|
253 | const otsReadPolicyName = ram.normalizeRoleOrPoliceName(`FunCreateOtsReadPolicy-${serviceName}-${functionName}`);
|
254 | yield ram.makePolicy(otsReadPolicyName, {
|
255 | 'Version': '1',
|
256 | 'Statement': [{
|
257 | 'Action': [
|
258 | 'ots:BatchGet*',
|
259 | 'ots:Describe*',
|
260 | 'ots:Get*',
|
261 | 'ots:List*'
|
262 | ],
|
263 | 'Resource': '*',
|
264 | 'Effect': 'Allow'
|
265 | }]
|
266 | });
|
267 | yield ram.attachPolicyToRole(otsReadPolicyName, invocationRoleName, 'Custom');
|
268 | return invocationRole.Role;
|
269 | }
|
270 | else if (triggerType === 'OSS') {
|
271 | const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`);
|
272 | const invocationRole = yield ram.makeRole(invocationRoleName, true, 'Used for fc invocation', {
|
273 | 'Statement': [
|
274 | {
|
275 | 'Action': 'sts:AssumeRole',
|
276 | 'Effect': 'Allow',
|
277 | 'Principal': {
|
278 | 'Service': [
|
279 | 'oss.aliyuncs.com'
|
280 | ]
|
281 | }
|
282 | }
|
283 | ],
|
284 | 'Version': '1'
|
285 | });
|
286 | const policyName = ram.normalizeRoleOrPoliceName(`FunCreateOSSPolicy-${serviceName}-${functionName}`);
|
287 | yield ram.makePolicy(policyName, {
|
288 | 'Version': '1',
|
289 | 'Statement': [{
|
290 | 'Action': [
|
291 | 'fc:InvokeFunction'
|
292 | ],
|
293 | 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`,
|
294 | 'Effect': 'Allow'
|
295 | }]
|
296 | });
|
297 | yield ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom');
|
298 | return invocationRole.Role;
|
299 | }
|
300 | else if (triggerType === 'CDN') {
|
301 | const invocationRoleName = ram.normalizeRoleOrPoliceName(`FunCreateRole-${serviceName}-${functionName}`);
|
302 | const invocationRole = yield ram.makeRole(invocationRoleName, true, 'Used for fc invocation', {
|
303 | 'Statement': [
|
304 | {
|
305 | 'Action': 'sts:AssumeRole',
|
306 | 'Effect': 'Allow',
|
307 | 'Principal': {
|
308 | 'Service': [
|
309 | 'cdn.aliyuncs.com'
|
310 | ]
|
311 | }
|
312 | }
|
313 | ],
|
314 | 'Version': '1'
|
315 | });
|
316 | const policyName = ram.normalizeRoleOrPoliceName(`FunCreateCDNPolicy-${serviceName}-${functionName}`);
|
317 | yield ram.makePolicy(policyName, {
|
318 | 'Version': '1',
|
319 | 'Statement': [{
|
320 | 'Action': [
|
321 | 'fc:InvokeFunction'
|
322 | ],
|
323 | 'Resource': `acs:fc:*:*:services/${serviceName}/functions/*`,
|
324 | 'Effect': 'Allow'
|
325 | }]
|
326 | });
|
327 | yield ram.attachPolicyToRole(policyName, invocationRoleName, 'Custom');
|
328 | return invocationRole.Role;
|
329 | }
|
330 | return;
|
331 | });
|
332 | }
|
333 | function deleteTrigger(serviceName, functionName, triggerName) {
|
334 | return __awaiter(this, void 0, void 0, function* () {
|
335 | const fc = yield getFcClient();
|
336 | yield fc.deleteTrigger(serviceName, functionName, triggerName);
|
337 | });
|
338 | }
|
339 | function makeTrigger({ serviceName, functionName, triggerName, triggerType, triggerProperties }) {
|
340 | return __awaiter(this, void 0, void 0, function* () {
|
341 | const fc = yield getFcClient();
|
342 | var trigger;
|
343 | try {
|
344 | trigger = yield fc.getTrigger(serviceName, functionName, triggerName);
|
345 | }
|
346 | catch (ex) {
|
347 | if (ex.code !== 'TriggerNotFound') {
|
348 | throw ex;
|
349 | }
|
350 | }
|
351 | const params = {
|
352 | triggerType: triggerTypeMapping[triggerType],
|
353 | triggerConfig: getTriggerConfig(triggerType, triggerProperties)
|
354 | };
|
355 | debug('serviceName is %s, functionName is %s, trigger params is %j', serviceName, functionName, params);
|
356 | let invocationRoleArn = triggerProperties.InvocationRole;
|
357 | if (!invocationRoleArn) {
|
358 | const invocationRole = yield makeInvocationRole(serviceName, functionName, triggerType);
|
359 | if (invocationRole) {
|
360 | invocationRoleArn = invocationRole.Arn;
|
361 | }
|
362 | }
|
363 | if (invocationRoleArn) {
|
364 | Object.assign(params, {
|
365 | 'invocationRole': invocationRoleArn
|
366 | });
|
367 | }
|
368 | const sourceArn = yield getSourceArn(triggerType, triggerProperties);
|
369 | if (sourceArn) {
|
370 | Object.assign(params, {
|
371 | 'sourceArn': sourceArn
|
372 | });
|
373 | }
|
374 | if (triggerProperties.Qualifier) {
|
375 | Object.assign(params, {
|
376 | 'qualifier': triggerProperties.Qualifier
|
377 | });
|
378 | }
|
379 | if (!trigger) {
|
380 | params.triggerName = triggerName;
|
381 | trigger = yield fc.createTrigger(serviceName, functionName, params);
|
382 | }
|
383 | else {
|
384 | if (triggerType === 'TableStore' || triggerType === 'MNSTopic') {
|
385 |
|
386 |
|
387 |
|
388 |
|
389 | console.log(red(`\t\tWarning: TableStore and MNSTopic Trigger cann't update`));
|
390 | return;
|
391 | }
|
392 | trigger = yield fc.updateTrigger(serviceName, functionName, triggerName, params);
|
393 | }
|
394 | return trigger;
|
395 | });
|
396 | }
|
397 | function displayTriggerInfo(serviceName, functionName, triggerName, triggerType, triggerProperties, wrap) {
|
398 | return __awaiter(this, void 0, void 0, function* () {
|
399 | if (triggerType === 'HTTP' || triggerType === 'http') {
|
400 | const profile = yield getProfile();
|
401 | const accountId = profile.accountId;
|
402 | const region = profile.defaultRegion;
|
403 | const resolveWrap = wrap || '';
|
404 | if (triggerName) {
|
405 | console.log(`${resolveWrap}triggerName: ${yellow(triggerName)}`);
|
406 | }
|
407 | console.log(`${resolveWrap}methods: ${yellow(triggerProperties.Methods || triggerProperties.methods)}`);
|
408 | console.log(`${resolveWrap}url: ` + yellow(`https://${accountId}.${region}.fc.aliyuncs.com/2016-08-15/proxy/${serviceName}/${functionName}/`));
|
409 | console.log(red(`${resolveWrap}Http Trigger will forcefully add a 'Content-Disposition: attachment' field to the response header, which cannot be overwritten \n${resolveWrap}and will cause the response to be downloaded as an attachment in the browser. This issue can be avoided by using CustomDomain.\n`));
|
410 | }
|
411 | });
|
412 | }
|
413 | module.exports = {
|
414 | getTriggerNameList,
|
415 | getTriggerConfig,
|
416 | makeTrigger,
|
417 | deleteTrigger,
|
418 | makeInvocationRole,
|
419 | displayTriggerInfo
|
420 | };
|