jets 0.10.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +12 -12
- data/README.md +4 -2
- data/exe/jets +2 -2
- data/lib/jets/application.rb +41 -5
- data/lib/jets/aws_info.rb +20 -0
- data/lib/jets/builders/code_builder.rb +201 -296
- data/lib/jets/builders/gem_replacer.rb +1 -1
- data/lib/jets/builders/handler_generator.rb +62 -43
- data/lib/jets/builders/md5.rb +55 -0
- data/lib/jets/builders/md5_zip.rb +60 -0
- data/lib/jets/builders/rack_packager.rb +29 -0
- data/lib/jets/builders/rackup_wrappers/rackup +23 -0
- data/lib/jets/builders/rackup_wrappers/rackup.rb +7 -0
- data/lib/jets/builders/reconfigure_rails/config/initializers/jets.rb +14 -0
- data/lib/jets/builders/reconfigure_rails.rb +99 -0
- data/lib/jets/builders/ruby_packager.rb +198 -0
- data/lib/jets/builders/{deducer.rb → shim_vars/app.rb} +14 -10
- data/lib/jets/builders/shim_vars/base.rb +24 -0
- data/lib/jets/builders/{shared_deducer.rb → shim_vars/shared.rb} +4 -3
- data/lib/jets/builders/shim_vars.rb +5 -0
- data/lib/jets/builders/templates/handler.js +9 -0
- data/lib/jets/builders/templates/shim.js +271 -0
- data/lib/jets/builders/tidy.rb +80 -0
- data/lib/jets/builders/util.rb +28 -0
- data/lib/jets/builders.rb +8 -2
- data/lib/jets/cfn/builders/function_builder.rb +0 -10
- data/lib/jets/cfn/builders/parent_builder.rb +7 -6
- data/lib/jets/cfn/ship.rb +9 -103
- data/lib/jets/cfn/upload.rb +139 -0
- data/lib/jets/cfn.rb +1 -0
- data/lib/jets/commands/build.rb +17 -19
- data/lib/jets/commands/deploy.rb +2 -0
- data/lib/jets/commands/help/deploy.md +2 -2
- data/lib/jets/commands/help/import/rack.md +13 -0
- data/lib/jets/commands/help/import/rails.md +11 -0
- data/lib/jets/commands/import/base.rb +39 -0
- data/lib/jets/commands/import/rack.rb +16 -0
- data/lib/jets/commands/import/rail.rb +68 -0
- data/lib/jets/commands/import/sequence.rb +68 -0
- data/lib/jets/commands/import.rb +14 -0
- data/lib/jets/commands/main.rb +2 -1
- data/lib/jets/commands/new.rb +1 -1
- data/lib/jets/commands/sequence.rb +26 -22
- data/lib/jets/commands/templates/skeleton/Gemfile.tt +5 -2
- data/lib/jets/commands/templates/skeleton/README.md +11 -2
- data/lib/jets/commands/templates/skeleton/app/jobs/application_job.rb +1 -1
- data/lib/jets/commands/templates/skeleton/app/views/layouts/application.html.erb.tt +1 -1
- data/lib/jets/commands/templates/skeleton/config/application.rb.tt +12 -5
- data/lib/jets/commands/templates/skeleton/config/database.yml.tt +5 -1
- data/lib/jets/commands/templates/skeleton/config/environments/development.rb +3 -0
- data/lib/jets/commands/templates/skeleton/config/environments/production.rb +5 -0
- data/lib/jets/commands/templates/skeleton/public/{images/favicon.ico → favicon.ico} +0 -0
- data/lib/jets/commands/templates/skeleton/spec/controllers/posts_controller_spec.rb +1 -3
- data/lib/jets/commands.rb +1 -0
- data/lib/jets/controller/base.rb +1 -1
- data/lib/jets/controller/layout.rb +3 -0
- data/lib/jets/controller/params.rb +3 -2
- data/lib/jets/controller/request.rb +4 -0
- data/lib/jets/core.rb +20 -18
- data/lib/jets/core_ext/kernel.rb +9 -5
- data/lib/jets/default/application.rb +1 -1
- data/lib/jets/inflections.rb +16 -8
- data/lib/jets/internal/app/controllers/jets/public_controller.rb +17 -22
- data/lib/jets/internal/app/controllers/jets/rack_controller.rb +15 -0
- data/lib/jets/naming.rb +0 -23
- data/lib/jets/rack/hash_converter.rb +25 -0
- data/lib/jets/rack/request.rb +71 -0
- data/lib/jets/rack/server.rb +47 -0
- data/lib/jets/rack.rb +7 -0
- data/lib/jets/rails_overrides/asset_tag_helper.rb +12 -11
- data/lib/jets/resource/function.rb +13 -5
- data/lib/jets/router.rb +1 -1
- data/lib/jets/ruby_server.rb +63 -18
- data/lib/jets/server/api_gateway.rb +1 -1
- data/lib/jets/stack/resource.rb +3 -1
- data/lib/jets/version.rb +1 -1
- data/lib/jets.rb +3 -5
- metadata +34 -9
- data/lib/jets/builders/node-hello.js +0 -73
- data/lib/jets/builders/node-shim.js +0 -182
- data/lib/jets/internal/app/controllers/jets/public_controller/python/show.py +0 -47
- data/lib/jets/internal/app/controllers/jets/public_controller/python/show.pyc +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Jets::Builders::ShimVars
|
|
2
|
+
class Base
|
|
3
|
+
include Jets::AwsServices
|
|
4
|
+
extend Memoist
|
|
5
|
+
|
|
6
|
+
def s3_bucket
|
|
7
|
+
Jets.aws.s3_bucket
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def rack_zip
|
|
11
|
+
checksum = Jets::Builders::Md5.checksums["stage/rack"]
|
|
12
|
+
"rack-#{checksum}.zip"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def bundled_zip
|
|
16
|
+
checksum = Jets::Builders::Md5.checksums["stage/bundled"]
|
|
17
|
+
"bundled-#{checksum}.zip"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def stage_area
|
|
21
|
+
"#{Jets.build_root}/stage"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Jets::Builders::ShimVars::Shared.new(fun)
|
|
2
2
|
#
|
|
3
3
|
# @deducer.functions.each do |function_name|
|
|
4
4
|
# @deducer.handler_for(function_name)
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
# handler_for(function_name)
|
|
11
11
|
# js_path
|
|
12
12
|
#
|
|
13
|
-
|
|
14
|
-
class
|
|
13
|
+
module Jets::Builders::ShimVars
|
|
14
|
+
class Shared < Base
|
|
15
|
+
# fun is a Jets::Stack::Function
|
|
15
16
|
def initialize(fun)
|
|
16
17
|
@fun = fun
|
|
17
18
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const shim = require("handlers/shim.js");
|
|
4
|
+
|
|
5
|
+
shim.once(); // runs in lambda execution context
|
|
6
|
+
|
|
7
|
+
<% @vars.functions.each do |function_name| -%>
|
|
8
|
+
exports.<%= function_name %> = shim.handler("<%= @vars.handler_for(function_name) %>");
|
|
9
|
+
<% end -%>
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
const TMP_LOG_PATH = '/tmp/shim-subprocess.log';
|
|
2
|
+
const JETS_DEBUG = process.env.JETS_DEBUG; // set JETS_DEBUG=1 to see more debugging info
|
|
3
|
+
const JETS_OUTPUT = '/tmp/jets-output.log';
|
|
4
|
+
const S3_BUCKET = '<%= @vars.s3_bucket %>';
|
|
5
|
+
<% if Jets.rack? -%>
|
|
6
|
+
const RACK_ZIP = '<%= @vars.rack_zip %>'; // jets/code/rack-checksum.zip
|
|
7
|
+
<% end -%>
|
|
8
|
+
<% if Jets.lazy_load? -%>
|
|
9
|
+
const BUNDLED_ZIP = '<%= @vars.bundled_zip %>'; // /tmp/bundled-checksum.zip
|
|
10
|
+
<% end -%>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const spawn = require('child_process').spawn;
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const readline = require('readline');
|
|
16
|
+
const subprocess_out = fs.openSync(TMP_LOG_PATH, 'a');
|
|
17
|
+
const subprocess_err = fs.openSync(TMP_LOG_PATH, 'a');
|
|
18
|
+
const { exec } = require('child_process');
|
|
19
|
+
const AWS = require('aws-sdk/global');
|
|
20
|
+
const S3 = require('aws-sdk/clients/s3'); // import individual service
|
|
21
|
+
const net = require('net');
|
|
22
|
+
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
// util methods
|
|
25
|
+
const sh = async (command, args) => {
|
|
26
|
+
var promise = new Promise(function(resolve, reject) {
|
|
27
|
+
log(`=> ${command}`);
|
|
28
|
+
exec(command, (err, stdout, stderr) => {
|
|
29
|
+
log(`stdout: ${stdout}`);
|
|
30
|
+
log(`stderr: ${stderr}`);
|
|
31
|
+
if (err) {
|
|
32
|
+
resolve({success: false, command: command});
|
|
33
|
+
} else {
|
|
34
|
+
resolve({success: true, command: command});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
return promise;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const download = async (key, dest) => {
|
|
42
|
+
var promise = new Promise(function(resolve, reject) {
|
|
43
|
+
var s3 = new AWS.S3();
|
|
44
|
+
var params = {Bucket: S3_BUCKET, Key: key};
|
|
45
|
+
var file = require('fs').createWriteStream(dest);
|
|
46
|
+
var stream = s3.getObject(params).createReadStream();
|
|
47
|
+
var pipe = stream.pipe(file);
|
|
48
|
+
file.on('finish', function () {
|
|
49
|
+
resolve({success: true, pipe: pipe});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
return promise;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const downloadAndExtract = async (zip_file, folder_dest) => {
|
|
56
|
+
var s3_key = `jets/code/${zip_file}`; // jets/code/bundled-checksum.zip
|
|
57
|
+
var download_path = `/tmp/${zip_file}`; // /tmp/bundled-checksum.zip
|
|
58
|
+
|
|
59
|
+
await download(s3_key, download_path);
|
|
60
|
+
await sh(`unzip -qo ${download_path} -d ${folder_dest}`);
|
|
61
|
+
// await sh(`ls ${folder_dest}*`);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Clean out files to prevent confusing double output when debugging.
|
|
65
|
+
// Also clean out the file whenever done printing out the output.
|
|
66
|
+
function truncate(path) {
|
|
67
|
+
if (fs.existsSync(path)) {
|
|
68
|
+
fs.truncateSync(path);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Produces an Error object that displays in the AWS Lambda test console nicely.
|
|
73
|
+
// The backtrace are the ruby lines, not the nodejs shim error lines.
|
|
74
|
+
// The json payload in the Lambda console looks something like this:
|
|
75
|
+
//
|
|
76
|
+
// {
|
|
77
|
+
// "errorMessage": "RubyError: RuntimeError: error in submethod",
|
|
78
|
+
// "errorType": "RubyError",
|
|
79
|
+
// "stackTrace": [
|
|
80
|
+
// [
|
|
81
|
+
// "line1",
|
|
82
|
+
// "line2",
|
|
83
|
+
// "line3"
|
|
84
|
+
// ]
|
|
85
|
+
// ]
|
|
86
|
+
// }
|
|
87
|
+
//
|
|
88
|
+
function rubyError(resp) {
|
|
89
|
+
var name = resp["errorType"];
|
|
90
|
+
var message = resp["errorMessage"];
|
|
91
|
+
var stack = resp["stackTrace"];
|
|
92
|
+
stack.unshift(message); // JS error includes the error message at the top of the stacktrac also
|
|
93
|
+
stack = stack.join("\n");
|
|
94
|
+
|
|
95
|
+
var error = new Error(message);
|
|
96
|
+
error.name = name;
|
|
97
|
+
error.stack = stack;
|
|
98
|
+
return error;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// On AWS Lambda, we can log to either stdout or stderr and we're okay.
|
|
102
|
+
// But locally when we're testing the shim, the log output can mess up piping
|
|
103
|
+
// to jq. So not logging to stdout because when testing this shim locally the
|
|
104
|
+
// stdout output messes up a pipe to jq.
|
|
105
|
+
function log(text, level="info") {
|
|
106
|
+
if (level == "info" && JETS_DEBUG) {
|
|
107
|
+
// only log if JETS_DEBUG is set
|
|
108
|
+
console.error(text);
|
|
109
|
+
} else if (level == "debug") {
|
|
110
|
+
// always log if "debug" passed into method
|
|
111
|
+
console.error(text);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
116
|
+
// Logic that runs once in the lambda execution context
|
|
117
|
+
|
|
118
|
+
const downloadTmp = async () => {
|
|
119
|
+
var start = Date.now();
|
|
120
|
+
|
|
121
|
+
<% if Jets.lazy_load? -%>
|
|
122
|
+
var bundled = downloadAndExtract(BUNDLED_ZIP, '/tmp/bundled');
|
|
123
|
+
<% end -%>
|
|
124
|
+
<% if Jets.rack? -%>
|
|
125
|
+
var rack = downloadAndExtract(RACK_ZIP, '/tmp/rack');
|
|
126
|
+
<% end -%>
|
|
127
|
+
<% if Jets.lazy_load? -%>
|
|
128
|
+
// Interestingly, doesnt seem to speed up download with parallization below.
|
|
129
|
+
// Think this is because node does not take advantage of multiple cores?
|
|
130
|
+
// Leaving this in place because speed seems the same as doing the await
|
|
131
|
+
// in serial and hope it is fast in the future.
|
|
132
|
+
await bundled;
|
|
133
|
+
<% end -%>
|
|
134
|
+
<% if Jets.rack? -%>
|
|
135
|
+
await rack;
|
|
136
|
+
<% end -%>
|
|
137
|
+
|
|
138
|
+
var took = Date.now() - start;
|
|
139
|
+
log(`Download /tmp time: ${took}`);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const rubyServer = () => {
|
|
143
|
+
// start tcp server and detach
|
|
144
|
+
const subprocess = spawn('bin/ruby_server', {
|
|
145
|
+
detached: true,
|
|
146
|
+
stdio: [ 'ignore', subprocess_out, subprocess_err ]
|
|
147
|
+
});
|
|
148
|
+
subprocess.on('error', function(err) {
|
|
149
|
+
log('bin/ruby_server error', err);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
subprocess.on('close', function(exit_code) {
|
|
153
|
+
log("Subprocess was shut down or deattached!");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// prevent the parent from waiting for a subprocess to exit
|
|
157
|
+
// https://nodejs.org/api/child_process.html
|
|
158
|
+
subprocess.unref();
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// once runs in lambda execution context
|
|
162
|
+
const once = async () => {
|
|
163
|
+
// Uncomment fake run once locally. No need to do this on real lambda environment.
|
|
164
|
+
// var filename = '/tmp/once.txt';
|
|
165
|
+
// if (fs.existsSync(filename)) {
|
|
166
|
+
// log("fake run only once. /tmp/once.txt exists. remove to run once again.");
|
|
167
|
+
// return;
|
|
168
|
+
// }
|
|
169
|
+
// fs.closeSync(fs.openSync(filename, 'w'));
|
|
170
|
+
|
|
171
|
+
<% if Jets.rack? || Jets.lazy_load? -%>
|
|
172
|
+
await downloadTmp();
|
|
173
|
+
<% end -%>
|
|
174
|
+
// The server related methods are fire and forget.
|
|
175
|
+
// Not using await here because had trouble resolving the promise on client.connect
|
|
176
|
+
rubyServer(); // no await
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// currying function to make handler code prettier
|
|
180
|
+
function handler(full_handler_name) {
|
|
181
|
+
return function(event, context, callback) {
|
|
182
|
+
request(event, full_handler_name, callback);
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
187
|
+
// main logic for handler
|
|
188
|
+
function request(event, handler, callback) {
|
|
189
|
+
truncate(JETS_OUTPUT);
|
|
190
|
+
truncate(TMP_LOG_PATH);
|
|
191
|
+
|
|
192
|
+
log("event:");
|
|
193
|
+
log(event);
|
|
194
|
+
var client = new net.Socket();
|
|
195
|
+
client.connect(8080, '127.0.0.01', function() {
|
|
196
|
+
log('Connected to socket');
|
|
197
|
+
client.write(JSON.stringify(event));
|
|
198
|
+
client.write("\r\n"); // important: \r\n is how server knows input is done
|
|
199
|
+
client.write(handler);
|
|
200
|
+
client.write("\r\n"); // important: \r\n is how server knows input is done
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// string concatation in javascript is faster than array concatation
|
|
204
|
+
// http://bit.ly/2gBMDs6
|
|
205
|
+
var response_buffer = ""; // response buffer
|
|
206
|
+
client.on('data', function(buffer) {
|
|
207
|
+
log('Received data from socket: ' + buffer);
|
|
208
|
+
response_buffer += buffer;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
client.on('close', function() {
|
|
212
|
+
log('Socket connection closed');
|
|
213
|
+
// If server is not yet running, socket immediately closes and response_buffer
|
|
214
|
+
// is still empty. Return right away for this case, so request can retry.
|
|
215
|
+
if (response_buffer == "") {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (fs.existsSync(JETS_OUTPUT)) {
|
|
220
|
+
// Thanks: https://stackoverflow.com/questions/6156501/read-a-file-one-line-at-a-time-in-node-js
|
|
221
|
+
var rd = readline.createInterface({
|
|
222
|
+
input: fs.createReadStream(JETS_OUTPUT),
|
|
223
|
+
// output: process.stdout,
|
|
224
|
+
console: false
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
rd.on('line', (line) => {
|
|
228
|
+
// Important to use console.error in case locally testing shim or we see
|
|
229
|
+
// stdout "twice", once from here and once from ruby-land and it's confusing.
|
|
230
|
+
// AWS lambda will write console.error and console.log to CloudWatch.
|
|
231
|
+
console.error(line); // output to AWS Lambda Logs
|
|
232
|
+
}).on('close', () => {
|
|
233
|
+
truncate(JETS_OUTPUT); // done reporting output, clear out file again
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var resp = JSON.parse(response_buffer);
|
|
238
|
+
if (resp && resp["errorMessage"]) {
|
|
239
|
+
// Customize error object for lambda format
|
|
240
|
+
var error = rubyError(resp);
|
|
241
|
+
callback(error);
|
|
242
|
+
} else {
|
|
243
|
+
callback(null, resp);
|
|
244
|
+
}
|
|
245
|
+
client.destroy(); // kill client after server's response
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
client.on('error', function(error) {
|
|
249
|
+
log("Socket error:");
|
|
250
|
+
log(error);
|
|
251
|
+
log("Retrying request");
|
|
252
|
+
setTimeout(function() {
|
|
253
|
+
if (fs.existsSync(TMP_LOG_PATH)) {
|
|
254
|
+
var contents = fs.readFileSync(TMP_LOG_PATH, 'utf8');
|
|
255
|
+
if (contents != "") {
|
|
256
|
+
log("subprocess output:", "debug");
|
|
257
|
+
log(contents, "debug");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
truncate(TMP_LOG_PATH);
|
|
261
|
+
|
|
262
|
+
log("Retrying request NOW");
|
|
263
|
+
request(event, handler, callback);
|
|
264
|
+
}, 500);
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
module.exports = {
|
|
269
|
+
handler: handler,
|
|
270
|
+
once: once
|
|
271
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
class Jets::Builders
|
|
2
|
+
class Tidy
|
|
3
|
+
def initialize(project_root, noop: false)
|
|
4
|
+
@project_root = project_root
|
|
5
|
+
@noop = noop
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def cleanup!
|
|
9
|
+
removals.each do |removal|
|
|
10
|
+
removal = removal.sub(%r{^/},'') # remove leading slash
|
|
11
|
+
path = "#{@project_root}/#{removal}"
|
|
12
|
+
rm_rf(path)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
tidy_bundled
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def removals
|
|
19
|
+
removals = always_removals
|
|
20
|
+
removals += get_removals("#{@project_root}/.gitignore")
|
|
21
|
+
removals += get_removals("#{@project_root}/.dockerignore")
|
|
22
|
+
removals = removals.reject do |p|
|
|
23
|
+
jetskeep.find do |keep|
|
|
24
|
+
p.include?(keep)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
removals.uniq
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_removals(file)
|
|
31
|
+
path = file
|
|
32
|
+
return [] unless File.exist?(path)
|
|
33
|
+
|
|
34
|
+
removal = File.read(path).split("\n")
|
|
35
|
+
removal.map {|i| i.strip}.reject {|i| i =~ /^#/ || i.empty?}
|
|
36
|
+
# IE: ["/handlers", "/bundled*", "/vendor/jets]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# We clean out ignored files pretty aggressively. So provide
|
|
40
|
+
# a way for users to keep files from being cleaned out.
|
|
41
|
+
def jetskeep
|
|
42
|
+
defaults = %w[.bundle bundled pack handlers public/assets]
|
|
43
|
+
path = "#{@project_root}/.jetskeep"
|
|
44
|
+
return defaults unless File.exist?(path)
|
|
45
|
+
|
|
46
|
+
keep = IO.readlines(path)
|
|
47
|
+
keep = keep.map {|i| i.strip}.reject { |i| i =~ /^#/ || i.empty? }
|
|
48
|
+
(defaults + keep).uniq
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# folders to remove in the bundled folder regardless of the level of the folder
|
|
52
|
+
def tidy_bundled
|
|
53
|
+
Dir.glob("#{@project_root}/bundled/**/*").each do |path|
|
|
54
|
+
next unless File.directory?(path)
|
|
55
|
+
dir = File.basename(path)
|
|
56
|
+
next unless always_removals.include?(dir)
|
|
57
|
+
|
|
58
|
+
rm_rf(path)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def rm_rf(path)
|
|
63
|
+
exists = File.exist?("#{path}/.gitkeep") || File.exist?("#{path}/.keep")
|
|
64
|
+
return if exists
|
|
65
|
+
|
|
66
|
+
# say " rm -rf #{path}".colorize(:yellow) # uncomment to debug
|
|
67
|
+
system("rm -rf #{path}") unless @noop
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# These directories will be removed regardless of dir level
|
|
71
|
+
def always_removals
|
|
72
|
+
%w[.git spec tmp]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def say(message)
|
|
76
|
+
message = "NOOP #{message}" if @noop
|
|
77
|
+
puts message
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class Jets::Builders
|
|
2
|
+
module Util
|
|
3
|
+
def sh(command)
|
|
4
|
+
puts "=> #{command}".colorize(:green)
|
|
5
|
+
success = system(command)
|
|
6
|
+
abort("#{command} failed to run") unless success
|
|
7
|
+
success
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def headline(message)
|
|
11
|
+
puts "=> #{message}".colorize(:cyan)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Provide pretty clear way to desinate full path.
|
|
15
|
+
# full("bundled") => /tmp/jets/demo/bundled
|
|
16
|
+
def full(relative_path)
|
|
17
|
+
"#{Jets.build_root}/#{relative_path}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Finds out of the app has polymorphic functions only and zero ruby functions.
|
|
21
|
+
# In this case, we can skip a lot of the ruby related building and speed up the
|
|
22
|
+
# deploy process.
|
|
23
|
+
def poly_only?
|
|
24
|
+
return true if ENV['POLY_ONLY'] # bypass to allow rapid development of handlers
|
|
25
|
+
Jets::Commands::Build.poly_only?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/jets/builders.rb
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
class Jets::Builders
|
|
2
2
|
autoload :CodeBuilder, "jets/builders/code_builder"
|
|
3
|
-
autoload :Deducer, "jets/builders/deducer"
|
|
4
3
|
autoload :GemReplacer, "jets/builders/gem_replacer"
|
|
5
4
|
autoload :HandlerGenerator, "jets/builders/handler_generator"
|
|
6
|
-
autoload :
|
|
5
|
+
autoload :Md5, "jets/builders/md5"
|
|
6
|
+
autoload :Md5Zip, "jets/builders/md5_zip"
|
|
7
|
+
autoload :RackPackager, "jets/builders/rack_packager"
|
|
8
|
+
autoload :ReconfigureRails, "jets/builders/reconfigure_rails"
|
|
9
|
+
autoload :RubyPackager, "jets/builders/ruby_packager"
|
|
10
|
+
autoload :ShimVars, "jets/builders/shim_vars"
|
|
11
|
+
autoload :Tidy, "jets/builders/tidy"
|
|
12
|
+
autoload :Util, "jets/builders/util"
|
|
7
13
|
end
|
|
@@ -10,15 +10,5 @@ class Jets::Cfn::Builders
|
|
|
10
10
|
add_common_parameters
|
|
11
11
|
add_functions
|
|
12
12
|
end
|
|
13
|
-
|
|
14
|
-
# For function stacks, ensure there's a _function.yml at the end of the
|
|
15
|
-
# template_path name for easy identification.
|
|
16
|
-
def template_path
|
|
17
|
-
path = super
|
|
18
|
-
unless path.include?("function.yml")
|
|
19
|
-
path = path.sub(".yml", "_function.yml")
|
|
20
|
-
end
|
|
21
|
-
path
|
|
22
|
-
end
|
|
23
13
|
end
|
|
24
14
|
end
|
|
@@ -12,10 +12,8 @@ class Jets::Cfn::Builders
|
|
|
12
12
|
|
|
13
13
|
# compose is an interface method
|
|
14
14
|
def compose
|
|
15
|
-
puts "Building parent CloudFormation template."
|
|
16
|
-
|
|
17
15
|
build_minimal_resources
|
|
18
|
-
build_child_resources if
|
|
16
|
+
build_child_resources if full?
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
# template_path is an interface method
|
|
@@ -29,6 +27,7 @@ class Jets::Cfn::Builders
|
|
|
29
27
|
add_resource(resource)
|
|
30
28
|
add_outputs(resource.outputs)
|
|
31
29
|
|
|
30
|
+
return unless full?
|
|
32
31
|
# Add application-wide IAM policy from Jets.config.iam_role
|
|
33
32
|
resource = Jets::Resource::Iam::ApplicationRole.new
|
|
34
33
|
add_resource(resource)
|
|
@@ -36,8 +35,6 @@ class Jets::Cfn::Builders
|
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
def build_child_resources
|
|
39
|
-
puts "Building child CloudFormation templates."
|
|
40
|
-
|
|
41
38
|
expression = "#{Jets::Naming.template_path_prefix}-app-*"
|
|
42
39
|
# IE: path: #{Jets.build_root}/templates/demo-dev-2-comments_controller.yml
|
|
43
40
|
Dir.glob(expression).each do |path|
|
|
@@ -53,12 +50,16 @@ class Jets::Cfn::Builders
|
|
|
53
50
|
add_shared_resources(path)
|
|
54
51
|
end
|
|
55
52
|
|
|
56
|
-
if
|
|
53
|
+
if full? and !Jets::Router.routes.empty?
|
|
57
54
|
add_api_gateway
|
|
58
55
|
add_api_deployment
|
|
59
56
|
end
|
|
60
57
|
end
|
|
61
58
|
|
|
59
|
+
def full?
|
|
60
|
+
@options[:templates] || @options[:stack_type] == :full
|
|
61
|
+
end
|
|
62
|
+
|
|
62
63
|
def add_app_class_stack(path)
|
|
63
64
|
resource = Jets::Resource::ChildStack::AppClass.new(@options[:s3_bucket], path: path)
|
|
64
65
|
add_child_resources(resource)
|
data/lib/jets/cfn/ship.rb
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
require 'action_view'
|
|
2
|
-
|
|
3
1
|
class Jets::Cfn
|
|
4
2
|
class Ship
|
|
5
3
|
include Jets::Timing
|
|
6
4
|
include Jets::AwsServices
|
|
7
|
-
include ActionView::Helpers::NumberHelper # number_to_human_size
|
|
8
5
|
|
|
9
6
|
def initialize(options)
|
|
10
7
|
@options = options
|
|
@@ -13,8 +10,8 @@ class Jets::Cfn
|
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
def run
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
# s3 bucket is available only when stack_type is full
|
|
14
|
+
upload_to_s3 if @options[:stack_type] == :full
|
|
18
15
|
|
|
19
16
|
stack_in_progress?(@parent_stack_name)
|
|
20
17
|
|
|
@@ -81,11 +78,15 @@ class Jets::Cfn
|
|
|
81
78
|
time :wait_for_stack
|
|
82
79
|
|
|
83
80
|
def prewarm
|
|
81
|
+
if ENV['SKIP_PREWARMING']
|
|
82
|
+
puts "Skipping prewarming" # useful for testing
|
|
83
|
+
return
|
|
84
|
+
end
|
|
84
85
|
return unless @options[:stack_type] == :full # s3 bucket is available
|
|
85
86
|
return unless Jets.config.prewarm.enable
|
|
86
87
|
return if Jets::Commands::Build.poly_only?
|
|
87
88
|
|
|
88
|
-
puts "Prewarming application
|
|
89
|
+
puts "Prewarming application."
|
|
89
90
|
if Jets::PreheatJob::CONCURRENCY > 1
|
|
90
91
|
Jets::PreheatJob.perform_now(:torch, {quiet: true})
|
|
91
92
|
else
|
|
@@ -143,104 +144,9 @@ class Jets::Cfn
|
|
|
143
144
|
def upload_to_s3
|
|
144
145
|
raise "Did not specify @options[:s3_bucket] #{@options[:s3_bucket].inspect}" unless @options[:s3_bucket]
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
upload_assets
|
|
147
|
+
uploader = Upload.new(@options[:s3_bucket])
|
|
148
|
+
uploader.upload
|
|
149
149
|
end
|
|
150
150
|
time :upload_to_s3
|
|
151
|
-
|
|
152
|
-
def bucket_name
|
|
153
|
-
@options[:s3_bucket]
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def upload_cfn_templates
|
|
157
|
-
puts "Uploading child CloudFormation templates to S3"
|
|
158
|
-
expression = "#{Jets::Naming.template_path_prefix}-*"
|
|
159
|
-
Dir.glob(expression).each do |path|
|
|
160
|
-
next unless File.file?(path)
|
|
161
|
-
|
|
162
|
-
key = "jets/cfn-templates/#{File.basename(path)}"
|
|
163
|
-
obj = s3_resource.bucket(bucket_name).object(key)
|
|
164
|
-
obj.upload_file(path)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def upload_code
|
|
169
|
-
md5_code_zipfile = Jets::Naming.md5_code_zipfile
|
|
170
|
-
file_size = number_to_human_size(File.size(md5_code_zipfile))
|
|
171
|
-
|
|
172
|
-
puts "Uploading #{md5_code_zipfile} (#{file_size}) to S3"
|
|
173
|
-
start_time = Time.now
|
|
174
|
-
key = Jets::Naming.code_s3_key
|
|
175
|
-
obj = s3_resource.bucket(bucket_name).object(key)
|
|
176
|
-
obj.upload_file(md5_code_zipfile)
|
|
177
|
-
puts "Time to upload code to s3: #{pretty_time(Time.now-start_time).colorize(:green)}"
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def upload_assets
|
|
181
|
-
puts "Uploading public assets"
|
|
182
|
-
start_time = Time.now
|
|
183
|
-
asset_folders = Jets.config.assets.folders
|
|
184
|
-
asset_folders.each do |folder|
|
|
185
|
-
upload_asset_folder(folder)
|
|
186
|
-
end
|
|
187
|
-
puts "Time to upload public assets to s3: #{pretty_time(Time.now-start_time).colorize(:green)}"
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def upload_asset_folder(folder)
|
|
191
|
-
expression = "#{Jets.root}public/#{folder}/**/*"
|
|
192
|
-
group_size = 10
|
|
193
|
-
Dir.glob(expression).each_slice(group_size) do |paths|
|
|
194
|
-
threads = []
|
|
195
|
-
paths.each do |path|
|
|
196
|
-
next unless File.file?(path)
|
|
197
|
-
|
|
198
|
-
regexp = Regexp.new(".*/#{folder}/")
|
|
199
|
-
relative_path = path.sub(regexp,'')
|
|
200
|
-
file = "#{folder}/#{relative_path}"
|
|
201
|
-
|
|
202
|
-
threads << Thread.new do
|
|
203
|
-
upload_asset_file(file)
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
threads.each(&:join)
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def upload_asset_file(file)
|
|
211
|
-
path = "#{Jets.root}public/#{file}"
|
|
212
|
-
key = "jets/public/#{file}"
|
|
213
|
-
puts "Uploading s3://#{bucket_name}/#{key}" # uncomment to see and debug
|
|
214
|
-
obj = s3_resource.bucket(bucket_name).object(key)
|
|
215
|
-
obj.upload_file(path, acl: "public-read", cache_control: cache_control)
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# If cache_control is provided, then it will set the entire cache-control header.
|
|
219
|
-
# If only max_age is provided, then we'll generate a cache_control header.
|
|
220
|
-
# Using max_age is the shorter and simply way of setting the cache_control header.
|
|
221
|
-
def cache_control
|
|
222
|
-
cache_control = Jets.config.assets.cache_control
|
|
223
|
-
unless cache_control
|
|
224
|
-
max_age = Jets.config.assets.max_age # defaults to 3600 in jets/application.rb
|
|
225
|
-
cache_control = "public, max-age=#{max_age}"
|
|
226
|
-
end
|
|
227
|
-
cache_control
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def s3_bucket
|
|
231
|
-
@options[:s3_bucket]
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# http://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby
|
|
235
|
-
def pretty_time(total_seconds)
|
|
236
|
-
minutes = (total_seconds / 60) % 60
|
|
237
|
-
seconds = total_seconds % 60
|
|
238
|
-
if total_seconds < 60
|
|
239
|
-
"#{seconds.to_i}s"
|
|
240
|
-
else
|
|
241
|
-
"#{minutes.to_i}m #{seconds.to_i}s"
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
151
|
end
|
|
246
152
|
end
|