diff --git a/README.md b/README.md deleted file mode 100644 index 61d238c..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Apache Config Generator - -Programmatically construct your Apache configuration using a powerful DSL built in Ruby. - -## Installation - -`gem install apache-config-generator` - -## Usage - -Run `apache-configurator ` to create a new directory to hold your config files. -A Rakefile and config.yml file will also be generated. - -## Building a config file - -Configs center around the Apache::Config.build method: - - Apache::Config.build('sites-available/my-site.conf') do - server_name 'my-cool-website.cool.wow' - document_root '/var/www/my-cool-website' - - directory '/' do - options :follow_sym_links, :indexes - allow_from_all - end - - location_match %r{^/secret} do - deny_from_all - - basic_authentication "My secret", '/etc/apache2/users/global.users', :user => :john - satisfy :any - end - end diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000..069a286 --- /dev/null +++ b/README.rdoc @@ -0,0 +1,34 @@ += Apache Config Generator + +Programmatically construct your Apache configuration using a powerful DSL built in Ruby. + +== Installation + +gem install apache-config-generator + +== Usage + +Run apache-configurator to create a new directory to hold your config files. +A Rakefile and config.yml file will also be generated. + +== Building a config file + +Configs center around the Apache::Config.build method: + + Apache::Config.build('sites-available/my-site.conf') do + server_name 'my-cool-website.cool.wow' + document_root '/var/www/my-cool-website' + + directory '/' do + options :follow_sym_links, :indexes + allow_from_all + end + + location_match %r{^/secret} do + deny_from_all + + basic_authentication "My secret", '/etc/apache2/users/global.users', :user => :john + satisfy :any + end + end + diff --git a/Rakefile b/Rakefile index 71ef6e0..1e1dc81 100644 --- a/Rakefile +++ b/Rakefile @@ -20,6 +20,7 @@ Echoe.new('apache-config-generator') do |p| p.summary = "A Ruby DSL for programmatically generating Apache configs" p.ignore_pattern = [ 'spec/**/*', 'test/**/*', 'docs/**/*' ] p.executable_pattern = [ 'bin/**/*' ] + p.runtime_dependencies = [ 'rainbow' ] end namespace :spec do @@ -34,6 +35,7 @@ end Rake::RDocTask.new do |rdoc| rdoc.template = 'direct' - rdoc.rdoc_files.add('lib') + rdoc.rdoc_files.add('lib', 'README.rdoc') + rdoc.main = 'README.rdoc' rdoc.rdoc_dir = 'docs' end diff --git a/lib/apache/config.rb b/lib/apache/config.rb index 8514887..539cca7 100644 --- a/lib/apache/config.rb +++ b/lib/apache/config.rb @@ -1,4 +1,5 @@ require 'fileutils' +require 'rainbow' Dir[File.join(File.dirname(__FILE__), '*.rb')].each { |f| require f } @@ -124,7 +125,9 @@ module Apache def apachify(name) case name when String, Symbol - name.to_s.split("_").collect(&:capitalize).join.gsub('Ssl', 'SSL').gsub('Cgi', 'CGI').gsub('Ldap', 'LDAP').gsub('Url', 'URL') + name.to_s.split("_").collect(&:capitalize).join.gsub('Ssl', 'SSL'). + gsub('Cgi', 'CGI').gsub('Ldap', 'LDAP').gsub('Url', 'URL'). + gsub('Etag', 'ETag') when Array name.collect { |n| apachify(n) } end @@ -230,17 +233,21 @@ module Apache "|#{@rotate_logs_path} #{path} #{time}" end + def warn_msg(from, color = :red) + "[warn::#{from}]".foreground(color) + end + private def writable?(path) - puts "[warn] #{path} may not be writable!" if !File.directory? File.split(path).first + puts " #{warn_msg('writable?')} #{path.foreground(:yellow)} may not be writable!" if !File.directory? File.split(path).first end def directory?(path) - puts "[warn] #{path} does not exist!" if !File.directory? path + puts " #{warn_msg('directory?')} #{path.foreground(:yellow)} does not exist!" if !File.directory? path end def exist?(path) - puts "[warn] #{path} does not exist!" if !File.exist?(path) + puts " #{warn_msg('exist?')} #{path.foreground(:yellow)} does not exist!" if !File.exist?(path) end end diff --git a/lib/apache/master.rb b/lib/apache/master.rb index 8bc58f0..53a2fd8 100644 --- a/lib/apache/master.rb +++ b/lib/apache/master.rb @@ -8,6 +8,11 @@ module Apache @config += Modules.build(*modules, &block) end + def listen(*opt) + opt.each { |o| self << "Listen #{quoteize(o)}" } + end + alias :listen! :listen + # Add a User/Group block # runner('www', 'www-data') #=> # User www diff --git a/lib/apache/permissions.rb b/lib/apache/permissions.rb index 0e42214..5cb4172 100644 --- a/lib/apache/permissions.rb +++ b/lib/apache/permissions.rb @@ -85,7 +85,7 @@ module Apache # Create an Apache require directive. # Used to get around Ruby reserved word. def apache_require(*opts) - self << "Require #{opts * " "}" + self << "Require #{opts.compact * " "}" end end end diff --git a/lib/apache/rake/create.rb b/lib/apache/rake/create.rb index 293132a..11d1fd4 100644 --- a/lib/apache/rake/create.rb +++ b/lib/apache/rake/create.rb @@ -2,6 +2,7 @@ require 'fileutils' require 'apache/config' require 'apache/rake/create' require 'yaml' +require 'rainbow' namespace :apache do desc "Create all defined configs for the specified environment" @@ -19,7 +20,7 @@ namespace :apache do Dir.chdir CONFIG[:dest_path] Dir[File.join(CONFIG[:source_path], '**', '*.rb')].each do |file| - puts file + puts file.foreground(:green) require file end end diff --git a/lib/apache/rewrites.rb b/lib/apache/rewrites.rb index 113d16d..96d0638 100644 --- a/lib/apache/rewrites.rb +++ b/lib/apache/rewrites.rb @@ -21,11 +21,15 @@ module Apache end # Pass the block to RewriteManager.build - def rewrites(&block) - self + indent(RewriteManager.build(&block)) + def rewrites(*opt, &block) + self + indent(RewriteManager.build(*opt, &block)) self << '' end + def rewrite(*opt, &block) + raise "You probably want rewrites #{quoteize(*opt) * " "} do" if block + end + # Create a permanent Redirect # # r301 '/here', '/there' #=> Redirect permanent "/here" "/there" @@ -45,12 +49,27 @@ module Apache end # Build rewritable things from the provided block - def build(&block) + def build(*opt, &block) reset! + @any_tests = false + @needs_tests = false self.instance_eval(&block) - @rewrites.collect(&:to_a).flatten + name = opt.first || (@rewrites.empty? ? 'unnamed block' : "#{@rewrites.first.from} => #{@rewrites.first.to}") + + if !@any_tests && !@rewrites.empty? + puts " [#{"rewrite".foreground(:blue)}] no tests found for #{name}" + end + + if @needs_tests + puts " [#{"rewrite".foreground(:blue)}] #{name} needs more tests" + end + + output = @rewrites.collect(&:to_a).flatten + output.unshift("# #{name}") if opt.first + + output end # Commit the latest rewritable thing to the list of rewrites @@ -98,21 +117,49 @@ module Apache # Test the rewritable things defined in this block def rewrite_test(from, to, opts = {}) + @any_tests = true orig_from = from.dup @rewrites.each do |r| pre_from = from.dup if r.match?(from, opts) from = r.test(from, opts) - from = pre_from if (from == '-') + from = pre_from if (r.to == '-') + from = :http_forbidden if (r.forbidden?) break if r.stop_if_match? end end if from != to - puts "[warn] #{orig_from} >> #{to} failed!" - puts "[warn] Result: #{from}" + puts " [#{"rewrite".foreground(:blue)}] #{orig_from} >> #{to} failed!" + puts " [#{"rewrite".foreground(:blue)}] Result: #{from}" end end + + def needs_tests + @needs_tests = true + end + + def cond_not_a_file(opts = {}) + cond_file_flag '!-f', opts + end + + def cond_is_a_file(opts = {}) + cond_file_flag '-f', opts + end + + def cond_not_a_directory(opts = {}) + cond_file_flag '!-d', opts + end + + def cond_is_a_directory(opts = {}) + cond_file_flag '-d', opts + end + + private + def cond_file_flag(flag, opts) + cond opts[:filename_only] ? "%{REQUEST_FILENAME}" : "%{DOCUMENT_ROOT}%{REQUEST_FILENAME}", flag + end + end end @@ -144,6 +191,8 @@ module Apache class MatchableThing include Apache::Quoteize + attr_reader :from, :to + # The Apache directive tag for this thing def tag; raise 'Override this method'; end @@ -166,6 +215,7 @@ module Apache end def stop_if_match?; false; end + def forbidden?; false; end end # A RewriteRule definition @@ -195,12 +245,18 @@ module Apache case key when :last 'L' + when :forbidden + 'F' + when :no_escape + 'NE' when :redirect - 'R' + (value == true) ? 'R' : "R=#{value}" when :pass_through 'PT' when :preserve_query_string 'QSA' + when :env + "E=#{value}" end end.compact.sort @@ -220,11 +276,12 @@ module Apache end def to_a - [ @conditions.collect(&:to_s), super ].flatten + [ ('' if !@conditions.empty?), @conditions.collect(&:to_s), super ].flatten end # Test this RewriteRule, ensuring the RewriteConds also match def test(from, opts = {}) + opts[:request_uri] = from result = from result = super(from, opts) if match?(from, opts) @@ -233,6 +290,7 @@ module Apache end def match?(from, opts = {}) + opts[:request_uri] = from ok = true @conditions.each do |c| @@ -249,6 +307,10 @@ module Apache def stop_if_match? @input_options[:last] end + + def forbidden? + @input_options[:forbidden] + end end # A permanent RedirectMatch @@ -313,20 +375,26 @@ module Apache super(from, opts) source = replace_placeholders(@from, opts) + to = @to + reverse = false + + if @to[0..0] == '!' + reverse = true + to = @to[1..-1] + end + result = false - case @to[0..0] - when '!' - result = !source[Regexp.new(@to[1..-1])] + case to[0..0] when '-' - case @to + case to when '-f' result = opts[:files].include? source if opts[:files] end else - result = source[Regexp.new(@to)] + result = source[Regexp.new(to)] end - result + reverse ? !result : result end end end diff --git a/spec/rewrites_spec.rb b/spec/rewrites_spec.rb index 2b8ed2c..244702c 100644 --- a/spec/rewrites_spec.rb +++ b/spec/rewrites_spec.rb @@ -26,7 +26,7 @@ describe Apache::Config, "rewrites" do rule %r{^/$}, '/test' end apache.to_a.should == [ - 'RewriteRule "^/$" "/test"', '' + '', 'RewriteRule "^/$" "/test"', '' ] end end @@ -46,6 +46,7 @@ describe Apache::RewriteManager, "specific rewrite rules" do rewrite_test '/fail', '/test' rewrite_test '/%{REQUEST_FILENAME}', '/test', :request_filename => 'success' end.should == [ + '', 'RewriteCond "%{REQUEST_FILENAME}" "test"', 'RewriteRule "^/$" "/test"', 'RedirectMatch permanent "^/success$" "/test"' @@ -79,6 +80,7 @@ describe Apache::RewriteRule, "a RewriteRule" do its(:to_s) { should == 'RewriteRule "^/test$" "/test2" [L,QSA]' } its(:to_a) { should == [ + '', 'RewriteCond "/%{REQUEST_FILENAME}" "^/test$"', 'RewriteRule "^/test$" "/test2" [L,QSA]' ] }