3
3
import traceback
4
4
from datetime import datetime
5
5
from functools import wraps
6
- from time import sleep
7
6
from typing import Any , Callable , MutableMapping , Optional , Tuple , Type , Union
8
7
9
8
from .boto3_proxy import SessionProxy , _get_boto_session
10
- from .callback import report_progress
11
9
from .exceptions import InternalFailure , InvalidRequest , _HandlerError
12
10
from .interface import (
13
11
Action ,
18
16
)
19
17
from .log_delivery import ProviderLogHandler
20
18
from .metrics import MetricsPublisherProxy
21
- from .scheduler import cleanup_cloudwatch_events , reschedule_after_minutes
22
19
from .utils import (
23
20
BaseModel ,
24
21
Credentials ,
32
29
LOG = logging .getLogger (__name__ )
33
30
34
31
MUTATING_ACTIONS = (Action .CREATE , Action .UPDATE , Action .DELETE )
35
- INVOCATION_TIMEOUT_MS = 60000
36
32
37
33
HandlerSignature = Callable [
38
34
[Optional [SessionProxy ], Any , MutableMapping [str , Any ]], ProgressEvent
@@ -72,38 +68,6 @@ def _add_handler(f: HandlerSignature) -> HandlerSignature:
72
68
73
69
return _add_handler
74
70
75
- @staticmethod
76
- def schedule_reinvocation (
77
- handler_request : HandlerRequest ,
78
- handler_response : ProgressEvent ,
79
- context : LambdaContext ,
80
- session : SessionProxy ,
81
- ) -> bool :
82
- if handler_response .status != OperationStatus .IN_PROGRESS :
83
- return False
84
- # modify requestContext dict in-place, so that invoke count is bumped on local
85
- # reinvoke too
86
- reinvoke_context = handler_request .requestContext
87
- reinvoke_context ["invocation" ] = reinvoke_context .get ("invocation" , 0 ) + 1
88
- callback_delay_s = handler_response .callbackDelaySeconds
89
- remaining_ms = context .get_remaining_time_in_millis ()
90
-
91
- # when a handler requests a sub-minute callback delay, and if the lambda
92
- # invocation has enough runtime (with 20% buffer), we can re-run the handler
93
- # locally otherwise we re-invoke through CloudWatchEvents
94
- needed_ms_remaining = callback_delay_s * 1200 + INVOCATION_TIMEOUT_MS
95
- if callback_delay_s < 60 and remaining_ms > needed_ms_remaining :
96
- sleep (callback_delay_s )
97
- return True
98
- callback_delay_min = int (callback_delay_s / 60 )
99
- reschedule_after_minutes (
100
- session ,
101
- function_arn = context .invoked_function_arn ,
102
- minutes_from_now = callback_delay_min ,
103
- handler_request = handler_request ,
104
- )
105
- return False
106
-
107
71
def _invoke_handler (
108
72
self ,
109
73
session : Optional [SessionProxy ],
@@ -169,26 +133,23 @@ def test_entrypoint(
169
133
def _parse_request (
170
134
event_data : MutableMapping [str , Any ]
171
135
) -> Tuple [
172
- Tuple [Optional [SessionProxy ], Optional [SessionProxy ], SessionProxy ],
136
+ Tuple [Optional [SessionProxy ], Optional [SessionProxy ]],
173
137
Action ,
174
138
MutableMapping [str , Any ],
175
139
HandlerRequest ,
176
140
]:
177
141
try :
178
142
event = HandlerRequest .deserialize (event_data )
179
- platform_sess = _get_boto_session (event .requestData .platformCredentials )
180
143
caller_sess = _get_boto_session (event .requestData .callerCredentials )
181
144
provider_sess = _get_boto_session (event .requestData .providerCredentials )
182
145
# credentials are used when rescheduling, so can't zero them out (for now)
183
- if platform_sess is None :
184
- raise ValueError ("No platform credentials" )
185
146
action = Action [event .action ]
186
- callback_context = event .requestContext . get ( " callbackContext" , {})
147
+ callback_context = event .callbackContext or {}
187
148
except Exception as e : # pylint: disable=broad-except
188
149
LOG .exception ("Invalid request" )
189
150
raise InvalidRequest (f"{ e } ({ type (e ).__name__ } )" ) from e
190
151
return (
191
- (caller_sess , provider_sess , platform_sess ),
152
+ (caller_sess , provider_sess ),
192
153
action ,
193
154
callback_context ,
194
155
event ,
@@ -224,66 +185,28 @@ def print_or_log(message: str) -> None:
224
185
225
186
try :
226
187
sessions , action , callback , event = self ._parse_request (event_data )
227
- caller_sess , provider_sess , platform_sess = sessions
188
+ caller_sess , provider_sess = sessions
228
189
ProviderLogHandler .setup (event , provider_sess )
229
190
logs_setup = True
230
191
231
192
request = self ._cast_resource_request (event )
232
193
233
194
metrics = MetricsPublisherProxy (event .awsAccountId , event .resourceType )
234
- metrics .add_metrics_publisher (platform_sess )
235
195
metrics .add_metrics_publisher (provider_sess )
236
- # Acknowledge the task for first time invocation
237
- if not event .requestContext :
238
- report_progress (
239
- platform_sess ,
240
- event .bearerToken ,
241
- None ,
242
- OperationStatus .IN_PROGRESS ,
243
- OperationStatus .PENDING ,
244
- None ,
245
- "" ,
246
- )
247
- else :
248
- # If this invocation was triggered by a 're-invoke' CloudWatch Event,
249
- # clean it up
250
- cleanup_cloudwatch_events (
251
- platform_sess ,
252
- event .requestContext .get ("cloudWatchEventsRuleName" , "" ),
253
- event .requestContext .get ("cloudWatchEventsTargetId" , "" ),
254
- )
255
- invoke = True
256
- while invoke :
257
- metrics .publish_invocation_metric (datetime .utcnow (), action )
258
- start_time = datetime .utcnow ()
259
- error = None
260
- try :
261
- progress = self ._invoke_handler (
262
- caller_sess , request , action , callback
263
- )
264
- except Exception as e : # pylint: disable=broad-except
265
- error = e
266
- m_secs = (datetime .utcnow () - start_time ).total_seconds () * 1000.0
267
- metrics .publish_duration_metric (datetime .utcnow (), action , m_secs )
268
- if error :
269
- metrics .publish_exception_metric (datetime .utcnow (), action , error )
270
- raise error
271
- if progress .callbackContext :
272
- callback = progress .callbackContext
273
- event .requestContext ["callbackContext" ] = callback
274
- if event .action in MUTATING_ACTIONS :
275
- report_progress (
276
- platform_sess ,
277
- event .bearerToken ,
278
- progress .errorCode ,
279
- progress .status ,
280
- OperationStatus .IN_PROGRESS ,
281
- progress .resourceModel ,
282
- progress .message ,
283
- )
284
- invoke = self .schedule_reinvocation (
285
- event , progress , context , platform_sess
286
- )
196
+
197
+ metrics .publish_invocation_metric (datetime .utcnow (), action )
198
+ start_time = datetime .utcnow ()
199
+ error = None
200
+
201
+ try :
202
+ progress = self ._invoke_handler (caller_sess , request , action , callback )
203
+ except Exception as e : # pylint: disable=broad-except
204
+ error = e
205
+ m_secs = (datetime .utcnow () - start_time ).total_seconds () * 1000.0
206
+ metrics .publish_duration_metric (datetime .utcnow (), action , m_secs )
207
+ if error :
208
+ metrics .publish_exception_metric (datetime .utcnow (), action , error )
209
+ raise error
287
210
except _HandlerError as e :
288
211
print_or_log ("Handler error" )
289
212
progress = e .to_progress_event ()
@@ -296,6 +219,4 @@ def print_or_log(message: str) -> None:
296
219
297
220
# use the raw event_data as a last-ditch attempt to call back if the
298
221
# request is invalid
299
- return progress ._serialize ( # pylint: disable=protected-access
300
- to_response = True , bearer_token = event_data .get ("bearerToken" )
301
- )
222
+ return progress ._serialize () # pylint: disable=protected-access
0 commit comments