engineyard-serverside 1.3.7 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,4 +7,4 @@ end
7
7
  $LOAD_PATH.push(File.expand_path("../lib", File.dirname(__FILE__)))
8
8
  require 'engineyard-serverside'
9
9
 
10
- EY::CLI.start
10
+ EY::Serverside::CLI.start
@@ -1,4 +1,3 @@
1
- $LOAD_PATH.push(File.expand_path("engineyard-serverside", File.dirname(__FILE__)))
2
1
  $LOAD_PATH.unshift File.expand_path('vendor/thor/lib', File.dirname(__FILE__))
3
2
  $LOAD_PATH.unshift File.expand_path('vendor/open4/lib', File.dirname(__FILE__))
4
3
  $LOAD_PATH.unshift File.expand_path('vendor/escape/lib', File.dirname(__FILE__))
@@ -9,44 +8,49 @@ require 'escape'
9
8
  require 'json'
10
9
  require 'dataflow'
11
10
 
12
- require 'strategies/git'
13
- require 'task'
14
- require 'server'
15
- require 'deploy'
16
- require 'deploy_hook'
17
- require 'lockfile_parser'
18
- require 'bundle_installer'
19
- require 'cli'
20
- require 'configuration'
11
+ require 'engineyard-serverside/version'
12
+ require 'engineyard-serverside/strategies/git'
13
+ require 'engineyard-serverside/task'
14
+ require 'engineyard-serverside/server'
15
+ require 'engineyard-serverside/deploy'
16
+ require 'engineyard-serverside/deploy_hook'
17
+ require 'engineyard-serverside/lockfile_parser'
18
+ require 'engineyard-serverside/bundle_installer'
19
+ require 'engineyard-serverside/cli'
20
+ require 'engineyard-serverside/configuration'
21
+ require 'engineyard-serverside/deprecation'
21
22
 
22
23
  module EY
23
- def self.node
24
- @node ||= deep_indifferentize(JSON.parse(dna_json))
25
- end
24
+ module Serverside
25
+
26
+ def self.node
27
+ @node ||= deep_indifferentize(JSON.parse(dna_json))
28
+ end
26
29
 
27
- def self.dna_json
28
- @dna_json ||= if File.exist?('/etc/chef/dna.json')
29
- `sudo cat /etc/chef/dna.json`
30
- else
31
- {}.to_json
32
- end
33
- end
30
+ def self.dna_json
31
+ @dna_json ||= if File.exist?('/etc/chef/dna.json')
32
+ `sudo cat /etc/chef/dna.json`
33
+ else
34
+ {}.to_json
35
+ end
36
+ end
34
37
 
35
- RemoteFailure = Class.new StandardError
38
+ RemoteFailure = Class.new StandardError
36
39
 
37
- private
38
- def self.deep_indifferentize(thing)
39
- if thing.kind_of?(Hash)
40
- indifferent_hash = Thor::CoreExt::HashWithIndifferentAccess.new
41
- thing.each do |k, v|
42
- indifferent_hash[k] = deep_indifferentize(v)
40
+ private
41
+ def self.deep_indifferentize(thing)
42
+ if thing.kind_of?(Hash)
43
+ indifferent_hash = Thor::CoreExt::HashWithIndifferentAccess.new
44
+ thing.each do |k, v|
45
+ indifferent_hash[k] = deep_indifferentize(v)
46
+ end
47
+ indifferent_hash
48
+ elsif thing.kind_of?(Array)
49
+ thing.map {|x| deep_indifferentize(x)}
50
+ else
51
+ thing
43
52
  end
44
- indifferent_hash
45
- elsif thing.kind_of?(Array)
46
- thing.map {|x| deep_indifferentize(x)}
47
- else
48
- thing
49
53
  end
54
+
50
55
  end
51
-
52
56
  end
@@ -1,4 +1,6 @@
1
1
  module EY
2
- class BundleInstaller < Struct.new(:version, :options)
2
+ module Serverside
3
+ class BundleInstaller < Struct.new(:version, :options)
4
+ end
3
5
  end
4
6
  end
@@ -2,255 +2,257 @@ require 'thor'
2
2
  require 'pathname'
3
3
 
4
4
  module EY
5
- class CLI < Thor
6
- include Dataflow
5
+ module Serverside
6
+ class CLI < Thor
7
+ include Dataflow
8
+
9
+ def self.start(*)
10
+ super
11
+ rescue RemoteFailure
12
+ exit(1)
13
+ end
7
14
 
8
- def self.start(*)
9
- super
10
- rescue RemoteFailure
11
- exit(1)
12
- end
15
+ method_option :migrate, :type => :string,
16
+ :desc => "Run migrations with this deploy",
17
+ :aliases => ["-m"]
13
18
 
14
- method_option :migrate, :type => :string,
15
- :desc => "Run migrations with this deploy",
16
- :aliases => ["-m"]
19
+ method_option :branch, :type => :string,
20
+ :desc => "Git ref to deploy, defaults to master. May be a branch, a tag, or a SHA",
21
+ :aliases => %w[-b --ref --tag]
17
22
 
18
- method_option :branch, :type => :string,
19
- :desc => "Git ref to deploy, defaults to master. May be a branch, a tag, or a SHA",
20
- :aliases => %w[-b --ref --tag]
23
+ method_option :repo, :type => :string,
24
+ :desc => "Remote repo to deploy",
25
+ :aliases => ["-r"]
21
26
 
22
- method_option :repo, :type => :string,
23
- :desc => "Remote repo to deploy",
24
- :aliases => ["-r"]
27
+ method_option :app, :type => :string,
28
+ :required => true,
29
+ :desc => "Application to deploy",
30
+ :aliases => ["-a"]
25
31
 
26
- method_option :app, :type => :string,
27
- :required => true,
28
- :desc => "Application to deploy",
29
- :aliases => ["-a"]
32
+ method_option :framework_env, :type => :string,
33
+ :desc => "Ruby web framework environment",
34
+ :aliases => ["-e"]
30
35
 
31
- method_option :framework_env, :type => :string,
32
- :desc => "Ruby web framework environment",
33
- :aliases => ["-e"]
36
+ method_option :config, :type => :string,
37
+ :desc => "Additional configuration"
34
38
 
35
- method_option :config, :type => :string,
36
- :desc => "Additional configuration"
39
+ method_option :stack, :type => :string,
40
+ :desc => "Web stack (so we can restart it correctly)"
37
41
 
38
- method_option :stack, :type => :string,
39
- :desc => "Web stack (so we can restart it correctly)"
42
+ method_option :instances, :type => :array,
43
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
40
44
 
41
- method_option :instances, :type => :array,
42
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
45
+ method_option :instance_roles, :type => :hash,
46
+ :default => {},
47
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
43
48
 
44
- method_option :instance_roles, :type => :hash,
45
- :default => {},
46
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
49
+ method_option :instance_names, :type => :hash,
50
+ :default => {},
51
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
47
52
 
48
- method_option :instance_names, :type => :hash,
49
- :default => {},
50
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
53
+ method_option :verbose, :type => :boolean,
54
+ :default => false,
55
+ :desc => "Verbose output",
56
+ :aliases => ["-v"]
51
57
 
52
- method_option :verbose, :type => :boolean,
53
- :default => false,
54
- :desc => "Verbose output",
55
- :aliases => ["-v"]
58
+ desc "deploy", "Deploy code from /data/<app>"
59
+ def deploy(default_task=:deploy)
60
+ config = EY::Serverside::Deploy::Configuration.new(options)
61
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
56
62
 
57
- desc "deploy", "Deploy code from /data/<app>"
58
- def deploy(default_task=:deploy)
59
- config = EY::Deploy::Configuration.new(options)
60
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
63
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
64
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
61
65
 
62
- EY::LoggedOutput.verbose = options[:verbose]
63
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
66
+ invoke :propagate
64
67
 
65
- invoke :propagate
68
+ EY::Serverside::Deploy.new(config).send(default_task)
69
+ end
66
70
 
67
- EY::Deploy.new(config).send(default_task)
68
- end
71
+ method_option :app, :type => :string,
72
+ :required => true,
73
+ :desc => "Which application's hooks to run",
74
+ :aliases => ["-a"]
69
75
 
70
- method_option :app, :type => :string,
71
- :required => true,
72
- :desc => "Which application's hooks to run",
73
- :aliases => ["-a"]
76
+ method_option :release_path, :type => :string,
77
+ :desc => "Value for #release_path in hooks (mostly for internal coordination)",
78
+ :aliases => ["-r"]
74
79
 
75
- method_option :release_path, :type => :string,
76
- :desc => "Value for #release_path in hooks (mostly for internal coordination)",
77
- :aliases => ["-r"]
80
+ method_option :current_roles, :type => :array,
81
+ :desc => "Value for #current_roles in hooks"
78
82
 
79
- method_option :current_roles, :type => :array,
80
- :desc => "Value for #current_roles in hooks"
83
+ method_option :framework_env, :type => :string,
84
+ :required => true,
85
+ :desc => "Ruby web framework environment",
86
+ :aliases => ["-e"]
81
87
 
82
- method_option :framework_env, :type => :string,
83
- :required => true,
84
- :desc => "Ruby web framework environment",
85
- :aliases => ["-e"]
88
+ method_option :config, :type => :string,
89
+ :desc => "Additional configuration"
86
90
 
87
- method_option :config, :type => :string,
88
- :desc => "Additional configuration"
91
+ method_option :current_name, :type => :string,
92
+ :desc => "Value for #current_name in hooks"
89
93
 
90
- method_option :current_name, :type => :string,
91
- :desc => "Value for #current_name in hooks"
94
+ desc "hook [NAME]", "Run a particular deploy hook"
95
+ def hook(hook_name)
96
+ EY::Serverside::DeployHook.new(options).run(hook_name)
97
+ end
92
98
 
93
- desc "hook [NAME]", "Run a particular deploy hook"
94
- def hook(hook_name)
95
- EY::DeployHook.new(options).run(hook_name)
96
- end
97
99
 
100
+ method_option :app, :type => :string,
101
+ :required => true,
102
+ :desc => "Application to deploy",
103
+ :aliases => ["-a"]
98
104
 
99
- method_option :app, :type => :string,
100
- :required => true,
101
- :desc => "Application to deploy",
102
- :aliases => ["-a"]
105
+ method_option :framework_env, :type => :string,
106
+ :required => true,
107
+ :desc => "Ruby web framework environment",
108
+ :aliases => ["-e"]
103
109
 
104
- method_option :framework_env, :type => :string,
105
- :required => true,
106
- :desc => "Ruby web framework environment",
107
- :aliases => ["-e"]
110
+ method_option :stack, :type => :string,
111
+ :desc => "Web stack (so we can restart it correctly)"
108
112
 
109
- method_option :stack, :type => :string,
110
- :desc => "Web stack (so we can restart it correctly)"
113
+ method_option :instances, :type => :array,
114
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
111
115
 
112
- method_option :instances, :type => :array,
113
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
116
+ method_option :instance_roles, :type => :hash,
117
+ :default => {},
118
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
114
119
 
115
- method_option :instance_roles, :type => :hash,
116
- :default => {},
117
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
120
+ method_option :instance_names, :type => :hash,
121
+ :default => {},
122
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
118
123
 
119
- method_option :instance_names, :type => :hash,
120
- :default => {},
121
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
124
+ method_option :verbose, :type => :boolean,
125
+ :default => false,
126
+ :desc => "Verbose output",
127
+ :aliases => ["-v"]
128
+ desc "integrate", "Integrate other instances into this cluster"
129
+ def integrate
130
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
131
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-integrate.log")
122
132
 
123
- method_option :verbose, :type => :boolean,
124
- :default => false,
125
- :desc => "Verbose output",
126
- :aliases => ["-v"]
127
- desc "integrate", "Integrate other instances into this cluster"
128
- def integrate
129
- EY::LoggedOutput.verbose = options[:verbose]
130
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-integrate.log")
133
+ app_dir = Pathname.new "/data/#{options[:app]}"
134
+ current_app_dir = app_dir + "current"
131
135
 
132
- app_dir = Pathname.new "/data/#{options[:app]}"
133
- current_app_dir = app_dir + "current"
136
+ # so that we deploy to the same place there that we have here
137
+ integrate_options = options.dup
138
+ integrate_options[:release_path] = current_app_dir.realpath.to_s
134
139
 
135
- # so that we deploy to the same place there that we have here
136
- integrate_options = options.dup
137
- integrate_options[:release_path] = current_app_dir.realpath.to_s
140
+ # we have to deploy the same SHA there as here
141
+ integrate_options[:branch] = (current_app_dir + 'REVISION').read.strip
138
142
 
139
- # we have to deploy the same SHA there as here
140
- integrate_options[:branch] = (current_app_dir + 'REVISION').read.strip
143
+ config = EY::Serverside::Deploy::Configuration.new(integrate_options)
141
144
 
142
- config = EY::Deploy::Configuration.new(integrate_options)
145
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
143
146
 
144
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
147
+ invoke :propagate
145
148
 
146
- invoke :propagate
149
+ EY::Serverside::Server.all.each do |server|
150
+ server.sync_directory app_dir
151
+ # we're just about to recreate this, so it has to be gone
152
+ # first. otherwise, non-idempotent deploy hooks could screw
153
+ # things up, and since we don't control deploy hooks, we must
154
+ # assume the worst.
155
+ server.run("rm -rf #{current_app_dir}")
156
+ end
147
157
 
148
- EY::Server.all.each do |server|
149
- server.sync_directory app_dir
150
- # we're just about to recreate this, so it has to be gone
151
- # first. otherwise, non-idempotent deploy hooks could screw
152
- # things up, and since we don't control deploy hooks, we must
153
- # assume the worst.
154
- server.run("rm -rf #{current_app_dir}")
158
+ # deploy local-ref to other instances into /data/$app/local-current
159
+ EY::Serverside::Deploy.new(config).cached_deploy
155
160
  end
156
161
 
157
- # deploy local-ref to other instances into /data/$app/local-current
158
- EY::Deploy.new(config).cached_deploy
159
- end
160
-
161
- method_option :app, :type => :string,
162
- :required => true,
163
- :desc => "Application to deploy",
164
- :aliases => ["-a"]
162
+ method_option :app, :type => :string,
163
+ :required => true,
164
+ :desc => "Application to deploy",
165
+ :aliases => ["-a"]
165
166
 
166
- method_option :stack, :type => :string,
167
- :desc => "Web stack (so we can restart it correctly)"
167
+ method_option :stack, :type => :string,
168
+ :desc => "Web stack (so we can restart it correctly)"
168
169
 
169
- method_option :instances, :type => :array,
170
- :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
170
+ method_option :instances, :type => :array,
171
+ :desc => "Hostnames of instances to deploy to, e.g. --instances localhost app1 app2"
171
172
 
172
- method_option :instance_roles, :type => :hash,
173
- :default => {},
174
- :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
173
+ method_option :instance_roles, :type => :hash,
174
+ :default => {},
175
+ :desc => "Roles of instances, keyed on hostname, comma-separated. e.g. instance1:app_master,etc instance2:db,memcached ..."
175
176
 
176
- method_option :instance_names, :type => :hash,
177
- :default => {},
178
- :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
177
+ method_option :instance_names, :type => :hash,
178
+ :default => {},
179
+ :desc => "Instance names, keyed on hostname. e.g. instance1:name1 instance2:name2"
179
180
 
180
- method_option :verbose, :type => :boolean,
181
- :default => false,
182
- :desc => "Verbose output",
183
- :aliases => ["-v"]
184
- desc "restart", "Restart app servers, conditionally enabling maintenance page"
185
- def restart
186
- EY::LoggedOutput.verbose = options[:verbose]
187
- EY::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-restart.log")
181
+ method_option :verbose, :type => :boolean,
182
+ :default => false,
183
+ :desc => "Verbose output",
184
+ :aliases => ["-v"]
185
+ desc "restart", "Restart app servers, conditionally enabling maintenance page"
186
+ def restart
187
+ EY::Serverside::LoggedOutput.verbose = options[:verbose]
188
+ EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-restart.log")
188
189
 
189
- config = EY::Deploy::Configuration.new(options)
190
- EY::Server.load_all_from_array(assemble_instance_hashes(config))
190
+ config = EY::Serverside::Deploy::Configuration.new(options)
191
+ EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
191
192
 
192
- invoke :propagate
193
+ invoke :propagate
193
194
 
194
- EY::Deploy.new(config).restart_with_maintenance_page
195
- end
195
+ EY::Serverside::Deploy.new(config).restart_with_maintenance_page
196
+ end
196
197
 
197
- desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
198
- def install_bundler(version)
199
- egrep_escaped_version = version.gsub(/\./, '\.')
200
- # the grep "bundler " is so that gems like bundler08 don't get
201
- # their versions considered too
202
- #
203
- # the [,$] is to stop us from looking for e.g. 0.9.2, seeing
204
- # 0.9.22, and mistakenly thinking 0.9.2 is there
205
- has_bundler_cmd = "gem list bundler | grep \"bundler \" | egrep -q '#{egrep_escaped_version}[,)]'"
206
-
207
- unless system(has_bundler_cmd)
208
- system("gem install bundler -q --no-rdoc --no-ri -v '#{version}'")
198
+ desc "install_bundler [VERSION]", "Make sure VERSION of bundler is installed (in system ruby)"
199
+ def install_bundler(version)
200
+ egrep_escaped_version = version.gsub(/\./, '\.')
201
+ # the grep "bundler " is so that gems like bundler08 don't get
202
+ # their versions considered too
203
+ #
204
+ # the [,$] is to stop us from looking for e.g. 0.9.2, seeing
205
+ # 0.9.22, and mistakenly thinking 0.9.2 is there
206
+ has_bundler_cmd = "gem list bundler | grep \"bundler \" | egrep -q '#{egrep_escaped_version}[,)]'"
207
+
208
+ unless system(has_bundler_cmd)
209
+ system("gem install bundler -q --no-rdoc --no-ri -v '#{version}'")
210
+ end
209
211
  end
210
- end
211
212
 
212
- desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{VERSION}."
213
- def propagate
214
- config = EY::Deploy::Configuration.new
215
- gem_filename = "engineyard-serverside-#{VERSION}.gem"
216
- local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
217
- remote_gem_file = File.join(Dir.tmpdir, gem_filename)
218
- gem_binary = File.join(Gem.default_bindir, 'gem')
219
-
220
- barrier(*(EY::Server.all.find_all do |server|
221
- !server.local? # of course this machine has it
222
- end.map do |server|
223
- need_later do
224
- egrep_escaped_version = VERSION.gsub(/\./, '\.')
225
- # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
226
- # 0.5.11, and mistakenly thinking 0.5.1 is there
227
- has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
228
-
229
- if !server.run(has_gem_cmd) # doesn't have this exact version
230
- puts "~> Installing engineyard-serverside on #{server.hostname}"
231
-
232
- system(Escape.shell_command([
233
- 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
234
- "-o", "StrictHostKeyChecking=no",
235
- local_gem_file,
236
- "#{config.user}@#{server.hostname}:#{remote_gem_file}",
237
- ]))
238
- server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
213
+ desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{EY::Serverside::VERSION}."
214
+ def propagate
215
+ config = EY::Serverside::Deploy::Configuration.new
216
+ gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
217
+ local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
218
+ remote_gem_file = File.join(Dir.tmpdir, gem_filename)
219
+ gem_binary = File.join(Gem.default_bindir, 'gem')
220
+
221
+ barrier(*(EY::Serverside::Server.all.find_all do |server|
222
+ !server.local? # of course this machine has it
223
+ end.map do |server|
224
+ need_later do
225
+ egrep_escaped_version = EY::Serverside::VERSION.gsub(/\./, '\.')
226
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
227
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
228
+ has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
229
+
230
+ if !server.run(has_gem_cmd) # doesn't have this exact version
231
+ puts "~> Installing engineyard-serverside on #{server.hostname}"
232
+
233
+ system(Escape.shell_command([
234
+ 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
235
+ "-o", "StrictHostKeyChecking=no",
236
+ local_gem_file,
237
+ "#{config.user}@#{server.hostname}:#{remote_gem_file}",
238
+ ]))
239
+ server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
240
+ end
239
241
  end
240
- end
241
- end))
242
- end
242
+ end))
243
+ end
243
244
 
244
- private
245
+ private
245
246
 
246
- def assemble_instance_hashes(config)
247
- options[:instances].collect { |hostname|
248
- { :hostname => hostname,
249
- :roles => options[:instance_roles][hostname].to_s.split(','),
250
- :name => options[:instance_names][hostname],
251
- :user => config.user,
247
+ def assemble_instance_hashes(config)
248
+ options[:instances].collect { |hostname|
249
+ { :hostname => hostname,
250
+ :roles => options[:instance_roles][hostname].to_s.split(','),
251
+ :name => options[:instance_names][hostname],
252
+ :user => config.user,
253
+ }
252
254
  }
253
- }
255
+ end
254
256
  end
255
257
  end
256
258
  end