17
17
import com .amazonaws .AmazonServiceException ;
18
18
import com .amazonaws .retry .RetryUtils ;
19
19
import com .fasterxml .jackson .core .type .TypeReference ;
20
+ import java .io .ByteArrayOutputStream ;
20
21
import java .io .IOException ;
21
22
import java .io .InputStream ;
22
23
import java .io .OutputStream ;
24
+ import java .net .URL ;
23
25
import java .nio .charset .StandardCharsets ;
24
26
import java .time .Instant ;
27
+ import java .util .Collections ;
25
28
import java .util .Date ;
29
+ import java .util .Map ;
30
+
31
+ import com .google .common .annotations .VisibleForTesting ;
26
32
import org .apache .commons .io .FileUtils ;
27
33
import org .apache .commons .io .IOUtils ;
28
34
import org .apache .commons .lang3 .exception .ExceptionUtils ;
31
37
import org .slf4j .Logger ;
32
38
import org .slf4j .LoggerFactory ;
33
39
import software .amazon .awssdk .awscore .exception .AwsServiceException ;
40
+ import software .amazon .awssdk .http .HttpExecuteRequest ;
41
+ import software .amazon .awssdk .http .HttpExecuteResponse ;
34
42
import software .amazon .awssdk .http .HttpStatusCode ;
35
43
import software .amazon .awssdk .http .HttpStatusFamily ;
36
44
import software .amazon .awssdk .http .SdkHttpClient ;
45
+ import software .amazon .awssdk .http .SdkHttpMethod ;
46
+ import software .amazon .awssdk .http .SdkHttpRequest ;
37
47
import software .amazon .awssdk .http .apache .ApacheHttpClient ;
48
+ import software .amazon .awssdk .utils .IoUtils ;
38
49
import software .amazon .cloudformation .encryption .Cipher ;
39
50
import software .amazon .cloudformation .encryption .KMSCipher ;
40
51
import software .amazon .cloudformation .exceptions .BaseHandlerException ;
63
74
import software .amazon .cloudformation .proxy .hook .HookInvocationRequest ;
64
75
import software .amazon .cloudformation .proxy .hook .HookProgressEvent ;
65
76
import software .amazon .cloudformation .proxy .hook .HookRequestContext ;
77
+ import software .amazon .cloudformation .proxy .hook .HookRequestData ;
66
78
import software .amazon .cloudformation .proxy .hook .HookStatus ;
67
79
import software .amazon .cloudformation .resource .SchemaValidator ;
68
80
import software .amazon .cloudformation .resource .Serializer ;
@@ -89,6 +101,9 @@ public abstract class HookAbstractWrapper<TargetT, CallbackT, ConfigurationT> {
89
101
final SchemaValidator validator ;
90
102
final TypeReference <HookInvocationRequest <ConfigurationT , CallbackT >> typeReference ;
91
103
104
+ final TypeReference <Map <String , Object >> hookStackPayloadS3TypeReference = new TypeReference <>() {
105
+ };
106
+
92
107
private MetricsPublisher providerMetricsPublisher ;
93
108
94
109
private CloudWatchLogHelper cloudWatchLogHelper ;
@@ -222,18 +237,20 @@ private ProgressEvent<TargetT, CallbackT> processInvocation(final JSONObject raw
222
237
223
238
assert request != null : "Invalid request object received. Request object is null" ;
224
239
225
- if (request .getRequestData () == null || request .getRequestData ().getTargetModel () == null ) {
226
- throw new TerminalException ("Invalid request object received. Target Model can not be null." );
227
- }
228
-
229
- // TODO: Include hook schema validation here after schema is finalized
240
+ boolean isPayloadRemote = isHookInvocationPayloadRemote (request .getRequestData ());
230
241
231
242
try {
232
243
// initialise dependencies with platform credentials
233
244
initialiseRuntime (request .getHookTypeName (), request .getRequestData ().getProviderCredentials (),
234
245
request .getRequestData ().getProviderLogGroupName (), request .getAwsAccountId (),
235
246
request .getRequestData ().getHookEncryptionKeyArn (), request .getRequestData ().getHookEncryptionKeyRole ());
236
247
248
+ if (isPayloadRemote ) {
249
+ Map <String , Object > targetModelData = retrieveHookInvocationPayloadFromS3 (request .getRequestData ().getPayload ());
250
+
251
+ request .getRequestData ().setTargetModel (targetModelData );
252
+ }
253
+
237
254
// transform the request object to pass to caller
238
255
HookHandlerRequest hookHandlerRequest = transform (request );
239
256
ConfigurationT typeConfiguration = request .getHookModel ();
@@ -366,6 +383,50 @@ private void writeResponse(final OutputStream outputStream, final HookProgressEv
366
383
outputStream .flush ();
367
384
}
368
385
386
+ public Map <String , Object > retrieveHookInvocationPayloadFromS3 (final String s3PresignedUrl ) {
387
+ if (s3PresignedUrl != null ) {
388
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream ();
389
+
390
+ try {
391
+ URL presignedUrl = new URL (s3PresignedUrl );
392
+ SdkHttpRequest httpRequest = SdkHttpRequest .builder ().method (SdkHttpMethod .GET ).uri (presignedUrl .toURI ())
393
+ .build ();
394
+
395
+ HttpExecuteRequest executeRequest = HttpExecuteRequest .builder ().request (httpRequest ).build ();
396
+
397
+ HttpExecuteResponse response = HTTP_CLIENT .prepareRequest (executeRequest ).call ();
398
+
399
+ response .responseBody ().ifPresentOrElse (abortableInputStream -> {
400
+ try {
401
+ IoUtils .copy (abortableInputStream , byteArrayOutputStream );
402
+ } catch (IOException e ) {
403
+ throw new RuntimeException (e );
404
+ }
405
+ }, () -> loggerProxy .log ("Hook invocation payload is empty." ));
406
+
407
+ String str = byteArrayOutputStream .toString (StandardCharsets .UTF_8 );
408
+
409
+ return this .serializer .deserialize (str , hookStackPayloadS3TypeReference );
410
+ } catch (Exception exp ) {
411
+ loggerProxy .log ("Failed to retrieve hook invocation payload" + exp .toString ());
412
+ }
413
+ }
414
+ return Collections .emptyMap ();
415
+ }
416
+
417
+ @ VisibleForTesting
418
+ protected boolean isHookInvocationPayloadRemote (HookRequestData hookRequestData ) {
419
+ if (hookRequestData == null || hookRequestData .getTargetModel () == null ) {
420
+ throw new TerminalException ("Invalid request object received. Target Model can not be null." );
421
+ }
422
+
423
+ if (hookRequestData .getTargetModel ().isEmpty () && hookRequestData .getPayload () == null ) {
424
+ throw new TerminalException ("No payload data set." );
425
+ }
426
+
427
+ return hookRequestData .getTargetModel ().isEmpty ();
428
+ }
429
+
369
430
/**
370
431
* Transforms the incoming request to the subset of typed models which the
371
432
* handler implementor needs
0 commit comments