UNPKG

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