11const { Component } = require ( '@serverless/core' )
2- const { MultiApigw , Scf, Apigw, Cns } = require ( 'tencent-component-toolkit' )
2+ const { Scf, Apigw, Cns, Cam } = require ( 'tencent-component-toolkit' )
33const { TypeError } = require ( 'tencent-component-toolkit/src/utils/error' )
4- const { uploadCodeToCos, getDefaultProtocol, deleteRecord , prepareInputs } = require ( './utils' )
4+ const { uploadCodeToCos, getDefaultProtocol, prepareInputs , deepClone } = require ( './utils' )
55const CONFIGS = require ( './config' )
66
77class ServerlessComponent extends Component {
@@ -27,135 +27,153 @@ class ServerlessComponent extends Component {
2727 }
2828
2929 async deployFunction ( credentials , inputs , regionList ) {
30- const uploadCodeHandler = [ ]
30+ if ( ! inputs . role ) {
31+ try {
32+ const camClient = new Cam ( credentials )
33+ const roleExist = await camClient . CheckSCFExcuteRole ( )
34+ if ( roleExist ) {
35+ inputs . role = 'QCS_SCFExcuteRole'
36+ }
37+ } catch ( e ) {
38+ // no op
39+ }
40+ }
41+
3142 const outputs = { }
3243 const appId = this . getAppId ( )
3344
34- for ( let eveRegionIndex = 0 ; eveRegionIndex < regionList . length ; eveRegionIndex ++ ) {
35- const curRegion = regionList [ eveRegionIndex ]
36- const funcDeployer = async ( ) => {
37- const code = await uploadCodeToCos ( this , appId , credentials , inputs , curRegion )
38- const scf = new Scf ( credentials , curRegion )
39- const tempInputs = {
40- ...inputs ,
41- code
42- }
43- const scfOutput = await scf . deploy ( tempInputs )
44- outputs [ curRegion ] = {
45- functionName : scfOutput . FunctionName ,
46- runtime : scfOutput . Runtime ,
47- namespace : scfOutput . Namespace
48- }
49-
50- this . state [ curRegion ] = {
51- ...( this . state [ curRegion ] ? this . state [ curRegion ] : { } ) ,
52- ...outputs [ curRegion ]
53- }
45+ const funcDeployer = async ( curRegion ) => {
46+ const code = await uploadCodeToCos ( this , appId , credentials , inputs , curRegion )
47+ const scf = new Scf ( credentials , curRegion )
48+ const tempInputs = {
49+ ...inputs ,
50+ code
51+ }
52+ const scfOutput = await scf . deploy ( deepClone ( tempInputs ) )
53+ outputs [ curRegion ] = {
54+ functionName : scfOutput . FunctionName ,
55+ runtime : scfOutput . Runtime ,
56+ namespace : scfOutput . Namespace
57+ }
5458
55- // default version is $LATEST
56- outputs [ curRegion ] . lastVersion = scfOutput . LastVersion
57- ? scfOutput . LastVersion
58- : this . state . lastVersion || '$LATEST'
59-
60- // default traffic is 1.0, it can also be 0, so we should compare to undefined
61- outputs [ curRegion ] . traffic =
62- scfOutput . Traffic !== undefined
63- ? scfOutput . Traffic
64- : this . state . traffic !== undefined
65- ? this . state . traffic
66- : 1
67-
68- if ( outputs [ curRegion ] . traffic !== 1 && scfOutput . ConfigTrafficVersion ) {
69- outputs [ curRegion ] . configTrafficVersion = scfOutput . ConfigTrafficVersion
70- this . state . configTrafficVersion = scfOutput . ConfigTrafficVersion
71- }
59+ this . state [ curRegion ] = {
60+ ...( this . state [ curRegion ] ? this . state [ curRegion ] : { } ) ,
61+ ...outputs [ curRegion ]
62+ }
7263
73- this . state . lastVersion = outputs [ curRegion ] . lastVersion
74- this . state . traffic = outputs [ curRegion ] . traffic
64+ // default version is $LATEST
65+ outputs [ curRegion ] . lastVersion = scfOutput . LastVersion
66+ ? scfOutput . LastVersion
67+ : this . state . lastVersion || '$LATEST'
68+
69+ // default traffic is 1.0, it can also be 0, so we should compare to undefined
70+ outputs [ curRegion ] . traffic =
71+ scfOutput . Traffic !== undefined
72+ ? scfOutput . Traffic
73+ : this . state . traffic !== undefined
74+ ? this . state . traffic
75+ : 1
76+
77+ if ( outputs [ curRegion ] . traffic !== 1 && scfOutput . ConfigTrafficVersion ) {
78+ outputs [ curRegion ] . configTrafficVersion = scfOutput . ConfigTrafficVersion
79+ this . state . configTrafficVersion = scfOutput . ConfigTrafficVersion
7580 }
76- uploadCodeHandler . push ( funcDeployer ( ) )
81+
82+ this . state . lastVersion = outputs [ curRegion ] . lastVersion
83+ this . state . traffic = outputs [ curRegion ] . traffic
84+ }
85+
86+ for ( let i = 0 ; i < regionList . length ; i ++ ) {
87+ const curRegion = regionList [ i ]
88+ await funcDeployer ( curRegion )
7789 }
78- await Promise . all ( uploadCodeHandler )
7990 this . save ( )
8091 return outputs
8192 }
8293
94+ // try to add dns record
95+ async tryToAddDnsRecord ( credentials , customDomains ) {
96+ try {
97+ const cns = new Cns ( credentials )
98+ for ( let i = 0 ; i < customDomains . length ; i ++ ) {
99+ const item = customDomains [ i ]
100+ if ( item . domainPrefix ) {
101+ await cns . deploy ( {
102+ domain : item . subDomain . replace ( `${ item . domainPrefix } .` , '' ) ,
103+ records : [
104+ {
105+ subDomain : item . domainPrefix ,
106+ recordType : 'CNAME' ,
107+ recordLine : '默认' ,
108+ value : item . cname ,
109+ ttl : 600 ,
110+ mx : 10 ,
111+ status : 'enable'
112+ }
113+ ]
114+ } )
115+ }
116+ }
117+ } catch ( e ) {
118+ console . log ( 'METHOD_tryToAddDnsRecord' , e . message )
119+ }
120+ }
121+
83122 async deployApigateway ( credentials , inputs , regionList ) {
84123 if ( inputs . isDisabled ) {
85124 return { }
86125 }
87- const apigw = new MultiApigw ( credentials , regionList )
88- const oldState = this . state [ regionList [ 0 ] ] || { }
89- inputs . oldState = {
90- apiList : oldState . apiList || [ ] ,
91- customDomains : oldState . customDomains || [ ]
126+
127+ const getServiceId = ( instance , region ) => {
128+ const regionState = instance . state [ region ]
129+ return inputs . serviceId || ( regionState && regionState . serviceId )
92130 }
93- const apigwOutputs = await apigw . deploy ( inputs )
94- const outputs = { }
95- Object . keys ( apigwOutputs ) . forEach ( ( curRegion ) => {
96- const curOutput = apigwOutputs [ curRegion ]
97- outputs [ curRegion ] = {
98- serviceId : curOutput . serviceId ,
99- subDomain : curOutput . subDomain ,
100- environment : curOutput . environment ,
101- url : `${ getDefaultProtocol ( inputs . protocols ) } ://${ curOutput . subDomain } /${
102- curOutput . environment
103- } /`
104- }
105- if ( curOutput . customDomains ) {
106- outputs [ curRegion ] . customDomains = curOutput . customDomains
107- }
108- this . state [ curRegion ] = {
109- created : curOutput . created ,
110- ...( this . state [ curRegion ] ? this . state [ curRegion ] : { } ) ,
111- ...outputs [ curRegion ] ,
112- apiList : curOutput . apiList
113- }
114- } )
115- this . save ( )
116- return outputs
117- }
118131
119- async deployCns ( credentials , inputs , regionList , apigwOutputs ) {
120- const cns = new Cns ( credentials )
121- const cnsRegion = { }
132+ const deployTasks = [ ]
133+ const outputs = { }
122134 regionList . forEach ( ( curRegion ) => {
123- const curApigwOutput = apigwOutputs [ curRegion ]
124- cnsRegion [ curRegion ] = curApigwOutput . subDomain
125- } )
135+ const apigwDeployer = async ( ) => {
136+ const apigw = new Apigw ( credentials , curRegion )
126137
127- const state = [ ]
128- const outputs = { }
129- const tempJson = { }
130- for ( let i = 0 ; i < inputs . length ; i ++ ) {
131- const curCns = inputs [ i ]
132- for ( let j = 0 ; j < curCns . records . length ; j ++ ) {
133- curCns . records [ j ] . value =
134- cnsRegion [ curCns . records [ j ] . value . replace ( 'temp_value_about_' , '' ) ]
135- }
136- const tencentCnsOutputs = await cns . deploy ( curCns )
137- outputs [ curCns . domain ] = tencentCnsOutputs . DNS
138- ? tencentCnsOutputs . DNS
139- : 'The domain name has already been added.'
140- tencentCnsOutputs . domain = curCns . domain
141- state . push ( tencentCnsOutputs )
142- }
138+ const oldState = this . state [ curRegion ] || { }
139+ const apigwInputs = {
140+ ...inputs ,
141+ oldState : {
142+ apiList : oldState . apiList || [ ] ,
143+ customDomains : oldState . customDomains || [ ]
144+ }
145+ }
146+ // different region deployment has different service id
147+ apigwInputs . serviceId = getServiceId ( this , curRegion )
148+ const apigwOutput = await apigw . deploy ( deepClone ( apigwInputs ) )
149+ outputs [ curRegion ] = {
150+ serviceId : apigwOutput . serviceId ,
151+ subDomain : apigwOutput . subDomain ,
152+ environment : apigwOutput . environment ,
153+ url : `${ getDefaultProtocol ( inputs . protocols ) } ://${ apigwOutput . subDomain } /${
154+ apigwOutput . environment
155+ } /`
156+ }
143157
144- // 删除serverless创建的但是不在本次列表中
145- try {
146- for ( let i = 0 ; i < state . length ; i ++ ) {
147- tempJson [ state [ i ] . domain ] = state [ i ] . records
148- }
149- const recordHistory = this . state . cns || [ ]
150- for ( let i = 0 ; i < recordHistory . length ; i ++ ) {
151- const delList = deleteRecord ( tempJson [ recordHistory [ i ] . domain ] , recordHistory [ i ] . records )
152- if ( delList && delList . length > 0 ) {
153- await cns . remove ( { deleteList : delList } )
158+ if ( apigwOutput . customDomains ) {
159+ // TODO: need confirm add cns authentication
160+ if ( inputs . autoAddDnsRecord === true ) {
161+ // await this.tryToAddDnsRecord(credentials, apigwOutput.customDomains)
162+ }
163+ outputs [ curRegion ] . customDomains = apigwOutput . customDomains
164+ }
165+ this . state [ curRegion ] = {
166+ created : true ,
167+ ...( this . state [ curRegion ] ? this . state [ curRegion ] : { } ) ,
168+ ...outputs [ curRegion ] ,
169+ apiList : apigwOutput . apiList
154170 }
155171 }
156- } catch ( e ) { }
172+ deployTasks . push ( apigwDeployer ( ) )
173+ } )
174+
175+ await Promise . all ( deployTasks )
157176
158- this . state [ 'cns' ] = state
159177 this . save ( )
160178 return outputs
161179 }
@@ -166,7 +184,7 @@ class ServerlessComponent extends Component {
166184 const credentials = this . getCredentials ( )
167185
168186 // 对Inputs内容进行标准化
169- const { regionList, functionConf, apigatewayConf, cnsConf } = await prepareInputs (
187+ const { regionList, functionConf, apigatewayConf } = await prepareInputs (
170188 this ,
171189 credentials ,
172190 inputs
@@ -198,11 +216,6 @@ class ServerlessComponent extends Component {
198216 outputs [ 'scf' ] = functionOutputs
199217 }
200218
201- // cns depends on apigw, so if disabled apigw, just ignore it.
202- if ( cnsConf . length > 0 && apigatewayConf . isDisabled !== true ) {
203- outputs [ 'cns' ] = await this . deployCns ( credentials , cnsConf , regionList , apigwOutputs )
204- }
205-
206219 this . state . region = regionList [ 0 ]
207220 this . state . regionList = regionList
208221 this . state . lambdaArn = functionConf . name
0 commit comments