# Copyright (C) 2011-2012 RightScale, Inc, All Rights Reserved Worldwide. # # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use, # reproduction, modification, or disclosure of this program is # strictly prohibited. Any use of this program by an authorized # licensee is strictly subject to the terms and conditions, # including confidentiality obligations, set forth in the applicable # License Agreement between RightScale.com, Inc. and # the licensee require 'fileutils' module RightConf class PassengerConfigurator PASSENGER_GEM_VERSION = '3.0.2' DEFAULT_NGINX_INSTALL = ['/opt/nginx', File.join(ENV['HOME'], 'nginx')] include Configurator register :passenger description "Installs and configure passenger with nginx\nNOTE: If the " + 'configuration file includes a ruby section before the ' + 'passenger section then that ruby will be used, otherwise ' + 'the default ruby will.' setting 'library_src_path', "Path to library sources, #{File.expand_path(File.join(Dir.getwd, '..', 'library'))} by default" setting 'gem_version', "Passenger gem version #{PASSENGER_GEM_VERSION} by default" setting 'install_path', 'Path to nginx installation directory, uses /opt/nginx if it ' + 'is writable by the current user or ~/nginx otherwise by default' # Check that passenger gem is installed and that passenger+nginx is installed # # === Return # true:: If both are installed # false:: Otherwise def check_linux set_defaults report_check("Checking for passenger gem #{gem_version}") res = Command.execute_in_ruby('gem', 'list', 'passenger') success = (res.output =~ /#{gem_version}/) report_result(success) if success report_check('Checking for passenger+nginx') res = Command.execute('which', 'nginx').success? res = File.exists?(File.join(install_path, 'sbin', 'nginx')) unless res report_result(res) success = res end success end alias :check_darwin :check_linux # Not implemented on Windows # # === Raise # (Exception):: Always raise def check_windows raise "Passenger is not supported on Windows!" end # Install passenger gem in given ruby then run nginx install script # # === Return # true:: Always return true def run_linux set_defaults install_gem install_passenger true end alias :run_darwin :run_linux # Not implemented on windows # # === Raise # (Exception):: Always raise def run_windows raise "Passenger is not supported on Windows!" end protected # Set default values for settings # # === Return # true:: Always return true def set_defaults right_site_src_path(File.expand_path(Dir.getwd)) library_src_path(File.expand_path(File.join(Dir.getwd, '..', 'library'))) if library_src_path.nil? gem_version(PASSENGER_GEM_VERSION) if gem_version.nil? install_path(default_install_path) if install_path.nil? user(ENV['USER']) if user.nil? true end # Retrieve default install path # Try /opt/nginx and revert to ~/nginx if not writable # # === Return # path:: Default passenger+nginx install path def default_install_path path = if File.exist?('/opt') if File.exist?('/opt/nginx') pick_default_path(File.writable?('/opt/nginx') && File.executable?('/opt/nginx/sbin/nginx')) else res = Command.execute('mkdir', '/opt/nginx') pick_default_path(res.success?) end else res = Command.execute('mkdir', '-p', '/opt/nginx') pick_default_path(res.success?) end end # First or second choice nginx install path # # === Parameter # first_choice(TrueClass|FalseClass):: Whether to use first or second choice path # # === Return # path(String):: Path def pick_default_path(first_choice) path = DEFAULT_NGINX_INSTALL[first_choice && 0 || 1] end # Install passenger gem # # === Return # true:: Always return true def install_gem report_check('Installing passenger gem') Command.execute_in_ruby('gem', 'install', 'passenger', '-v', gem_version, :abort_on_failure => "Failed to install passenger gem version #{gem_version}") report_success end # Install passenger+nginx # # === Return # true:: Always return true def install_passenger report_check('Installing passenger+nginx') # Grrrrr passenger installer expect rake where it's not... HACK ruby_dir = File.dirname(`which ruby`.chomp) if ruby_dir !~ /\.rvm/ post_note 'Please enable rvm before passenger is installed by running ' + 'cd ..;cd -'.blue report_failure else FileUtils.cp(`which rake`.chomp, ruby_dir) unless rake_exist = File.exist?(File.join(ruby_dir, 'rake')) FileUtils.mkdir_p(install_path) Dir.chdir(install_path) do Command.execute_in_ruby('passenger-install-nginx-module', '--auto', '--auto-download', '--prefix', install_path, :abort_on_failure => "Failed to install nginx into #{install_path}") end FileUtils.mkdir_p(File.join(install_path, 'logs')) File.open(File.join(install_path, 'conf', 'nginx.conf'), 'w') { |f| f.puts(nginx_config) } if File.writable?('/etc/hosts') && IO.read('/etc/hosts') !~ /hosts_entry/ File.open('/etc/hosts', 'a') { |f| f.puts hosts_entry } else post_note "Please add the following line to your /etc/hosts file:\n#{hosts_entry}\n" + "\nThe following aliases may help too:\n" + "alias nrestart='kill -HUP `cat #{pid_path}/nginx.pid`'\n" + "alias nstart='#{install_path}/sbin/nginx'\n" + "alias nstop='#{install_path}/sbin/nginx -s stop'" end post_note (post_note || '') + "\nThe nginx settings will make right_site available at http://right-site.rightscale.local:3080" File.delete(File.join(ruby_dir, 'rake')) unless rake_exist report_success end end # Hosts entry in /etc/hosts # # === Return # entry(String):: Hosts entry required to run local dev def hosts_entry "\n" + "# added by rconf's passenger_configurator\n" "#\n" + "255.255.255.255 broadcasthost\n" + "127.0.0.1 right-site.rightscale.local library.rightscale.local rightscale.local global.rightscale.local" end # Nginx error log path def log_path File.join(install_path, 'logs', 'nginx-error.log') end # Nginx pid file path def pid_path File.join(install_path, 'logs', 'nginx.pid') end # Nginx config # # === Parameters # username(String):: Username used to run nginx # # === Return # config(String):: Nginx config def nginx_config ruby_bin = Command.execute_in_ruby('which', 'ruby').output.chomp res = Command.execute_in_ruby('ruby', '-e', "require 'rubygems';puts Gem.source_index.find_name('passenger').detect { |g| g.version.to_s == '#{gem_version}' }.full_gem_path", :abort_on_failure => 'Could not find passenger gem to build passenger_root') passenger_root = res.output.chomp report_fatal('Could not find passenger gem') if passenger_root.empty? <<-EOS worker_processes 1; error_log #{log_path} debug; pid #{pid_path}; events { worker_connections 256; } http { passenger_root #{passenger_root}; passenger_ruby #{ruby_bin}; passenger_max_pool_size 2; passenger_pool_idle_time 0; passenger_max_instances_per_app 1; include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; gzip on; gzip_buffers 16 8k; gzip_disable "MSIE [1-6]\."; gzip_proxied any; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; server { ssi on; listen 3080; server_name right-site.rightscale.local; root #{File.join(right_site_src_path, 'public')}; passenger_enabled on; rails_env development; # Sketchy Redirects # Send sketchy references to the appropriate URL/port location ~ /sketchy2-4/ { proxy_pass http://46.137.105.242$request_uri; } # POST and AJAX requests need to proxied directly to the library app ##################################################################### set $is_library_direct ""; # request is for a library related action if ($request_uri ~ ^/library/) { set $is_library_direct L; } # request is a POST if ($request_method = POST) { set $is_library_direct "${is_library_direct}P"; } # request is an AJAX if ($http_x_requested_with = XMLHttpRequest) { set $is_library_direct "${is_library_direct}A"; } # library Post and Ajax request should be directly proxied if ( $is_library_direct ~ (LP|LPA|LA) ) { rewrite ^/library/(.*)$ /direct_library/$1 last; } location /direct_library/ { proxy_pass http://library.rightscale.local:3001/; proxy_set_header X-Embedded on; proxy_set_header X-Relative-Root library; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } location /library_rest/ { proxy_pass http://library.rightscale.local:3001/; proxy_set_header X-Embedded on; proxy_set_header X-Relative-Root library; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } location /library/publishing_assistant/image_upload_callback/ { proxy_pass http://library.rightscale.local:3001/publishing_assistant/image_upload_callback/; proxy_set_header X-Relative-Root library; proxy_set_header X-Embedded on; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } location ~ ^/library/publishing_organizations/(.+)/image_upload_callback { proxy_pass http://library.rightscale.local:3001/publishing_organizations/$1/image_upload_callback?$args; proxy_set_header X-Relative-Root library; proxy_set_header X-Embedded on; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } # library static assets get proxied directly to the library app location ~ ^/library/(.*)\.(css|js)$ { proxy_pass http://library.rightscale.local:3001/$1.$2; } # Library images are sent straight to the library location /library_images/ { proxy_pass http://library.rightscale.local:3001/library_images/; } location /api/reporting/ { proxy_pass http://library.rightscale.local:3001/report_service_proxy/; proxy_set_header X-Relative-Root library; proxy_set_header X-Embedded on; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } ##################################################################### set $is_global_system_direct ""; # request is for a library related action if ($request_uri ~ ^/global/) { set $is_global_system_direct L; } # request is a POST if ($request_method = POST) { set $is_global_system_direct "${is_global_system_direct}P"; } # request is an AJAX if ($http_x_requested_with = XMLHttpRequest) { set $is_global_system_direct "${is_global_system_direct}A"; } # library Post and Ajax request should be directly proxied if ( $is_global_system_direct ~ (LP|LPA|LA) ) { rewrite ^/global/(.*)$ /direct_global/$1 last; } # provisioning routes to the library if ($request_uri ~ ^/provisioning) { rewrite ^/provisioning(.*)$ /direct_global/provisioning$1 last; } if ($request_uri ~ ^/aria_provisioning_callbacks) { rewrite ^/aria_provisioning_callbacks(.*)$ /direct_global/aria_provisioning_callbacks$1 last; } location /direct_global/ { proxy_pass http://global.rightscale.local:3001/; proxy_set_header X-Embedded on; proxy_set_header X-Relative-Root global/; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } location /global_rest/ { proxy_pass http://global.rightscale.local:3001/; proxy_set_header X-Relative-Root global/; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } location ~ ^/global/users/(.+)/openid_consume { proxy_pass http://global.rightscale.local:3001/users/$1/openid_consume?$args; proxy_set_header X-Relative-Root global; proxy_set_header X-Embedded on; proxy_set_header X-Core-Site-Domain 'right-site.rightscale.local'; } } server { ssi on; listen 3001; server_name library.rightscale.local; root #{library_src_path}/public; passenger_enabled on; rails_env development; } } EOS end end end