rebi 0.2.3 → 0.3.2

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 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