engineyard 1.2.4 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -46,6 +46,8 @@ module EY
46
46
  :desc => "Name of the application to deploy"
47
47
  method_option :verbose, :type => :boolean, :aliases => %w(-v),
48
48
  :desc => "Be verbose"
49
+ method_option :extra_deploy_hook_options, :type => :hash, :default => {},
50
+ :desc => "Additional options to be made available in deploy hooks (in the 'config' hash)"
49
51
  def deploy
50
52
  app = fetch_app(options[:app])
51
53
  environment = fetch_environment(options[:environment], app)
@@ -65,12 +67,7 @@ module EY
65
67
 
66
68
  EY.ui.info "Beginning deploy for '#{app.name}' in '#{environment.name}' on server..."
67
69
 
68
- # missing means do what the yaml file says
69
- # nil means don't do it
70
- # true (the lazy default) means do it with the custom command
71
- # a string means do it with this specific command
72
-
73
- deploy_options = {}
70
+ deploy_options = {'extras' => options[:extra_deploy_hook_options]}
74
71
  deploy_options['migrate'] = options['migrate'] if options.has_key?('migrate')
75
72
  deploy_options['verbose'] = options['verbose'] if options.has_key?('verbose')
76
73
 
@@ -145,6 +142,8 @@ module EY
145
142
  :desc => "Name of the application to roll back"
146
143
  method_option :verbose, :type => :boolean, :aliases => %w(-v),
147
144
  :desc => "Be verbose"
145
+ method_option :extra_deploy_hook_options, :type => :hash, :default => {},
146
+ :desc => "Additional options to be made available in deploy hooks (in the 'config' hash)"
148
147
  def rollback
149
148
  app = fetch_app(options[:app])
150
149
  env = fetch_environment(options[:environment], app)
@@ -152,7 +151,9 @@ module EY
152
151
  loudly_check_engineyard_serverside(env)
153
152
 
154
153
  EY.ui.info("Rolling back '#{app.name}' in '#{env.name}'")
155
- if env.rollback(app, options[:verbose])
154
+ if env.rollback(app,
155
+ options[:extra_deploy_hook_options],
156
+ options[:verbose])
156
157
  EY.ui.info "Rollback complete"
157
158
  else
158
159
  raise EY::Error, "Rollback failed"
@@ -36,44 +36,17 @@ module EY
36
36
  end
37
37
 
38
38
  def deploy(app, ref, deploy_options={})
39
- # regarding deploy_options['migrate']:
40
- #
41
- # missing means migrate how the yaml file says to
42
- # nil means don't migrate
43
- # true means migrate w/custom command (if present) or default
44
- # a string means migrate with this specific command
45
-
46
- default_migration_command = config['migration_command'] || 'rake db:migrate --trace'
47
-
48
- migration_from_config = if config.has_key?('migrate')
49
- if config['migrate']
50
- default_migration_command
51
- else
52
- nil
53
- end
54
- else
55
- default_migration_command
56
- end
57
-
58
- migration_from_command_line = if deploy_options['migrate'].nil?
59
- nil
60
- elsif deploy_options['migrate'].respond_to?(:to_str)
61
- deploy_options['migrate'].to_str
62
- else
63
- default_migration_command
64
- end
65
-
66
- cmd = if deploy_options.has_key?('migrate')
67
- migration_from_command_line
68
- else
69
- migration_from_config
70
- end
71
-
72
- app_master!.deploy(app, ref, cmd, config, deploy_options['verbose'])
39
+ app_master!.deploy(app,
40
+ ref,
41
+ migration_command(deploy_options),
42
+ config.merge(deploy_options['extras']),
43
+ deploy_options['verbose'])
73
44
  end
74
45
 
75
- def rollback(app, verbose=false)
76
- app_master!.rollback(app, config, verbose)
46
+ def rollback(app, extra_deploy_hook_options={}, verbose=false)
47
+ app_master!.rollback(app,
48
+ config.merge(extra_deploy_hook_options),
49
+ verbose)
77
50
  end
78
51
 
79
52
  def take_down_maintenance_page(app, verbose=false)
@@ -151,7 +124,50 @@ module EY
151
124
  def shorten_name_for(app)
152
125
  name.gsub(/^#{Regexp.quote(app.name)}_/, '')
153
126
  end
127
+
128
+ private
129
+
130
+ def migration_command(deploy_options)
131
+ # regarding deploy_options['migrate']:
132
+ #
133
+ # missing means migrate how the yaml file says to
134
+ # nil means don't migrate
135
+ # true means migrate w/custom command (if present) or default
136
+ # a string means migrate with this specific command
137
+ if deploy_options.has_key?('migrate')
138
+ migration_command_from_command_line(deploy_options)
139
+ else
140
+ migration_command_from_config
141
+ end
142
+ end
143
+
144
+ def migration_command_from_config
145
+ if config.has_key?('migrate')
146
+ if config['migrate']
147
+ default_migration_command
148
+ else
149
+ nil
150
+ end
151
+ else
152
+ default_migration_command
153
+ end
154
+ end
155
+
156
+ def migration_command_from_command_line(deploy_options)
157
+ if deploy_options['migrate'].nil?
158
+ nil
159
+ elsif deploy_options['migrate'].respond_to?(:to_str)
160
+ deploy_options['migrate'].to_str
161
+ else
162
+ default_migration_command
163
+ end
164
+ end
165
+
166
+
167
+ def default_migration_command
168
+ config['migration_command'] || 'rake db:migrate --trace'
169
+ end
170
+
154
171
  end
155
172
  end
156
-
157
173
  end
@@ -96,6 +96,18 @@ module EY
96
96
  "cd `mktemp -d` && #{gem_path} install engineyard-serverside --no-rdoc --no-ri -v #{ENGINEYARD_SERVERSIDE_VERSION}"]))
97
97
  end
98
98
 
99
+ protected
100
+
101
+ def engineyard_serverside_hostname
102
+ # If we tell engineyard-serverside to use 'localhost', it'll run
103
+ # commands on the instance directly (#system). If we give it the
104
+ # instance's actual hostname, it'll SSH to itself.
105
+ #
106
+ # Using 'localhost' instead of its EC2 hostname speeds up
107
+ # deploys on solos and single-app-server clusters significantly.
108
+ app_master? ? 'localhost' : hostname
109
+ end
110
+
99
111
  private
100
112
 
101
113
  def ssh(remote_command, output = true)
@@ -113,13 +125,21 @@ module EY
113
125
 
114
126
  def invoke_engineyard_serverside(deploy_args, verbose=false)
115
127
  start = [engineyard_serverside_path, "_#{ENGINEYARD_SERVERSIDE_VERSION}_", 'deploy']
116
- instance_args = environment.instances.find_all do |inst|
117
- inst.has_app_code?
118
- end.inject(['--instances']) do |command, inst|
119
- instance_tuple = [inst.public_hostname, inst.role]
120
- instance_tuple << inst.name if inst.name
121
128
 
122
- command << instance_tuple.join(',')
129
+ instances = environment.instances.select { |inst| inst.has_app_code? }
130
+ instance_args = ['']
131
+ if !instances.empty?
132
+ instance_args << '--instances'
133
+ instance_args += instances.collect { |i| i.engineyard_serverside_hostname }
134
+
135
+ instance_args << '--instance-roles'
136
+ instance_args += instances.collect { |i| [i.engineyard_serverside_hostname, i.role].join(':') }
137
+
138
+ instance_names = instances.collect { |i| i.name ? [i.engineyard_serverside_hostname, i.name].join(':') : nil }.compact
139
+ unless instance_names.empty?
140
+ instance_args << '--instance-names'
141
+ instance_args += instance_names
142
+ end
123
143
  end
124
144
 
125
145
  framework_arg = ['--framework-env', environment.framework_env]
@@ -135,6 +155,10 @@ module EY
135
155
  "/usr/local/ey_resin/ruby/bin/engineyard-serverside"
136
156
  end
137
157
 
158
+ def app_master?
159
+ environment.app_master == self
160
+ end
161
+
138
162
  def gem_path
139
163
  "/usr/local/ey_resin/ruby/bin/gem"
140
164
  end
@@ -1,3 +1,3 @@
1
1
  module EY
2
- ENGINEYARD_SERVERSIDE_VERSION = ENV['ENGINEYARD_SERVERSIDE_VERSION'] || '1.2.2'
2
+ ENGINEYARD_SERVERSIDE_VERSION = ENV['ENGINEYARD_SERVERSIDE_VERSION'] || '1.3.1'
3
3
  end
@@ -1,3 +1,3 @@
1
1
  module EY
2
- VERSION = '1.2.4'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -264,6 +264,40 @@ describe "ey deploy" do
264
264
  end
265
265
  end
266
266
 
267
+ context "--extra-deploy-hook-options" do
268
+ before(:all) do
269
+ api_scenario "one app, one environment"
270
+ end
271
+
272
+ def extra_deploy_hook_options
273
+ if @ssh_commands.last =~ /--config (.*?)(?: -|$)/
274
+ # the echo strips off the layer of shell escaping, leaving us
275
+ # with pristine JSON
276
+ JSON.parse `echo #{$1}`
277
+ end
278
+ end
279
+
280
+ it "passes the extra configuration to engineyard-serverside" do
281
+ ey "deploy --extra-deploy-hook-options some:stuff more:crap"
282
+ extra_deploy_hook_options.should_not be_nil
283
+ extra_deploy_hook_options['some'].should == 'stuff'
284
+ extra_deploy_hook_options['more'].should == 'crap'
285
+ end
286
+
287
+ context "when ey.yml is present" do
288
+ before do
289
+ write_yaml({"environments" => {"giblets" => {"beer" => "stout"}}})
290
+ end
291
+
292
+ after { File.unlink("ey.yml") }
293
+
294
+ it "overrides what's in ey.yml" do
295
+ ey "deploy --extra-deploy-hook-options beer:esb"
296
+ extra_deploy_hook_options['beer'].should == 'esb'
297
+ end
298
+ end
299
+ end
300
+
267
301
  context "specifying the application" do
268
302
  before(:all) do
269
303
  api_scenario "one app, one environment"
@@ -27,4 +27,38 @@ describe "ey rollback" do
27
27
  @ssh_commands.last.should =~ /--stack nginx_mongrel/
28
28
  end
29
29
 
30
+ context "--extra-deploy-hook-options" do
31
+ before(:all) do
32
+ api_scenario "one app, one environment"
33
+ end
34
+
35
+ def extra_deploy_hook_options
36
+ if @ssh_commands.last =~ /--config (.*?)(?: -|$)/
37
+ # the echo strips off the layer of shell escaping, leaving us
38
+ # with pristine JSON
39
+ JSON.parse `echo #{$1}`
40
+ end
41
+ end
42
+
43
+ it "passes the extra configuration to engineyard-serverside" do
44
+ ey "rollback --extra-deploy-hook-options some:stuff more:crap"
45
+ extra_deploy_hook_options.should_not be_nil
46
+ extra_deploy_hook_options['some'].should == 'stuff'
47
+ extra_deploy_hook_options['more'].should == 'crap'
48
+ end
49
+
50
+ context "when ey.yml is present" do
51
+ before do
52
+ write_yaml({"environments" => {"giblets" => {"beer" => "stout"}}})
53
+ end
54
+
55
+ after { File.unlink("ey.yml") }
56
+
57
+ it "overrides what's in ey.yml" do
58
+ ey "deploy --extra-deploy-hook-options beer:esb"
59
+ extra_deploy_hook_options['beer'].should == 'esb'
60
+ end
61
+ end
62
+ end
63
+
30
64
  end
@@ -150,12 +150,12 @@ shared_examples_for "it invokes engineyard-serverside" do
150
150
 
151
151
  it "passes along instance information to engineyard-serverside" do
152
152
  instance_args = [
153
- Regexp.quote("app_master_hostname.compute-1.amazonaws.com,app_master"),
154
- Regexp.quote("app_hostname.compute-1.amazonaws.com,app"),
155
- Regexp.quote("util_fluffy_hostname.compute-1.amazonaws.com,util,fluffy"),
153
+ /--instances localhost app_hostname[^\s]+ util_fluffy/,
154
+ /--instance-roles localhost:app_master app_hostname[^\s]+:app util_fluffy[^\s]+:util/,
155
+ /--instance-names util_fluffy_hostname[^\s]+:fluffy/
156
156
  ]
157
157
 
158
- db_instance = Regexp.quote("db_master_hostname.compute-1.amazonaws.com,db_master")
158
+ db_instance = /db_master/
159
159
 
160
160
  # apps + utilities are all mentioned
161
161
  instance_args.each do |i|
@@ -164,15 +164,22 @@ shared_examples_for "it invokes engineyard-serverside" do
164
164
 
165
165
  # but not database instances
166
166
  @ssh_commands.last.should_not =~ /#{db_instance}/
167
-
168
- # and it's all after the option '--instances'
169
- @ssh_commands.last.should match(/--instances (#{instance_args.join('|')})/)
170
167
  end
171
168
 
172
169
  it "passes the framework environment" do
173
170
  @ssh_commands.last.should match(/--framework-env production/)
174
171
  end
172
+ end
173
+
174
+ context "when no instances have names" do
175
+ before(:each) do
176
+ api_scenario "two apps"
177
+ run_ey({:env => 'giblets', :app => 'rails232app', :ref => 'master', :verbose => true})
178
+ end
175
179
 
180
+ it "omits the --instance-names parameter" do
181
+ @ssh_commands.last.should_not include("--instance-names")
182
+ end
176
183
  end
177
184
 
178
185
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: engineyard
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 2
9
- - 4
10
- version: 1.2.4
8
+ - 3
9
+ - 0
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - EY Cloud Team
@@ -15,100 +15,100 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-31 00:00:00 -07:00
18
+ date: 2010-09-13 00:00:00 -07:00
19
19
  default_executable: ey
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- prerelease: false
23
22
  version_requirements: &id001 !ruby/object:Gem::Requirement
24
23
  none: false
25
24
  requirements:
26
25
  - - ~>
27
26
  - !ruby/object:Gem::Version
28
- hash: 7
27
+ hash: 39
29
28
  segments:
30
- - 1
31
- - 5
32
- - 2
33
- version: 1.5.2
29
+ - 0
30
+ - 14
31
+ - 0
32
+ version: 0.14.0
34
33
  requirement: *id001
35
34
  type: :runtime
36
- name: highline
37
- - !ruby/object:Gem::Dependency
35
+ name: thor
38
36
  prerelease: false
37
+ - !ruby/object:Gem::Dependency
39
38
  version_requirements: &id002 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ~>
43
42
  - !ruby/object:Gem::Version
44
- hash: 39
43
+ hash: 7
45
44
  segments:
46
- - 0
47
- - 14
48
- - 0
49
- version: 0.14.0
45
+ - 1
46
+ - 4
47
+ version: "1.4"
50
48
  requirement: *id002
51
49
  type: :runtime
52
- name: thor
53
- - !ruby/object:Gem::Dependency
50
+ name: rest-client
54
51
  prerelease: false
52
+ - !ruby/object:Gem::Dependency
55
53
  version_requirements: &id003 !ruby/object:Gem::Requirement
56
54
  none: false
57
55
  requirements:
58
- - - ">="
56
+ - - ~>
59
57
  - !ruby/object:Gem::Version
60
- hash: 3
58
+ hash: 7
61
59
  segments:
62
- - 0
63
- version: "0"
60
+ - 1
61
+ - 5
62
+ - 2
63
+ version: 1.5.2
64
64
  requirement: *id003
65
65
  type: :runtime
66
- name: ruby-termios
67
- - !ruby/object:Gem::Dependency
66
+ name: highline
68
67
  prerelease: false
68
+ - !ruby/object:Gem::Dependency
69
69
  version_requirements: &id004 !ruby/object:Gem::Requirement
70
70
  none: false
71
71
  requirements:
72
- - - ~>
72
+ - - ">="
73
73
  - !ruby/object:Gem::Version
74
- hash: 23
74
+ hash: 3
75
75
  segments:
76
76
  - 0
77
- - 0
78
- - 4
79
- version: 0.0.4
77
+ version: "0"
80
78
  requirement: *id004
81
79
  type: :runtime
82
- name: escape
83
- - !ruby/object:Gem::Dependency
80
+ name: ruby-termios
84
81
  prerelease: false
82
+ - !ruby/object:Gem::Dependency
85
83
  version_requirements: &id005 !ruby/object:Gem::Requirement
86
84
  none: false
87
85
  requirements:
88
- - - ~>
86
+ - - ">="
89
87
  - !ruby/object:Gem::Version
90
- hash: 7
88
+ hash: 3
91
89
  segments:
92
- - 1
93
- - 4
94
- version: "1.4"
90
+ - 0
91
+ version: "0"
95
92
  requirement: *id005
96
93
  type: :runtime
97
- name: rest-client
98
- - !ruby/object:Gem::Dependency
94
+ name: json_pure
99
95
  prerelease: false
96
+ - !ruby/object:Gem::Dependency
100
97
  version_requirements: &id006 !ruby/object:Gem::Requirement
101
98
  none: false
102
99
  requirements:
103
- - - ">="
100
+ - - ~>
104
101
  - !ruby/object:Gem::Version
105
- hash: 3
102
+ hash: 23
106
103
  segments:
107
104
  - 0
108
- version: "0"
105
+ - 0
106
+ - 4
107
+ version: 0.0.4
109
108
  requirement: *id006
110
109
  type: :runtime
111
- name: json_pure
110
+ name: escape
111
+ prerelease: false
112
112
  description: This gem allows you to deploy your rails application to the Engine Yard cloud directly from the command line.
113
113
  email: cloud@engineyard.com
114
114
  executables: