Module: TorqueBox::DeployUtils

Defined in:
lib/torquebox/deploy_utils.rb

Class Method Summary collapse

Class Method Details

.archive_name(root = Dir.pwd) ⇒ Object



106
107
108
# File 'lib/torquebox/deploy_utils.rb', line 106

def archive_name(root = Dir.pwd)
  normalize_archive_name( File.basename( root || Dir.pwd ) )
end

.basic_deployment_descriptor(options = {}) ⇒ Object



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/torquebox/deploy_utils.rb', line 288

def basic_deployment_descriptor(options = {})
  env = options[:env] || options['env']
  env ||= defined?(RACK_ENV) ? RACK_ENV : ENV['RACK_ENV']
  env ||= defined?(::Rails) && Rails.respond_to?(:env) ? ::Rails.env : ENV['RAILS_ENV']

  root = options[:root] || options['root'] || Dir.pwd
  context_path = options[:context_path] || options['context_path']

  d = {}
  d['application'] = {}
  d['application']['root'] = root
  d['environment'] = {}
  d['environment']['RACK_ENV'] = env.to_s if env

  if context_path
    d['web'] = {}
    d['web']['context'] = context_path
  end

  d
end

.check_opt_torqueboxObject



119
120
121
122
# File 'lib/torquebox/deploy_utils.rb', line 119

def check_opt_torquebox
  raise "TorqueBox not installed in #{opt_torquebox}" unless ( File.exist?( opt_torquebox ) )
  puts "TorqueBox install OK: #{opt_torquebox}"
end

.check_serverObject



114
115
116
117
# File 'lib/torquebox/deploy_utils.rb', line 114

def check_server
  raise "#{jboss_home} doesn't appear to be a valid TorqueBox install" unless File.exist?( torquebox_modules_dir )
  puts "TorqueBox installation appears OK"
end

.cluster_config_fileObject



77
78
79
# File 'lib/torquebox/deploy_utils.rb', line 77

def cluster_config_file
  eap? ? "standalone-full-ha.xml" : "standalone-ha.xml"
end

.config_dirObject



69
70
71
# File 'lib/torquebox/deploy_utils.rb', line 69

def config_dir
  File.join("#{server_dir}","configuration")
end

.create_archive(opts = {}) ⇒ Object

name: (string) what to call the resulting knob file app_dir: (string) where the application to be packaged is dest_dir: (string) where to put the resulting knob file excludes: (string) string or regex of files to exclude from the archive precompile_assets: (boolean) whether or not to precompile assets. this is rails-specific. package_gems: (boolean) whether or not to install all bundle gems to vendor/bundle (this

is rather convenient as it means that you don't have to run bundle
install on your production servers)

package_without: (array) all the bundler groups to run bundle install without (cuts down

on package size by snipping out potentially inappropriate
dependencies for a production environment).


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/torquebox/deploy_utils.rb', line 191

def create_archive(opts = {})
  archive = normalize_archive_name( find_option( opts, 'name' ) || archive_name )
  app_dir = find_option( opts, 'app_dir' ) || Dir.pwd
  dest_dir = find_option( opts, 'dest_dir' ) || Dir.pwd
  excludes = find_option( opts, 'exclude' ) || ""
  should_precompile_assets = find_option( opts, 'precompile_assets' ) == true
  should_package_gems = find_option( opts, 'package_gems' ) == true
  package_without = find_option( opts, 'package_without' ) || Array.new

  if should_precompile_assets
    precompile_assets( app_dir )
    raise 'Error precompiling assets' unless $? == 0
  end

  archive_path = File.join( dest_dir, archive )
  archive_proc = lambda { create_knob_archive( app_dir, archive_path, excludes ) }

  if should_package_gems
    package_gems( app_dir, package_without ) {
      raise 'Error packaging gems' unless $? == 0
      archive_proc.call
    }
  else
    archive_proc.call
  end

  archive_path
end

.create_knob_archive(app_dir, archive_path, excludes) ⇒ Object



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/torquebox/deploy_utils.rb', line 257

def create_knob_archive(app_dir, archive_path, excludes)
  default_skip_files = %w{ ^log/ ^tmp/ ^test/ ^spec/ ^[^/]*\.knob$ vendor/.*cache/.*\.gem$ }
  opts_skip_files = excludes.split( /,/ ).map { |r| "^[^/]*#{r}" }
  skip_files = default_skip_files + opts_skip_files

  Dir.chdir( app_dir ) do
    include_files = []
    Dir[ "**/**", ".bundle/**/**" ].each do |entry|
      unless File.directory?( entry ) || skip_files.any? { |regex| entry.match( regex ) }
        include_files << '"' + entry.to_s + '"'
      end
    end

    includes = Tempfile.new( "include-files" )
    includes.write( include_files.join( "\n" ) )
    includes.flush

    cmd = "jar cvf \"#{archive_path}\" @#{includes.path}"

    run_command( cmd )
    includes.close( true )
  end
end

TODO: This is not windows friendly



348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/torquebox/deploy_utils.rb', line 348

def create_symlink
  unless File.exist? opt_dir
    success = true
    if !File.writable?( sys_root )
      puts "Cannot write to #{sys_root}. Please ensure #{opt_torquebox} points to your torquebox installation."
      success = false
    else
      puts "Creating #{opt_dir}"
      Dir.new( opt_dir )
    end
  end

  unless File.exist?( opt_torquebox )
    if File.writable?( opt_dir )
      puts "Linking #{opt_torquebox} to #{torquebox_home}"
      File.symlink( torquebox_home, opt_torquebox )
    else
      puts "Cannot link #{opt_torquebox} to #{torquebox_home}"
      success = false
    end
  end
  success
end

.deploy_archive(opts = {}) ⇒ Object



321
322
323
324
325
326
327
328
329
# File 'lib/torquebox/deploy_utils.rb', line 321

def deploy_archive(opts = {})
  name = normalize_archive_name( find_option( opts, 'name' ) || archive_name )
  archive_path = find_option( opts, 'archive_path' ) || File.join( Dir.pwd, name )
  dest_dir = find_option( opts, 'dest_dir' ) || deploy_dir
  FileUtils.cp( archive_path, dest_dir )
  archive = File.basename( archive_path )
  FileUtils.touch( dodeploy_file( archive, dest_dir ) )
  [archive, dest_dir]
end

.deploy_dirObject



90
91
92
# File 'lib/torquebox/deploy_utils.rb', line 90

def deploy_dir
  File.join( "#{server_dir}", "deployments" )
end

.deploy_yaml(deployment_descriptor, opts = {}) ⇒ Object



310
311
312
313
314
315
316
317
318
319
# File 'lib/torquebox/deploy_utils.rb', line 310

def deploy_yaml(deployment_descriptor, opts = {})
  name = normalize_yaml_name( find_option( opts, 'name' ) || deployment_name(opts[:root] || opts['root']) )
  dest_dir = opts[:dest_dir] || opts['dest_dir'] || deploy_dir
  deployment = File.join( dest_dir, name )
  File.open( deployment, 'w' ) do |file|
    YAML.dump( deployment_descriptor, file )
  end
  FileUtils.touch( dodeploy_file( name, dest_dir ) )
  [name, dest_dir]
end

.deployed_file(name, deploy_dir = DeployUtils.deploy_dir) ⇒ Object



335
336
337
# File 'lib/torquebox/deploy_utils.rb', line 335

def deployed_file(name, deploy_dir = DeployUtils.deploy_dir)
  File.join( deploy_dir, "#{name}" ) + ".deployed"
end

.deployers_dirObject



94
95
96
# File 'lib/torquebox/deploy_utils.rb', line 94

def deployers_dir
  raise "Deployers directory no longer relevant"
end

.deployment_descriptorsObject



461
462
463
# File 'lib/torquebox/deploy_utils.rb', line 461

def deployment_descriptors
  Dir.glob( "#{deploy_dir}/*-knob.yml" ).collect { |d| File.basename( d ) }
end

.deployment_name(root = Dir.pwd) ⇒ Object



110
111
112
# File 'lib/torquebox/deploy_utils.rb', line 110

def deployment_name(root = Dir.pwd)
  normalize_yaml_name( File.basename( root || Dir.pwd ) )
end

.deployment_statusObject



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# File 'lib/torquebox/deploy_utils.rb', line 465

def deployment_status
  applications = {}
  deployment_descriptors.each do | descriptor |
    descriptor_path = File.join( deploy_dir, descriptor )
    appname = descriptor.sub( /\-knob.yml/, '' )
    applications[appname] = {}
    applications[appname][:descriptor] = descriptor_path
    applications[appname][:status] = case
                                     when File.exists?("#{descriptor_path}.dodeploy")
                                       "awaiting deployment"
                                     when File.exists?("#{descriptor_path}.deployed")
                                       "deployed"
                                     when File.exists?("#{descriptor_path}.failed")
                                       "deployment failed"
                                     else "unknown: try running `torquebox deploy #{appname}`"
                                     end
  end
  applications
end

.dodeploy_file(name, deploy_dir = DeployUtils.deploy_dir) ⇒ Object



331
332
333
# File 'lib/torquebox/deploy_utils.rb', line 331

def dodeploy_file(name, deploy_dir = DeployUtils.deploy_dir)
  File.join( deploy_dir, "#{name}" ) + ".dodeploy"
end

.eap?Boolean



81
82
83
84
# File 'lib/torquebox/deploy_utils.rb', line 81

def eap?
  index_html = File.join( jboss_home, 'welcome-content', 'index.html' )
  File.exists?( index_html ) && File.read( index_html ) =~ /EAP 6/
end

.exec_command(cmd) ⇒ Object

Used when we want to effectively replace this process with the given command. On Windows this does call Kernel#exec but on everything else we just delegate to fake_exec.

This is mainly so CTRL+C, STDIN, STDOUT, and STDERR work as expected across all operating systems.



388
389
390
# File 'lib/torquebox/deploy_utils.rb', line 388

def exec_command(cmd)
  windows? ? exec(cmd) : fake_exec(cmd)
end

.fake_exec(cmd) ⇒ Object

Used to run a command as a subprocess



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/torquebox/deploy_utils.rb', line 393

def fake_exec(cmd)
  exiting = false
  IO.popen4(cmd) do |pid, stdin, stdout, stderr|
    stdout.sync = true
    stderr.sync = true
    trap("INT") do
      exiting = true
      stdin.close
      puts "caught SIGINT, shutting down"
      `taskkill /F /T /PID #{pid}` if windows?
    end

    # Don't join on stdin since interrupting a blocking read on
    # JRuby is pretty tricky
    Thread.new(stdin) { |stdin_io|
      begin
        until exiting
          stdin_io.write(readpartial(STDIN))
          stdin_io.flush
        end
      rescue Errno::EBADF, IOError
      end
    }

    # Join on stdout/stderr since they'll be closed
    # automatically once TorqueBox exits
    [ Thread.new(stdout) { |stdout_io|
        begin
          while true
            STDOUT.write(readpartial(stdout_io))
          end
        rescue EOFError
        end
      },

      Thread.new(stderr) { |stderr_io|
        begin
          while true
            STDERR.write(readpartial(stderr_io))
          end
        rescue EOFError
        end
      }
    ].each( &:join)
  end

end

.find_option(opt, key) ⇒ Object



449
450
451
# File 'lib/torquebox/deploy_utils.rb', line 449

def find_option(opt, key)
  opt[key.to_sym] || opt[key] || ENV[key] || ENV[key.upcase]
end

.freeze_gems(app_dir = Dir.pwd) ⇒ Object



281
282
283
284
285
286
# File 'lib/torquebox/deploy_utils.rb', line 281

def freeze_gems(app_dir = Dir.pwd)
  Dir.chdir( app_dir ) do
    jruby_command( '-S bundle package' )
    jruby_command( '-S bundle install --local --path vendor/bundle' )
  end
end

.is_deployed?(appname = deployment_name) ⇒ Boolean



148
149
150
# File 'lib/torquebox/deploy_utils.rb', line 148

def is_deployed?( appname = deployment_name )
  File.exists?( File.join(deploy_dir, appname) )
end

.jboss_confObject



47
48
49
# File 'lib/torquebox/deploy_utils.rb', line 47

def jboss_conf
  ENV['TORQUEBOX_CONF'] || ENV['JBOSS_CONF'] || 'standalone'
end

.jboss_homeObject



30
31
32
33
34
35
# File 'lib/torquebox/deploy_utils.rb', line 30

def jboss_home
  jboss_home = File.expand_path(ENV['JBOSS_HOME']) if ENV['JBOSS_HOME']
  jboss_home ||= File.join(File.expand_path(ENV['TORQUEBOX_HOME']), "jboss") if ENV['TORQUEBOX_HOME']
  raise "$JBOSS_HOME is not set" unless jboss_home
  return jboss_home
end

.jruby_command(cmd) ⇒ Object



372
373
374
375
376
# File 'lib/torquebox/deploy_utils.rb', line 372

def jruby_command(cmd)
  jruby = File.join( RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] )
  RUBY_VERSION =~ /^1\.9\./ ? jruby << " --1.9" : jruby << " --1.8"
  run_command( "#{jruby} #{cmd}" )
end

.jruby_opts_propertiesObject



485
486
487
488
489
490
491
# File 'lib/torquebox/deploy_utils.rb', line 485

def jruby_opts_properties
  jruby_opts = ENV['JRUBY_OPTS']
  return "" if jruby_opts.nil?
  # Only convert -Xa.b, -Xa.b.c, -Xa.b.c.d style options to properties
  properties = jruby_opts.scan(/-X(\w+\..+?)(\s|$)/)
  properties.map { |matches| "-Djruby.#{matches.first}" }.join(' ')
end

.modules_dirObject



98
99
100
# File 'lib/torquebox/deploy_utils.rb', line 98

def modules_dir
  File.join( jboss_home, 'modules' )
end

.normalize_archive_name(name) ⇒ Object



457
458
459
# File 'lib/torquebox/deploy_utils.rb', line 457

def normalize_archive_name(name)
  name[-5..-1] == '.knob' ? name : name + '.knob'
end

.normalize_yaml_name(name) ⇒ Object



453
454
455
# File 'lib/torquebox/deploy_utils.rb', line 453

def normalize_yaml_name(name)
  name[-9..-1] == '-knob.yml' ? name : name + '-knob.yml'
end

.opt_dirObject

Used by upstart and launchd



57
58
59
# File 'lib/torquebox/deploy_utils.rb', line 57

def opt_dir
  File.join( sys_root, 'opt' )
end

.opt_torqueboxObject



61
62
63
# File 'lib/torquebox/deploy_utils.rb', line 61

def opt_torquebox
  File.join( opt_dir, 'torquebox' )
end

.package_gems(app_dir, package_without) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/torquebox/deploy_utils.rb', line 226

def package_gems(app_dir, package_without)
  # note - this is used instead of freeze gems because it
  # should cause the archive to capture .bundle/config,
  # thereby forcing the app to use the bundled gems. we delete
  # the deployment configuration for rubygems afterward
  bundler_config = File.join( app_dir, '.bundle/config' )
  if File.exists?( bundler_config )
    old_config = File.read( bundler_config )
  else
    old_config = nil
  end
  cmd = %w{-S bundle install --local --deployment}
  unless package_without.empty?
    cmd << '--without'
    cmd << package_without
  end
  Dir.chdir( app_dir ) do
    jruby_command( '-S bundle package' )
    jruby_command( cmd.flatten.join(' ') )
  end
  yield if block_given?
ensure
  if File.exists?( bundler_config )
    if old_config
      File.open( bundler_config, 'w' ) { |io| io.write( old_config ) }
    else
      File.delete( bundler_config ) # there wasn't originally a config file there
    end
  end
end

.precompile_assets(app_dir) ⇒ Object



220
221
222
223
224
# File 'lib/torquebox/deploy_utils.rb', line 220

def precompile_assets(app_dir)
  Dir.chdir( app_dir ) do
    jruby_command( "-S rake assets:precompile" )
  end
end

.properties_dirObject



86
87
88
# File 'lib/torquebox/deploy_utils.rb', line 86

def properties_dir
  config_dir
end

.readpartial(stream) ⇒ Object



441
442
443
# File 'lib/torquebox/deploy_utils.rb', line 441

def readpartial(stream)
  windows? ? stream.read(1) : stream.readpartial(1024)
end

.rubylib_with_bundler(load_path) ⇒ Object



509
510
511
512
513
514
515
516
# File 'lib/torquebox/deploy_utils.rb', line 509

def rubylib_with_bundler(load_path)
  bundler_load_paths = load_path.select { |p| p.include?('bundler') }
  rubylib = (ENV['RUBYLIB'] || '').dup # ENV strings are frozen
  unless rubylib.empty? || bundler_load_paths.empty?
    rubylib << ':'
  end
  rubylib << bundler_load_paths.join(':')
end

.run_command(cmd) ⇒ Object



378
379
380
# File 'lib/torquebox/deploy_utils.rb', line 378

def run_command(cmd)
  puts `#{cmd} 2>&1`
end

.run_command_line(opts = {}) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/torquebox/deploy_utils.rb', line 128

def run_command_line(opts={})
  options = ENV['JBOSS_OPTS'] || ''
  config_file = opts[:clustered] ? cluster_config_file : standalone_config_file
  options = "#{options} --server-config=#{config_file}"
  options = "#{options} -Dorg.torquebox.web.http.maxThreads=#{opts[:max_threads]}" if opts[:max_threads]
  options = "#{options} -b #{opts[:bind_address]}" if opts[:bind_address]
  options = "#{options} -Djboss.socket.binding.port-offset=#{opts[:port_offset]}" if opts[:port_offset]
  options = "#{options} -Dhttp.port=#{opts[:port]}" if opts[:port]
  options = "#{options} -Djboss.node.name=#{opts[:node_name]}" if opts[:node_name]
  options = "#{options} -Djboss.server.data.dir=#{opts[:data_directory]}" if opts[:data_directory]
  options = "#{options} #{opts[:pass_through]}" if opts[:pass_through]
  if windows?
    cmd = "#{jboss_home.gsub('/', '\\')}\\bin\\standalone.bat"
  else
    cmd = "/bin/sh bin/standalone.sh"
  end
  puts "#{cmd} #{options}" # Make it clear to the user what is being passed through to JBoss AS
  [cmd, options]
end

.run_server(options = {}) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/torquebox/deploy_utils.rb', line 152

def run_server(options={})
  puts "[WARNING] #{deployment_name} has not been deployed. Starting TorqueBox anyway." unless ( is_deployed? )

  Dir.chdir(jboss_home) do
    # don't send the gemfile from the current app, instead let
    # bundler suss it out itself for each deployed
    # app. Otherwise, they'll end up sharing this Gemfile, which
    # is probably not what we want.
    ENV.delete('BUNDLE_GEMFILE')
    # If called from rake within a rails app, bundler will try
    # to init itself via RUBYOPT, which we don't want
    ENV.delete('RUBYOPT')
    # Ensure bundler gets on the Ruby load path of the booted
    # TorqueBox instance if it's on the load path of this Ruby
    # runtime so we can find bundler and our own gems if used
    # with bundle install --deployment
    ENV['RUBYLIB'] = rubylib_with_bundler($:)

    options[:jvm_options] ||= ''
    options[:jvm_options] << " #{jruby_opts_properties}"
    options[:jvm_options] << " #{strip_jvm_properties_from_jruby_opts}"

    set_java_opts(options[:jvm_options].strip)
    print_server_config(options[:clustered])
    exec_command(run_command_line(options).join(' '))
  end
end

.server_dirObject



65
66
67
# File 'lib/torquebox/deploy_utils.rb', line 65

def server_dir
  File.join("#{jboss_home}","#{jboss_conf}" )
end

.set_java_opts(options) ⇒ Object



124
125
126
# File 'lib/torquebox/deploy_utils.rb', line 124

def set_java_opts(options)
  ENV['APPEND_JAVA_OPTS'] = options
end

.standalone_config_fileObject



73
74
75
# File 'lib/torquebox/deploy_utils.rb', line 73

def standalone_config_file
  eap? ? "standalone-full.xml" : "standalone.xml"
end

.strip_jvm_properties_from_jruby_optsObject



493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/torquebox/deploy_utils.rb', line 493

def strip_jvm_properties_from_jruby_opts
  jruby_opts = ENV['JRUBY_OPTS']
  return '' if jruby_opts.nil?
  jvm_properties = []
  properties = jruby_opts.split(' ')
  properties.each do |property|
    if property =~ /^-J.+/
      jvm_properties << property.sub(/-J/, '')
      ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].sub(property, '')
    end
  end
  # get rid of any leftover spaces
  ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].split(' ').join(' ')
  jvm_properties.join(' ')
end

.sys_rootObject

TODO: This is not windows friendly, is it?



52
53
54
# File 'lib/torquebox/deploy_utils.rb', line 52

def sys_root
  '/'
end

.torquebox_homeObject



37
38
39
40
41
42
43
44
45
# File 'lib/torquebox/deploy_utils.rb', line 37

def torquebox_home
  torquebox_home = nil
  if ( ENV['TORQUEBOX_HOME'] )
    torquebox_home = File.expand_path(ENV['TORQUEBOX_HOME'])
  else
    torquebox_home = TorqueBox::Server.torquebox_home
  end
  torquebox_home
end

.torquebox_modules_dirObject



102
103
104
# File 'lib/torquebox/deploy_utils.rb', line 102

def torquebox_modules_dir
  File.join( modules_dir, 'org', 'torquebox' )
end

.undeploy_archive(opts = {}) ⇒ Object



339
340
341
# File 'lib/torquebox/deploy_utils.rb', line 339

def undeploy_archive(opts = {})
  undeploy( normalize_archive_name( find_option( opts, 'name' ) || archive_name( opts[:root] ) ), opts )
end

.undeploy_yaml(opts = {}) ⇒ Object



343
344
345
# File 'lib/torquebox/deploy_utils.rb', line 343

def undeploy_yaml(opts = {})
  undeploy( normalize_yaml_name( find_option( opts, 'name' ) || deployment_name( opts[:root] ) ), opts )
end

.windows?Boolean



445
446
447
# File 'lib/torquebox/deploy_utils.rb', line 445

def windows?
  RbConfig::CONFIG['host_os'] =~ /mswin/
end