Class: Sunshine::App

Inherits:
Object
  • Object
show all
Defined in:
lib/sunshine/app.rb

Overview

App objects are the core of Sunshine deployment. The Sunshine paradygm is to construct an app object, and run custom deploy code by passing a block to its deploy method. The following is the simplest way to instantiate an app:

app = Sunshine::App.new :remote_shells => "deploy_server.com"

By default, Sunshine will look in the pwd for scm information and will extract the app’s name. The default root deploy path is specified by Sunshine.web_directory, in this case: “/srv/http/app_name”.

More complex setups may look like the following:

someserver = Sunshine::RemoteShell.new "[email protected]",
                                       :roles => [:web, :app]

myapprepo = Sunshine::SvnRepo.new "svn://my_repo/myapp/tags/release"

app = Sunshine::App.new :name          => 'myapp',
                        :repo          => myapprepo,
                        :root_path     => '/usr/local/myapp',
                        :remote_shells => someserver

Note: The instantiation options :repo and :remote_shells support the same data format that their respective initialize methods support. The :remote_shells option also supports arrays of remote shell instance arguments.

Once an App is instantiated it can be manipulated in a variety of ways, including deploying it:

app.deploy do |app|

  app_server = Sunshine::Rainbows.new app, :port => 3000
  web_server = Sunshine::Nginx.new app, :point_to => app_server

  app_server.setup
  web_server.setup
end

The constructor also supports reading multi-env configs fom a yaml file, which can also be the deploy script’s DATA, to allow for concise, encapsulated deploy files:

# Load from an explicit yaml file:
Sunshine::App.new "path/to/config.yml"

# Load from the yaml in the script file's DATA:
Sunshine::App.new

Yaml files must define settings on a per-environment basis. The default environment will be used as a base for all other environments:

#config.yml:
---
:default:
  :repo:
    :type: :svn
    :url:  http://subversion/repo/tags/release-001
  :remote_shells: dev.myserver.com

qa:
  :remote_shells:
    - qa1.myserver.com
    - qa2.myserver.com

qa_special:
  :inherits: qa
  :root_path: /path/to/application
  :deploy_env: qa

By default, App will get the deploy_env value from Sunshine.deploy_env, but it may also be passed in explicitely as an option.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_file = Sunshine::DATA, options = {}) ⇒ App

App instantiation can be done in several ways:

App.new instantiation_hash
App.new "path/to/config.yml", optional_extra_hash
App.new #=> will attempt to load ruby's file DATA as yaml

Options supported are:

:deploy_env

String - specify the env to deploy with; defaults to

Sunshine#deploy_env.

:deploy_name

String - if you want to specify a name for your deploy and

checkout directory (affects the checkout_path); defaults to Time.now.to_i.

:remote_checkout

Boolean - when true, will checkout the codebase

directly from the deploy servers; defaults to false.

:remote_shells

String|Array|Sunshine::Shell - the shell(s) to use for

deployment. Accepts any single instance or array of a Sunshine::Shell type instance or Sunshine::Shell instantiator-friendly arguments.

:repo

Hash|Sunshine::Repo - the scm and repo to use for deployment.

Accepts any hash that can be passed to Sunshine::Repo::new_of_type or any Sunshine::Repo object.

:root_path

String - the absolute path the deployed application

should live in; defaults to “#Sunshine.web_directory/#@name”.

:shell_env

Hash - environment variables to add to deploy shells.

:sudo

true|false|nil|String - which sudo value should be assigned to

deploy shells; defaults to Sunshine#sudo. For more information on using sudo, see the Using Permissions section in README.txt.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/sunshine/app.rb', line 130

def initialize config_file=Sunshine::DATA, options={}
  options, config_file = config_file, Sunshine::DATA if Hash === config_file


  @deploy_env = options[:deploy_env] || Sunshine.deploy_env

  binder  = Binder.new self
  binder.import_hash options
  binder.forward :deploy_env

  options = config_from_file(config_file, binder.get_binding).merge options


  @repo        = repo_from_config options[:repo]

  @name        = options[:name] || @repo.name

  @deploy_name = options[:deploy_name] || Time.now.to_i.to_s

  @deploy_env = options[:deploy_env] if options[:deploy_env]

  set_deploy_paths options[:root_path]

  @server_apps = server_apps_from_config options[:remote_shells]

  @remote_checkout = options[:remote_checkout] || Sunshine.remote_checkouts?

  self.sudo = options[:sudo] || Sunshine.sudo

  @shell_env = {
    "RACK_ENV"  => @deploy_env.to_s,
    "RAILS_ENV" => @deploy_env.to_s
  }
  shell_env options[:shell_env]

  @post_user_lambdas = []

  @on_sigint = @on_exception = nil
end

Instance Attribute Details

#checkout_pathObject (readonly)

Returns the value of attribute checkout_path.



92
93
94
# File 'lib/sunshine/app.rb', line 92

def checkout_path
  @checkout_path
end

#current_pathObject (readonly)

Returns the value of attribute current_path.



92
93
94
# File 'lib/sunshine/app.rb', line 92

def current_path
  @current_path
end

#deploy_envObject (readonly)

Returns the value of attribute deploy_env.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def deploy_env
  @deploy_env
end

#deploy_nameObject (readonly)

Returns the value of attribute deploy_name.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def deploy_name
  @deploy_name
end

#deploys_pathObject (readonly)

Returns the value of attribute deploys_path.



92
93
94
# File 'lib/sunshine/app.rb', line 92

def deploys_path
  @deploys_path
end

#log_pathObject (readonly)

Returns the value of attribute log_path.



93
94
95
# File 'lib/sunshine/app.rb', line 93

def log_path
  @log_path
end

#nameObject (readonly)

Returns the value of attribute name.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def name
  @name
end

#remote_checkoutObject

Returns the value of attribute remote_checkout.



94
95
96
# File 'lib/sunshine/app.rb', line 94

def remote_checkout
  @remote_checkout
end

#repoObject (readonly)

Returns the value of attribute repo.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def repo
  @repo
end

#root_pathObject (readonly)

Returns the value of attribute root_path.



92
93
94
# File 'lib/sunshine/app.rb', line 92

def root_path
  @root_path
end

#scripts_pathObject (readonly)

Returns the value of attribute scripts_path.



93
94
95
# File 'lib/sunshine/app.rb', line 93

def scripts_path
  @scripts_path
end

#server_appsObject (readonly)

Returns the value of attribute server_apps.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def server_apps
  @server_apps
end

#shared_pathObject (readonly)

Returns the value of attribute shared_path.



93
94
95
# File 'lib/sunshine/app.rb', line 93

def shared_path
  @shared_path
end

#sudoObject

Returns the value of attribute sudo.



91
92
93
# File 'lib/sunshine/app.rb', line 91

def sudo
  @sudo
end

Class Method Details

.deploy(*args, &block) ⇒ Object

Initialize and deploy an application in a single step. Takes any arguments supported by the constructor.



84
85
86
87
88
# File 'lib/sunshine/app.rb', line 84

def self.deploy(*args, &block)
  app = new(*args)
  app.deploy(&block)
  app
end

Instance Method Details

#add_shell_paths(*paths) ⇒ Object

Add paths the the shell $PATH env on all app shells.



435
436
437
438
439
440
# File 'lib/sunshine/app.rb', line 435

def add_shell_paths(*paths)
  path = @shell_env["PATH"] || "$PATH"
  paths << path

  shell_env "PATH" => paths.join(":")
end

#add_to_crontab(name, cronjob, options = nil) ⇒ Object

Add a command to the crontab to be generated remotely:

add_to_crontab "reboot", "@reboot /path/to/app/start", :role => :web

Note: This method will append jobs to already existing cron jobs for this application and job name, including previous deploys.



450
451
452
453
454
# File 'lib/sunshine/app.rb', line 450

def add_to_crontab name, cronjob, options=nil
  each options do |server_app|
    server_app.crontab[name] << cronjob
  end
end

#add_to_script(name, script, options = nil) ⇒ Object

Add a command to a control script to be generated remotely:

add_to_script :start, "do this on start"
add_to_script :start, "start_mail", :role => :mail


476
477
478
479
480
# File 'lib/sunshine/app.rb', line 476

def add_to_script name, script, options=nil
  each options do |server_app|
    server_app.scripts[name] << script
  end
end

#after_user_script(&block) ⇒ Object

Define lambdas to run right after the user’s yield.

app.after_user_script do |app|
  ...
end


489
490
491
# File 'lib/sunshine/app.rb', line 489

def after_user_script &block
  @post_user_lambdas << block
end

#any_connected?(options = nil) ⇒ Boolean

Check if any server apps are connected and returns a boolean. Supports any App#find options.

Returns:

  • (Boolean)


214
215
216
217
218
219
220
# File 'lib/sunshine/app.rb', line 214

def any_connected? options=nil
  each options do |server_app|
    return true if server_app.shell.connected?
  end

  false
end

#build_control_scripts(options = nil) ⇒ Object

Creates and uploads all control scripts for the application. To add to, or define a control script, see App#add_to_script.



498
499
500
501
502
# File 'lib/sunshine/app.rb', line 498

def build_control_scripts options=nil
  with_server_apps options,
    :msg  => "Building control scripts",
    :send => :build_control_scripts
end

#build_crontab(options = nil) ⇒ Object

Writes the crontab on all or selected server apps. To add or remove from the crontab, see App#add_to_crontab and App#remove_cronjob.



510
511
512
513
514
515
# File 'lib/sunshine/app.rb', line 510

def build_crontab options=nil
  with_server_apps options,
    :msg => "Building the crontab" do |server_app|
    server_app.crontab.write!
  end
end

#build_deploy_info_file(options = nil) ⇒ Object

Creates a yaml file with deploy information. To add custom information to the info file, use the app’s info hash attribute:

app.info[:key] = "some value"


523
524
525
526
527
# File 'lib/sunshine/app.rb', line 523

def build_deploy_info_file options=nil
  with_server_apps options,
    :msg  => "Creating info file",
    :send => :build_deploy_info_file
end

#build_erb(erb_file, custom_binding = nil) ⇒ Object

Parse an erb file and return the newly created string. Default binding is the app’s binding.



534
535
536
537
# File 'lib/sunshine/app.rb', line 534

def build_erb erb_file, custom_binding=nil
  str = File === erb_file ? erb_file.read : File.read(erb_file)
  ERB.new(str, nil, '-').result(custom_binding || binding)
end

#call(cmd, options = nil, &block) ⇒ Object

Call a command on specified server apps. Returns an array of responses. Supports any App#find and Shell#call options:

app.call "ls -1", :sudo => true, :host => "web.app.com"
#=> [".\n..\ndir1", ".\n..\ndir1", ".\n..\ndir2"]


177
178
179
180
181
# File 'lib/sunshine/app.rb', line 177

def call cmd, options=nil, &block
  with_server_apps options, :msg => "Running #{cmd}" do |server_app|
    server_app.shell.call cmd, options, &block
  end
end

#checkout_codebase(options = nil) ⇒ Object

Checks out the app’s codebase to one or all deploy servers. Supports all App#find options, plus:

:copy

Bool - Checkout locally and rsync; defaults to false.

:exclude

String|Array - Exclude the specified paths during

a deploy via copy.



577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
# File 'lib/sunshine/app.rb', line 577

def checkout_codebase options=nil
  copy_option = options[:copy]          if options
  exclude     = options.delete :exclude if options

  if RsyncRepo === @repo || (@remote_checkout && !copy_option)
    with_server_apps options,
      :msg  => "Checking out codebase (remotely)",
      :send => [:checkout_repo, @repo]

  else
    Sunshine.logger.info :app, "Checking out codebase (locally)" do

      tmp_path = File.join Sunshine::TMP_DIR, "#{@name}_checkout"
      scm_info = @repo.checkout_to tmp_path

      scm_info[:exclude] =
        [Sunshine.exclude_paths, exclude].flatten.compact

      with_server_apps options,
        :send => [:upload_codebase, tmp_path, scm_info]
    end
  end
end

#connect(options = nil) ⇒ Object

Start a persistant connection to app servers. Supports any App#find options.



188
189
190
191
192
193
194
# File 'lib/sunshine/app.rb', line 188

def connect options=nil
  Sunshine.logger.info :app, "Connecting..." do
    threaded_each options do |server_app|
      server_app.shell.connect
    end
  end
end

#connected?(options = nil) ⇒ Boolean

Check if all server apps are connected and returns a boolean. Supports any App#find options.

Returns:

  • (Boolean)


201
202
203
204
205
206
207
# File 'lib/sunshine/app.rb', line 201

def connected? options=nil
  each options do |server_app|
    return false unless server_app.shell.connected?
  end

  true
end

#console!Object

Starts an IRB console with the instance’s binding.



543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/sunshine/app.rb', line 543

def console!
  IRB.setup nil unless defined?(IRB::UnrecognizedSwitch)

  workspace = IRB::WorkSpace.new binding
  irb = IRB::Irb.new workspace

  irb.context.irb_name = "sunshine(#{@name})"
  irb.context.prompt_c = "%N:%03n:%i* "
  irb.context.prompt_i = "%N:%03n:%i> "
  irb.context.prompt_n = "%N:%03n:%i> "

  IRB.class_eval do
    @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
    @CONF[:MAIN_CONTEXT] = irb.context
  end

  #TODO: remove sigint trap when irb session is closed
  #trap("INT") do
  #  irb.signal_handle
  #end

  catch(:IRB_EXIT) do
    irb.eval_input
  end
end

#cronjob(name, cronjob, options = nil) ⇒ Object

Add a command to the crontab to be generated remotely:

cronjob "reboot", "@reboot /path/to/app/start", :role => :web

Note: This method will override already existing cron jobs for this application and job name, including previous deploys.



464
465
466
467
468
# File 'lib/sunshine/app.rb', line 464

def cronjob name, cronjob, options=nil
  each options do |server_app|
    server_app.crontab[name] = cronjob
  end
end

#deploy(options = nil) ⇒ Object

Deploy the application to deploy servers and call user’s post-deploy code. Supports any App#find options.

If the deploy fails or an exception is raised, it will attempt to run the Sunshine.exception_behavior, which is set to :revert by default. However, this is not true of ssh connection failures.

If the deploy is interrupted by a SIGINT, it will attempt to run the Sunshine.sigint_behavior, which is set to :revert by default.

The deploy method will stop the former deploy just before symlink and the passed block is run.

Once deployment is complete, the deploy method will attempt to run App#start! which will run any start script checked into App#scripts_path, or the start script that will have been generated by using Sunshine server setups.



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/sunshine/app.rb', line 254

def deploy options=nil

  state = {
    :success   => false,
    :stopped   => false,
    :symlinked => false
  }

  Sunshine.logger.info :app, "Beginning #{@name} deploy"

  with_session options do |app|

    interruptable state do
      raise DeployError, "No servers defined for #{@name}" if
        @server_apps.empty?

      make_app_directories
      checkout_codebase

      state[:stopped]   = true if stop
      state[:symlinked] = true if symlink_current_dir

      yield self if block_given?

      run_post_user_lambdas

      build_control_scripts
      build_deploy_info_file
      build_crontab

      register_as_deployed

      state[:success] = true if start! :force => true
    end

    remove_old_deploys if state[:success] rescue
      Sunshine.logger.error :app, "Could not remove old deploys"

    state[:success] &&= deployed?
  end

  Sunshine.logger.info :app, "Finished #{@name} deploy" if state[:success]
  state[:success]
end

#deploy_details(options = nil) ⇒ Object

Get a hash of deploy information for each server app. Post-deploy only.



606
607
608
609
610
611
612
613
614
# File 'lib/sunshine/app.rb', line 606

def deploy_details options=nil
  details = {}

  with_server_apps options, :msg => "Getting deploy info..." do |server_app|
    details[server_app.shell.host] = server_app.deploy_details
  end

  details
end

#deployed?(options = nil) ⇒ Boolean

Check if app has been deployed successfully by checking the name of the deploy on every app server with the instance’s deploy name.

Returns:

  • (Boolean)


621
622
623
624
625
626
627
628
# File 'lib/sunshine/app.rb', line 621

def deployed? options=nil
  with_server_apps options,
    :msg => "Checking deploy", :no_threads => true do |server_app|
    return false unless server_app.deployed?
  end

  true
end

#disconnect(options = nil) ⇒ Object

Disconnect from app servers. Supports any App#find options.



226
227
228
229
230
231
232
# File 'lib/sunshine/app.rb', line 226

def disconnect options=nil
  Sunshine.logger.info :app, "Disconnecting..." do
    threaded_each options do |server_app|
      server_app.shell.disconnect
    end
  end
end

#each(options = nil, &block) ⇒ Object

Iterate over each server app. Supports all App#find options. See Sunshine::ServerApp for more information.



635
636
637
638
# File 'lib/sunshine/app.rb', line 635

def each options=nil, &block
  server_apps = find options
  server_apps.each(&block)
end

#find(query = nil) ⇒ Object

Find server apps matching the passed requirements. Returns an array of server apps.

find :user => 'db'
find :host => 'someserver.com'
find :role => :web

The find method also supports passing arrays and will match any server app that matches any one condition:

find :user => ['root', 'john']

Returns all server apps who’s user is either ‘root’ or ‘john’.



654
655
656
657
658
659
660
661
662
663
664
665
# File 'lib/sunshine/app.rb', line 654

def find query=nil
  return @server_apps if query.nil? || query == :all

  @server_apps.select do |sa|
    next unless [*query[:user]].include? sa.shell.user if query[:user]
    next unless [*query[:host]].include? sa.shell.host if query[:host]

    next unless sa.has_roles?(query[:role], true)      if query[:role]

    true
  end
end

#handle_exception(exception, state = {}) ⇒ Object

Calls the Apps on_exception hook or the default Sunshine.exception_behavior.



339
340
341
342
# File 'lib/sunshine/app.rb', line 339

def handle_exception exception, state={}
  return @on_exception.call(exception, state) if @on_exception
  handle_interruption Sunshine.exception_behavior, state
end

#handle_interruption(behavior, state = {}) ⇒ Object

Handles the behavior of a failed or interrupted deploy. Takes a behavior symbol defining how to handle the interruption and a hash representing the state of the deploy when it was interrupted.

Supported bahavior symbols are: ::revert: Revert to previous deploy (default) ::console: Start an interactive console with the app’s binding ::exit: Stop deploy and exit ::prompt: Ask what to do

The state hash supports the following keys: ::stopped: Was the previous deploy stopped. ::symlinked: Was the new deployed symlinked as the current deploy.



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
# File 'lib/sunshine/app.rb', line 393

def handle_interruption behavior, state={}
  case behavior

  when :exit
    Sunshine.exit 1, "Error: Deploy of #{@name} failed"

  when :revert
    revert! if state[:symlinked]
    start   if state[:stopped]

  when Sunshine.interactive? && :console
    self.console!

  when Sunshine.interactive? && :prompt
    Sunshine.shell.choose do |menu|
      menu.prompt = "Deploy interrupted:"
      menu.choice(:revert) { handle_interruption :revert,  state }
      menu.choice(:console){ handle_interruption :console, state }
      menu.choice(:exit)   { handle_interruption :exit, state }
    end

  else
    raise DeployError, "Deploy of #{@name} was interrupted."
  end
end

#handle_sigint(state = {}) ⇒ Object

Calls the Apps on_sigint hook or the default Sunshine.sigint_behavior.



329
330
331
332
# File 'lib/sunshine/app.rb', line 329

def handle_sigint state={}
  return @on_sigint.call(state) if @on_sigint
  handle_interruption Sunshine.sigint_behavior, state
end

#install_deps(*deps) ⇒ Object

Install dependencies defined as a Sunshine dependency object:

rake   = Sunshine.dependencies.gem 'rake', :version => '~>0.8'
apache = Sunshine.dependencies.yum 'apache'
app.install_deps rake, apache

Deploy servers can also be specified as a dispatcher, array, or single deploy server, by passing standard ‘find’ options:

postgres = Sunshine.dependencies.yum 'postgresql'
pgserver = Sunshine.dependencies.yum 'postgresql-server'
app.install_deps postgres, pgserver, :role => 'db'

If a dependency was already defined in the Sunshine dependency tree, the dependency name may be passed instead of the object:

app.install_deps 'nginx', 'ruby'


684
685
686
687
688
689
690
# File 'lib/sunshine/app.rb', line 684

def install_deps(*deps)
  options = Hash === deps[-1] ? deps.delete_at(-1) : {}

  with_server_apps options,
    :msg  => "Installing dependencies: #{deps.map{|d| d.to_s}.join(" ")}",
    :send => [:install_deps, *deps]
end

#interruptable(options = {}) ⇒ Object

Runs the given block while handling SIGINTs and exceptions according to rules set by Sunshine.sigint_behavior and Sunshine.exception_behavior or with the override hooks App#on_sigint and App#on_exception.



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/sunshine/app.rb', line 306

def interruptable options={}
  interrupt_trap =
    TrapStack.add_trap "Interrupted #{@name}" do
      handle_sigint options
    end

  yield if block_given?

rescue => e
  Sunshine.logger.error :app, "#{e.class}: #{e.message}" do
    Sunshine.logger.error '>>', e.backtrace.join("\n")
  end

  handle_exception e, options

ensure
  TrapStack.delete_trap interrupt_trap
end

#make_app_directories(options = nil) ⇒ Object

Creates the required application directories. Supports all App#find options.



697
698
699
700
701
# File 'lib/sunshine/app.rb', line 697

def make_app_directories options=nil
  with_server_apps options,
    :msg  => "Creating #{@name} directories",
    :send => :make_app_directories
end

#on_exception(&block) ⇒ Object

Set this to define the behavior of exceptions during a deploy. Defines what to do when an exception is received when running a proc through App#interruptable. Used primarily to catch exceptions during deploys. Passes the block the exception and a hash with the state of the deploy:

app.on_exception do |exception, deploy_state_hash|
  # do something...
end


372
373
374
# File 'lib/sunshine/app.rb', line 372

def on_exception &block
  @on_exception = block
end

#on_sigint(&block) ⇒ Object

Set this to define the behavior of SIGINT during a deploy. Defines what to do when an INT signal is received when running a proc through App#interruptable. Used primarily to catch SIGINTs during deploys. Passes the block a hash with the state of the deploy:

app.on_sigint do |deploy_state_hash|
  deploy_state_hash
  #=> {:stopped => true, :symlinked => true, :success => false}
end


356
357
358
# File 'lib/sunshine/app.rb', line 356

def on_sigint &block
  @on_sigint = block
end

#prefer_pkg_manager(pkg_manager, options = nil) ⇒ Object

Assign the prefered package manager to all server_apps:

app.prefer_pkg_manager Sunshine::Yum

Package managers are typically detected automatically by each individual server_apps. Supports all App#find options.



712
713
714
715
# File 'lib/sunshine/app.rb', line 712

def prefer_pkg_manager pkg_manager, options=nil
  with_server_apps options,
    :send => [:pkg_manager=, pkg_manager]
end

#rake(command, options = nil) ⇒ Object

Run a rake task on any or all deploy servers. Supports all App#find options.



722
723
724
725
726
# File 'lib/sunshine/app.rb', line 722

def rake command, options=nil
  with_server_apps options,
    :msg  => "Running Rake task '#{command}'",
    :send => [:rake, command]
end

#register_as_deployed(options = nil) ⇒ Object

Adds the app to the deploy servers deployed-apps list. Supports all App#find options.



733
734
735
736
737
# File 'lib/sunshine/app.rb', line 733

def register_as_deployed options=nil
  with_server_apps options,
    :msg  => "Registering app with deploy servers",
    :send => :register_as_deployed
end

#remove_cronjob(name, options = nil) ⇒ Object

Remove a cron job from the remote crontabs:

remove_cronjob "reboot", :role => :web
remove_cronjob :all
#=> deletes all cronjobs related to this app

Supports all App#find options.



747
748
749
750
751
752
753
754
755
756
# File 'lib/sunshine/app.rb', line 747

def remove_cronjob name, options=nil
  with_server_apps options,
    :msg => "Removing cronjob #{name.inspect}" do |server_app|
    if name == :all
      server_app.crontab.clear
    else
      server_app.crontab.delete(name)
    end
  end
end

#remove_old_deploys(options = nil) ⇒ Object

Removes old deploys from the checkout_dir based on Sunshine’s max_deploy_versions. Supports all App#find options.



764
765
766
767
768
# File 'lib/sunshine/app.rb', line 764

def remove_old_deploys options=nil
  with_server_apps options,
    :msg  => "Removing old deploys (max = #{Sunshine.max_deploy_versions})",
    :send => :remove_old_deploys
end

#restart(options = nil) ⇒ Object

Run the restart script of a deployed app on the specified deploy servers. Post-deploy only. Supports all App#find options.



776
777
778
779
780
# File 'lib/sunshine/app.rb', line 776

def restart options=nil
  with_server_apps options,
    :msg  => "Running restart script",
    :send => :restart
end

#restart!(options = nil) ⇒ Object

Run the restart script of a deployed app on the specified deploy servers. Raises an exception on failure. Post-deploy only. Supports all App#find options.



788
789
790
791
792
# File 'lib/sunshine/app.rb', line 788

def restart! options=nil
  with_server_apps options,
    :msg  => "Running restart script",
    :send => :restart!
end

#revert!(options = nil) ⇒ Object

Symlink current directory to previous checkout and remove the current deploy directory. Supports any App#find options.

app.revert! :role => :web


425
426
427
428
429
# File 'lib/sunshine/app.rb', line 425

def revert! options=nil
  with_server_apps options,
    :msg  => "Reverting to previous deploy.",
    :send => :revert!
end

#run_bundler(options = nil) ⇒ Object

Runs bundler on deploy servers. Supports all App#find options.



798
799
800
801
802
# File 'lib/sunshine/app.rb', line 798

def run_bundler options=nil
  with_server_apps options,
    :msg  => "Running Bundler",
    :send => [:run_bundler, options]
end

#run_geminstaller(options = nil) ⇒ Object

Runs GemInstaller on deploy servers. Supports all App#find options.



808
809
810
811
812
# File 'lib/sunshine/app.rb', line 808

def run_geminstaller options=nil
  with_server_apps options,
    :msg  => "Running GemInstaller",
    :send => [:run_geminstaller, options]
end

#run_post_user_lambdasObject

Run lambdas that were saved for after the user’s script. See #after_user_script.



819
820
821
822
823
# File 'lib/sunshine/app.rb', line 819

def run_post_user_lambdas
  Sunshine.logger.info :app, "Running post deploy lambdas" do
    with_session{ @post_user_lambdas.each{|l| l.call self} }
  end
end

#run_script(name, options = nil) ⇒ Object

Run the given script of a deployed app on the specified deploy servers. Post-deploy only. Supports all App#find options.



831
832
833
834
835
# File 'lib/sunshine/app.rb', line 831

def run_script name, options=nil
  with_server_apps options,
    :msg  => "Running #{name} script",
    :send => [:run_script, name, options]
end

#run_script!(name, options = nil) ⇒ Object

Run the given script of a deployed app on the specified deploy servers. Raises an exception on failure. Post-deploy only. Supports all App#find options.



843
844
845
846
847
# File 'lib/sunshine/app.rb', line 843

def run_script! name, options=nil
  with_server_apps options,
    :msg  => "Running #{name} script",
    :send => [:run_script!, name, options]
end

#sass(*sass_names) ⇒ Object

Run a sass task on any or all deploy servers. Supports all App#find options.



854
855
856
857
858
859
860
# File 'lib/sunshine/app.rb', line 854

def sass *sass_names
  options = sass_names.delete_at(-1) if Hash === sass_names.last

  with_server_apps options,
    :msg  => "Running Sass for #{sass_names.join(' ')}",
    :send => [:sass, *sass_names]
end

#shell_env(env_hash = nil, options = nil) ⇒ Object

Set and return the remote shell env variables. Also assigns shell environment to the app’s deploy servers. Supports all App#find options.



868
869
870
871
872
873
874
875
876
877
878
879
880
881
# File 'lib/sunshine/app.rb', line 868

def shell_env env_hash=nil, options=nil
  env_hash ||= {}

  @shell_env.merge!(env_hash)

  with_server_apps options,
    :no_threads => true,
    :no_session => true,
    :msg => "Shell env: #{@shell_env.inspect}" do |server_app|
    server_app.shell_env.merge!(@shell_env)
  end

  @shell_env.dup
end

#start(options = nil) ⇒ Object

Run the start script of a deployed app on the specified deploy servers. Post-deploy only. Supports all App#find options.



889
890
891
892
893
# File 'lib/sunshine/app.rb', line 889

def start options=nil
  with_server_apps options,
    :msg  => "Running start script",
    :send => [:start, options]
end

#start!(options = nil) ⇒ Object

Run the start script of a deployed app on the specified deploy servers. Raises an exception on failure. Post-deploy only. Supports all App#find options.



901
902
903
904
905
# File 'lib/sunshine/app.rb', line 901

def start! options=nil
  with_server_apps options,
    :msg  => "Running start script",
    :send => [:start!, options]
end

#status(options = nil) ⇒ Object

Get a hash of which deploy server apps are :running or :down. Post-deploy only. Supports all App#find options.



912
913
914
915
916
917
918
919
920
# File 'lib/sunshine/app.rb', line 912

def status options=nil
  statuses = {}

  with_server_apps options, :msg => "Querying app status..." do |server_app|
    statuses[server_app.shell.host] = server_app.status
  end

  statuses
end

#stop(options = nil) ⇒ Object

Run the stop script of a deployed app on the specified deploy servers. Post-deploy only. Supports all App#find options.



928
929
930
931
932
# File 'lib/sunshine/app.rb', line 928

def stop options=nil
  with_server_apps options,
    :msg  => "Running stop script",
    :send => :stop
end

#stop!(options = nil) ⇒ Object

Run the stop script of a deployed app on the specified deploy servers. Raises an exception on failure. Post-deploy only. Supports all App#find options.



940
941
942
943
944
# File 'lib/sunshine/app.rb', line 940

def stop! options=nil
  with_server_apps options,
    :msg  => "Running stop script",
    :send => :stop!
end

Creates a symlink to the app’s checkout path. Supports all App#find options.



967
968
969
970
971
# File 'lib/sunshine/app.rb', line 967

def symlink_current_dir options=nil
  with_server_apps options,
    :msg  => "Symlinking #{@checkout_path} -> #{@current_path}",
    :send => :symlink_current_dir
end

#threaded_each(options = nil, &block) ⇒ Object

Iterate over all deploy servers but create a thread for each deploy server. Means you can’t return from the passed block! Calls App#each if Sunshine.use_threads? is false. Supports all App#find options.



980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
# File 'lib/sunshine/app.rb', line 980

def threaded_each options=nil, &block
  return each options, &block unless Sunshine.use_threads?

  mutex   = Mutex.new
  threads = []
  error   = nil

  return_val = each(options) do |server_app|

    thread = Thread.new do
      server_app.shell.with_mutex mutex do

        begin
          yield server_app

        rescue => e
          error = e
        end
      end
    end

    threads << thread
  end

  threads.each{|t| t.join }

  raise error if error

  return_val
end

#with_filter(filter_hash) ⇒ Object

Execute a block with a specified server app filter:

app.with_filter :role => :cdn do |app|
  app.sass 'file1', 'file2', 'file3'
  app.rake 'asset:packager:build_all'
end

Supports all App#find options.



1020
1021
1022
1023
1024
1025
1026
1027
# File 'lib/sunshine/app.rb', line 1020

def with_filter filter_hash
  old_server_apps, @server_apps = @server_apps, find(filter_hash)

  yield self

ensure
  @server_apps = old_server_apps
end

#with_server_apps(search_options, options = {}) ⇒ Object

Calls a method for server_apps found with the passed options, and with an optional log message. Will attempt to run the methods in a session to avoid multiple ssh login prompts. Supports all App#find options, plus:

:no_threads

bool - disable threaded execution

:no_session

bool - disable auto session creation

:msg

“some message” - log message

app.with_server_apps :all, :msg => "doing something" do |server_app|
  # do something here
end

app.with_server_apps :role => :db, :user => "bob" do |server_app|
  # do something here
end

Note: App#with_server_apps calls App#with_session. If you do not need or want a server connection you can pass :no_session.



1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
# File 'lib/sunshine/app.rb', line 1050

def with_server_apps search_options, options={}
  options = search_options.merge options if Hash === search_options

  message = options[:msg]
  method  = options[:no_threads] ? :each : :threaded_each
  auto_session = !options[:no_session]

  block = lambda do |*|
    send(method, search_options) do |server_app|

      if block_given?
        yield(server_app)

      elsif options[:send]
        server_app.send(*options[:send])
      end
    end
  end


  msg_block = lambda do |*|
    if message
      Sunshine.logger.info(:app, message, &block)

    else
      block.call
    end
  end

  auto_session ? with_session(&msg_block) : msg_block.call
end

#with_session(options = nil) ⇒ Object

Runs block ensuring a connection to remote_shells. Connecting and disconnecting will be ignored if a session already exists. Supports all App#find options.

Ensures that servers are disconnected after the block is run if servers were not previously connected.



1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
# File 'lib/sunshine/app.rb', line 1091

def with_session options=nil
  with_filter options do
    prev_connection = connected?

    begin
      connect unless prev_connection
      yield self

    ensure
      disconnect unless prev_connection
    end
  end
end