1414// Includes support for listing, installing, and removing triggers, when such triggers are attached to
1515// an action.
1616
17- // TEMPORARY: some of this code will, if requested, invoke actions in /nimbella/triggers/[create|delete|list]
18- // rather than the trigger APIs defined on api.digitalocean.com/v2/functions. The actions in question
19- // implement a prototype verion of the API which is still being used as a reference implementation until
20- // the trigger APIs are stable and adequate. The two are not functionally equivalent in that the prototype
21- // API will result in updates to the public scheduling service (not the production one in DOCC).
22- //
23- // To request the prototype API set TRIGGERS_USE_PROTOTYPE non-empty in the environment.
24- //
25- // Unless the prototype API is used, the DO_API_KEY environment variable must be set to the DO API key that
26- // should be used to contact the DO API endpoint. In doctl this key will be placed in the process
27- // environment of the subprocess that runs the deployer. If the deployer is invoked via nim rather than
28- // doctl, the invoking context must set this key (e.g. in the API build container or for testing).
29- //
30- // If neither environment variable is set, then trigger operations are skipped (become no-ops).
31- // Reasoning: the deployer should always be called by one of
32- // - doctl, which will always set DO_API_KEY
33- // - tests, which will knowingly set one of the two
34- // - the AP build container, which should have maximum flexibility to either (1) refuse to deploy if
35- // the project has triggers, (2) set DO_API_KEY in the subprocess enviornment when invoking nim, (3)
36- // proceed with a warning and without deploying triggers.
37- // And, the list operation, at least, will be called routinely during cleanup operations and must be
38- // able to act as a no-op (return the empty list) when triggers are not being used.
17+ // Note well: this code is prepared to succeed silently (no-op) in contexts where the authorization to
18+ // manage triggers is absent. To make the code do something, the DO_API_KEY environment variable must be
19+ // set to the DO API key that should be used to contact the DO API endpoint. In doctl, this key will be
20+ // placed in the process environment of the subprocess that runs the deployer. If the deployer is invoked
21+ // via nim rather than doctl, the invoking context must set this key (e.g. in the app platform build container
22+ // or for testing).
3923
40- import openwhisk from 'openwhisk'
4124import { TriggerSpec , SchedulerSourceDetails , DeploySuccess } from './deploy-struct'
4225import { default as axios , AxiosRequestConfig } from 'axios'
4326import makeDebug from 'debug'
4427const debug = makeDebug ( 'nim:deployer:triggers' )
45- const usePrototype = process . env . TRIGGERS_USE_PROTOTYPE
4628const doAPIKey = process . env . DO_API_KEY
4729const doAPIEndpoint = 'https://api.digitalocean.com'
4830
49- export async function deployTriggers ( triggers : TriggerSpec [ ] , functionName : string , wsk : openwhisk . Client ,
50- namespace : string ) : Promise < ( DeploySuccess | Error ) [ ] > {
31+ // Returns an array. Elements are a either DeploySuccess structure for use in reporting when the trigger
32+ // is deployed successfully, or an Error if the trigger failed to deploy. These are 1-1 with the input
33+ // triggers argument.
34+ export async function deployTriggers ( triggers : TriggerSpec [ ] , functionName : string , namespace : string ) : Promise < ( DeploySuccess | Error ) [ ] > {
5135 const result : ( DeploySuccess | Error ) [ ] = [ ]
5236 for ( const trigger of triggers ) {
53- result . push ( await deployTrigger ( trigger , functionName , wsk , namespace ) )
37+ result . push ( await deployTrigger ( trigger , functionName , namespace ) )
5438 }
5539 debug ( 'finished deploying triggers, returning result' )
5640 return result
5741}
5842
59- export async function undeployTriggers ( triggers : string [ ] , wsk : openwhisk . Client , namespace : string ) : Promise < ( Error | true ) [ ] > {
60- const result : ( Error | true ) [ ] = [ ]
43+ // Undeploy one or more triggers. Returns the empty array on success. Otherwise, the return
44+ // contains all the errors from attempted deletions.
45+ export async function undeployTriggers ( triggers : string [ ] , namespace : string ) : Promise < Error [ ] > {
46+ const errors : any [ ] = [ ]
6147 for ( const trigger of triggers ) {
6248 try {
63- await undeployTrigger ( trigger , wsk , namespace )
64- result . push ( true )
49+ await undeployTrigger ( trigger , namespace )
6550 } catch ( err ) {
66- // See comment in catch clause in deployTrigger
67- if ( typeof err === 'string' ) {
68- result . push ( Error ( err ) )
69- } else {
70- result . push ( err as Error )
71- }
51+ const msg = err . message ? err . message : err
52+ errors . push ( new Error ( `unable to undeploy trigger '${ trigger } ': ${ msg } ` ) )
7253 }
7354 }
74- return result
55+ return errors
7556}
7657
7758// Code to deploy a trigger.
7859// Note that basic structural validation of each trigger has been done previously
7960// so paranoid checking is omitted.
80- async function deployTrigger ( trigger : TriggerSpec , functionName : string , wsk : openwhisk . Client , namespace : string ) : Promise < DeploySuccess | Error > {
61+ async function deployTrigger ( trigger : TriggerSpec , functionName : string , namespace : string ) : Promise < DeploySuccess | Error > {
8162 const details = trigger . sourceDetails as SchedulerSourceDetails
8263 const { cron, withBody } = details
83- const { sourceType, enabled } = trigger
84- const params = {
85- triggerName : trigger . name ,
86- function : functionName ,
87- sourceType,
88- cron,
89- withBody,
90- overwrite : true ,
91- enabled
92- }
64+ const { enabled } = trigger
9365 try {
94- if ( ! usePrototype && doAPIKey ) {
95- // Call the real API
96- debug ( 'calling the real trigger API to create %s' , trigger . name )
66+ if ( doAPIKey ) {
67+ debug ( 'calling the trigger API to create %s' , trigger . name )
9768 return await doTriggerCreate ( trigger . name , functionName , namespace , cron , enabled , withBody )
98- } else if ( usePrototype ) {
99- // Call the prototype API
100- await wsk . actions . invoke ( {
101- name : '/nimbella/triggers/create' ,
102- params,
103- blocking : true ,
104- result : true
105- } )
106- return { name : trigger . name , kind : 'trigger' , skipped : false }
107- }
69+ } // otherwise do nothing
10870 } catch ( err ) {
10971 debug ( 'caught an error while deploying trigger; will return it' )
11072 // Assume 'err' is either string or Error in the following. Actually it can be anything but use of
@@ -155,22 +117,10 @@ async function doAxios(config: AxiosRequestConfig): Promise<object> {
155117}
156118
157119// Code to delete a trigger.
158- async function undeployTrigger ( trigger : string , wsk : openwhisk . Client , namespace : string ) {
120+ async function undeployTrigger ( trigger : string , namespace : string ) {
159121 debug ( 'undeploying trigger %s' , trigger )
160- if ( doAPIKey && ! usePrototype ) {
161- // Use the real API
122+ if ( doAPIKey ) {
162123 return doTriggerDelete ( trigger , namespace )
163- } else if ( usePrototype ) {
164- // Prototype API
165- const params = {
166- triggerName : trigger
167- }
168- return await wsk . actions . invoke ( {
169- name : '/nimbella/triggers/delete' ,
170- params,
171- blocking : true ,
172- result : true
173- } )
174124 }
175125 // Else no-up. A Promise<void> (non-error) is returned implicitly
176126}
@@ -184,32 +134,17 @@ async function doTriggerDelete(trigger: string, namespace: string): Promise<obje
184134 return doAxios ( config )
185135}
186136
187- // Code to get all the triggers for a namespace, or all the triggers for a function in the
188- // namespace.
189- export async function listTriggersForNamespace ( wsk : openwhisk . Client , namespace : string , fcn ?: string ) : Promise < string [ ] > {
137+ // Code to get all the triggers for a namespace, or all the triggers for a function in the namespace.
138+ export async function listTriggersForNamespace ( namespace : string , fcn ?: string ) : Promise < string [ ] > {
190139 debug ( 'listing triggers' )
191- if ( doAPIKey && ! usePrototype ) {
192- // Use the real API
140+ if ( doAPIKey ) {
193141 return doTriggerList ( namespace , fcn )
194- } else if ( usePrototype ) {
195- // Use the prototype API
196- const params : any = {
197- name : '/nimbella/triggers/list' ,
198- blocking : true ,
199- result : true
200- }
201- if ( fcn ) {
202- params . params = { function : fcn }
203- }
204- const triggers : any = await wsk . actions . invoke ( params )
205- debug ( 'triggers listed' )
206- return triggers . items . map ( ( trigger : any ) => trigger . triggerName )
207142 }
208143 // No-op if no envvars are set
209144 return [ ]
210145}
211146
212- // The following is my best guess on what the trigger list API is planning to return
147+ // The trigger list API returns this structure (abridged here to just what we care about)
213148interface TriggerList {
214149 triggers : TriggerInfo [ ]
215150}
0 commit comments