Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Import the CLI we prepared earlier.

  • Loading branch information...
commit 86dc2315eb42fb3bc195aeb1fe3692995de6e5bd 1 parent 13ba0e3
Manfred Stienstra Manfred authored
Showing with 2,052 additions and 51 deletions.
  1. +2 −1  .gitignore
  2. +1 −3 Rakefile
  3. +10 −0 bin/ppane
  4. +28 −0 lib/option_parser.rb
  5. +7 −0 lib/passenger_pane.rb
  6. +212 −0 lib/passenger_pane/application.rb
  7. +54 −0 lib/passenger_pane/configuration.rb
  8. +19 −0 lib/passenger_pane/directory_services.rb
  9. +66 −0 lib/passenger_pane/httpd_conf.rb
  10. +191 −0 lib/passenger_pane/runner.rb
  11. +255 −0 test/application_test.rb
  12. 0  test/{ → attic}/config_installer_test.rb
  13. 0  test/{ → attic}/config_uninstaller_test.rb
  14. 0  test/{ → attic}/fixtures/blog.vhost.conf
  15. 0  test/{ → attic}/fixtures/franky.vhost.conf
  16. 0  test/{ → attic}/fixtures/staging.vhost.conf
  17. 0  test/{ → attic}/fixtures/wiki.vhost.conf
  18. 0  test/{ → attic}/hosts_installer_test.rb
  19. 0  test/{ → attic}/passenger_application_test.rb
  20. 0  test/{ → attic}/passenger_pref_test.rb
  21. 0  test/{ → attic}/shared_passenger_behaviour_test.rb
  22. +53 −0 test/attic/test_helper.rb
  23. +35 −0 test/configuration_test.rb
  24. +25 −0 test/directory_services_test.rb
  25. +509 −0 test/fake/etc/apache2/httpd.conf
  26. +10 −0 test/fake/etc/apache2/passenger_pane_vhosts/blog.vhost.conf
  27. +9 −0 test/fake/etc/apache2/passenger_pane_vhosts/franky.vhost.conf
  28. +9 −0 test/fake/etc/apache2/passenger_pane_vhosts/staging.vhost.conf
  29. +12 −0 test/fake/etc/apache2/passenger_pane_vhosts/wiki.vhost.conf
  30. +126 −0 test/httpd_conf_test.rb
  31. +122 −0 test/runner_test.rb
  32. +20 −47 test/test_helper.rb
  33. +63 −0 test/test_helper/add_allow_switch.rb
  34. +151 −0 test/test_helper/add_allow_switch_test.rb
  35. +15 −0 test/test_helper/capture_output.rb
  36. +10 −0 test/test_helper/collector.rb
  37. +15 −0 test/test_helper/fake_apache_directory.rb
  38. +23 −0 test/test_helper/temporary_directory.rb
3  .gitignore
View
@@ -2,4 +2,5 @@ build
.DS_Store
Passenger.xcodeproj/*.mode1v3
Passenger.xcodeproj/*.pbxuser
-pkg
+pkg
+tmp
4 Rakefile
View
@@ -47,10 +47,8 @@ end
require 'rake/testtask'
Rake::TestTask.new do |t|
- t.libs << "test"
t.test_files = FileList['test/*_test.rb']
- t.verbose = nil
- t.options = '-rs'
+ t.verbose = true
end
desc "Generate Security.framework BridgeSupport file"
10 bin/ppane
View
@@ -0,0 +1,10 @@
+#!/usr/bin/env ruby
+
+if $0 == __FILE__
+ $:.unshift(File.expand_path('../../lib', __FILE__))
+end
+
+require 'passenger_pane'
+require 'option_parser'
+
+PassengerPane::Runner.run(*OptionParser.parse(ARGV))
28 lib/option_parser.rb
View
@@ -0,0 +1,28 @@
+class OptionParser
+ def self.parse(argv)
+ return [{},[]] if argv.empty?
+
+ options = {}
+ rest = []
+ switch = nil
+
+ for value in argv
+ # values is a switch
+ if value[0] == 45
+ switch = value.slice((value[1] == 45 ? 2 : 1)..-1)
+ options[switch] = nil
+ else
+ if switch
+ # we encountered a switch so this
+ # value belongs to that switch
+ options[switch] = value
+ switch = nil
+ else
+ rest << value
+ end
+ end
+ end
+
+ [options, rest]
+ end
+end
7 lib/passenger_pane.rb
View
@@ -0,0 +1,7 @@
+module PassengerPane
+ autoload :Application, 'passenger_pane/application'
+ autoload :Configuration, 'passenger_pane/configuration'
+ autoload :DirectoryServices, 'passenger_pane/directory_services'
+ autoload :HttpdConf, 'passenger_pane/httpd_conf'
+ autoload :Runner, 'passenger_pane/runner'
+end
212 lib/passenger_pane/application.rb
View
@@ -0,0 +1,212 @@
+require 'fileutils'
+
+module PassengerPane
+ class Application
+ RAILS_APP_REGEXP = /::Initializer\.run|Application\.initialize!/
+ ATTRIBUTES = [:config_filename, :host, :aliases, :path, :framework, :environment, :vhost_address, :user_defined_data]
+
+ def self.glob(configuration)
+ File.join(
+ configuration.apache_directory,
+ configuration.passenger_vhosts,
+ "*.#{configuration.passenger_vhosts_ext}"
+ )
+ end
+
+ def self.all(configuration)
+ Dir.glob(glob(configuration)).map do |config_filename|
+ new(configuration, :config_filename => config_filename)
+ end
+ end
+
+ def self.find(configuration, conditions={})
+ if conditions[:host]
+ all(configuration).detect do |app|
+ app.host == conditions[:host]
+ end
+ end
+ end
+
+ attr_accessor *ATTRIBUTES
+
+ def initialize(configuration, options={})
+ @configuration = configuration
+ set(options)
+ if options[:config_filename]
+ @new = false
+ _parse
+ elsif options[:path]
+ @new = true
+ _derive
+ else
+ raise ArgumentError, "Please specify either a :config_filename or :path"
+ end
+ @before_changes = to_hash
+ end
+
+ def set(options)
+ options.each do |key, value|
+ setter = "#{key}="
+ send(setter, value) if respond_to?(setter)
+ end
+ end
+
+ def new?
+ @new
+ end
+
+ # -- Virtual Host reading and writing
+
+ def contents
+ @contents ||= File.read(@config_filename)
+ end
+
+ attr_writer :contents
+
+ def _parse
+ data = contents.dup
+
+ data.gsub!(/\n\s*ServerName\s+(.+)/, '')
+ @host = $1
+
+ data.gsub!(/\n\s*ServerAlias\s+(.+)/, '')
+ @aliases = $1 || ''
+
+ data.gsub!(/\n\s*DocumentRoot\s+"(.+)\/public"/, '')
+ @path = $1
+
+ data.gsub!(/\n\s*(Rails|Rack)Env\s+(\w+)/, '')
+ @framework = $1.downcase
+ @environment = $2
+
+ data.gsub!(/<VirtualHost\s(.+?)>/, '')
+ @vhost_address = $1
+
+ data.gsub!(/\s*<\/VirtualHost>\n*/, '').gsub!(/^\n*/, '')
+ @user_defined_data = data.strip
+ end
+
+ def _document_root
+ File.join(@path, 'public')
+ end
+
+ def _directory_defaults
+ %{
+ <Directory "#{_document_root}">
+ Order allow,deny
+ Allow from all
+ </Directory>
+ }.strip
+ end
+
+ def _config_filename
+ File.join(
+ @configuration.apache_directory,
+ @configuration.passenger_vhosts,
+ "#{@host}.#{@configuration.passenger_vhosts_ext}"
+ )
+ end
+
+ def _framework
+ environment_file = File.join(@path, 'config', 'environment.rb')
+ if File.exist?(environment_file) and File.read(environment_file) =~ RAILS_APP_REGEXP
+ 'rails'
+ else
+ 'rack'
+ end
+ end
+
+ def _derive
+ @host ||= "#{File.basename(path).downcase.gsub('_','-')}.local"
+ @aliases ||= ''
+ @environment ||= 'development'
+ @vhost_address ||= '*:80'
+ @user_defined_data ||= _directory_defaults
+ @config_filename ||= _config_filename
+ @framework ||= _framework
+ end
+
+ def rails?; @framework == 'rails' end
+ def rack?; @framework == 'rack' end
+
+ def vhost_snippet
+ lines = []
+ lines << "<VirtualHost #{vhost_address}>"
+ lines << " ServerName #{host}"
+ lines << " ServerAlias #{aliases}" unless aliases == ''
+ lines << " DocumentRoot \"#{_document_root}\""
+ lines << " #{rails? ? 'RailsEnv' : 'RackEnv'} #{environment}"
+ lines << " #{user_defined_data}" unless user_defined_data.strip == ''
+ lines << "</VirtualHost>"
+ lines.join("\n")
+ end
+
+ def write
+ FileUtils.mkdir_p(File.dirname(@config_filename))
+ File.open(@config_filename, 'w') do |file|
+ file.write(vhost_snippet)
+ end; true
+ end
+
+ # -- Dirty checking
+
+ def to_hash
+ hash = { 'hosts' => [host, *aliases.split] }
+ ATTRIBUTES.each do |key|
+ hash[key.to_s] = instance_variable_get("@#{key}")
+ end; hash
+ end
+
+ def changed?
+ @before_changes != to_hash
+ end
+
+ def added_hosts
+ to_hash['hosts'] - @before_changes['hosts']
+ end
+
+ def removed_hosts
+ @before_changes['hosts'] - to_hash['hosts']
+ end
+
+ # -- Directory services
+
+ def register
+ PassengerPane::DirectoryServices.register(to_hash['hosts'])
+ end
+
+ def unregister
+ PassengerPane::DirectoryServices.unregister(to_hash['hosts'])
+ end
+
+ def sync_host_registration
+ if new?
+ register
+ else
+ PassengerPane::DirectoryServices.register(added_hosts) and
+ PassengerPane::DirectoryServices.unregister(removed_hosts)
+ end
+ end
+
+ # -- Persisting
+
+ def save
+ write and sync_host_registration
+ end
+
+ def delete
+ FileUtils.rm_rf(config_filename)
+ unregister
+ true
+ end
+
+ # -- Operational
+
+ def restart
+ if File.exist?(@path)
+ FileUtils.mkdir_p(File.join(File.join(@path, 'tmp')))
+ FileUtils.touch(File.join(@path, 'tmp', 'restart.txt'))
+ end
+ end
+ end
+end
54 lib/passenger_pane/configuration.rb
View
@@ -0,0 +1,54 @@
+module PassengerPane
+ class Configuration
+ CONFIG_FILENAME = '~/.passenger_pane.yml'
+ APACHE_DIRECTORY = '/private/etc/apache2'
+
+ def self.defaults
+ {
+ :ruby_binary => "/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby",
+ :httpd_binary => "/usr/sbin/httpd",
+ :httpd_conf => "httpd.conf",
+ :passenger_vhosts => "passenger_pane_vhosts",
+ :passenger_vhosts_ext => "vhost.conf",
+ :apache_restart_command => "/bin/launchctl stop org.apache.httpd"
+ }
+ end
+
+ def self.config_filename
+ File.expand_path(CONFIG_FILENAME)
+ end
+
+ def self.auto
+ configuration = new
+ if File.exist?(config_filename)
+ configuration.set(YAML.load_file(config_filename))
+ end
+ configuration
+ end
+
+ attr_accessor :apache_directory, *defaults.keys
+
+ def initialize(apache_directory=APACHE_DIRECTORY)
+ self.apache_directory = apache_directory
+ set(self.class.defaults)
+ end
+
+ def set(options)
+ options.each do |key, value|
+ begin
+ send("#{key}=", value)
+ rescue NoMethodError
+ raise ArgumentError, "There is no configuration named `#{key}', valid options are: #{self.class.defaults.keys.join(', ')} and apache_directory."
+ end
+ end
+ end
+
+ def httpd
+ @httpd ||= PassengerPane::HttpdConf.new(self)
+ end
+
+ def applications
+ PassengerPane::Application.all(self)
+ end
+ end
+end
19 lib/passenger_pane/directory_services.rb
View
@@ -0,0 +1,19 @@
+module PassengerPane
+ class DirectoryServices
+ def self.registered_hosts
+ `/usr/bin/dscl localhost -list /Local/Default/Hosts`.split("\n")
+ end
+
+ def self.register(hosts)
+ hosts.each do |host|
+ system "/usr/bin/dscl localhost -create /Local/Default/Hosts/#{host} IPAddress 127.0.0.1"
+ end
+ end
+
+ def self.unregister(hosts)
+ hosts.each do |host|
+ system "/usr/bin/dscl localhost -delete /Local/Default/Hosts/#{host}"
+ end
+ end
+ end
+end
66 lib/passenger_pane/httpd_conf.rb
View
@@ -0,0 +1,66 @@
+module PassengerPane
+ class HttpdConf
+ attr_accessor :filename
+
+ def initialize(configuration)
+ @configuration = configuration
+ @filename = File.expand_path(configuration.httpd_conf, configuration.apache_directory)
+ end
+
+ def contents
+ @contents ||= File.read(@filename)
+ end
+
+ attr_writer :contents
+
+ def passenger_vhost_include
+ "Include #{@configuration.apache_directory}/#{@configuration.passenger_vhosts}/*.#{@configuration.passenger_vhosts_ext}"
+ end
+
+ def passenger_configuration_snippet
+ %{
+# Added by the Passenger preference pane
+# Make sure to include the Passenger configuration (the LoadModule,
+# PassengerRoot, and PassengerRuby directives) before this section.
+<IfModule passenger_module>
+ NameVirtualHost *:80
+ <VirtualHost *:80>
+ ServerName _default_
+ </VirtualHost>
+ #{passenger_vhost_include}
+</IfModule>
+ }.strip
+ end
+
+ def valid?
+ `#{@configuration.httpd_binary} -t 2>&1`.strip == 'Syntax OK'
+ end
+
+ def restart
+ if valid?
+ system @configuration.apache_restart_command
+ else
+ puts "[!] Apache configuration is not valid, skipping Apache restart"
+ end
+ end
+
+ def passenger_module_installed?
+ `#{@configuration.httpd_binary} -D DUMP_MODULES 2>&1`.include? 'passenger_module'
+ end
+
+ def passenger_configured?
+ !!(contents =~ /Include.*#{@configuration.passenger_vhosts}/)
+ end
+
+ def configure_passenger
+ self.contents << "\n\n"
+ self.contents << passenger_configuration_snippet
+ end
+
+ def write
+ File.open(@filename, 'w') do |file|
+ file.write(contents)
+ end
+ end
+ end
+end
191 lib/passenger_pane/runner.rb
View
@@ -0,0 +1,191 @@
+require 'yaml'
+
+module PassengerPane
+ class Runner
+ def initialize(options)
+ @options = options
+ @configuration = PassengerPane::Configuration.auto
+ end
+
+ def machine_readable?
+ @options['machine_readable']
+ end
+
+ def info
+ if machine_readable?
+ puts YAML.dump({
+ 'passenger_module_installed' => @configuration.http.passenger_module_installed?,
+ 'passenger_configured' => @configuration.httpd.passenger_configured?
+ })
+ else
+ puts "Apache directory: #{@configuration.apache_directory}"
+ puts "Passenger installed: #{@configuration.httpd.passenger_module_installed? ? 'yes' : 'no'}"
+ puts "Passenger configured: #{@configuration.httpd.passenger_configured? ? 'yes' : 'no'}"
+ puts "Registered hostnames:"
+ PassengerPane::DirectoryServices.registered_hosts.each do |host|
+ puts " #{host}"
+ end
+ end
+ end
+
+ def configure
+ unless @configuration.httpd.passenger_configured?
+ @configuration.httpd.configure_passenger
+ @configuration.httpd.write
+ @configuration.httpd.restart
+ end
+ end
+
+ def add(directory)
+ options = @options.dup
+ options[:path] = File.expand_path(directory)
+ application = PassengerPane::Application.new(@configuration, options)
+ if application.save
+ @configuration.httpd.restart
+ end
+ if machine_readable?
+ puts YAML.dump(application.to_hash)
+ end
+ end
+
+ def update(host)
+ if application = PassengerPane::Application.find(@configuration, :host => host)
+ application.set(@options)
+ if application.save
+ @configuration.httpd.restart
+ end
+ if machine_readable?
+ puts YAML.dump(application.to_hash)
+ end
+ end
+ end
+
+ def delete(host)
+ if application = PassengerPane::Application.find(@configuration, :host => host)
+ application.delete
+ @configuration.httpd.restart
+ end
+ end
+
+ def list
+ if machine_readable?
+ puts YAML.dump(@configuration.applications.map do |app|
+ app.to_hash
+ end)
+ else
+ @configuration.applications.each_with_index do |app, index|
+ puts unless index == 0
+ puts "#{app.host}"
+ puts " Aliases: #{app.aliases}"
+ puts " Folder: #{app.path}"
+ puts " Environment: #{app.environment}"
+ end
+ end
+ end
+
+ def restart(host)
+ if host and application = PassengerPane::Application.find(@configuration, :host => host)
+ application.restart
+ else
+ @configuration.httpd.restart
+ end
+ end
+
+ def register
+ @configuration.applications.each do |app|
+ app.register
+ end
+ end
+
+ def self.usage
+ puts "Usage: #{File.basename($0)} <command> [options]"
+ puts
+ puts "Commands:"
+ puts " list: List all configured applications"
+ puts " register: Register all configured hostnames with Directory Services*"
+ puts " info: Show information about the system"
+ puts " configure: Configure Apache for use with the Passenger Pane*"
+ puts " add <directory>: Add an application in a directory*"
+ puts " update <host>: Update an application*"
+ puts " delete <host>: Deletes an application*"
+ puts " restart <host>: Restart an application"
+ puts " restart: Restart Apache to pick up configuration changes*"
+ puts
+ puts "* requires root privileges"
+ puts
+ puts "Options:"
+ puts " -h, --help: Show this help text"
+ puts " -m, --machine Use machine readable output"
+ puts
+ puts "Attributes:"
+ puts " --host: Hostname for the application (ie. myapp.local)"
+ puts " --aliases: Aliases for the application (ie. assets.myapp.local)"
+ puts " --path: The folder with the application"
+ puts " --environment: The environment to run, usually development or production"
+ puts " --framework: The framework, either rack or rails"
+ end
+
+ def self.run(flags, args)
+ options = {}
+ command = args.shift.to_s
+
+ return usage if command == ''
+
+ flags.each do |name, value|
+ case name
+ when 'h', 'help'
+ command = 'help'
+ when 'm', 'machine'
+ options['machine_readable'] = true
+ else
+ options[name] = value
+ end
+ end
+
+ unless $stdin.tty?
+ data = YAML.load($stdin.read)
+ data.each do |name, value|
+ options[name] = value
+ end if data
+ end
+
+ case command
+ when 'info'
+ new(options).info
+ when 'configure'
+ needs_root
+ new(options).configure
+ when 'add'
+ needs_root
+ new(options).add(args.first)
+ when 'update'
+ needs_root
+ new(options).update(args.first)
+ when 'delete'
+ needs_root
+ new(options).delete(args.first)
+ when 'restart'
+ host = args.first
+ needs_root unless host
+ new(options).restart(host)
+ when 'list'
+ new(options).list
+ when 'register'
+ needs_root
+ new(options).register
+ else
+ path = File.expand_path(command)
+ if File.exist?(path)
+ needs_root
+ new(options).add(path)
+ else
+ usage
+ end
+ end
+ end
+
+ def self.needs_root
+ puts "[!] The command might not run because it requires root privileges" unless ENV['USER'] == 'root'
+ end
+ end
+end
255 test/application_test.rb
View
@@ -0,0 +1,255 @@
+require File.expand_path('../test_helper', __FILE__)
+
+describe "Application" do
+ before do
+ @configuration = PassengerPane::Configuration.new
+ end
+
+ it "returns a glob to find all vhosts" do
+ PassengerPane::Application.glob(@configuration).should == "/private/etc/apache2/passenger_pane_vhosts/*.vhost.conf"
+ end
+end
+
+describe "Application, during initialization" do
+ before do
+ @configuration = PassengerPane::Configuration.new
+ @path = '/Users/jerry/Sites/uploader'
+ end
+
+ it "accepts additional attributes" do
+ host = 'uploader.facebook.local'
+ app = PassengerPane::Application.new(@configuration, :host => host, :path => @path)
+ app.host.should == host
+ app.config_filename.should.end_with("#{host}.#{@configuration.passenger_vhosts_ext}")
+ end
+end
+
+describe "Application, working on an existing installation" do
+ before do
+ use_fake_apache_directory
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ end
+
+ it "returns all applications" do
+ apps = PassengerPane::Application.all(@configuration)
+ apps.map { |a| a.host }.sort.should == %w(franky.local het-manfreds-blog.local het-manfreds-wiki.local staging.blog.local)
+ end
+
+ it "does not screw up vhost configurations read from disk" do
+ apps = PassengerPane::Application.all(@configuration)
+ apps.each do |app|
+ added, removed = _diff(File.read(app.config_filename), app.vhost_snippet)
+ message = "\nExpected configuration to stay the same but we added:\n#{added.join("\n")}"
+ added.should.messaging(message).be.empty
+ message = "\nExpected configuration to stay the same but we removed:\n#{removed.join("\n")}"
+ added.should.messaging(message).be.empty
+ end
+ end
+
+ it "finds an application by host" do
+ app = PassengerPane::Application.find(@configuration, :host => 'staging.blog.local')
+ app.host.should == 'staging.blog.local'
+ end
+
+ it "does not find unknown applications by host" do
+ PassengerPane::Application.find(@configuration, :host => 'unknown').should.be.nil
+ end
+
+ private
+
+ def _diff(left, right)
+ left_normalized = _normalize(left)
+ right_normalized = _normalize(right)
+
+ added = right_normalized - left_normalized
+ removed = left_normalized - right_normalized
+
+ [added, removed]
+ end
+
+ def _normalize(configuration)
+ configuration.strip.split("\n").map do |line|
+ line.strip
+ end
+ end
+end
+
+describe "A freshly added Application" do
+ before do
+ use_fake_apache_directory
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ @path = '/Users/jerry/Sites/uploader'
+ @application = PassengerPane::Application.new(@configuration, :path => @path)
+ end
+
+ it "is new" do
+ @application.should.be.new
+ end
+
+ it "derives the settings associated with the path" do
+ @application.host.should == 'uploader.local'
+ @application.aliases.should == ''
+ @application.path.should == @path
+ @application.framework.should == 'rack'
+ @application.environment.should == 'development'
+ @application.vhost_address.should == '*:80'
+ @application.config_filename.should == File.join(@configuration.apache_directory, 'passenger_pane_vhosts/uploader.local.vhost.conf')
+ end
+
+ it "converts its attributes to a virtual host configuration snippet" do
+ @application.vhost_snippet.should == %{<VirtualHost *:80>
+ ServerName uploader.local
+ DocumentRoot "/Users/jerry/Sites/uploader/public"
+ RackEnv development
+ <Directory "/Users/jerry/Sites/uploader/public">
+ Order allow,deny
+ Allow from all
+ </Directory>
+</VirtualHost>}
+ end
+
+ it "knows if the application was changed" do
+ @application.should.not.be.changed
+ @application.host = 'uploader.facebook.local'
+ @application.should.be.changed
+ @application.host = 'uploader.local'
+ @application.should.not.be.changed
+ end
+
+ it "knows which hosts were added" do
+ added = []
+ @application.added_hosts.should == added
+
+ @application.host = 'uploader.facebook.local'
+ added << @application.host
+
+ @application.added_hosts.should == added
+
+ @application.aliases = 'assets0.local assets1.local'
+ added += @application.aliases.split
+
+ @application.added_hosts.should == added
+ end
+
+ it "knows which hosts were removed" do
+ removed = []
+ @application.removed_hosts.should == removed
+
+ removed << @application.host
+ @application.host = 'uploader.facebook.local'
+
+ @application.removed_hosts.should == removed
+
+ @application.aliases = 'assets0.local assets1.local'
+
+ @application.removed_hosts.should == removed
+ end
+
+ it "registers all configured hosts" do
+ PassengerPane::DirectoryServices.expects(:register).with(@application.to_hash['hosts']).returns(true)
+ @application.register.should == true
+ end
+
+ it "unregisters all configured hosts" do
+ PassengerPane::DirectoryServices.expects(:unregister).with(@application.to_hash['hosts']).returns(true)
+ @application.unregister.should == true
+ end
+
+ it "syncs the host registration after an application was changed" do
+ @application.host = 'uploader.facebook.local'
+ @application.expects(:register).returns(true)
+ @application.sync_host_registration.should == true
+ end
+
+ it "saves all changed information to a vhost file and syncs the host registration" do
+ @application.expects(:write).returns(true)
+ @application.expects(:sync_host_registration).returns(true)
+ @application.save.should == true
+ end
+
+ it "does not sync the host registration when writing the vhost failes during a save" do
+ @application.expects(:write).returns(false)
+ @application.expects(:sync_host_registration).never
+ @application.save.should == false
+ end
+
+ it "writes its configuration file" do
+ File.should.not.exist(@application.config_filename)
+ @application.write
+ File.should.exist(@application.config_filename)
+ end
+end
+
+describe "An existing Application" do
+ before do
+ use_fake_apache_directory
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ @application = @configuration.applications.sort_by { |a| a.host}.first
+ end
+
+ it "is not new" do
+ @application.should.not.be.new
+ end
+
+ it "parses its attributes from the virtualhost configuration" do
+ @application.host.should == 'franky.local'
+ @application.aliases.should == ''
+ @application.path.should == '/Users/staging/sinatra-apps/franky'
+ @application.environment.should == 'production'
+ @application.vhost_address.should == '*:80'
+ @application.config_filename.should == File.join(fake_apache_directory, 'passenger_pane_vhosts/franky.vhost.conf')
+ @application.framework.should == 'rack'
+ end
+
+ it "returns its attributes as a hash" do
+ by_string = lambda { |k| k.to_s }
+ @application.to_hash.keys.sort_by(&by_string).should == ['hosts', *PassengerPane::Application::ATTRIBUTES.map { |a| a.to_s }].sort_by(&by_string)
+ end
+
+ it "syncs the host registration after an application was changed" do
+ @application.host = 'franky.facebook.local'
+ PassengerPane::DirectoryServices.expects(:register).with(@application.added_hosts).returns(true)
+ PassengerPane::DirectoryServices.expects(:unregister).with(@application.removed_hosts).returns(true)
+ @application.sync_host_registration.should == true
+ end
+
+ it "saves all changed information to a vhost file and syncs the host registration" do
+ @application.expects(:write).returns(true)
+ @application.expects(:sync_host_registration).returns(true)
+ @application.save.should == true
+ end
+
+ it "does not sync the host registration when writing the vhost failes during a save" do
+ @application.expects(:write).returns(false)
+ @application.expects(:sync_host_registration).never
+ @application.save.should == false
+ end
+
+ it "writes its configuration file" do
+ File.should.exist(@application.config_filename)
+ @application.write
+ File.should.exist(@application.config_filename)
+ end
+
+ it "deletes itself" do
+ @application.expects(:unregister).returns(true)
+ @application.delete.should == true
+ File.should.not.exist?(@application.config_filename)
+ end
+end
+
+describe "An Application, concerning a writeable application directory" do
+ before do
+ @path = File.join(temporary_directory, 'app')
+ FileUtils.mkdir_p(File.join(@path, 'public'))
+
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ @application = PassengerPane::Application.new(@configuration, :path => @path)
+ end
+
+ it "restarts the app" do
+ File.should.not.exist?(File.join(@path, 'tmp', 'restart.txt'))
+ @application.restart
+ File.should.exist?(File.join(@path, 'tmp', 'restart.txt'))
+ end
+end
0  test/config_installer_test.rb → test/attic/config_installer_test.rb
View
File renamed without changes
0  test/config_uninstaller_test.rb → test/attic/config_uninstaller_test.rb
View
File renamed without changes
0  test/fixtures/blog.vhost.conf → test/attic/fixtures/blog.vhost.conf
View
File renamed without changes
0  test/fixtures/franky.vhost.conf → test/attic/fixtures/franky.vhost.conf
View
File renamed without changes
0  test/fixtures/staging.vhost.conf → test/attic/fixtures/staging.vhost.conf
View
File renamed without changes
0  test/fixtures/wiki.vhost.conf → test/attic/fixtures/wiki.vhost.conf
View
File renamed without changes
0  test/hosts_installer_test.rb → test/attic/hosts_installer_test.rb
View
File renamed without changes
0  test/passenger_application_test.rb → test/attic/passenger_application_test.rb
View
File renamed without changes
0  test/passenger_pref_test.rb → test/attic/passenger_pref_test.rb
View
File renamed without changes
0  test/shared_passenger_behaviour_test.rb → test/attic/shared_passenger_behaviour_test.rb
View
File renamed without changes
53 test/attic/test_helper.rb
View
@@ -0,0 +1,53 @@
+require 'osx/cocoa'
+require 'test/unit'
+require 'rubygems' rescue LoadError
+require 'test/spec'
+require 'mocha'
+require 'rucola/test_case'
+
+module Rucola::TestCase::InstanceMethods
+ alias_method :pref_pane, :controller
+ alias_method :passenger_app, :controller
+end
+
+$: << File.expand_path('../../', __FILE__)
+
+require File.expand_path('../../app/utils/shared_passenger_behaviour', __FILE__)
+module SharedPassengerBehaviour
+ # Silencio!
+ def log(obj); end
+ module_function :log
+end
+
+# Silencio!
+def OSX.NSLog(*args); end
+
+class OSX::SecurityHelper
+ def self.sharedInstance
+ @sharedInstance ||= new
+ end
+
+ def authorizationRef=(ref)
+ @authorized = !ref.nil?
+ end
+
+ def authorized
+ @authorized ||= false
+ end
+
+ def deauthorize
+ @authorized = false
+ end
+
+ def authorized?
+ @authorized
+ end
+end
+
+class OSX::HelpHelper
+ def self.registerBooksInBundle(bundle)
+ end
+end
+
+ENV['TESTING_PASSENGER_PREF'] = 'true'
+require File.expand_path('../../app/controllers/passenger_pref', __FILE__)
35 test/configuration_test.rb
View
@@ -0,0 +1,35 @@
+require File.expand_path('../test_helper', __FILE__)
+
+describe "Configuration" do
+ it "raises an argument error when an unknown configuration key is set" do
+ configuration = PassengerPane::Configuration.new
+ lambda {
+ begin
+ configuration.set({'unknown' => 'value'})
+ rescue ArgumentError => e
+ e.message.should.start_with('There is no configuration named `unknown\'')
+ end
+ }.should.not.raise
+ end
+
+ it "initializes a configuration with default values" do
+ configuration = PassengerPane::Configuration.new
+ end
+
+ it "returns all applications" do
+ apps = mock('Applications')
+ PassengerPane::Application.stubs(:all).returns(apps)
+ PassengerPane::Configuration.new.applications.should == apps
+ end
+end
+
+describe "A Configuration" do
+ before do
+ use_fake_apache_directory
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ end
+
+ it "returns an httpd configuration instance" do
+ @configuration.httpd.should.be.kind_of?(PassengerPane::HttpdConf)
+ end
+end
25 test/directory_services_test.rb
View
@@ -0,0 +1,25 @@
+require File.expand_path('../test_helper', __FILE__)
+
+describe "DirectoryServices" do
+ it "returns registered hosts" do
+ expected = %w(assets0.local assets1.local)
+ PassengerPane::DirectoryServices.expects(:`).with('/usr/bin/dscl localhost -list /Local/Default/Hosts').returns(expected.join("\n"))
+ PassengerPane::DirectoryServices.registered_hosts.should == expected
+ end
+
+ it "registers hosts" do
+ hosts = %w(assets0.local assets1.local)
+ hosts.each do |host|
+ PassengerPane::DirectoryServices.expects(:system).with("/usr/bin/dscl localhost -create /Local/Default/Hosts/#{host} IPAddress 127.0.0.1")
+ end
+ PassengerPane::DirectoryServices.register(hosts)
+ end
+
+ it "unregisters hosts" do
+ hosts = %w(assets0.local assets1.local)
+ hosts.each do |host|
+ PassengerPane::DirectoryServices.expects(:system).with("/usr/bin/dscl localhost -delete /Local/Default/Hosts/#{host}")
+ end
+ PassengerPane::DirectoryServices.unregister(hosts)
+ end
+end
509 test/fake/etc/apache2/httpd.conf
View
@@ -0,0 +1,509 @@
+#
+# This is the main Apache HTTP server configuration file. It contains the
+# configuration directives that give the server its instructions.
+# See <URL:http://httpd.apache.org/docs/2.2> for detailed information.
+# In particular, see
+# <URL:http://httpd.apache.org/docs/2.2/mod/directives.html>
+# for a discussion of each configuration directive.
+#
+# Do NOT simply read the instructions in here without understanding
+# what they do. They're here only as hints or reminders. If you are unsure
+# consult the online docs. You have been warned.
+#
+# Configuration and logfile names: If the filenames you specify for many
+# of the server's control files begin with "/" (or "drive:/" for Win32), the
+# server will use that explicit path. If the filenames do *not* begin
+# with "/", the value of ServerRoot is prepended -- so "/private/var/log/apache2/foo_log"
+# with ServerRoot set to "/usr" will be interpreted by the
+# server as "/usr//private/var/log/apache2/foo_log".
+
+#
+# ServerRoot: The top of the directory tree under which the server's
+# configuration, error, and log files are kept.
+#
+# Do not add a slash at the end of the directory path. If you point
+# ServerRoot at a non-local disk, be sure to point the LockFile directive
+# at a local disk. If you wish to share the same ServerRoot for multiple
+# httpd daemons, you will need to change at least LockFile and PidFile.
+#
+ServerRoot "/usr"
+
+#
+# Listen: Allows you to bind Apache to specific IP addresses and/or
+# ports, instead of the default. See also the <VirtualHost>
+# directive.
+#
+# Change this to Listen on specific IP addresses as shown below to
+# prevent Apache from glomming onto all bound IP addresses.
+#
+#Listen 12.34.56.78:80
+Listen 80
+
+#
+# Dynamic Shared Object (DSO) Support
+#
+# To be able to use the functionality of a module which was built as a DSO you
+# have to place corresponding `LoadModule' lines at this location so the
+# directives contained in it are actually available _before_ they are used.
+# Statically compiled modules (those listed by `httpd -l') do not need
+# to be loaded here.
+#
+# Example:
+# LoadModule foo_module modules/mod_foo.so
+#
+LoadModule authn_file_module libexec/apache2/mod_authn_file.so
+LoadModule authn_dbm_module libexec/apache2/mod_authn_dbm.so
+LoadModule authn_anon_module libexec/apache2/mod_authn_anon.so
+LoadModule authn_dbd_module libexec/apache2/mod_authn_dbd.so
+LoadModule authn_default_module libexec/apache2/mod_authn_default.so
+LoadModule authz_host_module libexec/apache2/mod_authz_host.so
+LoadModule authz_groupfile_module libexec/apache2/mod_authz_groupfile.so
+LoadModule authz_user_module libexec/apache2/mod_authz_user.so
+LoadModule authz_dbm_module libexec/apache2/mod_authz_dbm.so
+LoadModule authz_owner_module libexec/apache2/mod_authz_owner.so
+LoadModule authz_default_module libexec/apache2/mod_authz_default.so
+LoadModule auth_basic_module libexec/apache2/mod_auth_basic.so
+LoadModule auth_digest_module libexec/apache2/mod_auth_digest.so
+LoadModule cache_module libexec/apache2/mod_cache.so
+LoadModule disk_cache_module libexec/apache2/mod_disk_cache.so
+LoadModule mem_cache_module libexec/apache2/mod_mem_cache.so
+LoadModule dbd_module libexec/apache2/mod_dbd.so
+LoadModule dumpio_module libexec/apache2/mod_dumpio.so
+LoadModule ext_filter_module libexec/apache2/mod_ext_filter.so
+LoadModule include_module libexec/apache2/mod_include.so
+LoadModule filter_module libexec/apache2/mod_filter.so
+LoadModule substitute_module libexec/apache2/mod_substitute.so
+LoadModule deflate_module libexec/apache2/mod_deflate.so
+LoadModule log_config_module libexec/apache2/mod_log_config.so
+LoadModule log_forensic_module libexec/apache2/mod_log_forensic.so
+LoadModule logio_module libexec/apache2/mod_logio.so
+LoadModule env_module libexec/apache2/mod_env.so
+LoadModule mime_magic_module libexec/apache2/mod_mime_magic.so
+LoadModule cern_meta_module libexec/apache2/mod_cern_meta.so
+LoadModule expires_module libexec/apache2/mod_expires.so
+LoadModule headers_module libexec/apache2/mod_headers.so
+LoadModule ident_module libexec/apache2/mod_ident.so
+LoadModule usertrack_module libexec/apache2/mod_usertrack.so
+#LoadModule unique_id_module libexec/apache2/mod_unique_id.so
+LoadModule setenvif_module libexec/apache2/mod_setenvif.so
+LoadModule version_module libexec/apache2/mod_version.so
+LoadModule proxy_module libexec/apache2/mod_proxy.so
+LoadModule proxy_connect_module libexec/apache2/mod_proxy_connect.so
+LoadModule proxy_ftp_module libexec/apache2/mod_proxy_ftp.so
+LoadModule proxy_http_module libexec/apache2/mod_proxy_http.so
+LoadModule proxy_scgi_module libexec/apache2/mod_proxy_scgi.so
+LoadModule proxy_ajp_module libexec/apache2/mod_proxy_ajp.so
+LoadModule proxy_balancer_module libexec/apache2/mod_proxy_balancer.so
+LoadModule ssl_module libexec/apache2/mod_ssl.so
+LoadModule mime_module libexec/apache2/mod_mime.so
+LoadModule dav_module libexec/apache2/mod_dav.so
+LoadModule status_module libexec/apache2/mod_status.so
+LoadModule autoindex_module libexec/apache2/mod_autoindex.so
+LoadModule asis_module libexec/apache2/mod_asis.so
+LoadModule info_module libexec/apache2/mod_info.so
+LoadModule cgi_module libexec/apache2/mod_cgi.so
+LoadModule dav_fs_module libexec/apache2/mod_dav_fs.so
+LoadModule vhost_alias_module libexec/apache2/mod_vhost_alias.so
+LoadModule negotiation_module libexec/apache2/mod_negotiation.so
+LoadModule dir_module libexec/apache2/mod_dir.so
+LoadModule imagemap_module libexec/apache2/mod_imagemap.so
+LoadModule actions_module libexec/apache2/mod_actions.so
+LoadModule speling_module libexec/apache2/mod_speling.so
+LoadModule userdir_module libexec/apache2/mod_userdir.so
+LoadModule alias_module libexec/apache2/mod_alias.so
+LoadModule rewrite_module libexec/apache2/mod_rewrite.so
+LoadModule bonjour_module libexec/apache2/mod_bonjour.so
+#LoadModule php5_module libexec/apache2/libphp5.so
+#LoadModule fastcgi_module libexec/apache2/mod_fastcgi.so
+
+<IfModule !mpm_netware_module>
+<IfModule !mpm_winnt_module>
+#
+# If you wish httpd to run as a different user or group, you must run
+# httpd as root initially and it will switch.
+#
+# User/Group: The name (or #number) of the user/group to run httpd as.
+# It is usually good practice to create a dedicated user and group for
+# running httpd, as with most system services.
+#
+User _www
+Group _www
+
+</IfModule>
+</IfModule>
+
+# 'Main' server configuration
+#
+# The directives in this section set up the values used by the 'main'
+# server, which responds to any requests that aren't handled by a
+# <VirtualHost> definition. These values also provide defaults for
+# any <VirtualHost> containers you may define later in the file.
+#
+# All of these directives may appear inside <VirtualHost> containers,
+# in which case these default settings will be overridden for the
+# virtual host being defined.
+#
+
+#
+# ServerAdmin: Your address, where problems with the server should be
+# e-mailed. This address appears on some server-generated pages, such
+# as error documents. e.g. admin@your-domain.com
+#
+ServerAdmin you@example.com
+
+#
+# ServerName gives the name and port that the server uses to identify itself.
+# This can often be determined automatically, but we recommend you specify
+# it explicitly to prevent problems during startup.
+#
+# If your host doesn't have a registered DNS name, enter its IP address here.
+#
+#ServerName www.example.com:80
+
+#
+# DocumentRoot: The directory out of which you will serve your
+# documents. By default, all requests are taken from this directory, but
+# symbolic links and aliases may be used to point to other locations.
+#
+DocumentRoot "/Library/WebServer/Documents"
+
+#
+# Each directory to which Apache has access can be configured with respect
+# to which services and features are allowed and/or disabled in that
+# directory (and its subdirectories).
+#
+# First, we configure the "default" to be a very restrictive set of
+# features.
+#
+<Directory />
+ Options FollowSymLinks
+ AllowOverride None
+ Order deny,allow
+ Deny from all
+</Directory>
+
+#
+# Note that from this point forward you must specifically allow
+# particular features to be enabled - so if something's not working as
+# you might expect, make sure that you have specifically enabled it
+# below.
+#
+
+#
+# This should be changed to whatever you set DocumentRoot to.
+#
+<Directory "/Library/WebServer/Documents">
+ #
+ # Possible values for the Options directive are "None", "All",
+ # or any combination of:
+ # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
+ #
+ # Note that "MultiViews" must be named *explicitly* --- "Options All"
+ # doesn't give it to you.
+ #
+ # The Options directive is both complicated and important. Please see
+ # http://httpd.apache.org/docs/2.2/mod/core.html#options
+ # for more information.
+ #
+ Options Indexes FollowSymLinks MultiViews
+
+ #
+ # AllowOverride controls what directives may be placed in .htaccess files.
+ # It can be "All", "None", or any combination of the keywords:
+ # Options FileInfo AuthConfig Limit
+ #
+ AllowOverride None
+
+ #
+ # Controls who can get stuff from this server.
+ #
+ Order allow,deny
+ Allow from all
+
+</Directory>
+
+#
+# DirectoryIndex: sets the file that Apache will serve if a directory
+# is requested.
+#
+<IfModule dir_module>
+ DirectoryIndex index.html
+</IfModule>
+
+#
+# The following lines prevent .htaccess and .htpasswd files from being
+# viewed by Web clients.
+#
+<FilesMatch "^\.([Hh][Tt]|[Dd][Ss]_[Ss])">
+ Order allow,deny
+ Deny from all
+ Satisfy All
+</FilesMatch>
+
+#
+# Apple specific filesystem protection.
+#
+<Files "rsrc">
+ Order allow,deny
+ Deny from all
+ Satisfy All
+</Files>
+<DirectoryMatch ".*\.\.namedfork">
+ Order allow,deny
+ Deny from all
+ Satisfy All
+</DirectoryMatch>
+
+#
+# ErrorLog: The location of the error log file.
+# If you do not specify an ErrorLog directive within a <VirtualHost>
+# container, error messages relating to that virtual host will be
+# logged here. If you *do* define an error logfile for a <VirtualHost>
+# container, that host's errors will be logged there and not here.
+#
+ErrorLog "/private/var/log/apache2/error_log"
+
+#
+# LogLevel: Control the number of messages logged to the error_log.
+# Possible values include: debug, info, notice, warn, error, crit,
+# alert, emerg.
+#
+LogLevel warn
+
+<IfModule log_config_module>
+ #
+ # The following directives define some format nicknames for use with
+ # a CustomLog directive (see below).
+ #
+ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+ LogFormat "%h %l %u %t \"%r\" %>s %b" common
+
+ <IfModule logio_module>
+ # You need to enable mod_logio.c to use %I and %O
+ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
+ </IfModule>
+
+ #
+ # The location and format of the access logfile (Common Logfile Format).
+ # If you do not define any access logfiles within a <VirtualHost>
+ # container, they will be logged here. Contrariwise, if you *do*
+ # define per-<VirtualHost> access logfiles, transactions will be
+ # logged therein and *not* in this file.
+ #
+ CustomLog "/private/var/log/apache2/access_log" common
+
+ #
+ # If you prefer a logfile with access, agent, and referer information
+ # (Combined Logfile Format) you can use the following directive.
+ #
+ #CustomLog "/private/var/log/apache2/access_log" combined
+</IfModule>
+
+<IfModule alias_module>
+ #
+ # Redirect: Allows you to tell clients about documents that used to
+ # exist in your server's namespace, but do not anymore. The client
+ # will make a new request for the document at its new location.
+ # Example:
+ # Redirect permanent /foo http://www.example.com/bar
+
+ #
+ # Alias: Maps web paths into filesystem paths and is used to
+ # access content that does not live under the DocumentRoot.
+ # Example:
+ # Alias /webpath /full/filesystem/path
+ #
+ # If you include a trailing / on /webpath then the server will
+ # require it to be present in the URL. You will also likely
+ # need to provide a <Directory> section to allow access to
+ # the filesystem path.
+
+ #
+ # ScriptAlias: This controls which directories contain server scripts.
+ # ScriptAliases are essentially the same as Aliases, except that
+ # documents in the target directory are treated as applications and
+ # run by the server when requested rather than as documents sent to the
+ # client. The same rules about trailing "/" apply to ScriptAlias
+ # directives as to Alias.
+ #
+ ScriptAliasMatch ^/cgi-bin/((?!(?i:webobjects)).*$) "/Library/WebServer/CGI-Executables/$1"
+
+</IfModule>
+
+<IfModule cgid_module>
+ #
+ # ScriptSock: On threaded servers, designate the path to the UNIX
+ # socket used to communicate with the CGI daemon of mod_cgid.
+ #
+ #Scriptsock /private/var/run/cgisock
+</IfModule>
+
+#
+# "/Library/WebServer/CGI-Executables" should be changed to whatever your ScriptAliased
+# CGI directory exists, if you have that configured.
+#
+<Directory "/Library/WebServer/CGI-Executables">
+ AllowOverride None
+ Options None
+ Order allow,deny
+ Allow from all
+</Directory>
+
+#
+# DefaultType: the default MIME type the server will use for a document
+# if it cannot otherwise determine one, such as from filename extensions.
+# If your server contains mostly text or HTML documents, "text/plain" is
+# a good value. If most of your content is binary, such as applications
+# or images, you may want to use "application/octet-stream" instead to
+# keep browsers from trying to display binary files as though they are
+# text.
+#
+DefaultType text/plain
+
+<IfModule mime_module>
+ #
+ # TypesConfig points to the file containing the list of mappings from
+ # filename extension to MIME-type.
+ #
+ TypesConfig /private/etc/apache2/mime.types
+
+ #
+ # AddType allows you to add to or override the MIME configuration
+ # file specified in TypesConfig for specific file types.
+ #
+ #AddType application/x-gzip .tgz
+ #
+ # AddEncoding allows you to have certain browsers uncompress
+ # information on the fly. Note: Not all browsers support this.
+ #
+ #AddEncoding x-compress .Z
+ #AddEncoding x-gzip .gz .tgz
+ #
+ # If the AddEncoding directives above are commented-out, then you
+ # probably should define those extensions to indicate media types:
+ #
+ AddType application/x-compress .Z
+ AddType application/x-gzip .gz .tgz
+
+ #
+ # AddHandler allows you to map certain file extensions to "handlers":
+ # actions unrelated to filetype. These can be either built into the server
+ # or added with the Action directive (see below)
+ #
+ # To use CGI scripts outside of ScriptAliased directories:
+ # (You will also need to add "ExecCGI" to the "Options" directive.)
+ #
+ #AddHandler cgi-script .cgi
+
+ # For type maps (negotiated resources):
+ #AddHandler type-map var
+
+ #
+ # Filters allow you to process content before it is sent to the client.
+ #
+ # To parse .shtml files for server-side includes (SSI):
+ # (You will also need to add "Includes" to the "Options" directive.)
+ #
+ #AddType text/html .shtml
+ #AddOutputFilter INCLUDES .shtml
+</IfModule>
+
+#
+# The mod_mime_magic module allows the server to use various hints from the
+# contents of the file itself to determine its type. The MIMEMagicFile
+# directive tells the module where the hint definitions are located.
+#
+#MIMEMagicFile /private/etc/apache2/magic
+
+#
+# Customizable error responses come in three flavors:
+# 1) plain text 2) local redirects 3) external redirects
+#
+# Some examples:
+#ErrorDocument 500 "The server made a boo boo."
+#ErrorDocument 404 /missing.html
+#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
+#ErrorDocument 402 http://www.example.com/subscription_info.html
+#
+
+#
+# EnableMMAP and EnableSendfile: On systems that support it,
+# memory-mapping or the sendfile syscall is used to deliver
+# files. This usually improves server performance, but must
+# be turned off when serving from networked-mounted
+# filesystems or if support for these functions is otherwise
+# broken on your system.
+#
+#EnableMMAP off
+#EnableSendfile off
+
+# 6894961
+TraceEnable off
+
+# Supplemental configuration
+#
+# The configuration files in the /private/etc/apache2/extra/ directory can be
+# included to add extra features or to modify the default configuration of
+# the server, or you may simply copy their contents here and change as
+# necessary.
+
+# Server-pool management (MPM specific)
+Include /private/etc/apache2/extra/httpd-mpm.conf
+
+# Multi-language error messages
+#Include /private/etc/apache2/extra/httpd-multilang-errordoc.conf
+
+# Fancy directory listings
+Include /private/etc/apache2/extra/httpd-autoindex.conf
+
+# Language settings
+Include /private/etc/apache2/extra/httpd-languages.conf
+
+# User home directories
+Include /private/etc/apache2/extra/httpd-userdir.conf
+
+# Real-time info on requests and configuration
+#Include /private/etc/apache2/extra/httpd-info.conf
+
+# Virtual hosts
+#Include /private/etc/apache2/extra/httpd-vhosts.conf
+
+# Local access to the Apache HTTP Server Manual
+Include /private/etc/apache2/extra/httpd-manual.conf
+
+# Distributed authoring and versioning (WebDAV)
+#Include /private/etc/apache2/extra/httpd-dav.conf
+
+# Various default settings
+#Include /private/etc/apache2/extra/httpd-default.conf
+
+# Secure (SSL/TLS) connections
+#Include /private/etc/apache2/extra/httpd-ssl.conf
+#
+# Note: The following must must be present to support
+# starting without SSL on platforms with no /dev/random equivalent
+# but a statically compiled-in mod_ssl.
+#
+<IfModule ssl_module>
+SSLRandomSeed startup builtin
+SSLRandomSeed connect builtin
+</IfModule>
+
+LoadModule passenger_module /Library/Ruby/Gems/1.8/gems/passenger-3.0.0/ext/apache2/mod_passenger.so
+PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-3.0.0
+PassengerRuby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
+
+Include /private/etc/apache2/other/*.conf
+
+# Added by the Passenger preference pane
+# Make sure to include the Passenger configuration (the LoadModule,
+# PassengerRoot, and PassengerRuby directives) before this section.
+<IfModule passenger_module>
+ NameVirtualHost *:80
+ <VirtualHost *:80>
+ ServerName _default_
+ </VirtualHost>
+ Include /private/etc/apache2/passenger_pane_vhosts/*.conf
+</IfModule>
+
+Include /private/etc/apache2/manual_vhosts/*.conf
10 test/fake/etc/apache2/passenger_pane_vhosts/blog.vhost.conf
View
@@ -0,0 +1,10 @@
+<VirtualHost *:80>
+ ServerName het-manfreds-blog.local
+ ServerAlias manfred-s-blog.local my-blog.local
+ DocumentRoot "/Users/het-manfred/rails code/blog/public"
+ RailsEnv development
+ <Directory "/Users/het-manfred/rails code/blog/public">
+ Order allow,deny
+ Allow from all
+ </Directory>
+</VirtualHost>
9 test/fake/etc/apache2/passenger_pane_vhosts/franky.vhost.conf
View
@@ -0,0 +1,9 @@
+<VirtualHost *:80>
+ ServerName franky.local
+ DocumentRoot "/Users/staging/sinatra-apps/franky/public"
+ RackEnv production
+ <Directory "/Users/staging/sinatra-apps/franky/public">
+ Order allow,deny
+ Allow from all
+ </Directory>
+</VirtualHost>
9 test/fake/etc/apache2/passenger_pane_vhosts/staging.vhost.conf
View
@@ -0,0 +1,9 @@
+<VirtualHost *:80>
+ ServerName staging.blog.local
+ DocumentRoot "/Users/staging/apps/blog/public"
+ RailsEnv staging
+ <Directory "/Users/staging/apps/blog/public">
+ Order allow,deny
+ Allow from all
+ </Directory>
+</VirtualHost>
12 test/fake/etc/apache2/passenger_pane_vhosts/wiki.vhost.conf
View
@@ -0,0 +1,12 @@
+<VirtualHost het-manfreds-wiki.local:443>
+ ServerName het-manfreds-wiki.local
+ BindAddress 192.168.0.123
+ DocumentRoot "/Users/het-manfred/rails code/wiki/public"
+ RailsEnv production
+ <Location "/">
+ AuthType Basic
+ AuthName "Development Preview"
+ AuthUserFile /home2/cogat/htpasswd
+ Require valid-user
+ </Location>
+</VirtualHost>
126 test/httpd_conf_test.rb
View
@@ -0,0 +1,126 @@
+require File.expand_path('../test_helper', __FILE__)
+
+describe "A HttpdConf" do
+ before do
+ @configuration = PassengerPane::Configuration.new
+ @httpd = PassengerPane::HttpdConf.new(@configuration)
+ end
+
+ it "returns an include line for the passenger vhosts" do
+ @httpd.passenger_vhost_include.should == "Include /private/etc/apache2/passenger_pane_vhosts/*.vhost.conf"
+ end
+
+ it "returns a passenger configuration snippet" do
+ snippet = @httpd.passenger_configuration_snippet
+ snippet.should.include("<IfModule passenger_module>")
+ snippet.should.include('NameVirtualHost')
+ snippet.should.include('VirtualHost')
+ snippet.should.include(@httpd.passenger_vhost_include)
+ snippet.should.include("</IfModule>")
+ end
+
+ it "knows when the passenger module is installed" do
+ @httpd.expects(:`).with('/usr/sbin/httpd -D DUMP_MODULES 2>&1').returns(%{
+Loaded Modules:
+ core_module (static)
+ rewrite_module (shared)
+ bonjour_module (shared)
+ passenger_module (shared)
+Syntax OK
+ }.strip)
+ @httpd.passenger_module_installed?.should == true
+ end
+
+ it "knows when the passenger module is not installed" do
+ @httpd.expects(:`).with('/usr/sbin/httpd -D DUMP_MODULES 2>&1').returns(%{
+Loaded Modules:
+ core_module (static)
+ rewrite_module (shared)
+ bonjour_module (shared)
+Syntax OK
+ }.strip)
+ @httpd.passenger_module_installed?.should == false
+ end
+end
+
+describe "A HttpdConf, with passenger configuration" do
+ before do
+ use_fake_apache_directory
+ @configuration = PassengerPane::Configuration.new(fake_apache_directory)
+ @httpd = @configuration.httpd
+ contents = %{
+# Secure (SSL/TLS) connections
+#Include /private/etc/apache2/extra/httpd-ssl.conf
+#
+# Note: The following must must be present to support
+# starting without SSL on platforms with no /dev/random equivalent
+# but a statically compiled-in mod_ssl.
+#
+<IfModule ssl_module>
+SSLRandomSeed startup builtin
+SSLRandomSeed connect builtin
+</IfModule>
+
+LoadModule passenger_module /Library/Ruby/Gems/1.8/gems/passenger-3.0.0/ext/apache2/mod_passenger.so
+PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-3.0.0
+PassengerRuby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
+
+Include /private/etc/apache2/other/*.conf
+
+# Added by the Passenger preference pane
+# Make sure to include the Passenger configuration (the LoadModule,
+# PassengerRoot, and PassengerRuby directives) before this section.
+<IfModule passenger_module>
+ NameVirtualHost *:80
+ <VirtualHost *:80>
+ ServerName _default_
+ </VirtualHost>
+ Include /private/etc/apache2/passenger_pane_vhosts/*.conf
+</IfModule>
+
+Include /private/etc/apache2/manual_vhosts/*.conf
+ }
+ File.open(@httpd.filename, 'w') do |file|
+ file.write(contents)
+ end
+ end
+
+ it "has passenger configured" do
+ @httpd.should.be.passenger_configured
+ end
+
+ it "restarts apache" do
+ @httpd.expects(:valid?).returns(true)
+ @httpd.expects(:system).with(@configuration.apache_restart_command)
+ @httpd.restart
+ end
+
+ it "does not restart when the configuration isn't valid" do
+ @httpd.expects(:valid?).returns(false)
+ @httpd.expects(:system).with(@configuration.apache_restart_command).never
+ capture_stdout do
+ @httpd.restart
+ end.should == "[!] Apache configuration is not valid, skipping Apache restart\n"
+ end
+
+end
+
+describe "A HttpdConf, without passenger configuration" do
+ before do
+ use_fake_apache_directory
+ @httpd = PassengerPane::Configuration.new(fake_apache_directory).httpd
+ File.open(@httpd.filename, 'w') do |file|
+ file.write('')
+ end
+ end
+
+ it "does not have passenger configured" do
+ @httpd.should.not.be.passenger_configured
+ end
+
+ it "configures passenger" do
+ @httpd.configure_passenger
+ @httpd.write
+ @httpd.should.be.passenger_configured
+ end
+end
122 test/runner_test.rb
View
@@ -0,0 +1,122 @@
+require File.expand_path('../test_helper', __FILE__)
+
+describe "Runner" do
+ before do
+ use_fake_apache_directory
+
+ application_directory = File.join(temporary_directory, 'app')
+ %w(app public).each do |directory|
+ FileUtils.mkdir_p(File.join(application_directory, directory))
+ end
+
+ @conf = PassengerPane::Configuration.new(fake_apache_directory)
+ @app = PassengerPane::Application.new(@conf, :path => application_directory)
+
+ PassengerPane::Configuration.stubs(:auto).returns(@conf)
+ end
+
+ it "lists all configured applications" do
+ output = capture_stdout do
+ PassengerPane::Runner.run({}, %w(list))
+ end
+ @conf.applications.each do |app|
+ output.should.include?(app.host)
+ end
+ end
+
+ it "registers all configured hostnames" do
+ @conf.applications.each do |app|
+ PassengerPane::DirectoryServices.expects(:register).with(app.to_hash['hosts'])
+ end
+ capture_stdout do
+ PassengerPane::Runner.run({}, %w(register))
+ end
+ end
+
+ it "shows information about the system" do
+ @conf.httpd.stubs(:passenger_module_installed?).returns(true)
+ PassengerPane::DirectoryServices.stubs(:registered_hosts).returns(%w(assets.skit.local skit.local weblog.local))
+
+ output = capture_stdout do
+ PassengerPane::Runner.run({}, %w(info))
+ end
+
+ output.should.include('Passenger installed: yes')
+ output.should.include('Passenger configured: yes')
+ end
+
+ it "configures Apache for use with the Passenger Pane" do
+ Kernel.allow_backtick = true
+ @conf.httpd.stubs(:restart)
+ File.open(@conf.httpd.filename, 'w') { |file| file.write('') }
+
+ capture_stdout do
+ PassengerPane::Runner.run({}, %w(configure))
+ end
+
+ @conf.httpd.should.be.passenger_configured
+ end
+
+ it "does not configure Apache for use with the Passenger Pane if it's already configured" do
+ @conf.httpd.expects(:write).never
+ capture_stdout do
+ PassengerPane::Runner.run({}, %w(configure))
+ end
+ end
+
+ it "adds a new application to the configuration" do
+ PassengerPane::DirectoryServices.expects(:register).with(%w(app.local))
+ capture_stdout do
+ PassengerPane::Runner.run({}, ['add', @app.path])
+ end
+ @conf.applications.map { |app| app.host }.should.include('app.local')
+ end
+
+ it "updates an application" do
+ @conf.httpd.stubs(:restart)
+ PassengerPane::DirectoryServices.expects(:register).with(%w(blog.local)).returns(true)
+ PassengerPane::DirectoryServices.expects(:unregister).with(%w(staging.blog.local)).returns(true)
+
+ app = PassengerPane::Application.find(@conf, :host => 'staging.blog.local')
+ capture_stdout do
+ PassengerPane::Runner.run({'host' => 'blog.local'}, ['update', app.host])
+ end
+
+ app.contents = nil
+ app._parse
+ app.host.should == 'blog.local'
+ end
+
+ it "deletes an application" do
+ @conf.httpd.stubs(:restart)
+ PassengerPane::DirectoryServices.expects(:unregister).with(%w(staging.blog.local)).returns(true)
+
+ app = PassengerPane::Application.find(@conf, :host => 'staging.blog.local')
+ capture_stdout do
+ PassengerPane::Runner.run({}, ['delete', app.host])
+ end
+
+ File.should.not.exist?(app.config_filename)
+ end
+
+ it "restarts an application" do
+ PassengerPane::DirectoryServices.stubs(:register).returns(true)
+ @app.should.save
+
+ File.should.not.exist?(File.join(@app.path, 'tmp', 'restart.txt'))
+ capture_stdout do
+ PassengerPane::Runner.run({}, ['restart', @app.host])
+ end
+ File.should.exist?(File.join(@app.path, 'tmp', 'restart.txt'))
+ end
+
+ it "restarts Apache" do
+ @conf.httpd.expects(:system).with(@conf.apache_restart_command).returns(true)
+ capture_stdout do
+ PassengerPane::Runner.run({}, %w(restart))
+ end
+ end
+end
+
+describe "Runner, interacting through YAML" do
+end
67 test/test_helper.rb
View
@@ -1,53 +1,26 @@
-require 'osx/cocoa'
-require 'test/unit'
-require 'rubygems' rescue LoadError
-require 'test/spec'
-require 'mocha'
-require 'rucola/test_case'
-
-module Rucola::TestCase::InstanceMethods
- alias_method :pref_pane, :controller
- alias_method :passenger_app, :controller
+begin
+ require 'rubygems'
+rescue LoadError
end
-$: << File.expand_path('../../', __FILE__)
-
-require File.expand_path('../../app/utils/shared_passenger_behaviour', __FILE__)
-module SharedPassengerBehaviour
- # Silencio!
- def log(obj); end
- module_function :log
-end
+require 'test/spec'
+require 'mocha'
-# Silencio!
-def OSX.NSLog(*args); end
+$:.unshift File.expand_path('../../lib', __FILE__)
+require 'passenger_pane'
-class OSX::SecurityHelper
- def self.sharedInstance
- @sharedInstance ||= new
- end
-
- def authorizationRef=(ref)
- @authorized = !ref.nil?
- end
-
- def authorized
- @authorized ||= false
- end
-
- def deauthorize
- @authorized = false
- end
-
- def authorized?
- @authorized
- end
-end
+$:.unshift(File.expand_path('../test_helper', __FILE__))
+require 'collector'
+require 'capture_output'
+require 'temporary_directory'
+require 'fake_apache_directory'
+require 'add_allow_switch'
-class OSX::HelpHelper
- def self.registerBooksInBundle(bundle)
- end
-end
+Kernel.add_allow_switch(:system)
+Kernel.add_allow_switch(:`)
-ENV['TESTING_PASSENGER_PREF'] = 'true'
-require File.expand_path('../../app/controllers/passenger_pref', __FILE__)
+module Test::Spec::TestCase::InstanceMethods
+ include TestHelper::CaptureOutput
+ include TestHelper::TemporaryDirectory
+ include TestHelper::FakeApacheDirectory
+end
63 test/test_helper/add_allow_switch.rb
View
@@ -0,0 +1,63 @@
+module AllowSwitch
+ def self.replacement_method_code(object, method, accessor)
+ temp_method = "_#{accessor}_before_allow_switch"
+ %{
+alias_method :#{temp_method}, :#{method}
+def #{method}(*args, &block)
+ if #{object}.allow_#{accessor}
+ #{temp_method}(*args, &block)
+ else
+ raise RuntimeError, "You're trying to call `#{method}' on `#{object}', which you probably don't want in a test."
+ end
+end
+ }.strip
+ end
+
+ def self.allow_switch_accessor(accessor)
+ allow = "allow_#{accessor}"
+ %{
+def self.#{allow}
+ @#{allow}
+end
+
+def self.#{allow}=(value)
+ @#{allow} = value
+end
+ }.strip
+ end
+
+ def self.switch_accessor(method)
+ if method.to_s == "`"
+ 'backtick'
+ else
+ method.to_sym
+ end
+ end
+end
+
+
+class Class
+ def add_allow_switch(method, options={})
+ default = options[:default] || false
+ accessor = AllowSwitch.switch_accessor(method)
+ class_eval AllowSwitch.allow_switch_accessor(accessor)
+ self.send("allow_#{accessor}=", default)
+ class_eval AllowSwitch.replacement_method_code(self, method, accessor)
+ end
+end
+
+class Module
+ def add_allow_switch(method, options={})
+ default = options[:default] || false
+ accessor = AllowSwitch.switch_accessor(method)
+ eval AllowSwitch.allow_switch_accessor(accessor)
+ self.send("allow_#{accessor}=", default)
+ replacement_method_code = AllowSwitch.replacement_method_code(self, method, accessor)
+ if respond_to?(method)
+ (class << self; self; end).class_eval(replacement_method_code)
+ end
+ if self.methods.include?('system')
+ class_eval(replacement_method_code)
+ end
+ end
+end
151 test/test_helper/add_allow_switch_test.rb
View
@@ -0,0 +1,151 @@
+begin
+ require 'rubygems'
+rescue LoadError
+end
+
+gem 'test-spec'
+require 'test/spec'
+
+$:.unshift(File.expand_path('../', __FILE__))
+require 'add_allow_switch'
+
+SILENT_COMMAND = 'ls > /dev/null'
+
+module Factory
+ def self.run
+ true
+ end
+
+ def self.ran(name, &block)
+ block.call("#{name} was run")
+ end
+end
+Factory.add_allow_switch :run, :default => true
+Factory.add_allow_switch :ran
+
+describe "Factory with an allow switch on run" do
+ it "should alias the original method" do
+ Factory.respond_to?(:_run_before_allow_switch, include_private=true).should == true
+ end
+
+ it "should define a getter and setter" do
+ Factory.should.respond_to(:allow_run)
+ Factory.should.respond_to(:allow_run=)
+ end
+
+ it "should switch off" do
+ Factory.allow_run = false
+ lambda {
+ Factory.run
+ }.should.raise(RuntimeError)
+ end
+
+ it "should switch on" do
+ Factory.allow_run = true
+ lambda {
+ Factory.run.should == true
+ }.should.not.raise
+ end
+
+ it "should forward passed blocks and arguments" do
+ Factory.allow_ran = true
+ Factory.ran('Machine') do |name|
+ name + '!'
+ end.should == 'Machine was run!'
+ end
+end
+
+class Bunny
+ def hip(name, &block)
+ block.call("#{name} is hip")
+ end
+
+ def hop
+ 'Hop hop!'
+ end
+end
+Bunny.add_allow_switch :hip
+Bunny.add_allow_switch :hop
+
+describe "Bunny with an allow switch on hop" do
+ before do
+ @bunny = Bunny.new
+ end
+
+ it "should alias the original method" do
+ @bunny.respond_to?(:_hop_before_allow_switch).should == true
+ end
+
+ it "should define a getter and setter" do
+ Bunny.should.respond_to(:allow_hop)
+ Bunny.should.respond_to(:allow_hop=)
+
+ Bunny.allow_hop.should == false
+ Bunny.allow_hop = true
+ Bunny.allow_hop.should == true
+ Bunny.allow_hop = false
+ end
+
+ it "should switch off" do
+ Bunny.allow_hop = false
+ lambda {
+ @bunny.hop
+ }.should.raise(RuntimeError)
+ end
+
+ it "should switch on" do
+ Bunny.allow_hop = true
+ lambda {
+ @bunny.hop.should == 'Hop hop!'
+ }.should.not.raise
+ Bunny.allow_hop = false
+ end
+
+ it "should forward passed blocks and arguments" do
+ Bunny.allow_hip = true
+ @bunny.hip('Bunny') do |name|
+ name + '!'
+ end.should == 'Bunny is hip!'
+ Bunny.allow_hop = false
+ end
+end
+
+Kernel.add_allow_switch :system
+
+describe "Kernel with an allow switch on system" do
+ it "should alias the original method" do
+ Kernel.respond_to?(:_system_before_allow_switch, include_private=true).should == true
+ end
+
+ it "should define a getter and setter" do
+ Kernel.should.respond_to(:allow_system)
+ Kernel.should.respond_to(:allow_system=)
+ end
+
+ it "should switch off" do
+ Kernel.allow_system = false
+ lambda {
+ system(SILENT_COMMAND)
+ }.should.raise(RuntimeError)
+ Kernel.allow_system = false
+ end
+
+ it "should switch on" do
+ Kernel.allow_system = true
+ lambda {
+ system(SILENT_COMMAND)
+ }.should.not.raise
+ Kernel.allow_system = false
+ end
+end
+
+Kernel.add_allow_switch :`
+
+describe "Kernel with an allow switch on `" do
+ it "switches off" do
+ Kernel.allow_backtick = false
+ lambda {
+ `#{SILENT_COMMAND}`
+ }.should.raise(RuntimeError)
+ end
+end
15 test/test_helper/capture_output.rb
View
@@ -0,0 +1,15 @@
+module TestHelper
+ module CaptureOutput
+ def capture_stdout(&block)
+ collector = Collector.new
+ stdout = $stdout
+ $stdout = collector
+ begin
+ block.call
+ ensure
+ $stdout = stdout
+ end
+ collector.written.join
+ end
+ end
+end
10 test/test_helper/collector.rb
View
@@ -0,0 +1,10 @@
+class Collector
+ attr_reader :written
+ def initialize
+ @written = []
+ end
+
+ def write(string)
+ @written << string
+ end
+end
15 test/test_helper/fake_apache_directory.rb
View
@@ -0,0 +1,15 @@
+module TestHelper
+ module FakeApacheDirectory
+ def apache_directory_fixture
+ File.expand_path('../../fake/etc/apache2', __FILE__)
+ end
+
+ def fake_apache_directory
+ File.join(temporary_directory, 'apache2')
+ end
+
+ def use_fake_apache_directory
+ FileUtils.cp_r(apache_directory_fixture, fake_apache_directory)
+ end
+ end
+end
23 test/test_helper/temporary_directory.rb
View
@@ -0,0 +1,23 @@
+require 'fileutils'
+
+module TestHelper
+ module TemporaryDirectory
+ def temporary_directory
+ File.expand_path('../../../tmp/.test', __FILE__)
+ end
+
+ def setup_temporary_directory
+ FileUtils.mkdir_p(temporary_directory)
+ end
+
+ def teardown_temporary_directory
+ FileUtils.rm_rf(temporary_directory)
+ end
+
+ def setup
+ super
+ teardown_temporary_directory
+ setup_temporary_directory
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.