rebi 0.2.3 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 067283c4a2dfb4c767f63b56de45adf553f8d74c
4
- data.tar.gz: 4ca5ffda0ca74e8005f5e25560d05b77ce67b418
3
+ metadata.gz: 8d5506d4a48ece06d7c2c3cc00b56e931ae6aeb3
4
+ data.tar.gz: 27748616915072f0eb9de0f9c588a64680212598
5
5
  SHA512:
6
- metadata.gz: 97ab460b6fcd7bb15c6de0f3b88ce875f4e391e60817fe06d94f364a4ae7102b8e6d466a368cc72885cb7b37ae3c1c46a6fb5b97aa49668fd9b2e9de6189dad3
7
- data.tar.gz: bec513df80694c8ea8a76dfb8ec6878fde813c467bea3df7ac63f288eb57b26ebd58b0078d2a51d8641a0ac51ec469cc3a3e4244faf3c53339b64fc10cc8f2ef
6
+ metadata.gz: c8575bb0477ce90c205cdbbcf07000d508594281605d1e406d7ccfa6247ee4f47d6c0e3886deb139982d929b120f4af9a82b3c77936522f5aebc8f8c0b558988
7
+ data.tar.gz: 1643039aaa197bbb8303b6ecbaa50ff3cb49522ab86b7f3e46ebaab129913403c6c477fe6e05098d24b3afef666ed99dde0738f5a663099d5e5b7100a22678e7
data/README.md CHANGED
@@ -2,11 +2,13 @@
2
2
  Deployment tool for Elasticbeanstalk
3
3
 
4
4
  # Features
5
- - Switchable + multiple ebextensions folder
5
+ - Switchable + multiple ebextensions folder
6
+ - Switchable + ERB Supported Dockerrun.aws.json
6
7
  - Support erb in ebextension config files
7
8
  - Support env_file for environment variables
8
9
  - Multiple deployment
9
10
  - Deploy source code along with updating beanstalk options
11
+ - Hook commands before and after deploy
10
12
  - Simple config
11
13
  - Simple ssh
12
14
 
@@ -17,28 +19,17 @@ $ gem install rebi
17
19
  ```
18
20
 
19
21
  ## Usage
20
- How to use my plugin.
21
-
22
- ### AWS authentication
23
- Rebi uses environment variables to get aws credentials
24
- To set environment variables use `export` or `.env` file
25
- ```bash
26
- # Use access key
27
- export AWS_ACCESS_KEY_ID=xxxxx
28
- export AWS_SECRET_ACCESS_KEY=xxxxxx
29
- ```
30
- Or
31
- ```bash
32
- # Use profile
33
- export AWS_PROFILE=xxxxx
34
- ```
35
-
36
- Refer http://docs.aws.amazon.com/sdkforruby/api/Aws/ElasticBeanstalk/Client.html for other settings
37
22
 
38
23
  ### Yaml config
39
- Default config file is `config/rebi.yml` use `-c` to switch
24
+ Default config file is `.rebi.yml` use `-c` to switch
40
25
  ```yaml
41
26
  app_name: app-name
27
+ profile: aws_profile # if use profile, can overwrite this by command option --profile
28
+
29
+ # if use key/secret credentials(If you dont want to commit credentials to version control system use environment variables instead (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY))
30
+ aws_key: aws_key
31
+ aws_secret: aws_secret
32
+
42
33
  stages:
43
34
  development:
44
35
  web:
@@ -47,9 +38,9 @@ stages:
47
38
  ebextensions: "web-ebextensions"
48
39
  ```
49
40
 
50
- For other configs( key_name, instance_type, instance_num, service_role,...), please refer sample config
41
+ For other configs(key_name, instance_type, instance_num, service_role,...), please refer sample config
51
42
  ```bash
52
- $ bundle exec rebi sample > rebi.yml
43
+ $ bundle exec rebi sample > .rebi.yml
53
44
  ```
54
45
 
55
46
  ### Deploy
data/bin/rebi CHANGED
@@ -4,6 +4,13 @@ require 'rubygems'
4
4
  require 'commander/import'
5
5
  require 'rebi'
6
6
 
7
+ # For debug
8
+ begin
9
+ require 'byebug'
10
+ rescue LoadError
11
+
12
+ end
13
+
7
14
  REBI_PATH = File.join(File.dirname(File.expand_path(__FILE__)), "..")
8
15
 
9
16
  program :name, 'Rebi'
@@ -15,6 +22,11 @@ global_option('-c', '--config FILE', 'Load config data for your commands to use'
15
22
  Rebi.config.reload!
16
23
  end
17
24
 
25
+ global_option('--profile STRING', 'Aws profile') do |profile|
26
+ Rebi.config.aws_profile = profile
27
+ Rebi.config.reload!
28
+ end
29
+
18
30
  command :deploy do |c|
19
31
  c.syntax = 'rebi deploy stage [env_name] [--options]'
20
32
  c.description = 'Deploy single or multiple ElasticBeanstalk environments'
@@ -22,6 +34,8 @@ command :deploy do |c|
22
34
  c.example 'Deploy all environments in development', 'rebi deploy development'
23
35
  c.option '--include-settings', 'Deploy source and settings'
24
36
  c.option '--settings-only', 'Deploy option_settings and environment variables only'
37
+ c.option '--staged', 'Deploy files staged in git rather than the HEAD commit'
38
+ c.option '-y', '--yes', 'Skip confirmation'
25
39
  c.action do |args, options|
26
40
  stage, env_name = args
27
41
  raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
@@ -29,7 +43,7 @@ command :deploy do |c|
29
43
  if env_name.present?
30
44
  Rebi.app.deploy stage, env_name, opts
31
45
  else
32
- if agree("Do you want to deploy all environments in #{stage} stage?(Y/n)")
46
+ if options.yes || agree("Do you want to deploy all environments in #{stage} stage?(Y/n)")
33
47
  Rebi.log("Preparing for deployment")
34
48
  Rebi.app.deploy stage, nil, opts
35
49
  end
@@ -72,12 +86,13 @@ end
72
86
  command :terminate do |c|
73
87
  c.syntax = 'rebi terminate stage env_name'
74
88
  c.description = 'Terminate environment'
89
+ c.option '-y', '--yes', 'Skip confirmation'
75
90
  c.action do |args, options|
76
91
  stage, env_name = args
77
92
  raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
78
93
  raise Rebi::Error.new("Env name cannot be nil") if env_name.blank?
79
94
  env_conf = Rebi.config.environment(stage, env_name)
80
- if ask("Type '#{env_conf.name}' to confirm termination") == env_conf.name
95
+ if options.yes || agree("Do you want to terminate '#{env_conf.name}' environment?(Y/n)")
81
96
  Rebi.app.terminate! stage, env_name
82
97
  end
83
98
  end
@@ -98,7 +113,6 @@ command :ssh do |c|
98
113
  c.action do |args, options|
99
114
  stage, env_name = args
100
115
  raise Rebi::Error.new("Stage cannot be nil") if stage.blank?
101
- raise Rebi::Error.new("Env name cannot be nil") if env_name.blank?
102
116
  Rebi.app.ssh_interaction stage, env_name, options.__hash__
103
117
  end
104
118
  end
@@ -1,5 +1,8 @@
1
1
  require 'active_support/all'
2
- require 'aws-sdk'
2
+ require 'aws-sdk-ec2'
3
+ require 'aws-sdk-s3'
4
+ require 'aws-sdk-elasticbeanstalk'
5
+ require 'aws-sdk-iam'
3
6
  require 'colorized_string'
4
7
  require 'singleton'
5
8
  require 'yaml'
@@ -13,7 +16,9 @@ require 'ostruct'
13
16
  require 'thread'
14
17
  require 'thwait'
15
18
  require 'subprocess'
19
+ require 'pathspec'
16
20
 
21
+ require 'rebi/log'
17
22
  require 'rebi/erb_helper'
18
23
  require 'rebi/zip_helper'
19
24
  require 'rebi/application'
@@ -24,11 +29,12 @@ require 'rebi/error'
24
29
  require 'rebi/ec2'
25
30
  require 'rebi/version'
26
31
 
27
- Dotenv.load
32
+ # Dotenv.load
28
33
 
29
34
  module Rebi
30
- extend self
35
+ include Rebi::Log
31
36
 
37
+ extend self
32
38
  attr_accessor :config_file
33
39
  @config_file = "config/rebi.yml"
34
40
 
@@ -36,18 +42,22 @@ module Rebi
36
42
  Dir.pwd
37
43
  end
38
44
 
39
- def client c=nil
40
- @@client = c || Aws::ElasticBeanstalk::Client.new
45
+ def eb c=nil
46
+ @@eb = Aws::ElasticBeanstalk::Client.new
41
47
  end
42
48
 
43
49
  def ec2
44
- @@ec2_client = Rebi::EC2.new
50
+ @@ec2_client = Rebi::EC2.new Aws::EC2::Client.new
45
51
  end
46
52
 
47
53
  def iam
48
54
  @@iam_client = Aws::IAM::Client.new
49
55
  end
50
56
 
57
+ def s3
58
+ @@s3_client = Aws::S3::Client.new
59
+ end
60
+
51
61
  def app
52
62
  return Rebi::Application.get_or_create_application(config.app_name)
53
63
  end
@@ -61,16 +71,4 @@ module Rebi
61
71
  config.reload!
62
72
  end
63
73
 
64
- def log mes, prefix=nil
65
- puts "#{prefix ? "#{colorize_prefix(prefix)}: " : ""}#{mes}"
66
- end
67
-
68
- COLORS = [:red, :green, :yellow, :blue, :magenta, :cyan, :white]
69
- def colorize_prefix(prefix)
70
- h = prefix.chars.inject(0) do |m, c|
71
- m + c.ord
72
- end
73
- return ColorizedString[prefix].colorize(COLORS[h % COLORS.count])
74
- end
75
-
76
74
  end
@@ -1,11 +1,11 @@
1
1
  module Rebi
2
2
  class Application
3
+ include Rebi::Log
3
4
  attr_reader :app, :app_name, :client, :s3_client
4
- def initialize app, client
5
+ def initialize app, client=Rebi.eb
5
6
  @app = app
6
7
  @app_name = app.application_name
7
8
  @client = client
8
- @s3_client = Aws::S3::Client.new
9
9
  end
10
10
 
11
11
  def bucket_name
@@ -16,18 +16,18 @@ module Rebi
16
16
  Rebi::Environment.all app_name
17
17
  end
18
18
 
19
+ def log_label
20
+ app_name
21
+ end
22
+
19
23
  def deploy stage_name, env_name=nil, opts={}
20
24
  return deploy_stage(stage_name, opts) if env_name.blank?
21
25
  env = get_environment stage_name, env_name
22
- app_version = create_app_version env
23
26
  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
27
+ env.deploy opts
28
+ rescue Interrupt
29
+ log("Interrupt")
29
30
  end
30
- req_id
31
31
  end
32
32
 
33
33
  def deploy_stage stage_name, opts={}
@@ -38,9 +38,9 @@ module Rebi
38
38
  begin
39
39
  deploy stage_name, env_name, opts
40
40
  rescue Exception => e
41
- Rebi.log(e.message, "ERROR")
42
- e.backtrace.each do |m|
43
- Rebi.log(m, "ERROR")
41
+ error(e.message)
42
+ opts[:trace] && e.backtrace.each do |m|
43
+ error(m)
44
44
  end
45
45
  end
46
46
  end
@@ -61,11 +61,11 @@ module Rebi
61
61
  env = get_environment stage_name, env_name
62
62
  env_vars = from_config ? env.config.environment_variables : env.environment_variables
63
63
 
64
- Rebi.log("#{from_config ? "Config" : "Current"} environment variables", env.name)
64
+ log("#{from_config ? "Config" : "Current"} environment variables")
65
65
  env_vars.each do |k,v|
66
- Rebi.log("#{k}=#{v}")
66
+ log("#{k}=#{v}")
67
67
  end
68
- Rebi.log("--------------------------------------", env.name)
68
+ log("--------------------------------------")
69
69
  end
70
70
 
71
71
  def print_environment_status stage_name, env_name
@@ -79,11 +79,11 @@ module Rebi
79
79
 
80
80
  env = get_environment stage_name, env_name
81
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)
82
+ log("--------- CURRENT STATUS -------------")
83
+ log("id: #{env.id}")
84
+ log("Status: #{env.status}")
85
+ log("Health: #{env.health}")
86
+ log("--------------------------------------")
87
87
  end
88
88
 
89
89
  def terminate! stage_name, env_name
@@ -92,37 +92,11 @@ module Rebi
92
92
  req_id = env.terminate!
93
93
  ThreadsWait.all_waits(env.watch_request req_id) if req_id
94
94
  rescue Rebi::Error::EnvironmentInUpdating => e
95
- Rebi.log("Environment in updating", env.name)
95
+ log("Environment in updating")
96
96
  raise e
97
97
  end
98
98
  end
99
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
100
  def print_list
127
101
  others = []
128
102
  configed = Hash.new {|h, k| h[k] = {} }
@@ -135,36 +109,35 @@ module Rebi
135
109
  end
136
110
 
137
111
  configed.each do |stg, envs|
138
- Rebi.log "-------------"
139
- Rebi.log "#{stg.camelize}:"
140
- envs.each do |k, v|
141
- Rebi.log "\t#{k.camelize}: #{v}"
112
+ log h1("#{stg.camelize}")
113
+ envs.each do |kname, env_name|
114
+ env = get_environment stg, kname
115
+ log "\t[#{kname.camelize}] #{env.name} #{h2(env.status)} #{hstatus(env.health)}"
142
116
  end
143
117
  end
144
118
 
145
119
  if others.present?
146
- Rebi.log "-------------"
147
- Rebi.log "Others:"
120
+ log h1("Others")
148
121
  others.each do |e|
149
- Rebi.log "\t- #{e}"
122
+ log "\t- #{e}"
150
123
  end
151
124
  end
152
125
  end
153
126
 
154
127
  def ssh_interaction stage_name, env_name, opts={}
128
+ env_name = Rebi.config.stage(stage_name).keys.first unless env_name
155
129
  env = get_environment stage_name, env_name
156
130
  instance_ids = env.instance_ids
157
131
  return if instance_ids.empty?
158
132
 
159
133
  instance_ids.each.with_index do |i,idx|
160
- Rebi.log "#{idx+1}) #{i}"
134
+ log "#{idx+1}) #{i}"
161
135
  end
162
136
 
163
137
  instance_id = instance_ids.first
164
138
 
165
139
  if instance_ids.count != 1 && opts[:select]
166
140
 
167
-
168
141
  idx = 0
169
142
  while idx < 1 || idx > instance_ids.count
170
143
  idx = ask_for_integer "Select an instance to ssh into:"
@@ -172,7 +145,7 @@ module Rebi
172
145
  instance_id = instance_ids[idx - 1]
173
146
  end
174
147
 
175
- Rebi.log "Preparing to ssh into [#{instance_id}]"
148
+ log "Preparing to ssh into [#{instance_id}]"
176
149
  env.ssh instance_id
177
150
  end
178
151
 
@@ -181,7 +154,7 @@ module Rebi
181
154
  end
182
155
 
183
156
  def self.client
184
- Rebi.client || Aws::ElasticBeanstalk::Client.new
157
+ Rebi.eb
185
158
  end
186
159
 
187
160
  def self.get_application app_name
@@ -2,16 +2,38 @@ module Rebi
2
2
  class Config
3
3
  include Singleton
4
4
 
5
+ def initialize
6
+ reload!
7
+ end
8
+
5
9
  def config_file
6
- @config_file ||= "#{Rebi.root}/config/rebi.yml"
10
+ @config_file ||= "#{Rebi.root}/.rebi.yml"
7
11
  end
8
12
 
9
13
  def config_file=path
10
14
  @config_file = Pathname.new(path).realpath.to_s
11
15
  end
12
16
 
17
+ attr_writer :aws_profile, :aws_key, :aws_secret, :region
18
+ def aws_profile
19
+ @aws_profile || data[:profile] || ENV["AWS_PROFILE"]
20
+ end
21
+
22
+ def aws_key
23
+ data[:aws_key] || ENV["AWS_ACCESS_KEY_ID"]
24
+ end
25
+
26
+ def aws_secret
27
+ data[:aws_secret] || ENV["AWS_SECRET_ACCESS_KEY"]
28
+ end
29
+
30
+ def region
31
+ data[:region]
32
+ end
33
+
13
34
  def reload!
14
35
  @data = nil
36
+ set_aws_config
15
37
  return data
16
38
  end
17
39
 
@@ -59,5 +81,27 @@ module Rebi
59
81
  raise Rebi::Error::ConfigInvalid.new("stages cannot be nil") if @data[:stages].blank?
60
82
  return @data
61
83
  end
84
+
85
+ def set_aws_config
86
+ conf = {}
87
+
88
+ if region
89
+ conf.merge!({
90
+ region: region
91
+ })
92
+ end
93
+
94
+ if aws_profile
95
+ conf.merge!({
96
+ profile: aws_profile
97
+ })
98
+ elsif aws_secret && aws_key
99
+ conf.merge!(
100
+ credentials: Aws::Credentials::new(aws_key, aws_secret)
101
+ )
102
+ end
103
+
104
+ Aws.config.update conf
105
+ end
62
106
  end
63
107
  end
@@ -61,7 +61,7 @@ module Rebi
61
61
  end
62
62
 
63
63
  def cname_prefix
64
- @cname_prefix ||= raw_conf[:cname_prefix] || "#{name}-#{stage}"
64
+ self.worker? ? nil : @cname_prefix || raw_conf[:cname_prefix]
65
65
  end
66
66
 
67
67
  def tier
@@ -95,6 +95,10 @@ module Rebi
95
95
  tier[:name] == "Worker" ? true : false
96
96
  end
97
97
 
98
+ def web?
99
+ !worker?
100
+ end
101
+
98
102
  def instance_type
99
103
  get_opt ns[:autoscaling_launch], :InstanceType
100
104
  end
@@ -135,6 +139,10 @@ module Rebi
135
139
  @env_file ||= raw_conf[:env_file]
136
140
  end
137
141
 
142
+ def dockerrun
143
+ raw_conf[:dockerrun]
144
+ end
145
+
138
146
  def cfg
139
147
  begin
140
148
  return nil if cfg_file.blank?
@@ -222,6 +230,30 @@ module Rebi
222
230
  key.present? ? NAMESPACE[key.to_sym] : NAMESPACE
223
231
  end
224
232
 
233
+ def hooks
234
+ return @hooks if @hooks
235
+
236
+ @hooks = {
237
+ pre: [],
238
+ post: [],
239
+ }.with_indifferent_access
240
+
241
+ [:pre, :post].each do |type|
242
+ next unless h = raw_conf[:hooks] && raw_conf[:hooks][type]
243
+ @hooks[type] = h.is_a?(Array) ? h : [h]
244
+ end
245
+
246
+ return @hooks
247
+ end
248
+
249
+ def pre_hooks
250
+ hooks[:pre]
251
+ end
252
+
253
+ def post_hooks
254
+ hooks[:post]
255
+ end
256
+
225
257
  private
226
258
 
227
259
  def has_value_by_keys(hash, *keys)
@@ -1,12 +1,18 @@
1
1
  module Rebi
2
2
  class EC2
3
3
 
4
+ include Rebi::Log
5
+
4
6
  attr_reader :client
5
7
 
6
- def initialize client=Aws::EC2::Client.new
8
+ def initialize client
7
9
  @client = client
8
10
  end
9
11
 
12
+ def log_label
13
+ "EC2"
14
+ end
15
+
10
16
  def describe_instance instance_id
11
17
  res = client.describe_instances instance_ids: [instance_id]
12
18
  return res.reservations.first.instances.first
@@ -52,8 +58,5 @@ module Rebi
52
58
  end
53
59
  end
54
60
 
55
- def log mes
56
- Rebi.log(mes, "EC2")
57
- end
58
61
  end
59
62
  end
@@ -1,6 +1,8 @@
1
1
  module Rebi
2
2
  class Environment
3
3
 
4
+ include Rebi::Log
5
+
4
6
  attr_reader :stage_name,
5
7
  :env_name,
6
8
  :app_name,
@@ -8,6 +10,7 @@ module Rebi
8
10
  :config
9
11
 
10
12
  attr_accessor :client,
13
+ :s3_client,
11
14
  :api_data
12
15
 
13
16
 
@@ -41,15 +44,20 @@ module Rebi
41
44
  'ec2.sshalreadyopen': 'the specified rule "peer: 0.0.0.0/0, TCP, from port: 22, to port: 22,',
42
45
  }
43
46
 
44
- def initialize stage_name, env_name, client=Rebi.client
47
+ def initialize stage_name, env_name, client=Rebi.eb
45
48
  @stage_name = stage_name
46
49
  @env_name = env_name
47
50
  @client = client
51
+ @s3_client = Rebi.s3
48
52
  @config = Rebi.config.environment(stage_name, env_name)
49
53
  @app_name = @config.app_name
50
54
  @api_data
51
55
  end
52
56
 
57
+ def bucket_name
58
+ @bucket_name ||= client.create_storage_location.s3_bucket
59
+ end
60
+
53
61
  def name
54
62
  created? ? api_data.environment_name : config.name
55
63
  end
@@ -128,6 +136,8 @@ module Rebi
128
136
  end
129
137
 
130
138
  def watch_request request_id
139
+ return unless request_id
140
+ log h1("WATCHING REQUEST [#{request_id}]")
131
141
  check_created!
132
142
  start = Time.now
133
143
  finished = false
@@ -169,23 +179,39 @@ module Rebi
169
179
  resp.environment_resources.instances.map(&:id).sort
170
180
  end
171
181
 
182
+ def create_app_version opts={}
183
+ return if opts[:settings_only]
184
+ start = Time.now.utc
185
+ source_bundle = Rebi::ZipHelper.new.gen(self.config, opts)
186
+ version_label = source_bundle[:label]
187
+ key = "#{app_name}/#{version_label}.zip"
188
+ log("Uploading source bundle: #{version_label}.zip")
189
+ s3_client.put_object(
190
+ bucket: bucket_name,
191
+ key: key,
192
+ body: source_bundle[:file].read
193
+ )
194
+ log("Creating app version: #{version_label}")
195
+ client.create_application_version({
196
+ application_name: app_name,
197
+ description: source_bundle[:message],
198
+ version_label: version_label,
199
+ source_bundle: {
200
+ s3_bucket: bucket_name,
201
+ s3_key: key
202
+ }
203
+ })
204
+ log("App version was created in: #{Time.now.utc - start}s")
205
+ return version_label
206
+ end
207
+
172
208
  def init version_label, opts={}
173
- log("Creating new environment")
209
+ log h2("Start creating new environment")
174
210
  start_time = Time.now
175
211
 
176
212
  self.check_instance_profile
177
213
 
178
- self.api_data = client.create_environment({
179
- application_name: config.app_name,
180
- environment_name: config.name,
181
- version_label: version_label,
182
- tier: config.tier,
183
- description: config.description,
184
- option_settings: config.opts_array,
185
- }.merge(
186
- config.worker? ? {} : { cname_prefix: config.cname_prefix }
187
- ).merge(config.platform_arn ? { platform_arn: config.platform_arn } : { solution_stack_name: config.solution_stack_name })
188
- )
214
+ self.api_data = client.create_environment _create_args(version_label, opts)
189
215
 
190
216
  request_id = events(start_time).select do |e|
191
217
  e.message.match(response_msgs('event.createstarting'))
@@ -195,31 +221,11 @@ module Rebi
195
221
 
196
222
  def update version_label, opts={}
197
223
 
198
- raise Rebi::Error::EnvironmentInUpdating.new(name) if in_updating?
199
- log("Start updating")
224
+ raise Rebi::Error::EnvironmentInUpdating.new("Environment is in updating: #{name}") if in_updating?
225
+ log h2("Start updating")
200
226
  start_time = Time.now
201
- deploy_opts = gen_deploy_opts
202
- deploy_args = {
203
- application_name: config.app_name,
204
- environment_name: config.name,
205
- version_label: version_label,
206
- description: config.description,
207
- }
208
227
 
209
- if opts[:include_settings] || opts[:settings_only]
210
- deploy_args.merge!({
211
- option_settings: deploy_opts[:option_settings],
212
- options_to_remove: deploy_opts[:options_to_remove],
213
- })
214
- deploy_args.delete(:version_label) if opts[:settings_only]
215
- else
216
- deploy_args.merge!({
217
- option_settings: deploy_opts[:env_only],
218
- options_to_remove: deploy_opts[:options_to_remove],
219
- })
220
- end
221
-
222
- self.api_data = client.update_environment(deploy_args)
228
+ self.api_data = client.update_environment(_update_args(version_label, opts))
223
229
 
224
230
  request_id = events(start_time).select do |e|
225
231
  e.message.match(response_msgs('event.updatestarting'))
@@ -228,18 +234,23 @@ module Rebi
228
234
  return request_id
229
235
  end
230
236
 
231
- def deploy version_label, opts={}
237
+ def deploy opts={}
238
+ _run_hooks :pre
239
+ version_label = create_app_version opts
232
240
  request_id = if created?
233
241
  update version_label, opts
234
242
  else
235
243
  init version_label, opts
236
244
  end
245
+ log h3("DEPLOYING")
246
+ _run_hooks :post
247
+ watch_request request_id
237
248
  return request_id
238
249
  end
239
250
 
240
251
  def terminate!
241
252
  check_created
242
- log("Start terminating")
253
+ log h2("Start terminating")
243
254
  client.terminate_environment({
244
255
  environment_name: name,
245
256
  environment_id: id,
@@ -252,8 +263,8 @@ module Rebi
252
263
  return request_id
253
264
  end
254
265
 
255
- def log mes
256
- Rebi.log(mes, name)
266
+ def log_label
267
+ name
257
268
  end
258
269
 
259
270
  def success_message? mes
@@ -280,31 +291,6 @@ module Rebi
280
291
  return false
281
292
  end
282
293
 
283
- def gen_deploy_opts
284
- to_deploy = []
285
- to_remove = []
286
- env_only = []
287
- config.opts_array.each do |o|
288
- o = o.deep_dup
289
-
290
- if o[:namespace] == config.ns(:app_env)
291
- if o[:value].blank?
292
- o.delete(:value)
293
- to_remove << o
294
- next
295
- else
296
- env_only << o
297
- end
298
- end
299
- to_deploy << o
300
- end
301
- return {
302
- option_settings: to_deploy,
303
- options_to_remove: to_remove,
304
- env_only: env_only,
305
- }
306
- end
307
-
308
294
  def ssh instance_id
309
295
  raise "Invalid instance_id" unless self.instance_ids.include?(instance_id)
310
296
 
@@ -390,15 +376,96 @@ module Rebi
390
376
  end
391
377
 
392
378
  # TODO
393
- def self.all app_name, client=Rebi.client
379
+ def self.all app_name, client=Rebi.eb
394
380
  client.describe_environments(application_name: app_name,
395
381
  include_deleted: false).environments
396
382
  end
397
383
 
398
- def self.get stage_name, env_name, client=Rebi.client
384
+ def self.get stage_name, env_name, client=Rebi.eb
399
385
  env = new stage_name, env_name, client
400
386
  return env.created? ? env : nil
401
387
  end
402
388
 
389
+ private
390
+
391
+ def _create_args version_label, opts={}
392
+ args = {
393
+ application_name: config.app_name,
394
+ environment_name: config.name,
395
+ version_label: version_label,
396
+ tier: config.tier,
397
+ description: config.description,
398
+ option_settings: config.opts_array,
399
+ }
400
+
401
+ args.merge!(cname_prefix: config.cname_prefix) if config.cname_prefix
402
+
403
+ args.merge!(config.platform_arn ? { platform_arn: config.platform_arn } : { solution_stack_name: config.solution_stack_name })
404
+ return args
405
+ end
406
+
407
+ def _update_args version_label, opts={}
408
+ deploy_opts = _gen_deploy_opts
409
+
410
+ args = {
411
+ application_name: config.app_name,
412
+ environment_name: config.name,
413
+ description: config.description,
414
+ }
415
+
416
+ args.merge!(version_label: version_label) if version_label
417
+
418
+ if opts[:include_settings] || opts[:settings_only]
419
+ args.merge!({
420
+ option_settings: deploy_opts[:option_settings],
421
+ options_to_remove: deploy_opts[:options_to_remove],
422
+ })
423
+ args.delete(:version_label) if opts[:settings_only]
424
+ else
425
+ args.merge!({
426
+ option_settings: deploy_opts[:env_only],
427
+ options_to_remove: deploy_opts[:options_to_remove],
428
+ })
429
+ end
430
+
431
+ return args
432
+ end
433
+
434
+ def _gen_deploy_opts
435
+ to_deploy = []
436
+ to_remove = []
437
+ env_only = []
438
+ config.opts_array.each do |o|
439
+ o = o.deep_dup
440
+
441
+ if o[:namespace] == config.ns(:app_env)
442
+ if o[:value].blank?
443
+ o.delete(:value)
444
+ to_remove << o
445
+ next
446
+ else
447
+ env_only << o
448
+ end
449
+ end
450
+ to_deploy << o
451
+ end
452
+ return {
453
+ option_settings: to_deploy,
454
+ options_to_remove: to_remove,
455
+ env_only: env_only,
456
+ }
457
+ end
458
+
459
+ def _run_hooks type
460
+ if (hooks = config.hooks[type]).present?
461
+ log h1("RUNNING #{type.upcase} HOOKS")
462
+ hooks.each do |cmd|
463
+ log h4(cmd)
464
+ system "#{cmd} 2>&1"
465
+ raise "Command failed" unless $?.success?
466
+ end
467
+ log h1("#{type.upcase} HOOKS FINISHED!!!")
468
+ end
469
+ end
403
470
  end
404
471
  end
@@ -0,0 +1,54 @@
1
+ module Rebi
2
+ module Log
3
+ def log mes, label=self.log_label
4
+ puts "#{label ? "#{colorize_prefix(label)}: " : ""}#{mes}"
5
+ end
6
+
7
+ def error mes, label=self.error_label
8
+ puts colorize(label, color: :white, background: :red) + ": " + mes
9
+ end
10
+
11
+ def log_label
12
+ "Rebi"
13
+ end
14
+
15
+ def error_label
16
+ "ERROR"
17
+ end
18
+
19
+ def colorize_prefix(prefix)
20
+ colors = ColorizedString.colors
21
+ colors.delete :light_black
22
+ h = prefix.chars.inject(0) do |m, c|
23
+ m + c.ord
24
+ end
25
+ return colorize(prefix, color: colors[h % colors.count], background: :light_black)
26
+ end
27
+
28
+ def h1 s
29
+ colorize(s, color: :light_yellow, background: :light_blue)
30
+ end
31
+
32
+ def h2 s
33
+ colorize(s, color: :light_blue, background: :light_cyan)
34
+ end
35
+
36
+ def h3 s
37
+ colorize(s, color: :light_yellow, background: :light_blue, mode: :bold)
38
+ end
39
+
40
+ def h4 s
41
+ colorize(s, color: :black, background: :green, mode: :italic)
42
+ end
43
+
44
+ def hstatus s
45
+ bg = s.downcase.to_sym
46
+ bg = :light_black unless ColorizedString.colors.include?(bg)
47
+ colorize(s, color: :black, background: bg, mode: :italic)
48
+ end
49
+
50
+ def colorize mes, opts={}
51
+ ColorizedString[mes].colorize(opts)
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Rebi
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.2'
3
3
  end
@@ -1,41 +1,65 @@
1
1
  module Rebi
2
2
  class ZipHelper
3
3
 
4
+ include Rebi::Log
5
+
6
+ EB_IGNORE = ".ebignore"
7
+
4
8
  def initialize
5
- `git status`
6
- raise Rebi::Error::NoGit.new("Not a git repository") unless $?.success?
9
+ # raise Rebi::Error::NoGit.new("Not a git repository") unless git?
10
+ end
11
+
12
+ def git?
13
+ `git status 2>&1`
14
+ $?.success?
7
15
  end
8
16
 
9
17
  def ls_files
10
- `git ls-files`.split("\n")
18
+ `git ls-files 2>&1`.split("\n")
11
19
  end
12
20
 
13
21
  def raw_zip_archive opts={}
14
22
  tmp_file = Tempfile.new("git_archive")
15
- system "git archive --format=zip HEAD > #{tmp_file.path}"
23
+ if !git? || ebignore?
24
+ Zip::File.open(tmp_file.path, Zip::File::CREATE) do |z|
25
+ spec = ebignore_spec
26
+ Dir.glob("**/*").each do |f|
27
+ next if ebignore_spec.match f
28
+ if File.directory?(f)
29
+ z.mkdir f unless z.find_entry f
30
+ else
31
+ z.add f, f
32
+ end
33
+ end
34
+ end
35
+ else
36
+ commit_id = opts[:staged] ? `git write-tree`.chomp : "HEAD"
37
+ system "git archive --format=zip #{commit_id} > #{tmp_file.path}"
38
+ end
16
39
  return tmp_file
17
40
  end
18
41
 
19
42
  def version_label
20
- `git describe --always --abbrev=8`.chomp
43
+ git? ? `git describe --always --abbrev=8`.chomp : SecureRandom.hex[0, 8]
21
44
  end
22
45
 
23
46
  def message
24
- `git log --oneline -1`.chomp.split(" ")[1..-1].join(" ")[0..190]
47
+ git? ? `git log --oneline -1`.chomp.split(" ")[1..-1].join(" ")[0..190] : "Deploy #{Time.now.strftime("%Y/%m/%d %H:%M")}"
25
48
  end
26
49
 
27
50
  # Create zip archivement
28
- def gen env_conf
29
- Rebi.log("Creating zip archivement", env_conf.name)
51
+ def gen env_conf,opts={}
52
+ log("Creating zip archivement", env_conf.name)
30
53
  start = Time.now
31
54
  ebextensions = env_conf.ebextensions
32
- files = ls_files
33
- tmp_file = raw_zip_archive
55
+ tmp_file = raw_zip_archive opts
34
56
  tmp_folder = Dir.mktmpdir
35
- Zip::File.open(tmp_file) do |z|
57
+ Zip::File.open(tmp_file.path) do |z|
36
58
  ebextensions.each do |ex_folder|
59
+
60
+ z.remove_folder ex_folder unless ex_folder == ".ebextension"
37
61
  Dir.glob("#{ex_folder}/*.config") do |fname|
38
- next unless (File.file?(fname) && files.include?(fname))
62
+ next unless File.file?(fname)
39
63
  next unless y = YAML::load(ErbHelper.new(File.read(fname), env_conf).result)
40
64
  basename = File.basename(fname)
41
65
  target = ".ebextensions/#{basename}"
@@ -47,9 +71,25 @@ module Rebi
47
71
  z.add target, tmp_yaml
48
72
  end
49
73
  end
74
+ dockerrun_file = env_conf.dockerrun || "Dockerrun.aws.json"
75
+
76
+ if File.exists?(dockerrun_file)
77
+ dockerrun = JSON.parse ErbHelper.new(File.read(dockerrun_file), env_conf).result
78
+ tmp_dockerrun = "#{tmp_folder}/Dockerrun.aws.json"
79
+ File.open(tmp_dockerrun, 'w') do |f|
80
+ f.write dockerrun.to_json
81
+ end
82
+ z.remove env_conf.dockerrun if z.find_entry env_conf.dockerrun
83
+ z.remove "Dockerrun.aws.json" if z.find_entry "Dockerrun.aws.json"
84
+ z.add "Dockerrun.aws.json", tmp_dockerrun
85
+ end
86
+
50
87
  end
88
+
51
89
  FileUtils.rm_rf tmp_folder
52
- Rebi.log("Zip was created in: #{Time.now - start}s", env_conf.name)
90
+
91
+
92
+ log("Zip was created in: #{Time.now - start}s", env_conf.name)
53
93
  return {
54
94
  label: Time.now.strftime("app_#{env_conf.name}_#{version_label}_%Y%m%d_%H%M%S"),
55
95
  file: File.open(tmp_file.path),
@@ -57,5 +97,32 @@ module Rebi
57
97
  }
58
98
  end
59
99
 
100
+ def ebignore_spec
101
+ if ebignore?
102
+ path_spec = PathSpec.from_filename(EB_IGNORE)
103
+ path_spec.add(".git")
104
+ return path_spec
105
+ else
106
+ return PathSpec.new(".git")
107
+ end
108
+ end
109
+
110
+ def ebignore?
111
+ File.exist?(EB_IGNORE)
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ module Zip
118
+ class File
119
+ def remove_folder fname
120
+ if folder = find_entry(fname)
121
+ remove folder if folder.directory?
122
+ end
123
+ glob("#{fname}/**/*").each do |f|
124
+ remove f
125
+ end
126
+ end
60
127
  end
61
128
  end
@@ -1,6 +1,12 @@
1
1
  app_name: App_name # Required
2
2
  app_description: Description # Optional
3
3
 
4
+ profile: aws_profile # if use profile config, overwrite with --profile option
5
+
6
+ # If use credentials(key, secret)
7
+ aws_key: aws_key
8
+ aws_secret: aws_secret
9
+
4
10
  stages: #Required: Hash
5
11
  development: #Stage name
6
12
  web: # Env name
@@ -33,8 +39,13 @@ stages: #Required: Hash
33
39
  # Result will has 01.config, 02.config(from web1_extensions) and 03.config
34
40
  cfg_file: File path or cfg name # Optional
35
41
 
42
+ dockerrun: Dockerrun.aws.json.stg # Dockerrun file
36
43
  options: #Hash, Other custom options for using in erb
37
44
  use_basic: true # rebi.opts.use_basic
45
+
46
+ hooks:
47
+ pre: ls # String or Array, run before upload source bundle
48
+ post: ls # String or Array, run right after sending deploy request
38
49
  worker:
39
50
  # ...
40
51
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rebi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - KhiemNS
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-13 00:00:00.000000000 Z
11
+ date: 2018-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -25,19 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
- name: aws-sdk
28
+ name: aws-sdk-ec2
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-s3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-elasticbeanstalk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
32
67
  - !ruby/object:Gem::Version
33
- version: '2.10'
68
+ version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: aws-sdk-iam
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
34
76
  type: :runtime
35
77
  prerelease: false
36
78
  version_requirements: !ruby/object:Gem::Requirement
37
79
  requirements:
38
- - - ">="
80
+ - - "~>"
39
81
  - !ruby/object:Gem::Version
40
- version: '2.10'
82
+ version: '1.0'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: dotenv
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +150,34 @@ dependencies:
108
150
  - - "~>"
109
151
  - !ruby/object:Gem::Version
110
152
  version: '1.3'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pathspec
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.2'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.2'
167
+ - !ruby/object:Gem::Dependency
168
+ name: byebug
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '9'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '9'
111
181
  description: Deploy ElasticBeanstalk with multiple deploy, switchable and dynamic
112
182
  generated ebextensions with erb
113
183
  email:
@@ -129,7 +199,7 @@ files:
129
199
  - lib/rebi/environment.rb
130
200
  - lib/rebi/erb_helper.rb
131
201
  - lib/rebi/error.rb
132
- - lib/rebi/iam.rb
202
+ - lib/rebi/log.rb
133
203
  - lib/rebi/version.rb
134
204
  - lib/rebi/zip_helper.rb
135
205
  - lib/tasks/rebi.rake
@@ -1,43 +0,0 @@
1
- # module Rebi
2
- # class IAM
3
- #
4
- # attr_reader :client
5
- #
6
- # def initialize client=Aws::IAM::Client.new
7
- # @client = client
8
- # end
9
- #
10
- # def check_or_create_eb_profile profile
11
- #
12
- # end
13
- #
14
- # def create_instance_profile profile
15
- # clieng.create_instance_profile({
16
- # instance_profile_name: profile
17
- # })
18
- # end
19
- #
20
- # def get_default_role
21
- # role = Rebi::ConfigEnvironment::DEFAULT_IAM_INSTANCE_PROFILE
22
- # document = '{"Version": "2008-10-17","Statement": [{"Action":' \
23
- # ' "sts:AssumeRole","Principal": {"Service": ' \
24
- # '"ec2.amazonaws.com"},"Effect": "Allow","Sid": ""}]}'
25
- # client.create_role({
26
- # role_name: role,
27
- # assume_role_policy_document: document
28
- # })
29
- # return role
30
- # end
31
- #
32
- # def add_role_to_profile profile, role
33
- # client.add_role_to_instance_profile({
34
- # instance_profile_name: profile,
35
- # role_name: role
36
- # })
37
- # end
38
- #
39
- # def log mes
40
- # Rebi.log(mes, "IAM")
41
- # end
42
- # end
43
- # end