1 | var url = require('url');
|
2 | var https = require('https');
|
3 | var crypto = require('crypto');
|
4 |
|
5 | module.exports = Response;
|
6 |
|
7 | /**
|
8 | * Represents a response to CloudFormation indicating whether or not the custom
|
9 | * resource was created successfully.
|
10 | * @param {object} event - the Lambda invocation event
|
11 | * @param {object} context - the Lambda invocation context
|
12 | * @example
|
13 | * function MyCustomLambdaFunction(event, context) {
|
14 | * var response = new Response(event, context);
|
15 | *
|
16 | * manageCustomResource(event.RequestType, event.RequestProperties, function(err, id) {
|
17 | * if (err) return response.send(err);
|
18 | * response.setId(id);
|
19 | * response.send(null, { additional: 'properties' });
|
20 | * });
|
21 | * }
|
22 | */
|
23 | function Response(event, context) {
|
24 | var parsedUrl = url.parse(event.ResponseURL);
|
25 |
|
26 | /**
|
27 | * Data to be sent in the response body
|
28 | * @memberof Response
|
29 | * @instance
|
30 | * @property {string} Status - either `FAILED` or `SUCCESS`
|
31 | * @property {string} Reason - a description is required if status is set to `FAILED`
|
32 | * @property {string} PhysicalResourceId - the physical id of the managed backend
|
33 | * resource. This will be provided in an `Update` or `Delete` event, and must be
|
34 | * generated during a `Create` event.
|
35 | * @property {string} StackId - set by the invocation event and should not be adjusted
|
36 | * @property {string} LogicalResourceId - set by the invocation event and should not be adjusted
|
37 | * @property {string} RequestId - set by the invocation event and should not be adjusted
|
38 | * @private
|
39 | */
|
40 | this.responseData = {
|
41 | PhysicalResourceId: event.PhysicalResourceId || crypto.randomBytes(16).toString('hex'),
|
42 | StackId: event.StackId,
|
43 | LogicalResourceId: event.LogicalResourceId,
|
44 | RequestId: event.RequestId
|
45 | };
|
46 |
|
47 | /**
|
48 | * Request options used to send the response
|
49 | * @memberof Response
|
50 | * @instance
|
51 | * @private
|
52 | */
|
53 | this.options = {
|
54 | hostname: parsedUrl.hostname,
|
55 | port: 443,
|
56 | path: parsedUrl.path,
|
57 | method: 'PUT',
|
58 | headers: {
|
59 | 'content-type': '',
|
60 | 'content-length': 0
|
61 | }
|
62 | };
|
63 |
|
64 | /**
|
65 | * Function to finish the Lambda invocation
|
66 | * @memberof Response
|
67 | * @instance
|
68 | * @private
|
69 | */
|
70 | this.done = context.done.bind(context);
|
71 | }
|
72 |
|
73 | /**
|
74 | * Set the PhysicalResourceId to be used in the response to CloudFormation. You
|
75 | * **must** provide this id when responding to a `Create` event.
|
76 | * @param {string} id - the id to be provided
|
77 | */
|
78 | Response.prototype.setId = function(id) {
|
79 | this.responseData.PhysicalResourceId = id;
|
80 | };
|
81 |
|
82 | /**
|
83 | * Send the response and finish the Lambda invocation
|
84 | * @param {object} [err] - set to null if no error occurred, or provide an Error object
|
85 | * @param {object} [data] - A hash of key-value pairs accessible via `Fn::GetAtt` calls on this custom resource
|
86 | */
|
87 | Response.prototype.send = function(err, data) {
|
88 | if (err) console.log(err);
|
89 |
|
90 | this.responseData.Status = err ? 'FAILED' : 'SUCCESS';
|
91 | this.responseData.Reason = err ? err.message || 'Unspecified failure' : '';
|
92 | this.responseData.Data = data;
|
93 |
|
94 | var body = JSON.stringify(this.responseData);
|
95 | var options = this.options;
|
96 | options.headers['content-length'] = body.length;
|
97 | var done = this.done;
|
98 |
|
99 | console.log('Response body: %j', this.responseData);
|
100 | console.log('Response options: %j', this.options);
|
101 |
|
102 | (function sendResponse(attempts) {
|
103 | if (attempts > 5) return done(new Error('Failed to respond to CloudFormation'));
|
104 |
|
105 | var req = https.request(options, function() {
|
106 | done(null, err || body);
|
107 | }).on('error', function(requestError) {
|
108 | console.log(requestError);
|
109 | attempts++;
|
110 | sendResponse(attempts);
|
111 | });
|
112 |
|
113 | req.write(body);
|
114 | req.end();
|
115 | })(0);
|
116 | };
|