rebi 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|