1
+ /*********************************************************************************************************************
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. *
3
+ * *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance *
5
+ * with the License. A copy of the License is located at *
6
+ * *
7
+ * http://www.apache.org/licenses/LICENSE-2.0 *
8
+ * *
9
+ * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES *
10
+ * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions *
11
+ * and limitations under the License. *
12
+ *********************************************************************************************************************/
13
+
14
+ /**
15
+ * @author Solution Builders
16
+ */
17
+
18
+ 'use strict' ;
19
+
20
+ let AWS = require ( 'aws-sdk' ) ;
21
+ const fs = require ( 'fs' ) ;
22
+
23
+ /**
24
+ * Helper function to interact with AWS S3 for cfn custom resource.
25
+ *
26
+ * @class s3Helper
27
+ */
28
+ class s3Helper {
29
+
30
+ /**
31
+ * @class s3Helper
32
+ * @constructor
33
+ */
34
+ constructor ( ) {
35
+ this . creds = new AWS . EnvironmentCredentials ( 'AWS' ) ; // Lambda provided credentials
36
+ this . downloadLocation = '/tmp/manifest.json' ;
37
+ }
38
+
39
+ /**
40
+ * validateBuckets
41
+ * Cross-checks provided bucket names against existing bucket names in the account for
42
+ * validation.
43
+ * @param {String } strBuckets - String of bucket names from the template params.
44
+ */
45
+ async validateBuckets ( strBuckets ) {
46
+ const formatted = strBuckets . replace ( / \s / g, '' ) ;
47
+ console . log ( `Attempting to check if the following buckets exist: ${ formatted } ` ) ;
48
+ const buckets = formatted . split ( ',' ) ;
49
+ const errorBuckets = [ ] ;
50
+ for ( let i = 0 ; i < buckets . length ; i ++ ) {
51
+ const s3 = new AWS . S3 ( { signatureVersion : 'v4' } ) ;
52
+ const params = { Bucket : buckets [ i ] } ;
53
+ try {
54
+ await s3 . headBucket ( params ) . promise ( ) ;
55
+ console . log ( `Found bucket: ${ buckets [ i ] } ` ) ;
56
+ } catch ( err ) {
57
+ console . log ( `Could not find bucket: ${ buckets [ i ] } ` ) ;
58
+ console . log ( err ) ;
59
+ errorBuckets . push ( buckets [ i ] ) ;
60
+ }
61
+ }
62
+ if ( errorBuckets . length === 0 ) return Promise . resolve ( ) ;
63
+ else return Promise . reject ( errorBuckets ) ;
64
+ }
65
+
66
+ /**
67
+ * putConfigFile
68
+ * Saves a JSON config file to S3 location.
69
+ * @param {JSON } content - JSON object.
70
+ * @param {JSON } destS3Bucket - S3 destination bucket.
71
+ * @param {JSON } destS3key - S3 destination key.
72
+ */
73
+ putConfigFile ( content , destS3Bucket , destS3key ) {
74
+ console . log ( `Attempting to save content blob destination location: ${ destS3Bucket } /${ destS3key } ` ) ;
75
+ console . log ( JSON . stringify ( content ) ) ;
76
+
77
+ return new Promise ( ( resolve , reject ) => {
78
+ let _content = `'use strict';\n\nconst appVariables = {\n` ;
79
+
80
+ let i = 0 ;
81
+ for ( let key in content ) {
82
+ if ( i > 0 ) {
83
+ _content += ', \n' ;
84
+ }
85
+ _content += `${ key } : '${ content [ key ] } '` ;
86
+ i ++ ;
87
+ }
88
+ _content += '\n};' ;
89
+
90
+ let params = {
91
+ Bucket : destS3Bucket ,
92
+ Key : destS3key ,
93
+ Body : _content
94
+ } ;
95
+
96
+ let s3 = new AWS . S3 ( {
97
+ signatureVersion : 'v4'
98
+ } ) ;
99
+ s3 . putObject ( params , function ( err , data ) {
100
+ if ( err ) {
101
+ console . log ( err ) ;
102
+ reject ( `Error creating ${ destS3Bucket } /${ destS3key } content \n${ err } ` ) ;
103
+ } else {
104
+ console . log ( data ) ;
105
+ resolve ( data ) ;
106
+ }
107
+ } ) ;
108
+ } ) ;
109
+ }
110
+
111
+ copyAssets ( manifestKey , sourceS3Bucket , sourceS3prefix , destS3Bucket ) {
112
+ console . log ( `source bucket: ${ sourceS3Bucket } ` ) ;
113
+ console . log ( `source prefix: ${ sourceS3prefix } ` ) ;
114
+ console . log ( `destination bucket: ${ destS3Bucket } ` ) ;
115
+
116
+ let _self = this ;
117
+ return new Promise ( ( resolve , reject ) => {
118
+
119
+ this . _downloadManifest ( sourceS3Bucket , manifestKey ) . then ( ( data ) => {
120
+
121
+ fs . readFile ( _self . downloadLocation , 'utf8' , function ( err , data ) {
122
+ if ( err ) {
123
+ console . log ( err ) ;
124
+ reject ( err ) ;
125
+ }
126
+
127
+ let _manifest = _self . _validateJSON ( data ) ;
128
+
129
+ if ( ! _manifest ) {
130
+ reject ( 'Unable to validate downloaded manifest file JSON' ) ;
131
+ } else {
132
+ _self . _uploadFile ( _manifest . files , 0 , destS3Bucket , `${ sourceS3Bucket } /${ sourceS3prefix } ` ) . then ( ( resp ) => {
133
+ console . log ( resp ) ;
134
+ resolve ( resp )
135
+ } ) . catch ( ( err ) => {
136
+ console . log ( err ) ;
137
+ reject ( err ) ;
138
+ } ) ;
139
+ }
140
+
141
+ } ) ;
142
+ } ) . catch ( ( err ) => {
143
+ console . log ( err ) ;
144
+ reject ( err ) ;
145
+ } ) ;
146
+
147
+ } ) ;
148
+ } ;
149
+
150
+ /**
151
+ * Helper function to validate the JSON structure of contents of an import manifest file.
152
+ * @param {string } body - JSON object stringify-ed.
153
+ * @returns {JSON } - The JSON parsed string or null if string parsing failed
154
+ */
155
+ _validateJSON ( body ) {
156
+ try {
157
+ let data = JSON . parse ( body ) ;
158
+ console . log ( data ) ;
159
+ return data ;
160
+ } catch ( e ) {
161
+ // failed to parse
162
+ console . log ( 'Manifest file contains invalid JSON.' ) ;
163
+ return null ;
164
+ }
165
+ } ;
166
+
167
+ _uploadFile ( filelist , index , destS3Bucket , sourceS3prefix ) {
168
+ let _self = this ;
169
+ return new Promise ( ( resolve , reject ) => {
170
+
171
+ if ( filelist . length > index ) {
172
+ let params = {
173
+ Bucket : destS3Bucket ,
174
+ Key : filelist [ index ] ,
175
+ CopySource : [ sourceS3prefix , filelist [ index ] ] . join ( '/' ) ,
176
+ MetadataDirective : 'REPLACE'
177
+ } ;
178
+
179
+ params . ContentType = this . _setContentType ( filelist [ index ] ) ;
180
+ params . Metadata = {
181
+ 'Content-Type' : params . ContentType
182
+ } ;
183
+ console . log ( params ) ;
184
+ let s3 = new AWS . S3 ( {
185
+ signatureVersion : 'v4'
186
+ } ) ;
187
+ s3 . copyObject ( params , function ( err , data ) {
188
+ if ( err ) {
189
+ console . log ( err ) ;
190
+ reject ( `error copying ${ sourceS3prefix } /${ filelist [ index ] } \n${ err } ` ) ;
191
+ } else {
192
+ console . log ( `${ sourceS3prefix } /${ filelist [ index ] } uploaded successfully` ) ;
193
+ let _next = index + 1 ;
194
+ _self . _uploadFile ( filelist , _next , destS3Bucket , sourceS3prefix ) . then ( ( resp ) => {
195
+ resolve ( resp ) ;
196
+ } ) . catch ( ( err2 ) => {
197
+ reject ( err2 ) ;
198
+ } ) ;
199
+ }
200
+ } ) ;
201
+ } else {
202
+ resolve ( `${ index } files copied` ) ;
203
+ }
204
+
205
+ } ) ;
206
+
207
+ }
208
+
209
+ /**
210
+ * Helper function to download a manifest to local storage for processing.
211
+ * @param {string } s3Bucket - Amazon S3 bucket of the manifest to download.
212
+ * @param {string } s3Key - Amazon S3 key of the manifest to download.
213
+ * @param {string } downloadLocation - Local storage location to download the Amazon S3 object.
214
+ */
215
+ _downloadManifest ( s3Bucket , s3Key ) {
216
+ let _self = this ;
217
+ return new Promise ( ( resolve , reject ) => {
218
+
219
+ let params = {
220
+ Bucket : s3Bucket ,
221
+ Key : s3Key
222
+ } ;
223
+
224
+ console . log ( `Attempting to download manifest: ${ JSON . stringify ( params ) } ` ) ;
225
+
226
+ // check to see if the manifest file exists
227
+ let s3 = new AWS . S3 ( {
228
+ signatureVersion : 'v4'
229
+ } ) ;
230
+ s3 . headObject ( params , function ( err , metadata ) {
231
+ if ( err ) {
232
+ console . log ( err ) ;
233
+ }
234
+
235
+ if ( err && err . code === 'NotFound' ) {
236
+ // Handle no object on cloud here
237
+ console . log ( 'manifest file doesn\'t exist' ) ;
238
+ reject ( 'Manifest file was not found.' ) ;
239
+ } else {
240
+ console . log ( 'manifest file exists' ) ;
241
+ console . log ( metadata ) ;
242
+ let file = require ( 'fs' ) . createWriteStream ( _self . downloadLocation ) ;
243
+
244
+ s3 . getObject ( params ) .
245
+ on ( 'httpData' , function ( chunk ) {
246
+ file . write ( chunk ) ;
247
+ } ) .
248
+ on ( 'httpDone' , function ( ) {
249
+ file . end ( ) ;
250
+ console . log ( 'manifest downloaded for processing...' ) ;
251
+ resolve ( 'success' ) ;
252
+ } ) .
253
+ send ( ) ;
254
+ }
255
+ } ) ;
256
+ } ) ;
257
+ }
258
+
259
+ _setContentType ( file ) {
260
+ let _contentType = 'binary/octet-stream' ;
261
+ if ( file . endsWith ( '.html' ) ) {
262
+ _contentType = 'text/html' ;
263
+ } else if ( file . endsWith ( '.css' ) ) {
264
+ _contentType = 'text/css' ;
265
+ } else if ( file . endsWith ( '.png' ) ) {
266
+ _contentType = 'image/png' ;
267
+ } else if ( file . endsWith ( '.svg' ) ) {
268
+ _contentType = 'image/svg+xml' ;
269
+ } else if ( file . endsWith ( '.jpg' ) ) {
270
+ _contentType = 'image/jpeg' ;
271
+ } else if ( file . endsWith ( '.js' ) ) {
272
+ _contentType = 'application/javascript' ;
273
+ }
274
+
275
+ return _contentType ;
276
+ }
277
+
278
+
279
+ }
280
+
281
+ module . exports = s3Helper ;
0 commit comments