Class: Sunshine::ServerApp
- Inherits:
-
Object
- Object
- Sunshine::ServerApp
- Defined in:
- lib/sunshine/server_app.rb
Overview
Handles App deployment functionality for a single deploy server.
Server apps can be assigned any number of roles for classification.
- :roles
-
sym|array - roles assigned (web, db, app, etc…)
By default server apps get the special :all role which will always return true when calling:
server_app.has_roles? :some_role
ServerApp objects can be instantiated several ways:
ServerApp.new app_instance, shell_instance,
When passing an App instance, the new ServerApp will keep an active link to the app’s properties. Name, deploy, and path attributes will be actively linked.
Rely on ServerApp to create a RemoteShell instance to use:
ServerApp.new app_instance, "host.com",
Instantiate with app name and rely on Sunshine defaults for app paths:
ServerApp.new "app_name", shell_instance,
Explicitely assign the app’s root path:
ServerApp.new "app_name", ..., :root_path => "/path/to/app_root"
Assigning a specific deploy name to use can be done with the :deploy_name option:
ServerApp.new "app_name", ..., :deploy_name => "deploy"
Instance Attribute Summary collapse
-
#app ⇒ Object
Returns the value of attribute app.
-
#crontab ⇒ Object
Returns the value of attribute crontab.
-
#health ⇒ Object
Returns the value of attribute health.
-
#info ⇒ Object
Returns the value of attribute info.
-
#pkg_manager ⇒ Object
Returns the type of package management system to use.
-
#roles ⇒ Object
Returns the value of attribute roles.
-
#scripts ⇒ Object
Returns the value of attribute scripts.
-
#shell ⇒ Object
Returns the value of attribute shell.
Class Method Summary collapse
-
.app_attr(*attribs) ⇒ Object
Define an attribue that will get a value from app, or locally if.
Instance Method Summary collapse
-
#add_shell_paths(*paths) ⇒ Object
Add paths the the shell $PATH env.
-
#build_control_scripts ⇒ Object
Creates and uploads all control scripts for the application.
-
#build_deploy_info_file ⇒ Object
Creates a yaml file with deploy information.
-
#checkout_repo(repo) ⇒ Object
Checks out the app’s codebase to the checkout path.
-
#deploy_details(reload = false) ⇒ Object
Get post-mortum information about the app’s deploy, from the generated deploy info file.
-
#deployed? ⇒ Boolean
Checks if the server_app’s current info file deploy_name matches the server_app’s deploy_name attribute.
-
#directories ⇒ Object
An array of all directories used by the app.
-
#get_deploy_info ⇒ Object
Returns information about the deploy at hand.
-
#gpg_decrypt(gpg_file, options = {}) ⇒ Object
Decrypt a file using gpg.
-
#has_roles?(*roles) ⇒ Boolean
Check if this server app includes the specified roles:.
-
#initialize(app, host, options = {}) ⇒ ServerApp
constructor
A new instance of ServerApp.
-
#install_deps(*deps) ⇒ Object
Install dependencies previously defined in Sunshine.dependencies.
-
#make_app_directories ⇒ Object
Creates the required application directories.
-
#make_bash_script(name, cmds) ⇒ Object
Makes an array of bash commands into a script that echoes ‘true’ on success.
-
#make_env_bash_script ⇒ Object
Creates the one-off env script that will be used by other scripts to correctly set their env variables.
-
#rake(command) ⇒ Object
Run a rake task the deploy server.
-
#register_as_deployed ⇒ Object
Adds the app to the deploy server’s deployed-apps list.
-
#remove_old_deploys ⇒ Object
Removes old deploys from the checkout_dir based on Sunshine’s max_deploy_versions.
-
#restart ⇒ Object
Run the app’s restart script.
-
#revert! ⇒ Object
Symlink current directory to previous checkout and remove the current deploy directory.
-
#run_bundler ⇒ Object
Runs bundler.
-
#run_geminstaller ⇒ Object
Runs geminstaller.
-
#running? ⇒ Boolean
Check if the app pids are present.
-
#sass(*sass_names) ⇒ Object
Run a sass task on any or all deploy servers.
-
#shell_env ⇒ Object
Get the deploy server’s shell environment.
-
#start(options = nil) ⇒ Object
Run the app’s start script.
-
#status ⇒ Object
Get the app’s status: :running or :down.
-
#stop ⇒ Object
Run the app’s stop script.
-
#symlink_current_dir ⇒ Object
Creates a symlink to the app’s checkout path.
-
#upload_tasks(*files) ⇒ Object
Upload common rake tasks from a local path or the sunshine lib.
-
#write_script(name, contents) ⇒ Object
Write an executable bash script to the app’s checkout dir on the deploy server, and symlink them to the current dir.
Constructor Details
#initialize(app, host, options = {}) ⇒ ServerApp
Returns a new instance of ServerApp.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/sunshine/server_app.rb', line 57 def initialize app, host, ={} @app = App === app ? app : nil name = @app && @app.name || app assign_local_app_attr name, @deploy_details = nil @roles = [:roles] || [:all] @roles = @roles.split(" ") if String === @roles @roles = [*@roles].compact.map{|r| r.to_sym } @scripts = Hash.new{|h, k| h[k] = []} @info = {:ports => {}} @pkg_manager = nil @shell = case host when String then RemoteShell.new host, when Shell then host else raise "Could not get remote shell '#{host}'" end @crontab = Crontab.new name, @shell @health = Healthcheck.new shared_path, @shell end |
Instance Attribute Details
#app ⇒ Object
Returns the value of attribute app.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def app @app end |
#crontab ⇒ Object
Returns the value of attribute crontab.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def crontab @crontab end |
#health ⇒ Object
Returns the value of attribute health.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def health @health end |
#info ⇒ Object
Returns the value of attribute info.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def info @info end |
#pkg_manager ⇒ Object
Returns the type of package management system to use.
314 315 316 317 |
# File 'lib/sunshine/server_app.rb', line 314 def pkg_manager @pkg_manager ||= (@shell.call("yum --version") && Yum) rescue Apt end |
#roles ⇒ Object
Returns the value of attribute roles.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def roles @roles end |
#scripts ⇒ Object
Returns the value of attribute scripts.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def scripts @scripts end |
#shell ⇒ Object
Returns the value of attribute shell.
54 55 56 |
# File 'lib/sunshine/server_app.rb', line 54 def shell @shell end |
Class Method Details
.app_attr(*attribs) ⇒ Object
Define an attribue that will get a value from app, or locally if
39 40 41 42 43 44 45 46 47 |
# File 'lib/sunshine/server_app.rb', line 39 def self.app_attr *attribs attribs.each do |attrib| class_eval <<-STR, __FILE__, __LINE__ + 1 def #{attrib} @app ? @app.send(:#{attrib}) : @#{attrib} end STR end end |
Instance Method Details
#add_shell_paths(*paths) ⇒ Object
Add paths the the shell $PATH env.
90 91 92 93 94 95 |
# File 'lib/sunshine/server_app.rb', line 90 def add_shell_paths(*paths) path = shell_env["PATH"] || "$PATH" paths << path shell_env.merge! "PATH" => paths.join(":") end |
#build_control_scripts ⇒ Object
Creates and uploads all control scripts for the application. To add to, or define a control script, see App#add_to_script.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/sunshine/server_app.rb', line 102 def build_control_scripts write_script "env", make_env_bash_script build_scripts = @scripts.dup if build_scripts[:restart].empty? && !build_scripts[:start].empty? && !build_scripts[:stop].empty? build_scripts[:restart] << "#{self.root_path}/stop" build_scripts[:restart] << "#{self.root_path}/start" end if build_scripts[:status].empty? build_scripts[:status] << "echo 'No daemons for #{self.name}'; exit 1;" end build_scripts.each do |name, cmds| if cmds.empty? Sunshine.logger.warn @shell.host, "#{name} script is empty" end bash = make_bash_script name, cmds write_script name, bash end end |
#build_deploy_info_file ⇒ 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"
135 136 137 138 139 140 141 142 |
# File 'lib/sunshine/server_app.rb', line 135 def build_deploy_info_file deploy_info = get_deploy_info.to_yaml @shell.make_file "#{self.checkout_path}/info", deploy_info @shell.symlink "#{self.current_path}/info", "#{self.root_path}/info" end |
#checkout_repo(repo) ⇒ Object
Checks out the app’s codebase to the checkout path.
148 149 150 151 |
# File 'lib/sunshine/server_app.rb', line 148 def checkout_repo repo install_deps repo.scm @info[:scm] = repo.checkout_to self.checkout_path, @shell end |
#deploy_details(reload = false) ⇒ Object
Get post-mortum information about the app’s deploy, from the generated deploy info file. Post-deploy only.
159 160 161 162 163 |
# File 'lib/sunshine/server_app.rb', line 159 def deploy_details reload=false return @deploy_details if @deploy_details && !reload @deploy_details = YAML.load @shell.call("cat #{self.current_path}/info") rescue nil end |
#deployed? ⇒ Boolean
Checks if the server_app’s current info file deploy_name matches the server_app’s deploy_name attribute.
170 171 172 173 174 175 176 177 |
# File 'lib/sunshine/server_app.rb', line 170 def deployed? success = @deploy_details[:deploy_name] == self.deploy_name if @deploy_details return success if success deploy_details(true)[:deploy_name] == self.deploy_name rescue false end |
#directories ⇒ Object
An array of all directories used by the app. Does not include symlinked directories.
184 185 186 |
# File 'lib/sunshine/server_app.rb', line 184 def directories [root_path, deploys_path, shared_path, log_path, checkout_path] end |
#get_deploy_info ⇒ Object
Returns information about the deploy at hand.
192 193 194 195 196 197 198 199 200 |
# File 'lib/sunshine/server_app.rb', line 192 def get_deploy_info { :deployed_at => Time.now.to_s, :deployed_as => @shell.call("whoami"), :deployed_by => Sunshine.shell.user, :deploy_name => File.basename(self.checkout_path), :roles => @roles, :path => self.root_path }.merge @info end |
#gpg_decrypt(gpg_file, options = {}) ⇒ Object
Decrypt a file using gpg. Allows options:
- :output
-
str - the path the output file should go to
- :passphrase
-
str - the passphrase gpg should use
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/sunshine/server_app.rb', line 208 def gpg_decrypt gpg_file, ={} output_file = [:output] || gpg_file.gsub(/\.gpg$/, '') passphrase = [:passphrase] passphrase_file = "#{self.root_path}/tmp/gpg_passphrase" gpg_cmd = "gpg --batch --no-tty --yes --output #{output_file} "+ "--passphrase-file #{passphrase_file} --decrypt #{gpg_file}" @shell.call "mkdir -p #{File.dirname(passphrase_file)}" @shell.make_file passphrase_file, passphrase @shell.call "cd #{self.checkout_path} && #{gpg_cmd}" @shell.call "rm -f #{passphrase_file}" end |
#has_roles?(*roles) ⇒ Boolean
Check if this server app includes the specified roles:
229 230 231 232 233 234 235 236 237 |
# File 'lib/sunshine/server_app.rb', line 229 def has_roles? *roles return true if @roles.include? :all roles.each do |role| return false unless @roles.include? role end true end |
#install_deps(*deps) ⇒ Object
Install dependencies previously defined in Sunshine.dependencies. Will not execute if Sunshine.auto_dependencies? is false.
262 263 264 265 266 267 268 269 270 |
# File 'lib/sunshine/server_app.rb', line 262 def install_deps(*deps) return unless Sunshine.auto_dependencies? = {:call => @shell, :prefer => pkg_manager} .merge! deps.delete_at(-1) if Hash === deps.last args = deps << Sunshine.dependencies.install(*args) end |
#make_app_directories ⇒ Object
Creates the required application directories.
276 277 278 |
# File 'lib/sunshine/server_app.rb', line 276 def make_app_directories @shell.call "mkdir -p #{self.directories.join(" ")}" end |
#make_bash_script(name, cmds) ⇒ Object
Makes an array of bash commands into a script that echoes ‘true’ on success.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/sunshine/server_app.rb', line 285 def make_bash_script name, cmds cmds = cmds.map{|cmd| "(#{cmd})" } cmds << "echo true" bash = <<-STR #!/bin/bash if [ "$1" == "--no-env" ]; then #{cmds.flatten.join(" && ")} else #{self.root_path}/env #{self.root_path}/#{name} --no-env fi STR end |
#make_env_bash_script ⇒ Object
Creates the one-off env script that will be used by other scripts to correctly set their env variables.
305 306 307 308 |
# File 'lib/sunshine/server_app.rb', line 305 def make_env_bash_script env_str = shell_env.map{|e| e.join("=")}.join(" ") "#!/bin/bash\nenv #{env_str} \"$@\"" end |
#rake(command) ⇒ Object
Run a rake task the deploy server.
323 324 325 326 |
# File 'lib/sunshine/server_app.rb', line 323 def rake command install_deps 'rake', :type => Gem @shell.call "cd #{self.checkout_path} && rake #{command}" end |
#register_as_deployed ⇒ Object
Adds the app to the deploy server’s deployed-apps list
332 333 334 |
# File 'lib/sunshine/server_app.rb', line 332 def register_as_deployed AddCommand.exec self.root_path, 'servers' => [@shell] end |
#remove_old_deploys ⇒ Object
Removes old deploys from the checkout_dir based on Sunshine’s max_deploy_versions.
341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/sunshine/server_app.rb', line 341 def remove_old_deploys deploys = @shell.call("ls -1 #{self.deploys_path}").split("\n") return unless deploys.length > Sunshine.max_deploy_versions lim = Sunshine.max_deploy_versions + 1 rm_deploys = deploys[0..-lim] rm_deploys.map!{|d| "#{self.deploys_path}/#{d}"} @shell.call("rm -rf #{rm_deploys.join(" ")}") end |
#restart ⇒ Object
Run the app’s restart script. Returns false on failure. Post-deploy only.
359 360 361 |
# File 'lib/sunshine/server_app.rb', line 359 def restart @shell.call "#{self.root_path}/restart" rescue false end |
#revert! ⇒ Object
Symlink current directory to previous checkout and remove the current deploy directory.
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/sunshine/server_app.rb', line 368 def revert! @shell.call "rm -rf #{self.checkout_path}" last_deploy = @shell.call("ls -rc1 #{self.deploys_path}").split("\n").last if last_deploy && !last_deploy.empty? @shell.symlink "#{self.deploys_path}/#{last_deploy}", self.current_path Sunshine.logger.info @shell.host, "Reverted to #{last_deploy}" unless start :force => true Sunshine.logger.error @shell.host, "Failed #{@name} startup" end else @crontab.delete! Sunshine.logger.info @shell.host, "No previous deploy to revert to." end end |
#run_bundler ⇒ Object
Runs bundler. Installs the bundler gem if missing.
393 394 395 396 |
# File 'lib/sunshine/server_app.rb', line 393 def run_bundler install_deps 'bundler', :type => Gem @shell.call "cd #{self.checkout_path} && gem bundle" end |
#run_geminstaller ⇒ Object
Runs geminstaller. :( Deprecated: use bundler
403 404 405 406 |
# File 'lib/sunshine/server_app.rb', line 403 def run_geminstaller install_deps 'geminstaller', :type => Gem @shell.call "cd #{self.checkout_path} && geminstaller -e" end |
#running? ⇒ Boolean
Check if the app pids are present. Post-deploy only.
413 414 415 |
# File 'lib/sunshine/server_app.rb', line 413 def running? @shell.call "#{self.root_path}/status" rescue false end |
#sass(*sass_names) ⇒ Object
Run a sass task on any or all deploy servers.
421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/sunshine/server_app.rb', line 421 def sass *sass_names install_deps 'haml', :type => Gem sass_names.flatten.each do |name| sass_file = "public/stylesheets/sass/#{name}.sass" css_file = "public/stylesheets/#{name}.css" sass_cmd = "cd #{self.checkout_path} && sass #{sass_file} #{css_file}" @shell.call sass_cmd end end |
#shell_env ⇒ Object
Get the deploy server’s shell environment.
437 438 439 |
# File 'lib/sunshine/server_app.rb', line 437 def shell_env @shell.env end |
#start(options = nil) ⇒ Object
Run the app’s start script. Returns false on failure. Post-deploy only.
446 447 448 449 450 451 452 453 454 455 |
# File 'lib/sunshine/server_app.rb', line 446 def start =nil ||= {} if running? return unless [:force] stop end @shell.call "#{self.root_path}/start" rescue false end |
#status ⇒ Object
Get the app’s status: :running or :down.
461 462 463 |
# File 'lib/sunshine/server_app.rb', line 461 def status running? ? :running : :down end |
#stop ⇒ Object
Run the app’s stop script. Returns false on failure. Post-deploy only.
470 471 472 |
# File 'lib/sunshine/server_app.rb', line 470 def stop @shell.call "#{self.root_path}/stop" rescue false end |
#symlink_current_dir ⇒ Object
Creates a symlink to the app’s checkout path.
478 479 480 |
# File 'lib/sunshine/server_app.rb', line 478 def symlink_current_dir @shell.symlink self.checkout_path, self.current_path end |
#upload_tasks(*files) ⇒ Object
Upload common rake tasks from a local path or the sunshine lib.
app.upload_tasks
#=> upload all tasks
app.upload_tasks 'app', 'common', ...
#=> upload app and common rake files
File paths may also be used instead of the file’s base name but directory structures will not be followed:
app.upload_tasks 'lib/common/app.rake', 'lib/do_thing.rake'
Allows options:
- :local_path
-
str - the path to get rake tasks from
- :remote_path
-
str - the remote absolute path to upload the files to
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 |
# File 'lib/sunshine/server_app.rb', line 498 def upload_tasks *files = Hash === files[-1] ? files.delete_at(-1) : {} remote_path = [:remote_path] || "#{self.checkout_path}/lib/tasks" local_path = [:local_path] || "#{Sunshine::ROOT}/templates/tasks" @shell.call "mkdir -p #{remote_path}" files.map! do |file| if File.basename(file) == file File.join(local_path, "#{file}.rake") else file end end files = Dir.glob("#{Sunshine::ROOT}/templates/tasks/*") if files.empty? files.each do |file| remote_file = File.join remote_path, File.basename(file) @shell.upload file, remote_file end end |
#write_script(name, contents) ⇒ Object
Write an executable bash script to the app’s checkout dir on the deploy server, and symlink them to the current dir.
526 527 528 529 530 531 532 533 |
# File 'lib/sunshine/server_app.rb', line 526 def write_script name, contents @shell.make_file "#{self.checkout_path}/#{name}", contents, :flags => '--chmod=ugo=rwx' @shell.symlink "#{self.current_path}/#{name}", "#{self.root_path}/#{name}" end |