jack_and_the_elastic_beanstalk 0.1.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +36 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/jeb +7 -0
- data/jack_and_the_elastic_beanstalk.gemspec +34 -0
- data/lib/jack_and_the_elastic_beanstalk/cli.rb +345 -0
- data/lib/jack_and_the_elastic_beanstalk/config.rb +71 -0
- data/lib/jack_and_the_elastic_beanstalk/eb.rb +240 -0
- data/lib/jack_and_the_elastic_beanstalk/runner.rb +73 -0
- data/lib/jack_and_the_elastic_beanstalk/service.rb +193 -0
- data/lib/jack_and_the_elastic_beanstalk/version.rb +3 -0
- data/lib/jack_and_the_elastic_beanstalk.rb +24 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4578032c8d9235e27f4491ace04ec78f8929d63f
|
4
|
+
data.tar.gz: '06419fd23e051d5205a536a1c2e6f076819b881c'
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9886c279514422cb879ca4c7c598cea9a868df7058efebebb9f775bdeb194993ff198bbc189f94345f025459a0ad9aa9ca9b89ff426b25e230f498d3d80f97f5
|
7
|
+
data.tar.gz: 1096c0027dfc9b5e5da7539b9f543bccfca76a8630ad9cdb2caf2ea677af075bba3cb58a12a5f6b207d1d1cea81a69ac7ab4cf07434a2e8c8b14c3ffa0ca660a
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.3
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# JackAndTheElasticBeanstalk
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/jack_and_the_elastic_beanstalk`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'jack_and_the_elastic_beanstalk'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install jack_and_the_elastic_beanstalk
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jack_and_the_elastic_beanstalk.
|
36
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "jack_and_the_elastic_beanstalk"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/jeb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'jack_and_the_elastic_beanstalk/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "jack_and_the_elastic_beanstalk"
|
8
|
+
spec.version = JackAndTheElasticBeanstalk::VERSION
|
9
|
+
spec.authors = ["Soutaro Matsumoto"]
|
10
|
+
spec.email = ["matsumoto@soutaro.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Jack and the Elastic Beanstalk.}
|
13
|
+
spec.description = %q{Jack and the Elastic Beanstalk.}
|
14
|
+
spec.homepage = "https://github.com/sideci/jack_and_the_elastic_beanstalk"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
|
27
|
+
spec.add_runtime_dependency 'dotenv', "~> 2.1"
|
28
|
+
spec.add_runtime_dependency 'rainbow', '~> 2.1'
|
29
|
+
spec.add_runtime_dependency "aws-sdk", "~> 2.6"
|
30
|
+
spec.add_runtime_dependency "thor", "~> 0.19"
|
31
|
+
spec.add_runtime_dependency "parallel", "~> 1.10"
|
32
|
+
spec.add_runtime_dependency "activesupport", "~> 5.0"
|
33
|
+
spec.add_runtime_dependency "rubyzip", "~> 1.2"
|
34
|
+
end
|
@@ -0,0 +1,345 @@
|
|
1
|
+
module JackAndTheElasticBeanstalk
|
2
|
+
class CLI < Thor
|
3
|
+
no_commands do
|
4
|
+
def client
|
5
|
+
@client ||= Aws::ElasticBeanstalk::Client.new(region: config.region)
|
6
|
+
end
|
7
|
+
|
8
|
+
def runner
|
9
|
+
@runner ||= JackAndTheElasticBeanstalk::Runner.new(stdin: STDIN, stdout: STDOUT, stderr: STDERR, logger: logger)
|
10
|
+
end
|
11
|
+
|
12
|
+
def logger
|
13
|
+
@logger ||= Logger.new(STDERR).tap do |logger|
|
14
|
+
logger.level = Logger.const_get(options[:loglevel].upcase)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def eb
|
19
|
+
@eb ||= JackAndTheElasticBeanstalk::EB.new(application_name: config.app_name, logger: logger, client: client).tap do |eb|
|
20
|
+
eb.timeout = options[:timeout] * 60
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def config
|
25
|
+
@config ||= JackAndTheElasticBeanstalk::Config.load(path: Pathname(options[:jack_dir]))
|
26
|
+
end
|
27
|
+
|
28
|
+
def service
|
29
|
+
@service ||= JackAndTheElasticBeanstalk::Service.new(source_dir: Pathname(options[:source_dir]), config: config, eb: eb, runner: runner, logger: logger)
|
30
|
+
end
|
31
|
+
|
32
|
+
def output_dir
|
33
|
+
Dir.mktmpdir do |dir|
|
34
|
+
yield Pathname(dir)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def try_process(name, is:)
|
39
|
+
if !is || is == name
|
40
|
+
yield(name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_in_parallel(array)
|
45
|
+
Parallel.each_with_index(array, in_threads: array.size) do |a, index|
|
46
|
+
sleep index*5
|
47
|
+
yield a
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_env_args(args)
|
52
|
+
args.each.with_object({}) do |arg, hash|
|
53
|
+
k,v = arg.split("=", 2)
|
54
|
+
hash[k] = v
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class_option :timeout, type: :numeric, default: 10, desc: "Minutes to timeout for each EB operation"
|
60
|
+
class_option :loglevel, type: :string, enum: ["info", "debug", "error"], default: "error", desc: "Loglevel"
|
61
|
+
class_option :jack_dir, type: :string, default: (Pathname.pwd + "jack").to_s, desc: "Directory to app.yml"
|
62
|
+
class_option :source_dir, type: :string, default: Pathname.pwd.to_s, desc: "Directory for source code"
|
63
|
+
|
64
|
+
desc "create CONFIGURATION GROUP ENV_VAR=VALUE...", "Create new group"
|
65
|
+
def create(configuration, group, *env_var_args)
|
66
|
+
processes = config.each_process(configuration).to_a
|
67
|
+
env_vars = parse_env_args(env_var_args)
|
68
|
+
|
69
|
+
output_dir do |base_path|
|
70
|
+
processes.each do |process, hash|
|
71
|
+
path = base_path + process
|
72
|
+
runner.stdout.puts "Staging for #{process}..."
|
73
|
+
service.stage(target_dir: path, process: process)
|
74
|
+
end
|
75
|
+
|
76
|
+
each_in_parallel(processes) do |process, hash|
|
77
|
+
runner.stdout.puts "Creating new environment for #{process}..."
|
78
|
+
|
79
|
+
path = base_path + process
|
80
|
+
|
81
|
+
service.eb_init(target_dir: path)
|
82
|
+
service.eb_create(target_dir: path, configuration: configuration, group: group, process: process, env_vars: env_vars)
|
83
|
+
|
84
|
+
if hash["type"] == "oneoff"
|
85
|
+
runner.stdout.puts "Scaling to 0 (#{process} is a oneoff process)"
|
86
|
+
env = service.each_environment(group: group).find {|_, p| p == process }.first
|
87
|
+
env.set_scale(0)
|
88
|
+
env.synchronize_update
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "deploy GROUP", "Deploy to group"
|
95
|
+
def deploy(group)
|
96
|
+
envs = service.each_environment(group: group).to_a
|
97
|
+
|
98
|
+
prefix = Time.now.utc.iso8601
|
99
|
+
|
100
|
+
output_dir do |base_path|
|
101
|
+
archives = {}
|
102
|
+
|
103
|
+
envs.each do |_, process|
|
104
|
+
path = base_path + process
|
105
|
+
path.mkpath
|
106
|
+
|
107
|
+
runner.stdout.puts "Staging for #{process}..."
|
108
|
+
service.stage(target_dir: path, process: process)
|
109
|
+
|
110
|
+
name = "#{group}-#{prefix}-#{process}"
|
111
|
+
key = "#{config.app_name}/#{name}"
|
112
|
+
|
113
|
+
archive_path = base_path + "#{name}.zip"
|
114
|
+
service.archive(input_dir: path, output_path: archive_path)
|
115
|
+
|
116
|
+
archives[process] = [key, name, archive_path]
|
117
|
+
end
|
118
|
+
|
119
|
+
each_in_parallel(envs) do |_, process|
|
120
|
+
runner.stdout.puts "Deploying to #{process}..."
|
121
|
+
|
122
|
+
s3_key, label, archive_path = archives[process]
|
123
|
+
|
124
|
+
service.deploy(group: group,
|
125
|
+
process: process,
|
126
|
+
archive_path: archive_path,
|
127
|
+
s3_key: s3_key,
|
128
|
+
label: label)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "stage PROCESS OUTPUT_DIR", "Prepare application to deploy"
|
134
|
+
def stage(process, output_dir)
|
135
|
+
path = Pathname(output_dir)
|
136
|
+
|
137
|
+
if path.directory?
|
138
|
+
runner.stdout.puts "Deleting #{path}..."
|
139
|
+
path.rmtree
|
140
|
+
end
|
141
|
+
|
142
|
+
runner.stdout.puts "Staging for #{process} in #{path}..."
|
143
|
+
|
144
|
+
path.mkpath
|
145
|
+
service.eb_init target_dir: path
|
146
|
+
service.stage(target_dir: path, process: process)
|
147
|
+
end
|
148
|
+
|
149
|
+
desc "archive PROCESS OUTPUT_PATH", "Prepare application bundle at OUTPUT_PATH"
|
150
|
+
def archive(process, output_path)
|
151
|
+
zip_path = Pathname(output_path)
|
152
|
+
|
153
|
+
Dir.mktmpdir do |dir|
|
154
|
+
dir_path = Pathname(dir)
|
155
|
+
|
156
|
+
runner.stdout.puts "Staging for #{process}..."
|
157
|
+
|
158
|
+
dir_path.mkpath
|
159
|
+
service.stage(target_dir: dir_path, process: process)
|
160
|
+
|
161
|
+
runner.stdout.puts "Making application bundle to #{zip_path}..."
|
162
|
+
|
163
|
+
service.archive(input_dir: dir_path, output_path: zip_path)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
desc "printenv GROUP [PROCESS]", "Print environment variables"
|
168
|
+
def printenv(group, process=nil)
|
169
|
+
service.each_environment(group: group) do |env, p|
|
170
|
+
try_process(p, is: process) do
|
171
|
+
puts "#{p} (#{env.environment_name}):"
|
172
|
+
env.env_vars.each do |key, value|
|
173
|
+
runner.stdout.puts " #{key}=#{value}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
desc "setenv GROUP PROCESS name=var name= ...", "Set environment variables"
|
180
|
+
def setenv(group, *args)
|
181
|
+
process = if args.first !~ /=/
|
182
|
+
args.shift
|
183
|
+
end
|
184
|
+
|
185
|
+
hash = parse_env_args(args)
|
186
|
+
|
187
|
+
logger.info("jeb::cli") { "Setting environment hash: #{hash.inspect}" }
|
188
|
+
|
189
|
+
envs = service.each_environment(group: group)
|
190
|
+
each_in_parallel(envs) do |env, p|
|
191
|
+
try_process(p, is: process) do
|
192
|
+
runner.stdout.puts "Updating #{p}'s environment variable..."
|
193
|
+
env.synchronize_update do
|
194
|
+
env.set_env_vars hash
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
desc "restart GROUP [PROCESS]", "Restart applications"
|
201
|
+
def restart(group, process=nil)
|
202
|
+
envs = service.each_environment(group: group)
|
203
|
+
each_in_parallel(envs) do |env, p|
|
204
|
+
try_process(p, is: process) do
|
205
|
+
runner.stdout.puts "Restarting #{p}..."
|
206
|
+
env.synchronize_update do
|
207
|
+
env.restart
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
desc "destroy GROUP [PROCESS]", "Terminate environments associated to GROUP"
|
214
|
+
def destroy(group, process=nil)
|
215
|
+
service.each_environment(group: group) do |env, p|
|
216
|
+
try_process p, is: process do
|
217
|
+
runner.stdout.puts "Destroying #{p}: #{env.environment_name}..."
|
218
|
+
env.destroy
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
desc "status GROUP [PROCESS]", "Print status of environments associated to GROUP"
|
224
|
+
def status(group, process=nil)
|
225
|
+
service.each_environment(group: group) do |env, p|
|
226
|
+
try_process p, is: process do
|
227
|
+
h = env.health
|
228
|
+
ih = h.instances_health
|
229
|
+
total = ih.no_data + ih.ok + ih.info + ih.warning + ih.degraded + ih.severe + ih.pending
|
230
|
+
runner.stdout.puts "#{p}: name=#{env.environment_name}, status=#{env.status}, health: #{h.health_status}, instances: #{total}, scale: #{env.scale}"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
desc "exec GROUP command...", "Run oneoff command"
|
236
|
+
option :keep, type: :boolean, default: false, desc: "Keep started oneoff environment"
|
237
|
+
def exec(group, *command)
|
238
|
+
env = service.each_environment(group: group).find {|_, p| p == "oneoff" }&.first
|
239
|
+
if env
|
240
|
+
begin
|
241
|
+
env.synchronize_update do
|
242
|
+
runner.stdout.puts "Starting #{env.environment_name} for oneoff process..."
|
243
|
+
env.set_scale 1
|
244
|
+
end
|
245
|
+
|
246
|
+
output_dir do |path|
|
247
|
+
service.eb_init target_dir: path
|
248
|
+
|
249
|
+
runner.stdout.puts "Waiting for EB to complete deploy..."
|
250
|
+
sleep 30
|
251
|
+
|
252
|
+
start = Time.now
|
253
|
+
|
254
|
+
while true
|
255
|
+
dirs, _ = runner.capture3! "eb", "ssh", env.environment_name, "-c", "ls /var/app"
|
256
|
+
|
257
|
+
if dirs =~ /ondeck/
|
258
|
+
logger.info("jeb::cli") { "Waiting for deploy..." }
|
259
|
+
end
|
260
|
+
if dirs =~ /current/ && dirs !~ /ondeck/
|
261
|
+
break
|
262
|
+
end
|
263
|
+
if Time.now - start > options[:timeout]*60
|
264
|
+
raise "Timed out for waiting deploy..."
|
265
|
+
end
|
266
|
+
|
267
|
+
sleep 15
|
268
|
+
end
|
269
|
+
|
270
|
+
commandline = "cd /var/app/current && sudo -E -u webapp env PATH=$PATH #{command.join(' ')}"
|
271
|
+
out, err, status = runner.capture3 "eb", "ssh", env.environment_name, "-c", commandline
|
272
|
+
|
273
|
+
runner.stdout.print out
|
274
|
+
runner.stderr.print err
|
275
|
+
|
276
|
+
raise status.to_s unless status.success?
|
277
|
+
end
|
278
|
+
ensure
|
279
|
+
unless options[:keep]
|
280
|
+
env.synchronize_update do
|
281
|
+
runner.stdout.puts "Shutting down #{env.environment_name}..."
|
282
|
+
env.set_scale 0
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
else
|
287
|
+
runner.stdout.puts "Could not find environment associated to oneoff process..."
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
desc "scale GROUP PROCESS min max", "Scale instances"
|
292
|
+
def scale(group, process, min, max=min)
|
293
|
+
service.each_environment(group: group) do |env, p|
|
294
|
+
try_process p, is: process do
|
295
|
+
runner.stdout.puts "Scaling #{group} (#{env.environment_name}) to min=#{min}, max=#{max}..."
|
296
|
+
env.synchronize_update do
|
297
|
+
env.set_scale(min...max)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
desc "list", "List groups"
|
304
|
+
def list
|
305
|
+
service.each_group do |group, envs|
|
306
|
+
runner.stdout.puts "#{group}: #{envs.map(&:environment_name).join(", ")}"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
desc "synchronize", "Wait for update"
|
311
|
+
def synchronize(group)
|
312
|
+
service.each_environment(group: group) do |env, process|
|
313
|
+
if env.status != "Ready"
|
314
|
+
runner.stdout.puts "Waiting for #{process} (#{env.environment_name})..."
|
315
|
+
env.synchronize_update
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
desc "resources GROUP [PROCESS]", "Download resources associated to each environment"
|
321
|
+
def resources(group, process_name=nil)
|
322
|
+
resources = {}
|
323
|
+
|
324
|
+
service.each_environment(group: group) do |env, process|
|
325
|
+
try_process process, is: process_name do
|
326
|
+
ress = env.resources.environment_resources
|
327
|
+
resources[process] = {
|
328
|
+
environment_name: env.environment_name,
|
329
|
+
environment_id: env.environment_id,
|
330
|
+
auto_scaling_groups: ress.auto_scaling_groups.map(&:name),
|
331
|
+
instances: ress.instances.map(&:id),
|
332
|
+
launch_configurations: ress.launch_configurations.map(&:name),
|
333
|
+
load_balancers: ress.load_balancers.map(&:name),
|
334
|
+
queues: ress.queues.map(&:url).reject(&:empty?),
|
335
|
+
triggers: ress.triggers.map(&:name)
|
336
|
+
}
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
unless resources.empty?
|
341
|
+
runner.stdout.puts JSON.pretty_generate(resources)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module JackAndTheElasticBeanstalk
|
2
|
+
class Config
|
3
|
+
attr_reader :app_hash
|
4
|
+
attr_reader :eb_configs
|
5
|
+
|
6
|
+
def initialize(app_hash:, eb_configs:)
|
7
|
+
@app_hash = app_hash
|
8
|
+
@eb_configs = eb_configs
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.load(path:)
|
12
|
+
app_yml = path + "app.yml"
|
13
|
+
app_hash = YAML.load_file(app_yml.to_s)
|
14
|
+
|
15
|
+
eb_configs = path.children.each.with_object({}) do |file, acc|
|
16
|
+
relative_path = file.relative_path_from(path)
|
17
|
+
acc[relative_path] = file.read if file.extname == ".config"
|
18
|
+
end
|
19
|
+
|
20
|
+
Config.new(app_hash: app_hash, eb_configs: eb_configs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def app_name
|
24
|
+
app_hash.dig("application", "name")
|
25
|
+
end
|
26
|
+
|
27
|
+
def region
|
28
|
+
app_hash.dig("application", "region")
|
29
|
+
end
|
30
|
+
|
31
|
+
def platform
|
32
|
+
app_hash.dig("application", "platform")
|
33
|
+
end
|
34
|
+
|
35
|
+
def configurations
|
36
|
+
app_hash["configurations"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def s3_bucket
|
40
|
+
app_hash["s3_bucket"]
|
41
|
+
end
|
42
|
+
|
43
|
+
def processes(configuration)
|
44
|
+
configurations[configuration].select {|_, value| value["type"] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_type(configuration, process)
|
48
|
+
processes(configuration)[process]["type"].to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
def each_config
|
52
|
+
if block_given?
|
53
|
+
eb_configs.each do |path, content|
|
54
|
+
yield path, ERB.new(content).result
|
55
|
+
end
|
56
|
+
else
|
57
|
+
enum_for :each_config
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def each_process(configuration)
|
62
|
+
if block_given?
|
63
|
+
processes(configuration).each do |key, hash|
|
64
|
+
yield key, hash
|
65
|
+
end
|
66
|
+
else
|
67
|
+
enum_for :each_process, configuration
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module JackAndTheElasticBeanstalk
|
2
|
+
class EB
|
3
|
+
attr_reader :application_name
|
4
|
+
attr_reader :logger
|
5
|
+
attr_reader :client
|
6
|
+
attr_accessor :timeout
|
7
|
+
|
8
|
+
def initialize(application_name:, logger:, client:)
|
9
|
+
@application_name = application_name
|
10
|
+
@logger = logger
|
11
|
+
@client = client
|
12
|
+
@env_stack = []
|
13
|
+
@timeout = 600
|
14
|
+
end
|
15
|
+
|
16
|
+
def environments
|
17
|
+
@environments = client.describe_environments(application_name: application_name, include_deleted: false).environments.map {|env|
|
18
|
+
Environment.new(application_name: application_name,
|
19
|
+
environment_name: env.environment_name,
|
20
|
+
logger: logger,
|
21
|
+
client: client).tap do |e|
|
22
|
+
e.timeout = timeout
|
23
|
+
e.data = env
|
24
|
+
end
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh
|
29
|
+
@environments = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_version(s3_bucket:, s3_key:, label:)
|
33
|
+
client.create_application_version(application_name: application_name,
|
34
|
+
description: label,
|
35
|
+
version_label: label,
|
36
|
+
source_bundle: {
|
37
|
+
s3_bucket: s3_bucket,
|
38
|
+
s3_key: s3_key,
|
39
|
+
},
|
40
|
+
process: true)
|
41
|
+
end
|
42
|
+
|
43
|
+
class Environment
|
44
|
+
attr_reader :application_name
|
45
|
+
attr_reader :logger
|
46
|
+
attr_reader :client
|
47
|
+
attr_reader :environment_name
|
48
|
+
attr_accessor :timeout
|
49
|
+
|
50
|
+
def initialize(application_name:, logger:, client:, environment_name:)
|
51
|
+
@application_name = application_name
|
52
|
+
@logger = logger
|
53
|
+
@client = client
|
54
|
+
@environment_name = environment_name
|
55
|
+
@timeout = 600
|
56
|
+
end
|
57
|
+
|
58
|
+
def refresh
|
59
|
+
@data = nil
|
60
|
+
@configuration_setting = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def data=(v)
|
64
|
+
@data = v
|
65
|
+
end
|
66
|
+
|
67
|
+
def data
|
68
|
+
@data ||= client.describe_environments(application_name: application_name).environments.find {|env|
|
69
|
+
env.environment_name == environment_name
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def configuration_setting
|
74
|
+
@configuration_setting ||= client.describe_configuration_settings(application_name: application_name, environment_name: environment_name).configuration_settings.first
|
75
|
+
end
|
76
|
+
|
77
|
+
def env_vars
|
78
|
+
configuration_setting.option_settings.each.with_object({}) do |option, hash|
|
79
|
+
if option.namespace == "aws:elasticbeanstalk:application:environment"
|
80
|
+
hash[option.option_name] = option.value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_env_vars(env)
|
86
|
+
need_update = env.all? {|key, value|
|
87
|
+
if value
|
88
|
+
env_vars[key] == value
|
89
|
+
else
|
90
|
+
!env_vars.key?(key)
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
if need_update
|
95
|
+
logger.info("jeb::eb") { "Env vars looks like identical; skip" }
|
96
|
+
else
|
97
|
+
logger.info("jeb::eb") { "Updating environment variables" }
|
98
|
+
|
99
|
+
options_to_update = []
|
100
|
+
options_to_remove = []
|
101
|
+
|
102
|
+
env.each do |key, value|
|
103
|
+
if value
|
104
|
+
options_to_update << {
|
105
|
+
namespace: "aws:elasticbeanstalk:application:environment",
|
106
|
+
option_name: key.to_s,
|
107
|
+
value: value.to_s
|
108
|
+
}
|
109
|
+
else
|
110
|
+
options_to_remove << {
|
111
|
+
namespace: "aws:elasticbeanstalk:application:environment",
|
112
|
+
option_name: key.to_s
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
client.update_environment(application_name: application_name,
|
118
|
+
environment_name: environment_name,
|
119
|
+
option_settings: options_to_update,
|
120
|
+
options_to_remove: options_to_remove)
|
121
|
+
|
122
|
+
refresh
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def synchronize_update(timeout: self.timeout)
|
127
|
+
logger.info("jeb::eb") { "Synchronizing update started... (timeout = #{timeout})" }
|
128
|
+
|
129
|
+
yield if block_given?
|
130
|
+
|
131
|
+
start = Time.now
|
132
|
+
wait = 1
|
133
|
+
|
134
|
+
while true
|
135
|
+
refresh
|
136
|
+
st = status
|
137
|
+
|
138
|
+
logger.info("jeb::eb") { "#{environment_name}:: status=#{st}" }
|
139
|
+
|
140
|
+
case st
|
141
|
+
when "Ready"
|
142
|
+
break
|
143
|
+
when "Updating", "Launching", "Aborting"
|
144
|
+
# ok
|
145
|
+
else
|
146
|
+
raise "Unexpected status: #{st}"
|
147
|
+
end
|
148
|
+
|
149
|
+
if Time.now - start > timeout
|
150
|
+
raise "Timeout exceeded"
|
151
|
+
end
|
152
|
+
|
153
|
+
sleep wait
|
154
|
+
wait = [wait*2, 30].min
|
155
|
+
end
|
156
|
+
|
157
|
+
logger.info("jeb::eb") { "Synchronized in #{(Time.now - start).to_i} seconds" }
|
158
|
+
end
|
159
|
+
|
160
|
+
def scale
|
161
|
+
option_settings = configuration_setting.option_settings
|
162
|
+
|
163
|
+
min = option_settings.find {|option| option.namespace == "aws:autoscaling:asg" && option.option_name == "MinSize" }.value.to_i
|
164
|
+
max = option_settings.find {|option| option.namespace == "aws:autoscaling:asg" && option.option_name == "MaxSize" }.value.to_i
|
165
|
+
|
166
|
+
min...max
|
167
|
+
end
|
168
|
+
|
169
|
+
def status
|
170
|
+
data.status
|
171
|
+
end
|
172
|
+
|
173
|
+
def set_scale(scale)
|
174
|
+
if scale.is_a?(Integer)
|
175
|
+
scale = scale...scale
|
176
|
+
end
|
177
|
+
|
178
|
+
if self.scale == scale
|
179
|
+
logger.info("jeb::eb") { "New scale is identical to current scale; skip" }
|
180
|
+
else
|
181
|
+
logger.info("jeb::eb") { "Scaling to #{scale}" }
|
182
|
+
|
183
|
+
client.update_environment(application_name: application_name,
|
184
|
+
environment_name: environment_name,
|
185
|
+
option_settings: [
|
186
|
+
{
|
187
|
+
namespace: "aws:autoscaling:asg",
|
188
|
+
option_name: "MinSize",
|
189
|
+
value: scale.begin.to_s
|
190
|
+
},
|
191
|
+
{
|
192
|
+
namespace: "aws:autoscaling:asg",
|
193
|
+
option_name: "MaxSize",
|
194
|
+
value: scale.end.to_s
|
195
|
+
}
|
196
|
+
])
|
197
|
+
|
198
|
+
refresh
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def environment_id
|
203
|
+
data.environment_id
|
204
|
+
end
|
205
|
+
|
206
|
+
def destroy
|
207
|
+
logger.info("jeb::eb") { "Terminating #{environment_name}..." }
|
208
|
+
client.terminate_environment(environment_id: environment_id)
|
209
|
+
end
|
210
|
+
|
211
|
+
def health
|
212
|
+
logger.info("jeb::eb") { "Downloading health data on #{environment_name}..." }
|
213
|
+
|
214
|
+
client.describe_environment_health(environment_id: environment_id, attribute_names: ["All"])
|
215
|
+
end
|
216
|
+
|
217
|
+
def resources
|
218
|
+
logger.info("jeb::eb") { "Downloading resources on #{environment_name}..."}
|
219
|
+
|
220
|
+
client.describe_environment_resources(environment_id: environment_id)
|
221
|
+
end
|
222
|
+
|
223
|
+
def restart
|
224
|
+
logger.info("jeb::eb") { "Restarting #{environment_name}..." }
|
225
|
+
client.restart_app_server(environment_id: environment_id)
|
226
|
+
end
|
227
|
+
|
228
|
+
def deploy(label:)
|
229
|
+
client.update_environment(environment_id: environment_id,
|
230
|
+
version_label: label)
|
231
|
+
end
|
232
|
+
|
233
|
+
def ensure_version!(expected_label:)
|
234
|
+
unless data.version_label == expected_label
|
235
|
+
raise "Unexpected version label: expected=#{expected_label}, actual=#{data.version_label}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module JackAndTheElasticBeanstalk
|
2
|
+
class Runner
|
3
|
+
attr_reader :stdin
|
4
|
+
attr_reader :stdout
|
5
|
+
attr_reader :stderr
|
6
|
+
attr_reader :logger
|
7
|
+
|
8
|
+
def initialize(stdin:, stdout:, stderr:, logger:)
|
9
|
+
@stdin = stdin
|
10
|
+
@stdout = stdout
|
11
|
+
@stderr = stderr
|
12
|
+
@paths = [Pathname.pwd]
|
13
|
+
@logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def paths
|
17
|
+
id = "#{inspect}:paths".to_sym
|
18
|
+
|
19
|
+
unless Thread.current[id]
|
20
|
+
Thread.current[id] = @paths.dup
|
21
|
+
end
|
22
|
+
|
23
|
+
Thread.current[id]
|
24
|
+
end
|
25
|
+
|
26
|
+
def chdir(dir)
|
27
|
+
paths.push dir
|
28
|
+
yield
|
29
|
+
ensure
|
30
|
+
paths.pop
|
31
|
+
end
|
32
|
+
|
33
|
+
def pwd
|
34
|
+
paths.last
|
35
|
+
end
|
36
|
+
|
37
|
+
def each_line(string, prefix: nil)
|
38
|
+
Array(string).flat_map {|s| s.split(/\n/) }.each do |line|
|
39
|
+
if prefix
|
40
|
+
yield "#{prefix}: #{line}"
|
41
|
+
else
|
42
|
+
yield line
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def capture3(*commands, options: {}, env: {})
|
48
|
+
logger.debug("jeb") { commands.inspect }
|
49
|
+
|
50
|
+
Open3.capture3(env, *commands, { chdir: pwd.to_s }.merge(options)).tap do |out, err, status|
|
51
|
+
logger.debug("jeb") { status.inspect }
|
52
|
+
|
53
|
+
each_line(out, prefix: "stdout") do |line|
|
54
|
+
logger.debug("jeb") { line }
|
55
|
+
end
|
56
|
+
|
57
|
+
each_line(err, prefix: "stderr") do |line|
|
58
|
+
logger.debug("jeb") { line }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def capture3!(*commands, options: {}, env: {})
|
64
|
+
out, err, status = capture3(*commands, options: options, env: env)
|
65
|
+
|
66
|
+
unless status.success?
|
67
|
+
raise "Faiiled to execute command: #{commands.inspect}"
|
68
|
+
end
|
69
|
+
|
70
|
+
[out, err]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module JackAndTheElasticBeanstalk
|
2
|
+
class Service
|
3
|
+
attr_reader :config
|
4
|
+
attr_reader :source_dir
|
5
|
+
attr_reader :eb
|
6
|
+
attr_reader :runner
|
7
|
+
attr_reader :logger
|
8
|
+
|
9
|
+
def initialize(config:, source_dir:, eb:, runner:, logger:)
|
10
|
+
@config = config
|
11
|
+
@source_dir = source_dir
|
12
|
+
@eb = eb
|
13
|
+
@runner = runner
|
14
|
+
@logger = logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def eb_init(target_dir:)
|
18
|
+
runner.chdir target_dir do
|
19
|
+
runner.capture3!("eb", "init", config.app_name, "-r", config.region, "-p", config.platform)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def eb_deploy(target_dir:, group:, process:)
|
24
|
+
runner.chdir target_dir do
|
25
|
+
runner.capture3!("eb", "deploy", env_name(group: group, process: process), "--nohang")
|
26
|
+
end
|
27
|
+
|
28
|
+
env = eb.environments.find {|env| env.environment_name == env_name(group: group, process: process) }
|
29
|
+
env.synchronize_update
|
30
|
+
end
|
31
|
+
|
32
|
+
def eb_create(target_dir:, configuration:, group:, process:, env_vars:)
|
33
|
+
if eb.environments.none? {|env| env.environment_name == env_name(group: group, process: process) }
|
34
|
+
logger.info("jeb::service") { "Creating eb environment for #{process} from #{target_dir}..." }
|
35
|
+
|
36
|
+
commandline = ["eb", "create", env_name(group: group, process: process), "--nohang", "--scale", "1"]
|
37
|
+
|
38
|
+
hash = config.processes(configuration)[process]
|
39
|
+
|
40
|
+
commandline.concat(["-t", "worker"]) if config.process_type(configuration, process) == "worker"
|
41
|
+
commandline.concat(["--region", hash["region"] || config.region])
|
42
|
+
commandline.concat(["--platform", hash["platform"] || config.platform])
|
43
|
+
commandline.concat(["--instance_profile", hash["instance_profile"]]) if hash["instance_profile"]
|
44
|
+
commandline.concat(["--keyname", hash["keyname"]]) if hash["keyname"]
|
45
|
+
commandline.concat(["--instance_type", hash["instance_type"]]) if hash["instance_type"]
|
46
|
+
commandline.concat(["--service-role", hash["service_role"]]) if hash["service_role"]
|
47
|
+
|
48
|
+
if hash["tags"]&.any?
|
49
|
+
commandline.concat(["--tags", hash["tags"].each.with_object([]) do |(key, value), acc|
|
50
|
+
acc << "#{key}=#{value}"
|
51
|
+
end.concat(",")])
|
52
|
+
end
|
53
|
+
|
54
|
+
if hash["vpc"]
|
55
|
+
vpc = hash["vpc"]
|
56
|
+
|
57
|
+
commandline.concat(["--vpc", "--vpc.id", vpc["id"]])
|
58
|
+
commandline.concat(["--vpc.ec2subnets", vpc["ec2subnets"].join(",")]) if vpc["ec2subnets"]&.any?
|
59
|
+
commandline.concat(["--vpc.elbpublic"]) if vpc["elbpublic"]
|
60
|
+
commandline.concat(["--vpc.elbsubnets", vpc["elbsubnets"].join(",")]) if vpc["elbsubnets"]&.any?
|
61
|
+
commandline.concat(["--vpc.publicip"]) if vpc["publicip"]
|
62
|
+
commandline.concat(["--vpc.securitygroups", vpc["securitygroups"].join(",")]) if vpc["securitygroups"]&.any?
|
63
|
+
end
|
64
|
+
|
65
|
+
unless env_vars.empty?
|
66
|
+
vars = env_vars.to_a.map {|(k, v)| "#{k}=#{v}" }.join(",")
|
67
|
+
commandline.concat(["--envvars", vars])
|
68
|
+
end
|
69
|
+
|
70
|
+
runner.chdir target_dir do
|
71
|
+
runner.capture3!(*commandline)
|
72
|
+
end
|
73
|
+
|
74
|
+
eb.refresh
|
75
|
+
env = eb.environments.find {|env| env.environment_name == env_name(group: group, process: process) }
|
76
|
+
env.synchronize_update
|
77
|
+
else
|
78
|
+
logger.info("jeb::service") { "Environment #{env_name(group:group, process: process)} already exists..." }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def env_name(group:, process:)
|
83
|
+
"jeb-#{group}-#{process}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def stage(target_dir:, process:)
|
87
|
+
logger.info("jeb::service") { "Staging files in #{target_dir} for #{process}" }
|
88
|
+
|
89
|
+
ENV["JEB_PROCESS"] = process
|
90
|
+
|
91
|
+
export_files(dest: target_dir)
|
92
|
+
|
93
|
+
eb_extensions = target_dir + ".ebextensions"
|
94
|
+
eb_extensions.mkpath
|
95
|
+
config.each_config do |path, content|
|
96
|
+
logger.debug("jeb::service") { "Writing #{path} ..." }
|
97
|
+
(eb_extensions + path).write content
|
98
|
+
end
|
99
|
+
ensure
|
100
|
+
ENV.delete("JEB_PROCESS")
|
101
|
+
end
|
102
|
+
|
103
|
+
def archive(input_dir:, output_path:)
|
104
|
+
paths = Pathname.glob(input_dir + "**/*", File::FNM_DOTMATCH).select(&:file?)
|
105
|
+
|
106
|
+
Zip::File.open(output_path.to_s, Zip::File::CREATE) do |zip|
|
107
|
+
paths.each do |path|
|
108
|
+
zip.add(path.relative_path_from(input_dir).to_s, path.to_s)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def export_files(dest:)
|
114
|
+
files = runner.chdir(source_dir) do
|
115
|
+
runner.capture3!("git", "ls-files", "-z").first.split("\x0")
|
116
|
+
end
|
117
|
+
|
118
|
+
files.each do |f|
|
119
|
+
logger.debug("jeb::service") { "Copying #{f} ..."}
|
120
|
+
|
121
|
+
source_path = source_dir + f
|
122
|
+
target_path = dest + f
|
123
|
+
|
124
|
+
unless target_path.parent.directory?
|
125
|
+
target_path.parent.mkpath
|
126
|
+
end
|
127
|
+
|
128
|
+
FileUtils.copy(source_path.to_s, target_path.to_s)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def upload(archive_path:, name:)
|
133
|
+
logger.debug("jeb::service") { "Uploading #{archive_path} to #{config.s3_bucket}/#{name}..." }
|
134
|
+
s3 = Aws::S3::Client.new(region: config.region)
|
135
|
+
archive_path.open do |io|
|
136
|
+
s3.put_object(bucket: config.s3_bucket, key: name, body: io)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def deploy(group:, process:, archive_path:, s3_key:, label:)
|
141
|
+
upload(archive_path: archive_path, name: s3_key)
|
142
|
+
eb.create_version(s3_bucket: config.s3_bucket, s3_key: s3_key, label: label)
|
143
|
+
|
144
|
+
eb.environments.find {|env| env.environment_name == env_name(group: group, process: process) }.try do |env|
|
145
|
+
env.synchronize_update do
|
146
|
+
env.deploy(label: label)
|
147
|
+
end
|
148
|
+
|
149
|
+
env.ensure_version!(expected_label: label)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def destroy(group:)
|
154
|
+
logger.info("jeb::service") { "Destroying #{group} ..." }
|
155
|
+
|
156
|
+
each_environment(group: group) do |env, _|
|
157
|
+
env.destroy
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def each_environment(group:)
|
162
|
+
if block_given?
|
163
|
+
regexp = /\Ajeb-#{group}-([^\-]+)\Z/
|
164
|
+
|
165
|
+
eb.environments.each do |env|
|
166
|
+
if env.environment_name =~ regexp
|
167
|
+
yield env, $1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
else
|
171
|
+
enum_for :each_environment, group: group
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def each_group
|
176
|
+
if block_given?
|
177
|
+
regexp = /\Ajeb-(.+)-([^\-]+)\Z/
|
178
|
+
|
179
|
+
eb.environments.group_by {|env|
|
180
|
+
if env.environment_name =~ regexp
|
181
|
+
$1
|
182
|
+
end
|
183
|
+
}.each do |group, envs|
|
184
|
+
if group
|
185
|
+
yield group, envs
|
186
|
+
end
|
187
|
+
end
|
188
|
+
else
|
189
|
+
enum_for :each_group
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "jack_and_the_elastic_beanstalk/version"
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "pathname"
|
5
|
+
require 'dotenv'
|
6
|
+
require "rainbow"
|
7
|
+
require "erb"
|
8
|
+
require "yaml"
|
9
|
+
require "open3"
|
10
|
+
require "logger"
|
11
|
+
require "tmpdir"
|
12
|
+
require "pp"
|
13
|
+
require "aws-sdk"
|
14
|
+
require "thor"
|
15
|
+
require "parallel"
|
16
|
+
require "active_support"
|
17
|
+
require "active_support/core_ext"
|
18
|
+
require "zip"
|
19
|
+
|
20
|
+
require "jack_and_the_elastic_beanstalk/eb"
|
21
|
+
require "jack_and_the_elastic_beanstalk/config"
|
22
|
+
require "jack_and_the_elastic_beanstalk/runner"
|
23
|
+
require "jack_and_the_elastic_beanstalk/service"
|
24
|
+
require "jack_and_the_elastic_beanstalk/cli"
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jack_and_the_elastic_beanstalk
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.pre
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Soutaro Matsumoto
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dotenv
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rainbow
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aws-sdk
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.6'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: thor
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.19'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.19'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: parallel
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.10'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.10'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: activesupport
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '5.0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '5.0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubyzip
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '1.2'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '1.2'
|
153
|
+
description: Jack and the Elastic Beanstalk.
|
154
|
+
email:
|
155
|
+
- matsumoto@soutaro.com
|
156
|
+
executables:
|
157
|
+
- jeb
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".gitignore"
|
162
|
+
- ".ruby-version"
|
163
|
+
- ".travis.yml"
|
164
|
+
- Gemfile
|
165
|
+
- README.md
|
166
|
+
- Rakefile
|
167
|
+
- bin/console
|
168
|
+
- bin/setup
|
169
|
+
- exe/jeb
|
170
|
+
- jack_and_the_elastic_beanstalk.gemspec
|
171
|
+
- lib/jack_and_the_elastic_beanstalk.rb
|
172
|
+
- lib/jack_and_the_elastic_beanstalk/cli.rb
|
173
|
+
- lib/jack_and_the_elastic_beanstalk/config.rb
|
174
|
+
- lib/jack_and_the_elastic_beanstalk/eb.rb
|
175
|
+
- lib/jack_and_the_elastic_beanstalk/runner.rb
|
176
|
+
- lib/jack_and_the_elastic_beanstalk/service.rb
|
177
|
+
- lib/jack_and_the_elastic_beanstalk/version.rb
|
178
|
+
homepage: https://github.com/sideci/jack_and_the_elastic_beanstalk
|
179
|
+
licenses: []
|
180
|
+
metadata: {}
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.3.1
|
195
|
+
requirements: []
|
196
|
+
rubyforge_project:
|
197
|
+
rubygems_version: 2.5.2
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: Jack and the Elastic Beanstalk.
|
201
|
+
test_files: []
|