jack_and_the_elastic_beanstalk 0.1.0.pre
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 +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: []
|