rebi 0.1.6
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/MIT-LICENSE +20 -0
- data/README.md +95 -0
- data/Rakefile +34 -0
- data/bin/rebi +84 -0
- data/lib/rebi/application.rb +152 -0
- data/lib/rebi/config.rb +48 -0
- data/lib/rebi/config_environment.rb +317 -0
- data/lib/rebi/environment.rb +305 -0
- data/lib/rebi/erb_helper.rb +16 -0
- data/lib/rebi/error.rb +13 -0
- data/lib/rebi/version.rb +3 -0
- data/lib/rebi/zip_helper.rb +61 -0
- data/lib/rebi.rb +66 -0
- data/lib/tasks/rebi.rake +4 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e86f234a1a4dcdb347b576c07f449d68f4ef0918
|
4
|
+
data.tar.gz: 26fc0b37540c17cc4008f3bafb278db933591e01
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0b3818bc2410ee9b0bfb567b8ee76f6c98ffd312f11b806837dc21c4766c9428f5fb7c6c6cb4cbe7d8a4f49641aeada918c43c9557112c791676f1aecf9accf1
|
7
|
+
data.tar.gz: 14f13a7ced8fcec217bbff5149f86b18914a5b63b3a27444253631b57174a45dedd13c04d6ca897eb9b60f197074968276cc2bc36db00d7cfc4b8e22e139bc80
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Rebi
|
2
|
+
Deployment tool for Elasticbeanstalk
|
3
|
+
|
4
|
+
# Features
|
5
|
+
- Switchable + multiple ebextensions folder
|
6
|
+
- Support erb in ebextension config files
|
7
|
+
- Support env_file for environment variables
|
8
|
+
- Multiple deployment
|
9
|
+
- Deploy source code along with updating beanstalk options
|
10
|
+
- Simple config
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
Or install it yourself as:
|
14
|
+
```bash
|
15
|
+
$ gem install rebi
|
16
|
+
```
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
How to use my plugin.
|
20
|
+
|
21
|
+
### AWS authentication
|
22
|
+
Rebi uses environment variables to get aws credentials
|
23
|
+
To set environment variables use `export` or `.env` file
|
24
|
+
```bash
|
25
|
+
# Use access key
|
26
|
+
export AWS_ACCESS_KEY_ID=xxxxx
|
27
|
+
export AWS_SECRET_ACCESS_KEY=xxxxxx
|
28
|
+
```
|
29
|
+
Or
|
30
|
+
```bash
|
31
|
+
# Use profile
|
32
|
+
export AWS_PROFILE=xxxxx
|
33
|
+
```
|
34
|
+
|
35
|
+
Refer http://docs.aws.amazon.com/sdkforruby/api/Aws/ElasticBeanstalk/Client.html for other settings
|
36
|
+
|
37
|
+
### Yaml config
|
38
|
+
Default config file is `config/rebi.yml` use `-c` to switch
|
39
|
+
```yaml
|
40
|
+
app_name: app-name
|
41
|
+
stages:
|
42
|
+
development:
|
43
|
+
web:
|
44
|
+
name: web01
|
45
|
+
env_file: .env.development
|
46
|
+
ebextensions: "web-ebextensions"
|
47
|
+
```
|
48
|
+
|
49
|
+
For other configs( key_name, instance_type, instance_num, service_role,...), please refer sample config
|
50
|
+
```bash
|
51
|
+
$ bundle exec rebi sample > rebi.yml
|
52
|
+
```
|
53
|
+
|
54
|
+
### Deploy
|
55
|
+
```bash
|
56
|
+
# Single deploy
|
57
|
+
$ bundle exec rebi deploy development web
|
58
|
+
```
|
59
|
+
|
60
|
+
```bash
|
61
|
+
# Multiple deploy (if development has more than one environments)
|
62
|
+
$ bundle exec rebi deploy development
|
63
|
+
```
|
64
|
+
|
65
|
+
### Get envronment variables and status
|
66
|
+
```bash
|
67
|
+
# Running envronment variables
|
68
|
+
$ bundle exec rebi get_env development
|
69
|
+
# envronment variables for config
|
70
|
+
$ bundle exec rebi get_env development --from-config
|
71
|
+
# Status
|
72
|
+
$ bundle exec rebi status development
|
73
|
+
```
|
74
|
+
|
75
|
+
###For more help
|
76
|
+
```bash
|
77
|
+
$ bundle exec rebi --help
|
78
|
+
```
|
79
|
+
|
80
|
+
### ERB in ebextensions config
|
81
|
+
Use `rebi_env` to get environment variables config in .ebextensions
|
82
|
+
```yaml
|
83
|
+
# Ex
|
84
|
+
# .ebextensions/00-envrionments.config
|
85
|
+
option_settings:
|
86
|
+
- option_name: KEY
|
87
|
+
value: <%= rebi_env[KEY] %>
|
88
|
+
```
|
89
|
+
|
90
|
+
## Contributing
|
91
|
+
Feel free to fork and request a pull, or submit a ticket
|
92
|
+
https://github.com/khiemns54/rebi/issues
|
93
|
+
|
94
|
+
## License
|
95
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Rebi'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
data/bin/rebi
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'commander/import'
|
5
|
+
require 'rebi'
|
6
|
+
|
7
|
+
REBI_PATH = File.join(File.dirname(File.expand_path(__FILE__)), "..")
|
8
|
+
|
9
|
+
program :name, 'Rebi'
|
10
|
+
program :version, Rebi::VERSION
|
11
|
+
program :description, 'Elasticbeanstalk deployment tool'
|
12
|
+
|
13
|
+
global_option('-c', '--config FILE', 'Load config data for your commands to use') do |file|
|
14
|
+
Rebi.config.config_file = file
|
15
|
+
Rebi.config.reload!
|
16
|
+
end
|
17
|
+
|
18
|
+
command :deploy do |c|
|
19
|
+
c.syntax = 'rebi deploy stage [env_name] [--options]'
|
20
|
+
c.description = 'Deploy single or multiple ElasticBeanstalk environments'
|
21
|
+
c.example 'Deploy only web environment in development', 'rebi deploy development web'
|
22
|
+
c.example 'Deploy all environments in development', 'rebi deploy development'
|
23
|
+
c.option '--source-only', 'Deploy source only'
|
24
|
+
c.option '--settings-only', 'Deploy option_settings and environment variables only'
|
25
|
+
c.action do |args, options|
|
26
|
+
stage, env_name = args
|
27
|
+
raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
|
28
|
+
opts = options.__hash__
|
29
|
+
if env_name.present?
|
30
|
+
Rebi.app.deploy stage, env_name, opts
|
31
|
+
else
|
32
|
+
if agree("Do you want to deploy all environments in #{stage} stage?(Y/n)")
|
33
|
+
Rebi.log("Preparing for deployment")
|
34
|
+
Rebi.app.deploy stage, nil, opts
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
command :get_env do |c|
|
41
|
+
c.syntax = 'rebi get_env stage [env_name] [--options]'
|
42
|
+
c.description = 'Get using environment variables or environment variables from config file'
|
43
|
+
c.example 'Get environment variables of web in development', 'rebi get_env development web'
|
44
|
+
c.example 'Get environment variables of development stages', 'rebi get_env development'
|
45
|
+
c.option '--from-config', 'Get environment variables from config'
|
46
|
+
c.action do |args, options|
|
47
|
+
stage, env_name = args
|
48
|
+
raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
|
49
|
+
Rebi.app.print_environment_variables stage, env_name, options.from_config
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
command :status do |c|
|
54
|
+
c.syntax = 'rebi status stage [env_name]'
|
55
|
+
c.description = 'Get current status'
|
56
|
+
c.action do |args, options|
|
57
|
+
stage, env_name = args
|
58
|
+
raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
|
59
|
+
Rebi.app.print_environment_status stage, env_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
command :sample do |c|
|
64
|
+
c.syntax = 'rebi sample '
|
65
|
+
c.description = 'Create sample config yaml'
|
66
|
+
c.example 'Create sample', 'rebi sample > rebi.yml '
|
67
|
+
c.action do |args, options|
|
68
|
+
puts File.read("#{REBI_PATH}/sample/rebi.yml")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
command :terminate do |c|
|
73
|
+
c.syntax = 'rebi terminate stage env_name '
|
74
|
+
c.description = 'Terminate environment'
|
75
|
+
c.action do |args, options|
|
76
|
+
stage, env_name = args
|
77
|
+
raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
|
78
|
+
raise Rebi::Error.new("Env name cannot be nil") if env_name.blank?
|
79
|
+
env_conf = Rebi.config.environment(stage, env_name)
|
80
|
+
if ask("Type '#{env_conf.name}' to confirm termination") == env_conf.name
|
81
|
+
Rebi.app.terminate! stage, env_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Rebi
|
2
|
+
class Application
|
3
|
+
attr_reader :app, :app_name, :client, :s3_client
|
4
|
+
def initialize app, client
|
5
|
+
@app = app
|
6
|
+
@app_name = app.application_name
|
7
|
+
@client = client
|
8
|
+
@s3_client = Aws::S3::Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def bucket_name
|
12
|
+
@bucket_name ||= client.create_storage_location.s3_bucket
|
13
|
+
end
|
14
|
+
|
15
|
+
def environments
|
16
|
+
Rebi::Environment.all app_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def deploy stage_name, env_name=nil, opts={}
|
20
|
+
return deploy_stage(stage_name, opts) if env_name.blank?
|
21
|
+
env = Rebi::Environment.new stage_name, env_name, client
|
22
|
+
app_version = create_app_version env
|
23
|
+
begin
|
24
|
+
req_id = env.deploy app_version, opts
|
25
|
+
env.watch_request req_id if req_id
|
26
|
+
rescue Rebi::Error::EnvironmentInUpdating => e
|
27
|
+
Rebi.log("Environment in updating", env.name)
|
28
|
+
raise e
|
29
|
+
end
|
30
|
+
req_id
|
31
|
+
end
|
32
|
+
|
33
|
+
def deploy_stage stage_name, opts={}
|
34
|
+
threads = []
|
35
|
+
Rebi.config.stage(stage_name).each do |env_name, conf|
|
36
|
+
next if conf.blank?
|
37
|
+
threads << Thread.new do
|
38
|
+
begin
|
39
|
+
deploy stage_name, env_name, opts
|
40
|
+
rescue Exception => e
|
41
|
+
Rebi.log(e.message, "ERROR")
|
42
|
+
e.backtrace.each do |m|
|
43
|
+
Rebi.log(m, "ERROR")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ThreadsWait.all_waits(*threads)
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_environment_variables stage_name, env_name, from_config=false
|
53
|
+
if env_name.blank?
|
54
|
+
Rebi.config.stage(stage_name).each do |e_name, conf|
|
55
|
+
next if conf.blank?
|
56
|
+
print_environment_variables stage_name, e_name, from_config
|
57
|
+
end
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
env = Rebi::Environment.new stage_name, env_name, client
|
62
|
+
env_vars = from_config ? env.config.environment_variables : env.environment_variables
|
63
|
+
|
64
|
+
Rebi.log("#{from_config ? "Config" : "Current"} environment variables", env.name)
|
65
|
+
env_vars.each do |k,v|
|
66
|
+
Rebi.log("#{k}=#{v}")
|
67
|
+
end
|
68
|
+
Rebi.log("--------------------------------------", env.name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def print_environment_status stage_name, env_name
|
72
|
+
if env_name.blank?
|
73
|
+
Rebi.config.stage(stage_name).each do |e_name, conf|
|
74
|
+
next if conf.blank?
|
75
|
+
print_environment_status stage_name, e_name
|
76
|
+
end
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
env = Rebi::Environment.new stage_name, env_name, client
|
81
|
+
env.check_created!
|
82
|
+
Rebi.log("--------- CURRENT STATUS -------------", env.name)
|
83
|
+
Rebi.log("id: #{env.id}", env.name)
|
84
|
+
Rebi.log("Status: #{env.status}", env.name)
|
85
|
+
Rebi.log("Health: #{env.health}", env.name)
|
86
|
+
Rebi.log("--------------------------------------", env.name)
|
87
|
+
end
|
88
|
+
|
89
|
+
def terminate! stage_name, env_name
|
90
|
+
env = Rebi::Environment.new stage_name, env_name, client
|
91
|
+
begin
|
92
|
+
req_id = env.terminate!
|
93
|
+
ThreadsWait.all_waits(env.watch_request req_id) if req_id
|
94
|
+
rescue Rebi::Error::EnvironmentInUpdating => e
|
95
|
+
Rebi.log("Environment in updating", env.name)
|
96
|
+
raise e
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_app_version env
|
101
|
+
start = Time.now.utc
|
102
|
+
source_bundle = Rebi::ZipHelper.new.gen(env.config)
|
103
|
+
version_label = source_bundle[:label]
|
104
|
+
key = "#{app_name}/#{version_label}.zip"
|
105
|
+
Rebi.log("Uploading source bundle: #{version_label}.zip", env.config.name)
|
106
|
+
s3_client.put_object(
|
107
|
+
bucket: bucket_name,
|
108
|
+
key: key,
|
109
|
+
body: source_bundle[:file].read
|
110
|
+
)
|
111
|
+
Rebi.log("Creating app version: #{version_label}", env.config.name)
|
112
|
+
client.create_application_version({
|
113
|
+
application_name: app_name,
|
114
|
+
description: source_bundle[:message],
|
115
|
+
version_label: version_label,
|
116
|
+
source_bundle: {
|
117
|
+
s3_bucket: bucket_name,
|
118
|
+
s3_key: key
|
119
|
+
}
|
120
|
+
})
|
121
|
+
Rebi.log("App version was created in: #{Time.now.utc - start}s", env.config.name)
|
122
|
+
return version_label
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
def self.client
|
127
|
+
Rebi.client || Aws::ElasticBeanstalk::Client.new
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.get_application app_name
|
131
|
+
raise Error::ApplicationNotFound.new unless app = client.describe_applications(application_names: [app_name]).applications.first
|
132
|
+
return Rebi::Application.new(app, client)
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.create_application app_name, description=nil
|
136
|
+
res = client.create_application(
|
137
|
+
application_name: app_name,
|
138
|
+
description: description,
|
139
|
+
)
|
140
|
+
return Rebi::Application.new(res.application, client)
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.get_or_create_application app_name
|
144
|
+
begin
|
145
|
+
return get_application app_name
|
146
|
+
rescue Error::ApplicationNotFound
|
147
|
+
return create_application app_name, Rebi.config.app_description
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
data/lib/rebi/config.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rebi
|
2
|
+
class Config
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def config_file
|
6
|
+
@config_file ||= "#{Rebi.root}/config/rebi.yml"
|
7
|
+
end
|
8
|
+
|
9
|
+
def config_file=path
|
10
|
+
@config_file = Pathname.new(path).realpath.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def reload!
|
14
|
+
@data = nil
|
15
|
+
return data
|
16
|
+
end
|
17
|
+
|
18
|
+
def app_name
|
19
|
+
data[:app_name]
|
20
|
+
end
|
21
|
+
|
22
|
+
def app_description
|
23
|
+
data[:app_description] || "Created via rebi"
|
24
|
+
end
|
25
|
+
|
26
|
+
def stage stage
|
27
|
+
data[:stages][stage] || raise(Rebi::Error::ConfigNotFound.new("Stage: #{stage}"))
|
28
|
+
end
|
29
|
+
|
30
|
+
def timeout
|
31
|
+
(data[:timeout] || 60*10).second
|
32
|
+
end
|
33
|
+
|
34
|
+
def environment stg_name, env_name
|
35
|
+
stg = stage stg_name
|
36
|
+
raise(Rebi::Error::ConfigNotFound.new("Environment config: #{env_name}")) unless stg.key?(env_name)
|
37
|
+
return Rebi::ConfigEnvironment.new(stg_name, env_name, stg[env_name] || {})
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def data
|
42
|
+
@data ||= YAML::load(ERB.new(IO.read(config_file)).result).with_indifferent_access
|
43
|
+
raise Rebi::Error::ConfigInvalid.new("app_name cannot be nil") if @data[:app_name].blank?
|
44
|
+
raise Rebi::Error::ConfigInvalid.new("stages cannot be nil") if @data[:stages].blank?
|
45
|
+
return @data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Rebi
|
4
|
+
class ConfigEnvironment
|
5
|
+
|
6
|
+
attr_reader :stage,
|
7
|
+
:env_name,
|
8
|
+
:name,
|
9
|
+
:description,
|
10
|
+
:cname_prefix,
|
11
|
+
:tier,
|
12
|
+
:instance_type,
|
13
|
+
:instance_num,
|
14
|
+
:key_name,
|
15
|
+
:service_role,
|
16
|
+
:ebextensions,
|
17
|
+
:solution_stack_name,
|
18
|
+
:cfg_file,
|
19
|
+
:env_file,
|
20
|
+
:environment_variables,
|
21
|
+
:option_settings,
|
22
|
+
:raw_conf
|
23
|
+
|
24
|
+
NAMESPACE ={
|
25
|
+
app_env: "aws:elasticbeanstalk:application:environment",
|
26
|
+
eb_env: "aws:elasticbeanstalk:environment",
|
27
|
+
autoscaling_launch: "aws:autoscaling:launchconfiguration",
|
28
|
+
autoscaling_asg: "aws:autoscaling:asg",
|
29
|
+
elb_policies: "aws:elb:policies",
|
30
|
+
elb_health: "aws:elb:healthcheck",
|
31
|
+
elb_loadbalancer: "aws:elb:loadbalancer",
|
32
|
+
healthreporting: "aws:elasticbeanstalk:healthreporting:system",
|
33
|
+
eb_command: "aws:elasticbeanstalk:command",
|
34
|
+
}
|
35
|
+
|
36
|
+
UPDATEABLE_NS = {
|
37
|
+
autoscaling_asg: [:MaxSize, :MinSize],
|
38
|
+
autoscaling_launch: [:InstanceType, :EC2KeyName],
|
39
|
+
eb_env: [:ServiceRole],
|
40
|
+
}
|
41
|
+
|
42
|
+
def initialize stage, env_name, env_conf={}
|
43
|
+
@raw_conf = env_conf.with_indifferent_access
|
44
|
+
@stage = stage.to_sym
|
45
|
+
@env_name = env_name.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
@name ||= raw_conf[:name] || "#{env_name}-#{stage}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def app_name
|
53
|
+
Rebi.config.app_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def description
|
57
|
+
@description ||= raw_conf[:description] || "Created via rebi"
|
58
|
+
end
|
59
|
+
|
60
|
+
def cname_prefix
|
61
|
+
@cname_prefix ||= raw_conf[:cname_prefix] || "#{name}-#{stage}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def tier
|
65
|
+
return @tier if @tier
|
66
|
+
t = if raw_conf[:tier]
|
67
|
+
raw_conf[:tier].to_sym
|
68
|
+
elsif cfg.present? && cfg[:EnvironmentTier].present? && cfg[:EnvironmentTier][:Name].present?
|
69
|
+
cfg[:EnvironmentTier][:Name] == "Worker" ? :worker : :web
|
70
|
+
else
|
71
|
+
:web
|
72
|
+
end
|
73
|
+
|
74
|
+
@tier = if t == :web
|
75
|
+
{
|
76
|
+
name: "WebServer",
|
77
|
+
type: "Standard",
|
78
|
+
}
|
79
|
+
elsif t == :worker
|
80
|
+
{
|
81
|
+
name: "Worker",
|
82
|
+
type: "SQS/HTTP",
|
83
|
+
}
|
84
|
+
else
|
85
|
+
Rebi::Error::ConfigInvalid.new("Invalid tier")
|
86
|
+
end
|
87
|
+
|
88
|
+
return @tier = @tier.with_indifferent_access
|
89
|
+
end
|
90
|
+
|
91
|
+
def worker?
|
92
|
+
tier[:name] == "Worker" ? true : false
|
93
|
+
end
|
94
|
+
|
95
|
+
def instance_type
|
96
|
+
get_opt ns[:autoscaling_launch], :InstanceType
|
97
|
+
end
|
98
|
+
|
99
|
+
def instance_num
|
100
|
+
{
|
101
|
+
min: get_opt(ns[:autoscaling_asg], :MinSize),
|
102
|
+
max: get_opt(ns[:autoscaling_asg], :MaxSize)
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def key_name
|
107
|
+
get_opt(ns[:autoscaling_launch], :EC2KeyName)
|
108
|
+
end
|
109
|
+
|
110
|
+
def service_role
|
111
|
+
get_opt(ns[:eb_env], :ServiceRole)
|
112
|
+
end
|
113
|
+
|
114
|
+
def cfg_file
|
115
|
+
@cfg_file ||= raw_conf[:cfg_file]
|
116
|
+
return @cfg_file if @cfg_file.blank?
|
117
|
+
return @cfg_file if Pathname.new(@cfg_file).absolute?
|
118
|
+
@cfg_file = ".elasticbeanstalk/saved_configs/#{@cfg_file}.cfg.yml" unless @cfg_file.match(".yml$")
|
119
|
+
return (@cfg_file = "#{Rebi.root}/#{@cfg_file}")
|
120
|
+
end
|
121
|
+
|
122
|
+
def env_file
|
123
|
+
@env_file ||= raw_conf[:env_file]
|
124
|
+
end
|
125
|
+
|
126
|
+
def cfg
|
127
|
+
begin
|
128
|
+
return nil if cfg_file.blank?
|
129
|
+
return (@cfg ||= YAML.load(ERB.new(IO.read(cfg_file)).result).with_indifferent_access)
|
130
|
+
rescue Errno::ENOENT
|
131
|
+
raise Rebi::Error::ConfigInvalid.new("cfg_file: #{cfg_file}")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def solution_stack_name
|
136
|
+
@solution_stack_name ||= raw_conf[:solution_stack_name] || "64bit Amazon Linux 2017.03 v2.4.1 running Ruby 2.3 (Puma)"
|
137
|
+
end
|
138
|
+
|
139
|
+
def platform_arn
|
140
|
+
cfg && cfg[:Platform] && cfg[:Platform][:PlatformArn]
|
141
|
+
end
|
142
|
+
|
143
|
+
def ebextensions
|
144
|
+
return @ebextensions ||= if (ebx = raw_conf[:ebextensions])
|
145
|
+
ebx = [ebx] if ebx.is_a?(String)
|
146
|
+
ebx.prepend(".ebextensions") unless ebx.include?(".ebextensions")
|
147
|
+
ebx
|
148
|
+
else
|
149
|
+
[".ebextensions"]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def raw_environment_variables
|
154
|
+
raw_conf[:environment_variables] || {}
|
155
|
+
end
|
156
|
+
|
157
|
+
def dotenv
|
158
|
+
env_file.present? ? Dotenv.load(env_file).with_indifferent_access : {}
|
159
|
+
end
|
160
|
+
|
161
|
+
def environment_variables
|
162
|
+
get_opt(ns(:app_env))
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_opt namespace, opt_name=nil
|
166
|
+
has_value_by_keys(option_settings, namespace, opt_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_raw_opt namespace, opt_name=nil
|
170
|
+
has_value_by_keys(raw_conf[:option_settings], namespace, opt_name)
|
171
|
+
end
|
172
|
+
|
173
|
+
def opts_array opts=option_settings
|
174
|
+
res = []
|
175
|
+
opts.each do |namespace, v|
|
176
|
+
namespace, resource_name = namespace.split(".").reverse
|
177
|
+
v.each do |option_name, value|
|
178
|
+
res << {
|
179
|
+
resource_name: resource_name,
|
180
|
+
namespace: namespace,
|
181
|
+
option_name: option_name,
|
182
|
+
value: value.to_s,
|
183
|
+
}.with_indifferent_access
|
184
|
+
end
|
185
|
+
end
|
186
|
+
return res
|
187
|
+
end
|
188
|
+
|
189
|
+
def option_settings
|
190
|
+
return @opt if @opt.present?
|
191
|
+
opt = (cfg && cfg[:OptionSettings]) || {}.with_indifferent_access
|
192
|
+
|
193
|
+
opt = opt.deep_merge(raw_conf[:option_settings] || {})
|
194
|
+
|
195
|
+
ns.values.each do |ns|
|
196
|
+
opt[ns] = {}.with_indifferent_access if opt[ns].blank?
|
197
|
+
end
|
198
|
+
|
199
|
+
opt = set_opt_default opt
|
200
|
+
opt = set_opt_env_var opt
|
201
|
+
opt = set_opt_keyname opt
|
202
|
+
opt = set_opt_instance_type opt
|
203
|
+
opt = set_opt_instance_num opt
|
204
|
+
opt = set_opt_service_role opt
|
205
|
+
|
206
|
+
return @opt = opt
|
207
|
+
end
|
208
|
+
|
209
|
+
def ns key=nil
|
210
|
+
key.present? ? NAMESPACE[key.to_sym] : NAMESPACE
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def has_value_by_keys(hash, *keys)
|
216
|
+
if keys.empty? || hash.blank?
|
217
|
+
return hash
|
218
|
+
else
|
219
|
+
return hash unless k = keys.shift
|
220
|
+
return hash[k] && has_value_by_keys(hash[k], *keys)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def set_opt_keyname opt
|
225
|
+
k = if raw_conf.key?(:key_name)
|
226
|
+
raw_conf[:key_name]
|
227
|
+
elsif get_raw_opt(ns[:autoscaling_launch], :EC2KeyName)
|
228
|
+
get_raw_opt(ns[:autoscaling_launch], :EC2KeyName)
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
if k.present?
|
233
|
+
opt[ns[:autoscaling_launch]].merge!({
|
234
|
+
EC2KeyName: k
|
235
|
+
}.with_indifferent_access)
|
236
|
+
end
|
237
|
+
return opt
|
238
|
+
end
|
239
|
+
|
240
|
+
def set_opt_service_role opt
|
241
|
+
s_role = if raw_conf.key?(:service_role)
|
242
|
+
raw_conf[:service_role]
|
243
|
+
elsif role = get_raw_opt(ns[:eb_env], :ServiceRole)
|
244
|
+
role
|
245
|
+
else
|
246
|
+
'aws-elasticbeanstalk-service-role'
|
247
|
+
end
|
248
|
+
if s_role.present?
|
249
|
+
opt[ns[:eb_env]].merge!({
|
250
|
+
ServiceRole: s_role,
|
251
|
+
}.with_indifferent_access)
|
252
|
+
end
|
253
|
+
return opt
|
254
|
+
end
|
255
|
+
|
256
|
+
def set_opt_instance_type opt
|
257
|
+
itype = raw_conf[:instance_type] \
|
258
|
+
|| get_raw_opt(ns[:autoscaling_launch], :InstanceType) \
|
259
|
+
|| "t2.small"
|
260
|
+
if itype.present?
|
261
|
+
opt[ns[:autoscaling_launch]].merge!({
|
262
|
+
InstanceType: itype
|
263
|
+
}.with_indifferent_access)
|
264
|
+
end
|
265
|
+
return opt
|
266
|
+
end
|
267
|
+
|
268
|
+
def set_opt_instance_num opt
|
269
|
+
max = min = 1
|
270
|
+
if raw_conf[:instance_num].present?
|
271
|
+
min = raw_conf[:instance_num][:min]
|
272
|
+
max = raw_conf[:instance_num][:min]
|
273
|
+
elsif mi = get_raw_opt(ns[:autoscaling_asg], :MinSize) \
|
274
|
+
|| ma = get_raw_opt(ns[:autoscaling_asg], :MaxSize)
|
275
|
+
min = mi if mi
|
276
|
+
max = ma if ma
|
277
|
+
end
|
278
|
+
opt[ns[:autoscaling_asg]].merge!({
|
279
|
+
MaxSize: max,
|
280
|
+
MinSize: min,
|
281
|
+
}.with_indifferent_access)
|
282
|
+
return opt
|
283
|
+
end
|
284
|
+
|
285
|
+
def set_opt_env_var opt
|
286
|
+
opt[ns[:app_env]].merge!(dotenv.merge(raw_environment_variables))
|
287
|
+
return opt
|
288
|
+
end
|
289
|
+
|
290
|
+
def set_opt_default opt
|
291
|
+
|
292
|
+
opt[ns[:healthreporting]].reverse_merge!({
|
293
|
+
SystemType: 'enhanced',
|
294
|
+
}.with_indifferent_access)
|
295
|
+
|
296
|
+
opt[ns[:eb_command]].reverse_merge!({
|
297
|
+
BatchSize: "50",
|
298
|
+
BatchSizeType: "Percentage",
|
299
|
+
}.with_indifferent_access)
|
300
|
+
|
301
|
+
unless worker?
|
302
|
+
opt[ns[:elb_policies]].reverse_merge!({
|
303
|
+
ConnectionDrainingEnabled: true
|
304
|
+
}.with_indifferent_access)
|
305
|
+
|
306
|
+
opt[NAMESPACE[:elb_health]].reverse_merge!({
|
307
|
+
Interval: 30
|
308
|
+
}.with_indifferent_access)
|
309
|
+
|
310
|
+
opt[NAMESPACE[:elb_loadbalancer]].reverse_merge!({
|
311
|
+
CrossZone: true
|
312
|
+
}.with_indifferent_access)
|
313
|
+
end
|
314
|
+
return opt
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
@@ -0,0 +1,305 @@
|
|
1
|
+
module Rebi
|
2
|
+
class Environment
|
3
|
+
|
4
|
+
attr_reader :stage_name,
|
5
|
+
:env_name,
|
6
|
+
:app_name,
|
7
|
+
:name,
|
8
|
+
:config
|
9
|
+
|
10
|
+
attr_accessor :client,
|
11
|
+
:api_data
|
12
|
+
|
13
|
+
|
14
|
+
RESPONSES = {
|
15
|
+
'event.createstarting': 'createEnvironment is starting.',
|
16
|
+
'event.terminatestarting': 'terminateEnvironment is starting.',
|
17
|
+
'event.updatestarting': 'Environment update is starting.',
|
18
|
+
'event.redmessage': 'Environment health has been set to RED',
|
19
|
+
'event.commandfailed': 'Command failed on instance',
|
20
|
+
'event.launchfailed': 'Failed to launch',
|
21
|
+
'event.deployfailed': 'Failed to deploy application.',
|
22
|
+
'event.redtoyellowmessage': 'Environment health has transitioned from YELLOW to RED',
|
23
|
+
'event.yellowmessage': 'Environment health has been set to YELLOW',
|
24
|
+
'event.greenmessage': 'Environment health has been set to GREEN',
|
25
|
+
'event.launchsuccess': 'Successfully launched environment:',
|
26
|
+
'event.launchbad': 'Create environment operation is complete, but with errors',
|
27
|
+
'event.updatebad': 'Update environment operation is complete, but with errors.',
|
28
|
+
'git.norepository': 'Error: Not a git repository (or any of the parent directories): .git',
|
29
|
+
'env.updatesuccess': 'Environment update completed successfully.',
|
30
|
+
'env.cnamenotavailable': 'DNS name \([^ ]+\) is not available.',
|
31
|
+
'env.nameexists': 'Environment [^ ]+ already exists.',
|
32
|
+
'app.deletesuccess': 'The application has been deleted successfully.',
|
33
|
+
'app.exists': 'Application {app-name} already exists.',
|
34
|
+
'app.notexists': 'No Application named {app-name} found.',
|
35
|
+
'logs.pulled': 'Pulled logs for environment instances.',
|
36
|
+
'logs.successtail': 'Successfully finished tailing',
|
37
|
+
'logs.successbundle': 'Successfully finished bundling',
|
38
|
+
'env.terminated': 'terminateEnvironment completed successfully.',
|
39
|
+
'env.invalidstate': 'Environment named {env-name} is in an invalid state for this operation. Must be Ready.',
|
40
|
+
'loadbalancer.notfound': 'There is no ACTIVE Load Balancer named',
|
41
|
+
'ec2.sshalreadyopen': 'the specified rule "peer: 0.0.0.0/0, TCP, from port: 22, to port: 22,',
|
42
|
+
}
|
43
|
+
|
44
|
+
def initialize stage_name, env_name, client=Rebi.client
|
45
|
+
@stage_name = stage_name
|
46
|
+
@env_name = env_name
|
47
|
+
@client = client
|
48
|
+
@config = Rebi.config.environment(stage_name, env_name)
|
49
|
+
@app_name = @config.app_name
|
50
|
+
@api_data
|
51
|
+
end
|
52
|
+
|
53
|
+
def name
|
54
|
+
created? ? api_data.environment_name : config.name
|
55
|
+
end
|
56
|
+
|
57
|
+
def cname
|
58
|
+
created? ? api_data.cname : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def version_label
|
62
|
+
created? ? api_data.version_label : nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def id
|
66
|
+
created? ? api_data.environment_id : nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def status
|
70
|
+
check_created! && api_data.status
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_updating?
|
74
|
+
!!status.match("ing$")
|
75
|
+
end
|
76
|
+
|
77
|
+
def health
|
78
|
+
check_created! && api_data.health
|
79
|
+
end
|
80
|
+
|
81
|
+
def option_settings
|
82
|
+
check_created!
|
83
|
+
client.describe_configuration_settings({
|
84
|
+
application_name: app_name,
|
85
|
+
environment_name: name
|
86
|
+
}).configuration_settings.first.option_settings.map do |o|
|
87
|
+
{
|
88
|
+
namespace: o.namespace,
|
89
|
+
value: o.value,
|
90
|
+
resource_name: o.resource_name,
|
91
|
+
option_name: o.option_name,
|
92
|
+
}.with_indifferent_access
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def environment_variables
|
97
|
+
option_settings.select do |o|
|
98
|
+
o[:namespace] == config.ns(:app_env)
|
99
|
+
end.map do |o|
|
100
|
+
[o[:option_name], o[:value]]
|
101
|
+
end.to_h.with_indifferent_access
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_created
|
105
|
+
raise Rebi::Error::EnvironmentNotExisted.new("#{name} not exists") unless created?
|
106
|
+
return created?
|
107
|
+
end
|
108
|
+
|
109
|
+
# refresh data
|
110
|
+
def check_created!
|
111
|
+
refresh
|
112
|
+
check_created
|
113
|
+
end
|
114
|
+
|
115
|
+
def created?
|
116
|
+
!!api_data
|
117
|
+
end
|
118
|
+
|
119
|
+
def api_data
|
120
|
+
@api_data ||= client.describe_environments(application_name: config.app_name,
|
121
|
+
environment_names:[config.name],
|
122
|
+
include_deleted: false).environments.first
|
123
|
+
end
|
124
|
+
|
125
|
+
def response_msgs key=nil
|
126
|
+
@response_msgs ||= RESPONSES.with_indifferent_access
|
127
|
+
return key ? @response_msgs[key] : @response_msgs
|
128
|
+
end
|
129
|
+
|
130
|
+
def watch_request request_id
|
131
|
+
check_created!
|
132
|
+
start = Time.now
|
133
|
+
finished = false
|
134
|
+
last_time = Time.now - 30.minute
|
135
|
+
thread = Thread.new do
|
136
|
+
while (start + Rebi.config.timeout) > Time.now && !finished
|
137
|
+
events(last_time, request_id).reverse.each do |e|
|
138
|
+
finished ||= success_message?(e.message)
|
139
|
+
last_time = [last_time + 1.second, e.event_date + 1.second].max
|
140
|
+
log(e.message)
|
141
|
+
end
|
142
|
+
sleep(5) unless finished
|
143
|
+
end
|
144
|
+
log ("Timeout") unless finished
|
145
|
+
end
|
146
|
+
thread.join
|
147
|
+
return thread
|
148
|
+
end
|
149
|
+
|
150
|
+
def events start_time=Time.now, request_id=nil
|
151
|
+
client.describe_events({
|
152
|
+
application_name: app_name,
|
153
|
+
environment_name: name,
|
154
|
+
start_time: start_time,
|
155
|
+
}.merge( request_id ? {request_id: request_id} : {})).events
|
156
|
+
end
|
157
|
+
|
158
|
+
def refresh
|
159
|
+
self.api_data = nil
|
160
|
+
return self
|
161
|
+
end
|
162
|
+
|
163
|
+
def init version_label, opts={}
|
164
|
+
log("Creating new environment")
|
165
|
+
start_time = Time.now
|
166
|
+
self.api_data = client.create_environment({
|
167
|
+
application_name: config.app_name,
|
168
|
+
cname_prefix: config.cname_prefix,
|
169
|
+
environment_name: config.name,
|
170
|
+
version_label: version_label,
|
171
|
+
tier: config.tier,
|
172
|
+
description: config.description,
|
173
|
+
option_settings: config.opts_array,
|
174
|
+
}.merge(if(config.platform_arn)
|
175
|
+
{ platform_arn: config.platform_arn}
|
176
|
+
else
|
177
|
+
{ solution_stack_name: config.solution_stack_name}
|
178
|
+
end))
|
179
|
+
request_id = events(start_time).select do |e|
|
180
|
+
e.message.match(response_msgs('event.createstarting'))
|
181
|
+
end.map(&:request_id).first
|
182
|
+
return request_id
|
183
|
+
end
|
184
|
+
|
185
|
+
def update version_label, opts={}
|
186
|
+
raise Rebi::Error::EnvironmentInUpdating.new(name) if in_updating?
|
187
|
+
log("Start updating")
|
188
|
+
start_time = Time.now
|
189
|
+
deploy_opts = gen_deploy_opts
|
190
|
+
deploy_args = {
|
191
|
+
application_name: config.app_name,
|
192
|
+
environment_name: config.name,
|
193
|
+
version_label: version_label,
|
194
|
+
description: config.description,
|
195
|
+
option_settings: deploy_opts[:option_settings],
|
196
|
+
options_to_remove: deploy_opts[:options_to_remove],
|
197
|
+
}
|
198
|
+
|
199
|
+
if opts[:source_only]
|
200
|
+
deploy_args.delete(:option_settings)
|
201
|
+
deploy_args.delete(:options_to_remove)
|
202
|
+
elsif opts[:settings_only]
|
203
|
+
deploy_args.delete(:version_label)
|
204
|
+
end
|
205
|
+
|
206
|
+
self.api_data = client.update_environment(deploy_args)
|
207
|
+
|
208
|
+
request_id = events(start_time).select do |e|
|
209
|
+
e.message.match(response_msgs('event.updatestarting'))
|
210
|
+
end.map(&:request_id).first
|
211
|
+
|
212
|
+
return request_id
|
213
|
+
end
|
214
|
+
|
215
|
+
def deploy version_label, opts={}
|
216
|
+
request_id = if created?
|
217
|
+
update version_label, opts
|
218
|
+
else
|
219
|
+
init version_label, opts
|
220
|
+
end
|
221
|
+
return request_id
|
222
|
+
end
|
223
|
+
|
224
|
+
def terminate!
|
225
|
+
check_created
|
226
|
+
log("Start terminating")
|
227
|
+
client.terminate_environment({
|
228
|
+
environment_name: name,
|
229
|
+
environment_id: id,
|
230
|
+
})
|
231
|
+
start_time = Time.now
|
232
|
+
|
233
|
+
request_id = events(start_time).select do |e|
|
234
|
+
e.message.match(response_msgs('event.updatestarting'))
|
235
|
+
end.map(&:request_id).first
|
236
|
+
return request_id
|
237
|
+
end
|
238
|
+
|
239
|
+
def log mes
|
240
|
+
Rebi.log(mes, name)
|
241
|
+
end
|
242
|
+
|
243
|
+
def success_message? mes
|
244
|
+
return true if [
|
245
|
+
'event.greenmessage',
|
246
|
+
'event.launchsuccess',
|
247
|
+
'logs.pulled',
|
248
|
+
'env.terminated',
|
249
|
+
'env.updatesuccess',
|
250
|
+
'app.deletesuccess',
|
251
|
+
].map{|k| response_msgs(k)}.any?{|s| mes.match(s)}
|
252
|
+
|
253
|
+
if [
|
254
|
+
'event.redmessage',
|
255
|
+
'event.launchbad',
|
256
|
+
'event.updatebad',
|
257
|
+
'event.commandfailed',
|
258
|
+
'event.launchfailed',
|
259
|
+
'event.deployfailed',
|
260
|
+
].map {|k| response_msgs(k)}.any? {|s| mes.match(s)}
|
261
|
+
raise Rebi::Error::ServiceError.new(mes)
|
262
|
+
end
|
263
|
+
|
264
|
+
return false
|
265
|
+
end
|
266
|
+
|
267
|
+
def gen_deploy_opts
|
268
|
+
to_deploy = []
|
269
|
+
to_remove = []
|
270
|
+
config.opts_array.each do |o|
|
271
|
+
o = o.deep_dup
|
272
|
+
if o[:namespace] == config.ns(:app_env) && o[:value].blank?
|
273
|
+
o.delete(:value)
|
274
|
+
to_remove << o
|
275
|
+
next
|
276
|
+
end
|
277
|
+
to_deploy << o
|
278
|
+
end
|
279
|
+
return {
|
280
|
+
option_settings: to_deploy,
|
281
|
+
options_to_remove: to_remove,
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
def self.create stage_name, env_name, version_label, client
|
286
|
+
env = new stage_name, env_name, client
|
287
|
+
raise Rebi::Error::EnvironmentExisted.new if env.created?
|
288
|
+
|
289
|
+
env.init version_label
|
290
|
+
return env
|
291
|
+
end
|
292
|
+
|
293
|
+
# TODO
|
294
|
+
def self.all client=Rebi.client
|
295
|
+
client.describe_environments(application_name: app_name,
|
296
|
+
include_deleted: false).environments
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.get stage_name, env_name, client=Rebi.client
|
300
|
+
env = new stage_name, env_name, client
|
301
|
+
return env.created? ? env : nil
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
end
|
data/lib/rebi/error.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rebi
|
2
|
+
class Error < StandardError
|
3
|
+
ApplicationNotFound = Class.new(self)
|
4
|
+
EnvironmentExisted = Class.new(self)
|
5
|
+
EnvironmentNotExisted = Class.new(self)
|
6
|
+
ConfigFileNotFound = Class.new(self)
|
7
|
+
ConfigNotFound = Class.new(self)
|
8
|
+
ConfigInvalid = Class.new(self)
|
9
|
+
NoGit = Class.new(self)
|
10
|
+
ServiceError = Class.new(self)
|
11
|
+
EnvironmentInUpdating = Class.new(self)
|
12
|
+
end
|
13
|
+
end
|
data/lib/rebi/version.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Rebi
|
2
|
+
class ZipHelper
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
`git status`
|
6
|
+
raise Rebi::Error::NoGit.new("Not a git repository") unless $?.success?
|
7
|
+
end
|
8
|
+
|
9
|
+
def ls_files
|
10
|
+
`git ls-files`.split("\n")
|
11
|
+
end
|
12
|
+
|
13
|
+
def raw_zip_archive opts={}
|
14
|
+
tmp_file = Tempfile.new("git_archive")
|
15
|
+
system "git archive --format=zip HEAD > #{tmp_file.path}"
|
16
|
+
return tmp_file
|
17
|
+
end
|
18
|
+
|
19
|
+
def version_label
|
20
|
+
`git describe --always --abbrev=8`.chomp
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
`git log --oneline -1`.chomp.split(" ")[1..-1].join(" ")[0..190]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create zip archivement
|
28
|
+
def gen env_conf
|
29
|
+
Rebi.log("Creating zip archivement", env_conf.name)
|
30
|
+
start = Time.now
|
31
|
+
ebextensions = env_conf.ebextensions
|
32
|
+
files = ls_files
|
33
|
+
tmp_file = raw_zip_archive
|
34
|
+
tmp_folder = Dir.mktmpdir
|
35
|
+
Zip::File.open(tmp_file) do |z|
|
36
|
+
ebextensions.each do |ex_folder|
|
37
|
+
Dir.glob("#{ex_folder}/*.config") do |fname|
|
38
|
+
next unless (File.file?(fname) && files.include?(fname))
|
39
|
+
next unless y = YAML::load(ErbHelper.new(File.read(fname), env_conf.environment_variables).result)
|
40
|
+
basename = File.basename(fname)
|
41
|
+
target = ".ebextensions/#{basename}"
|
42
|
+
tmp_yaml = "#{tmp_folder}/#{basename}"
|
43
|
+
File.open(tmp_yaml, 'w') do |f|
|
44
|
+
f.write y.to_yaml
|
45
|
+
end
|
46
|
+
z.remove target if z.find_entry target
|
47
|
+
z.add target, tmp_yaml
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
FileUtils.rm_rf tmp_folder
|
52
|
+
Rebi.log("Zip was created in: #{Time.now - start}s", env_conf.name)
|
53
|
+
return {
|
54
|
+
label: Time.now.strftime("app_#{env_conf.name}_#{version_label}_%Y%m%d_%H%M%S"),
|
55
|
+
file: File.open(tmp_file.path),
|
56
|
+
message: message,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/rebi.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'colorized_string'
|
4
|
+
require 'singleton'
|
5
|
+
require 'yaml'
|
6
|
+
require 'dotenv'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'pathname'
|
9
|
+
require 'zip'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'erb'
|
12
|
+
require 'ostruct'
|
13
|
+
require 'thread'
|
14
|
+
require 'thwait'
|
15
|
+
|
16
|
+
require 'rebi/erb_helper'
|
17
|
+
require 'rebi/zip_helper'
|
18
|
+
require 'rebi/application'
|
19
|
+
require 'rebi/environment'
|
20
|
+
require 'rebi/config'
|
21
|
+
require 'rebi/config_environment'
|
22
|
+
require 'rebi/error'
|
23
|
+
require 'rebi/version'
|
24
|
+
|
25
|
+
Dotenv.load
|
26
|
+
|
27
|
+
module Rebi
|
28
|
+
extend self
|
29
|
+
|
30
|
+
attr_accessor :config_file
|
31
|
+
@config_file = "config/rebi.yml"
|
32
|
+
|
33
|
+
def root
|
34
|
+
Dir.pwd
|
35
|
+
end
|
36
|
+
|
37
|
+
def client c=nil
|
38
|
+
@@client = c || Aws::ElasticBeanstalk::Client.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def app
|
42
|
+
return Rebi::Application.get_or_create_application(config.app_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def config
|
46
|
+
yield Rebi::Config.instance if block_given?
|
47
|
+
return Rebi::Config.instance
|
48
|
+
end
|
49
|
+
|
50
|
+
def reload!
|
51
|
+
config.reload!
|
52
|
+
end
|
53
|
+
|
54
|
+
def log mes, prefix=nil
|
55
|
+
puts "#{prefix ? "#{colorize_prefix(prefix)}: " : ""}#{mes}"
|
56
|
+
end
|
57
|
+
|
58
|
+
COLORS = [:red, :green, :yellow, :blue, :magenta, :cyan, :white]
|
59
|
+
def colorize_prefix(prefix)
|
60
|
+
h = prefix.chars.inject(0) do |m, c|
|
61
|
+
m + c.ord
|
62
|
+
end
|
63
|
+
return ColorizedString[prefix].colorize(COLORS[h % COLORS.count])
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/tasks/rebi.rake
ADDED
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rebi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- KhiemNS
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubyzip
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.10'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: commander
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.4'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.4'
|
97
|
+
description: Deploy ElasticBeanstalk with multiple deploy, switchable and dynamic
|
98
|
+
generated ebextensions with erb
|
99
|
+
email:
|
100
|
+
- khiemns.k54@gmail.com
|
101
|
+
executables:
|
102
|
+
- rebi
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- MIT-LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- bin/rebi
|
110
|
+
- lib/rebi.rb
|
111
|
+
- lib/rebi/application.rb
|
112
|
+
- lib/rebi/config.rb
|
113
|
+
- lib/rebi/config_environment.rb
|
114
|
+
- lib/rebi/environment.rb
|
115
|
+
- lib/rebi/erb_helper.rb
|
116
|
+
- lib/rebi/error.rb
|
117
|
+
- lib/rebi/version.rb
|
118
|
+
- lib/rebi/zip_helper.rb
|
119
|
+
- lib/tasks/rebi.rake
|
120
|
+
homepage: https://github.com/khiemns54/rebi
|
121
|
+
licenses:
|
122
|
+
- MIT
|
123
|
+
metadata: {}
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
requirements: []
|
139
|
+
rubyforge_project:
|
140
|
+
rubygems_version: 2.5.1
|
141
|
+
signing_key:
|
142
|
+
specification_version: 4
|
143
|
+
summary: ElasticBeanstalk Deployment Tool
|
144
|
+
test_files: []
|