diff options
161 files changed, 3132 insertions, 684 deletions
@@ -10,6 +10,7 @@ Contributors and Copyright holders: * Copyright 2008, Bryan McLellan <btm@loftninjas.org> * Copyright 2008, Ezra Zygmuntowicz <ezra@engineyard.com> * Copyright 2009, Sean Cribbs <seancribbs@gmail.com> + * Copyright 2009, Christopher Brown <cb@opscode.com> Chef incorporates code modified from Open4 (http://www.codeforpeople.com/lib/ruby/open4/), which was written by Ara T. Howard. diff --git a/README.rdoc b/README.rdoc index d3c26977db..a7a4ea8dea 100644 --- a/README.rdoc +++ b/README.rdoc @@ -19,7 +19,7 @@ chef: * stomp * ohai -chef-server, same requires as chef above, plus: +chef-server and the chefserverslice (merb slice), same requires as chef above, plus: * stompserver * ferret @@ -30,6 +30,15 @@ chef-server, same requires as chef above, plus: * ruby-openid * syntax +Interim Note: + +Once the {chef,chef-server,chefserverslice}.gem set is installed, do the following: + + > cd /Library/Ruby/Gems/1.8/gems/chef-server<xxx> # or wherever your gems land + > sudo rake slices:chefserverslice:install + +This installs the chefserverslice into the chef-server merb application + External Servers: * stompserver (for easy stomp mq testing) @@ -1,8 +1,7 @@ +gems = %w[chef chefserverslice chef-server] require 'rubygems' require 'cucumber/rake/task' -gems = %w[chef chef-server] - desc "Build the chef gems" task :gem do gems.each do |dir| @@ -17,6 +16,13 @@ task :install do end end +desc "Uninstall the chef gems" +task :uninstall do + gems.reverse.each do |dir| + Dir.chdir(dir) { sh "rake uninstall" } + end +end + desc "Run the rspec tests" task :spec do gems.each do |dir| @@ -113,6 +119,7 @@ namespace :dev do end Dir.chdir("example-repository") { sh("rake install") } end + desc "Install a test instance of Chef for doing features against" task :features do diff --git a/chef-server/NOTICE b/chef-server/NOTICE index 02f03a496d..165a89d0d5 100644 --- a/chef-server/NOTICE +++ b/chef-server/NOTICE @@ -1,10 +1,17 @@ Chef NOTICE =========== -Developed at HJK Solutions (http://www.hjksolutions.com). +Developed at Opscode (http://www.opscode.com). Contributors and Copyright holders: * Copyright 2008, Adam Jacob <adam@opscode.com> * Copyright 2008, Arjuna Christensen <aj@hjksolutions.com> + * Copyright 2008, Bryan McLellan <btm@loftninjas.org> * Copyright 2008, Ezra Zygmuntowicz <ezra@engineyard.com> + * Copyright 2008, Sean Cribbs <seancribbs@gmail.com> + * Copyright 2009, Christopher Brown <cb@opscode.com> + +Chef incorporates code modified from Open4 (http://www.codeforpeople.com/lib/ruby/open4/), which was written by Ara T. Howard. + +Chef incorporates code modified from Merb (http://www.merbivore.com), which is Copyright (c) 2008 Engine Yard. diff --git a/chef-server/README.txt b/chef-server/README.rdoc index c269193de0..a7a4ea8dea 100644 --- a/chef-server/README.txt +++ b/chef-server/README.rdoc @@ -1,32 +1,43 @@ -= chef-server += chef -* http://oss.hjksolutions.com/chef +* http://www.opscode.com/chef == DESCRIPTION: -Chef is a configuration management tool inspired by Puppet. +Chef is a systems management framework masquerading as a configuration management tool. I'm in ur netwerk, cookin up yer servers. :) -== FEATURES/PROBLEMS: +== REQUIREMENTS: +chef: -== SYNOPSIS: +* ruby-openid +* json +* erubis +* extlib +* stomp +* ohai +chef-server and the chefserverslice (merb slice), same requires as chef above, plus: -== REQUIREMENTS: +* stompserver +* ferret +* merb-core +* merb-haml +* mongrel +* haml +* ruby-openid +* syntax + +Interim Note: -RubyGems: +Once the {chef,chef-server,chefserverslice}.gem set is installed, do the following: -* stomp -* stompserver -* ultraviolet -* facter -* ferret -* merb-core -* haml -* ruby-openid -* json + > cd /Library/Ruby/Gems/1.8/gems/chef-server<xxx> # or wherever your gems land + > sudo rake slices:chefserverslice:install + +This installs the chefserverslice into the chef-server merb application External Servers: @@ -41,19 +52,25 @@ Install all of the above. To fire up a develpment environment, do the following * Start stompserver with 'stompserver' * Start chef-indexer with: - ./bin/chef-indexer -l debug -c ./config/chef-server.rb + chef-indexer -l debug + + * Start chef-server: + + chef-server -N -c 2 - * Start chef-server on port 4000 with: + * Test run chef to begin node registration: - ./bin/chef-server + sudo ./bin/chef-client - * Start chef-server on port 4001 with: + * Validate the node registration: - ./bin/chef-server -p 4001 + Visit http://localhost:4000 + Login, enter an openid URL (see http://openid.net/get/) + Registrations, click Validate * Test run chef with: - sudo ./bin/chef-client -l debug -c ./examples/config/chef-solo.rb + chef-client == LICENSE: diff --git a/chef-server/Rakefile b/chef-server/Rakefile index d62f8c13ce..c5a05d3b8f 100644 --- a/chef-server/Rakefile +++ b/chef-server/Rakefile @@ -1,56 +1,45 @@ require 'rubygems' -require 'rake/gempackagetask' +require 'rake/rdoctask' + +require 'merb-core' +require 'merb-core/tasks/merb' +require 'spec' +require 'spec/rake/spectask' +require 'chef' + +include FileUtils GEM = "chef-server" CHEF_SERVER_VERSION = "0.5.5" -AUTHOR = "Adam Jacob" -EMAIL = "adam@opscode.com" +AUTHOR = "Opscode" +EMAIL = "chef@opscode.com" HOMEPAGE = "http://wiki.opscode.com/display/chef" SUMMARY = "A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure." -spec = Gem::Specification.new do |s| - s.name = GEM - s.version = CHEF_SERVER_VERSION - s.platform = Gem::Platform::RUBY - s.has_rdoc = true - s.extra_rdoc_files = ["README.txt", "LICENSE", 'NOTICE'] - s.summary = SUMMARY - s.description = s.summary - s.author = AUTHOR - s.email = EMAIL - s.homepage = HOMEPAGE - - s.add_dependency "stomp" - s.add_dependency "stompserver" - s.add_dependency "ferret" - s.add_dependency "merb-core" - s.add_dependency "merb-haml" - s.add_dependency "merb-assets" - s.add_dependency "merb-helpers" - s.add_dependency "mongrel" - s.add_dependency "haml" - s.add_dependency "ruby-openid" - s.add_dependency "json" - s.add_dependency "syntax" - - s.bindir = "bin" - s.executables = %w( chef-indexer chef-server ) - - s.files = %w(LICENSE README.txt Rakefile) + Dir.glob("{lib}/**/*") -end +# Load the basic runtime dependencies; this will include +# any plugins and therefore plugin rake tasks. +init_env = ENV['MERB_ENV'] || 'rake' +Merb.load_dependencies(:environment => init_env) + +# Get Merb plugins and dependencies +Merb::Plugins.rakefiles.each { |r| require r } -Rake::GemPackageTask.new(spec) do |pkg| - pkg.gem_spec = spec -end +# Load any app level custom rakefile extensions from lib/tasks +tasks_path = File.join(File.dirname(__FILE__), "lib", "tasks") +rake_files = Dir["#{tasks_path}/*.rake"] +rake_files.each{|rake_file| load rake_file } -task :install => :package do - sh %{sudo gem install pkg/#{GEM}-#{CHEF_SERVER_VERSION} --no-rdoc --no-ri} +desc "Start runner environment" +task :merb_env do + Merb.start_environment(:environment => init_env, :adapter => 'runner') end -desc "create a gemspec file" -task :make_spec do - File.open("#{GEM}.gemspec", "w") do |file| - file.puts spec.to_ruby - end -end +require 'spec/rake/spectask' +require 'merb-core/test/tasks/spectasks' +desc 'Default: run spec examples' +task :default => 'spec' +############################################################################## +# ADD YOUR CUSTOM TASKS IN /lib/tasks +# NAME YOUR RAKE FILES file_name.rake +############################################################################## diff --git a/chef-server/app/controllers/application.rb b/chef-server/app/controllers/application.rb new file mode 100644 index 0000000000..5ce39a0102 --- /dev/null +++ b/chef-server/app/controllers/application.rb @@ -0,0 +1,2 @@ +class Application < Merb::Controller +end
\ No newline at end of file diff --git a/chef-server/app/controllers/exceptions.rb b/chef-server/app/controllers/exceptions.rb new file mode 100644 index 0000000000..4fdb5665a0 --- /dev/null +++ b/chef-server/app/controllers/exceptions.rb @@ -0,0 +1,13 @@ +class Exceptions < Merb::Controller + + # handle NotFound exceptions (404) + def not_found + render :format => :html + end + + # handle NotAcceptable exceptions (406) + def not_acceptable + render :format => :html + end + +end
\ No newline at end of file diff --git a/chef-server/app/helpers/global_helpers.rb b/chef-server/app/helpers/global_helpers.rb new file mode 100644 index 0000000000..9c9e5aa197 --- /dev/null +++ b/chef-server/app/helpers/global_helpers.rb @@ -0,0 +1,5 @@ +module Merb + module GlobalHelpers + # helpers defined here available to all views. + end +end diff --git a/chef-server/app/models/user.rb b/chef-server/app/models/user.rb new file mode 100644 index 0000000000..ae84896cda --- /dev/null +++ b/chef-server/app/models/user.rb @@ -0,0 +1,17 @@ +# This is a default user class used to activate merb-auth. Feel free to change from a User to +# Some other class, or to remove it altogether. If removed, merb-auth may not work by default. +# +# Don't forget that by default the salted_user mixin is used from merb-more +# You'll need to setup your db as per the salted_user mixin, and you'll need +# To use :password, and :password_confirmation when creating a user +# +# see merb/merb-auth/setup.rb to see how to disable the salted_user mixin +# +# You will need to setup your database and create a user. +class User + include DataMapper::Resource + + property :id, Serial + property :login, String + +end diff --git a/chef-server/app/views/exceptions/not_acceptable.html.erb b/chef-server/app/views/exceptions/not_acceptable.html.erb new file mode 100644 index 0000000000..a7b7752ab2 --- /dev/null +++ b/chef-server/app/views/exceptions/not_acceptable.html.erb @@ -0,0 +1,63 @@ +<div id="container"> + <div id="header-container"> + <img src="/images/merb.jpg" /> + <!-- <h1>Mongrel + Erb</h1> --> + <h2>pocket rocket web framework</h2> + <hr /> + </div> + + <div id="left-container"> + <h3>Exception:</h3> + <p><%= request.exceptions.first.message %></p> + </div> + + <div id="main-container"> + <h3>Why am I seeing this page?</h3> + <p>Merb couldn't find an appropriate content_type to return, + based on what you said was available via provides() and + what the client requested.</p> + + <h3>How to add a mime-type</h3> + <pre><code> + Merb.add_mime_type :pdf, :to_pdf, %w[application/pdf], "Content-Encoding" => "gzip" + </code></pre> + <h3>What this means is:</h3> + <ul> + <li>Add a mime-type for :pdf</li> + <li>Register the method for converting objects to PDF as <code>#to_pdf</code>.</li> + <li>Register the incoming mime-type "Accept" header as <code>application/pdf</code>.</li> + <li>Specify a new header for PDF types so it will set <code>Content-Encoding</code> to gzip.</li> + </ul> + + <h3>You can then do:</h3> + <pre><code> + class Foo < Application + provides :pdf + end + </code></pre> + + <h3>Where can I find help?</h3> + <p>If you have any questions or if you can't figure something out, please take a + look at our <a href="http://merbivore.com/"> project page</a>, + feel free to come chat at irc.freenode.net, channel #merb, + or post to <a href="http://groups.google.com/group/merb">merb mailing list</a> + on Google Groups.</p> + + <h3>What if I've found a bug?</h3> + <p>If you want to file a bug or make your own contribution to Merb, + feel free to register and create a ticket at our + <a href="http://merb.lighthouseapp.com/">project development page</a> + on Lighthouse.</p> + + <h3>How do I edit this page?</h3> + <p>You can change what people see when this happens by editing <tt>app/views/exceptions/not_acceptable.html.erb</tt>.</p> + + </div> + + <div id="footer-container"> + <hr /> + <div class="left"></div> + <div class="right">© 2008 the merb dev team</div> + <p> </p> + </div> +</div> diff --git a/chef-server/app/views/exceptions/not_found.html.erb b/chef-server/app/views/exceptions/not_found.html.erb new file mode 100644 index 0000000000..42b41a89dc --- /dev/null +++ b/chef-server/app/views/exceptions/not_found.html.erb @@ -0,0 +1,47 @@ +<div id="container"> + <div id="header-container"> + <img src="/images/merb.jpg" /> + <!-- <h1>Mongrel + Erb</h1> --> + <h2>pocket rocket web framework</h2> + <hr /> + </div> + + <div id="left-container"> + <h3>Exception:</h3> + <p><%= request.exceptions.first.message %></p> + </div> + + <div id="main-container"> + <h3>Welcome to Merb!</h3> + <p>Merb is a light-weight MVC framework written in Ruby. We hope you enjoy it.</p> + + <h3>Where can I find help?</h3> + <p>If you have any questions or if you can't figure something out, please take a + look at our <a href="http://merbivore.com/"> project page</a>, + feel free to come chat at irc.freenode.net, channel #merb, + or post to <a href="http://groups.google.com/group/merb">merb mailing list</a> + on Google Groups.</p> + + <h3>What if I've found a bug?</h3> + <p>If you want to file a bug or make your own contribution to Merb, + feel free to register and create a ticket at our + <a href="http://merb.lighthouseapp.com/">project development page</a> + on Lighthouse.</p> + + <h3>How do I edit this page?</h3> + <p>You're seeing this page because you need to edit the following files: + <ul> + <li>config/router.rb <strong><em>(recommended)</em></strong></li> + <li>app/views/exceptions/not_found.html.erb <strong><em>(recommended)</em></strong></li> + <li>app/views/layout/application.html.erb <strong><em>(change this layout)</em></strong></li> + </ul> + </p> + </div> + + <div id="footer-container"> + <hr /> + <div class="left"></div> + <div class="right">© 2008 the merb dev team</div> + <p> </p> + </div> +</div> diff --git a/chef-server/app/views/layout/application.html.erb b/chef-server/app/views/layout/application.html.erb new file mode 100644 index 0000000000..4637ef2ded --- /dev/null +++ b/chef-server/app/views/layout/application.html.erb @@ -0,0 +1,12 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us"> + <head> + <title>Fresh Merb App</title> + <meta http-equiv="content-type" content="text/html; charset=utf-8" /> + <link rel="stylesheet" href="/stylesheets/master.css" type="text/css" media="screen" charset="utf-8" /> + </head> + <body> + <%#= message[:notice] %> + <%= catch_content :for_layout %> + </body> +</html>
\ No newline at end of file diff --git a/chef-server/autotest/discover.rb b/chef-server/autotest/discover.rb new file mode 100644 index 0000000000..8df3b0f34e --- /dev/null +++ b/chef-server/autotest/discover.rb @@ -0,0 +1,2 @@ +Autotest.add_discovery { "merb" } +Autotest.add_discovery { "rspec" }
\ No newline at end of file diff --git a/chef-server/autotest/merb.rb b/chef-server/autotest/merb.rb new file mode 100644 index 0000000000..f78df15702 --- /dev/null +++ b/chef-server/autotest/merb.rb @@ -0,0 +1,152 @@ +# Adapted from Autotest::Rails +require 'autotest' + +class Autotest::Merb < Autotest + + # +model_tests_dir+:: the directory to find model-centric tests + # +controller_tests_dir+:: the directory to find controller-centric tests + # +view_tests_dir+:: the directory to find view-centric tests + # +fixtures_dir+:: the directory to find fixtures in + attr_accessor :model_tests_dir, :controller_tests_dir, :view_tests_dir, :fixtures_dir + + def initialize + super + + initialize_test_layout + + # Ignore any happenings in these directories + add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3)% + # Ignore SCM directories and custom Autotest mappings + %w[.svn .hg .git .autotest].each { |exception| add_exception(exception) } + + + # Ignore any mappings that Autotest may have already set up + clear_mappings + + # Any changes to a file in the root of the 'lib' directory will run any + # model test with a corresponding name. + add_mapping %r%^lib\/.*\.rb% do |filename, _| + files_matching Regexp.new(["^#{model_test_for(filename)}$"]) + end + + # Any changes to a fixture will run corresponding view, controller and + # model tests + add_mapping %r%^#{fixtures_dir}/(.*)s.yml% do |_, m| + [ + model_test_for(m[1]), + controller_test_for(m[1]), + view_test_for(m[1]) + ] + end + + # Any change to a test will cause it to be run + add_mapping %r%^test/(unit|models|integration|controllers|views|functional)/.*rb$% do |filename, _| + filename + end + + # Any change to a model will cause it's corresponding test to be run + add_mapping %r%^app/models/(.*)\.rb$% do |_, m| + model_test_for(m[1]) + end + + # Any change to the global helper will result in all view and controller + # tests being run + add_mapping %r%^app/helpers/global_helpers.rb% do + files_matching %r%^test/(views|functional|controllers)/.*_test\.rb$% + end + + # Any change to a helper will run it's corresponding view and controller + # tests, unless the helper is the global helper. Changes to the global + # helper run all view and controller tests. + add_mapping %r%^app/helpers/(.*)_helper(s)?.rb% do |_, m| + if m[1] == "global" then + files_matching %r%^test/(views|functional|controllers)/.*_test\.rb$% + else + [ + view_test_for(m[1]), + controller_test_for(m[1]) + ] + end + end + + # Changes to views result in their corresponding view and controller test + # being run + add_mapping %r%^app/views/(.*)/% do |_, m| + [ + view_test_for(m[1]), + controller_test_for(m[1]) + ] + end + + # Changes to a controller result in its corresponding test being run. If + # the controller is the exception or application controller, all + # controller tests are run. + add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m| + if ["application", "exception"].include?(m[1]) + files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$% + else + controller_test_for(m[1]) + end + end + + # If a change is made to the router, run all controller and view tests + add_mapping %r%^config/router.rb$% do # FIX + files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$% + end + + # If any of the major files governing the environment are altered, run + # everything + add_mapping %r%^test/test_helper.rb|config/(init|rack|environments/test.rb|database.yml)% do # FIX + files_matching %r%^test/(unit|models|controllers|views|functional)/.*_test\.rb$% + end + end + +private + + # Determines the paths we can expect tests or specs to reside, as well as + # corresponding fixtures. + def initialize_test_layout + self.model_tests_dir = "test/unit" + self.controller_tests_dir = "test/functional" + self.view_tests_dir = "test/views" + self.fixtures_dir = "test/fixtures" + end + + # Given a filename and the test type, this method will return the + # corresponding test's or spec's name. + # + # ==== Arguments + # +filename+<String>:: the file name of the model, view, or controller + # +kind_of_test+<Symbol>:: the type of test we that we should run + # + # ==== Returns + # String:: the name of the corresponding test or spec + # + # ==== Example + # + # > test_for("user", :model) + # => "user_test.rb" + # > test_for("login", :controller) + # => "login_controller_test.rb" + # > test_for("form", :view) + # => "form_view_spec.rb" # If you're running a RSpec-like suite + def test_for(filename, kind_of_test) + name = [filename] + name << kind_of_test.to_s if kind_of_test == :view + name << "test" + return name.join("_") + ".rb" + end + + def model_test_for(filename) + [model_tests_dir, test_for(filename, :model)].join("/") + end + + def controller_test_for(filename) + [controller_tests_dir, test_for(filename, :controller)].join("/") + end + + def view_test_for(filename) + [view_tests_dir, test_for(filename, :view)].join("/") + end + +end
\ No newline at end of file diff --git a/chef-server/autotest/merb_rspec.rb b/chef-server/autotest/merb_rspec.rb new file mode 100644 index 0000000000..536665d89b --- /dev/null +++ b/chef-server/autotest/merb_rspec.rb @@ -0,0 +1,165 @@ +# Adapted from Autotest::Rails, RSpec's autotest class, as well as merb-core's. +require 'autotest' + +class RspecCommandError < StandardError; end + +# This class maps your application's structure so Autotest can understand what +# specs to run when files change. +# +# Fixtures are _not_ covered by this class. If you change a fixture file, you +# will have to run your spec suite manually, or, better yet, provide your own +# Autotest map explaining how your fixtures are set up. +class Autotest::MerbRspec < Autotest + def initialize + super + + # Ignore any happenings in these directories + add_exception %r%^\./(?:doc|log|public|tmp|\.git|\.hg|\.svn|framework|gems|schema|\.DS_Store|autotest|bin|.*\.sqlite3|.*\.thor)% + # Ignore SCM directories and custom Autotest mappings + %w[.svn .hg .git .autotest].each { |exception| add_exception(exception) } + + # Ignore any mappings that Autotest may have already set up + clear_mappings + + # Anything in /lib could have a spec anywhere, if at all. So, look for + # files with roughly the same name as the file in /lib + add_mapping %r%^lib\/(.*)\.rb% do |_, m| + files_matching %r%^spec\/#{m[1]}% + end + + add_mapping %r%^spec/(spec_helper|shared/.*)\.rb$% do + all_specs + end + + # Changing a spec will cause it to run itself + add_mapping %r%^spec/.*\.rb$% do |filename, _| + filename + end + + # Any change to a model will cause it's corresponding test to be run + add_mapping %r%^app/models/(.*)\.rb$% do |_, m| + spec_for(m[1], 'model') + end + + # Any change to global_helpers will result in all view and controller + # tests being run + add_mapping %r%^app/helpers/global_helpers\.rb% do + files_matching %r%^spec/(views|controllers|helpers|requests)/.*_spec\.rb$% + end + + # Any change to a helper will cause its spec to be run + add_mapping %r%^app/helpers/((.*)_helper(s)?)\.rb% do |_, m| + spec_for(m[1], 'helper') + end + + # Changes to a view cause its spec to be run + add_mapping %r%^app/views/(.*)/% do |_, m| + spec_for(m[1], 'view') + end + + # Changes to a controller result in its corresponding spec being run. If + # the controller is the exception or application controller, all + # controller specs are run. + add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m| + if ["application", "exception"].include?(m[1]) + files_matching %r%^spec/controllers/.*_spec\.rb$% + else + spec_for(m[1], 'controller') + end + end + + # If a change is made to the router, run controller, view and helper specs + add_mapping %r%^config/router.rb$% do + files_matching %r%^spec/(controllers|views|helpers)/.*_spec\.rb$% + end + + # If any of the major files governing the environment are altered, run + # everything + add_mapping %r%^config/(init|rack|environments/test).*\.rb|database\.yml% do + all_specs + end + end + + def failed_results(results) + results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m) + end + + def handle_results(results) + @failures = failed_results(results) + @files_to_test = consolidate_failures(@failures) + @files_to_test.empty? && !$TESTING ? hook(:green) : hook(:red) + @tainted = !@files_to_test.empty? + end + + def consolidate_failures(failed) + filters = Hash.new { |h,k| h[k] = [] } + failed.each do |spec, failed_trace| + if f = test_files_for(failed).find { |f| f =~ /spec\// } + filters[f] << spec + break + end + end + filters + end + + def make_test_cmd(specs_to_runs) + [ + ruby, + "-S", + spec_command, + add_options_if_present, + files_to_test.keys.flatten.join(' ') + ].join(' ') + end + + def add_options_if_present + File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : "" + end + + # Finds the proper spec command to use. Precendence is set in the + # lazily-evaluated method spec_commands. Alias + Override that in + # ~/.autotest to provide a different spec command then the default + # paths provided. + def spec_command(separator=File::ALT_SEPARATOR) + unless defined?(@spec_command) + @spec_command = spec_commands.find { |cmd| File.exists?(cmd) } + + raise RspecCommandError, "No spec command could be found" unless @spec_command + + @spec_command.gsub!(File::SEPARATOR, separator) if separator + end + @spec_command + end + + # Autotest will look for spec commands in the following + # locations, in this order: + # + # * default spec bin/loader installed in Rubygems + # * any spec command found in PATH + def spec_commands + [File.join(Config::CONFIG['bindir'], 'spec'), 'spec'] + end + +private + + # Runs +files_matching+ for all specs + def all_specs + files_matching %r%^spec/.*_spec\.rb$% + end + + # Generates a path to some spec given its kind and the match from a mapping + # + # ==== Arguments + # match<String>:: the match from a mapping + # kind<String>:: the kind of spec that the match represents + # + # ==== Returns + # String + # + # ==== Example + # > spec_for('post', :view') + # => "spec/views/post_spec.rb" + def spec_for(match, kind) + File.join("spec", kind + 's', "#{match}_spec.rb") + end +end diff --git a/chef-server/bin/chef-server b/chef-server/bin/chef-server index a68cf1b202..b6a82777c3 100755..100644 --- a/chef-server/bin/chef-server +++ b/chef-server/bin/chef-server @@ -24,13 +24,17 @@ require "rubygems" require "merb-core" require "chef" +Dir.chdir File.join(File.dirname(__FILE__),"..") +__DIR__ = Dir.getwd + if ARGV[0] && ARGV[0] =~ /^[^-]/ ARGV.push "-H" end unless %w[-a --adapter -i --irb-console -r --script-runner].any? { |o| ARGV.index(o) } ARGV.push *%w[-a mongrel] end -ARGV.push *[ "-I", File.join(File.dirname(__FILE__), "..", "lib", "init.rb") ] +ARGV.push *[ "-I", File.join(__DIR__, "config", "init.rb") ] +ARGV.push *[ "-m", __DIR__] if index = ARGV.index("-C") config = ARGV[index+1] diff --git a/chef-server/config/database.yml b/chef-server/config/database.yml new file mode 100644 index 0000000000..ba3a4f824e --- /dev/null +++ b/chef-server/config/database.yml @@ -0,0 +1,33 @@ +--- +# This is a sample database file for the DataMapper ORM +development: &defaults + # These are the settings for repository :default + adapter: sqlite3 + database: sample_development.db + + # Add more repositories + # repositories: + # repo1: + # adapter: sqlite3 + # database: sample_1_development.db + # repo2: + # ... + +test: + <<: *defaults + database: sample_test.db + + # repositories: + # repo1: + # database: sample_1_test.db + +production: + <<: *defaults + database: production.db + + # repositories: + # repo1: + # database: sample_production.db + +rake: + <<: *defaults
\ No newline at end of file diff --git a/chef-server/config/dependencies.rb b/chef-server/config/dependencies.rb new file mode 100644 index 0000000000..58df78e4d6 --- /dev/null +++ b/chef-server/config/dependencies.rb @@ -0,0 +1,35 @@ +# dependencies are generated using a strict version, don't forget to edit the dependency versions when upgrading. +merb_gems_version = "1.0.9" +dm_gems_version = "0.9.10" +do_gems_version = "0.9.11" + +# For more information about each component, please read http://wiki.merbivore.com/faqs/merb_components +dependency "merb-core", merb_gems_version +dependency "merb-action-args", merb_gems_version +dependency "merb-assets", merb_gems_version +dependency("merb-cache", merb_gems_version) do + Merb::Cache.setup do + register(Merb::Cache::FileStore) unless Merb.cache + end +end +dependency "merb-helpers", merb_gems_version +dependency "merb-mailer", merb_gems_version +dependency "merb-slices", merb_gems_version +dependency "merb-auth-core", merb_gems_version +dependency "merb-auth-more", merb_gems_version +# dependency "merb-auth-slice-password", merb_gems_version +dependency "merb-param-protection", merb_gems_version +dependency "merb-exceptions", merb_gems_version + +dependency "data_objects", do_gems_version +dependency "do_sqlite3", do_gems_version # If using another database, replace this +dependency "dm-core", dm_gems_version +dependency "dm-aggregates", dm_gems_version +dependency "dm-migrations", dm_gems_version +dependency "dm-timestamps", dm_gems_version +dependency "dm-types", dm_gems_version +dependency "dm-validations", dm_gems_version +dependency "dm-serializer", dm_gems_version + +dependency "merb_datamapper", merb_gems_version +dependency "chefserverslice" diff --git a/chef-server/config/environments/development.rb b/chef-server/config/environments/development.rb new file mode 100644 index 0000000000..d6d4ab3833 --- /dev/null +++ b/chef-server/config/environments/development.rb @@ -0,0 +1,15 @@ +Merb.logger.info("Loaded DEVELOPMENT Environment...") +Merb::Config.use { |c| + c[:exception_details] = true + c[:reload_templates] = true + c[:reload_classes] = true + c[:reload_time] = 0.5 + c[:ignore_tampered_cookies] = true + c[:log_auto_flush ] = true + c[:log_level] = :debug + + c[:log_stream] = STDOUT + c[:log_file] = nil + # Or redirect logging into a file: + # c[:log_file] = Merb.root / "log" / "development.log" +} diff --git a/chef-server/config/environments/production.rb b/chef-server/config/environments/production.rb new file mode 100644 index 0000000000..17a2d4b14b --- /dev/null +++ b/chef-server/config/environments/production.rb @@ -0,0 +1,10 @@ +Merb.logger.info("Loaded PRODUCTION Environment...") +Merb::Config.use { |c| + c[:exception_details] = false + c[:reload_classes] = false + c[:log_level] = :error + + c[:log_file] = Merb.root / "log" / "production.log" + # or redirect logger using IO handle + # c[:log_stream] = STDOUT +} diff --git a/chef-server/config/environments/rake.rb b/chef-server/config/environments/rake.rb new file mode 100644 index 0000000000..5e4b9a77f6 --- /dev/null +++ b/chef-server/config/environments/rake.rb @@ -0,0 +1,11 @@ +Merb.logger.info("Loaded RAKE Environment...") +Merb::Config.use { |c| + c[:exception_details] = true + c[:reload_classes] = false + c[:log_auto_flush ] = true + + c[:log_stream] = STDOUT + c[:log_file] = nil + # Or redirect logging into a file: + # c[:log_file] = Merb.root / "log" / "development.log" +} diff --git a/chef-server/config/environments/staging.rb b/chef-server/config/environments/staging.rb new file mode 100644 index 0000000000..f5b5058938 --- /dev/null +++ b/chef-server/config/environments/staging.rb @@ -0,0 +1,10 @@ +Merb.logger.info("Loaded STAGING Environment...") +Merb::Config.use { |c| + c[:exception_details] = false + c[:reload_classes] = false + c[:log_level] = :error + + c[:log_file] = Merb.root / "log" / "staging.log" + # or redirect logger using IO handle + # c[:log_stream] = STDOUT +} diff --git a/chef-server/config/environments/test.rb b/chef-server/config/environments/test.rb new file mode 100644 index 0000000000..671ec76e70 --- /dev/null +++ b/chef-server/config/environments/test.rb @@ -0,0 +1,12 @@ +Merb.logger.info("Loaded TEST Environment...") +Merb::Config.use { |c| + c[:testing] = true + c[:exception_details] = true + c[:log_auto_flush ] = true + # log less in testing environment + c[:log_level] = :error + + #c[:log_file] = Merb.root / "log" / "test.log" + # or redirect logger using IO handle + c[:log_stream] = STDOUT +} diff --git a/chef-server/config/init.rb b/chef-server/config/init.rb new file mode 100644 index 0000000000..7941bb1649 --- /dev/null +++ b/chef-server/config/init.rb @@ -0,0 +1,27 @@ +# Go to http://wiki.merbivore.com/pages/init-rb + +require 'config/dependencies.rb' +require 'chef' + +use_orm :datamapper +use_test :rspec +use_template_engine :haml + +Merb::Config.use do |c| + c[:use_mutex] = false + c[:session_id_key] = '_chef_server_session_id' + c[:session_secret_key] = Chef::Config.manage_secret_key + c[:session_store] = 'cookie' + c[:exception_details] = true + c[:reload_classes] = false + c[:log_level] = Chef::Config[:log_level] + c[:log_stream] = Chef::Config[:log_location] +end + +Merb::BootLoader.before_app_loads do + # This will get executed after dependencies have been loaded but before your app's classes have loaded. +end + +Merb::BootLoader.after_app_loads do + # This will get executed after your app's classes have been loaded. +end diff --git a/chef-server/config/rack.rb b/chef-server/config/rack.rb new file mode 100644 index 0000000000..494c687342 --- /dev/null +++ b/chef-server/config/rack.rb @@ -0,0 +1,11 @@ +# use PathPrefix Middleware if :path_prefix is set in Merb::Config +if prefix = ::Merb::Config[:path_prefix] + use Merb::Rack::PathPrefix, prefix +end + +# comment this out if you are running merb behind a load balancer +# that serves static files +use Merb::Rack::Static, Merb.dir_for(:public) + +# this is our main merb application +run Merb::Rack::Application.new
\ No newline at end of file diff --git a/chef-server/config/router.rb b/chef-server/config/router.rb new file mode 100644 index 0000000000..6fb537c593 --- /dev/null +++ b/chef-server/config/router.rb @@ -0,0 +1,44 @@ +# Merb::Router is the request routing mapper for the merb framework. +# +# You can route a specific URL to a controller / action pair: +# +# match("/contact"). +# to(:controller => "info", :action => "contact") +# +# You can define placeholder parts of the url with the :symbol notation. These +# placeholders will be available in the params hash of your controllers. For example: +# +# match("/books/:book_id/:action"). +# to(:controller => "books") +# +# Or, use placeholders in the "to" results for more complicated routing, e.g.: +# +# match("/admin/:module/:controller/:action/:id"). +# to(:controller => ":module/:controller") +# +# You can specify conditions on the placeholder by passing a hash as the second +# argument of "match" +# +# match("/registration/:course_name", :course_name => /^[a-z]{3,5}-\d{5}$/). +# to(:controller => "registration") +# +# You can also use regular expressions, deferred routes, and many other options. +# See merb/specs/merb/router.rb for a fairly complete usage sample. + +Merb.logger.info("Compiling routes...") +Merb::Router.prepare do + # RESTful routes + # resources :posts + + # Adds the required routes for merb-auth using the password slice + # slice(:merb_auth_slice_password, :name_prefix => nil, :path_prefix => "") + slice(:chefserverslice) + # This is the default route for /:controller/:action/:id + # This is fine for most cases. If you're heavily using resource-based + # routes, you may want to comment/remove this line to prevent + # clients from calling your create or destroy actions with a GET + default_routes + + # Change this for your home page to be available at / + # match('/').to(:controller => 'whatever', :action =>'index') +end diff --git a/chef-server/lib/init.rb b/chef-server/lib/init.rb deleted file mode 100644 index 7f4b91c57f..0000000000 --- a/chef-server/lib/init.rb +++ /dev/null @@ -1,225 +0,0 @@ -# -# Author:: Adam Jacob (<adam@opscode.com>) -# Copyright:: Copyright (c) 2008 Opscode, Inc. -# License:: Apache License, Version 2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require 'chef' - -Merb.root = Chef::Config[:merb_root] - -# -# ==== Structure of Merb initializer -# -# 1. Load paths. -# 2. Dependencies configuration. -# 3. Libraries (ORM, testing tool, etc) you use. -# 4. Application-specific configuration. - -# -# ==== Set up load paths -# - -# Make the app's "gems" directory a place where gems are loaded from. -# Note that gems directory must have a structure RubyGems uses for -# directories under which gems are kept. -# -# To conveniently set it up use gem install -i <merb_app_root/gems> -# when installing gems. This will set up the structure under /gems -# automagically. -# -# An example: -# -# You want to bundle ActiveRecord and ActiveSupport with your Merb -# application to be deployment environment independent. To do so, -# install gems into merb_app_root/gems directory like this: -# -# gem install -i ~/dev/merbapp/gems activesupport-post-2.0.2gem activerecord-post-2.0.2.gem -# -# Since RubyGems will search merb_app_root/gems for dependencies, order -# in statement above is important: we need to install ActiveSupport which -# ActiveRecord depends on first. -# -# Remember that bundling of dependencies as gems with your application -# makes it independent of the environment it runs in and is a very -# good, encouraged practice to follow. -Gem.clear_paths -Gem.path.unshift(Merb.root / "gems") - -# If you want modules and classes from libraries organized like -# merbapp/lib/magicwand/lib/magicwand.rb to autoload, -# uncomment this. - - -# Merb.push_path(:lib, Merb.root / "lib") # uses **/*.rb as path glob. - - -# disable the ActiveSupport json annoying stuff, as it breaks our to_json -Merb.disable :json - -# ==== Dependencies - -# These are some examples of how you might specify dependencies. -# Dependencies load is delayed to one of later Merb app -# boot stages. It may be important when -# later part of your configuration relies on libraries specified -# here. -# -# dependencies "RedCloth", "merb_helpers" -# OR -# dependency "RedCloth", "> 3.0" -# OR -# dependencies "RedCloth" => "> 3.0", "ruby-aes-cext" => "= 1.0" -Merb::BootLoader.after_app_loads do - Chef::Queue.connect - - # create the couch design docs for nodes and openid registrations - Chef::Node.create_design_document - Chef::OpenIDRegistration.create_design_document - # dependency "magic_admin" # this gem uses the app's model classes -end - -# -# ==== Set up your ORM of choice -# - -# Merb doesn't come with database support by default. You need -# an ORM plugin. Install one, and uncomment one of the following lines, -# if you need a database. - -# Uncomment for DataMapper ORM -# use_orm :datamapper - -# Uncomment for ActiveRecord ORM -# use_orm :activerecord - -# Uncomment for Sequel ORM -# use_orm :sequel - -Merb.push_path(:lib, File.join(File.dirname(__FILE__), "..", "chef")) -Merb.push_path(:controller, File.join(File.dirname(__FILE__), "controllers")) -Merb.push_path(:model, File.join(File.dirname(__FILE__), "models")) -Merb.push_path(:view, File.join(File.dirname(__FILE__), "views")) -Merb.push_path(:helper, File.join(File.dirname(__FILE__), "helpers")) -Merb.push_path(:public, File.join(File.dirname(__FILE__), "public")) - -require 'merb-haml' -require 'merb-assets' -require 'merb-helpers' -require 'syntax/convertors/html' - - -# -# ==== Pick what you test with -# - -# This defines which test framework the generators will use -# rspec is turned on by default -# -# Note that you need to install the merb_rspec if you want to ue -# rspec and merb_test_unit if you want to use test_unit. -# merb_rspec is installed by default if you did gem install -# merb. -# -# use_test :test_unit -use_test :rspec - -# -# ==== Set up your basic configuration -# -Merb::Config.use do |c| - c[:fork_for_class_load] = false - # Sets up a custom session id key, if you want to piggyback sessions of other applications - # with the cookie session store. If not specified, defaults to '_session_id'. - c[:session_id_key] = '_chef_server_session_id' - - newpass = nil - if Chef::FileCache.has_key?("chef_server_cookie_id") - newpass = Chef::FileCache.load("chef_server_cookie_id") - else - chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a - newpass = "" - 1.upto(40) { |i| newpass << chars[rand(chars.size-1)] } - Chef::FileCache.store("chef_server_cookie_id", newpass) - end - - c[:session_secret_key] = newpass - c[:session_store] = 'cookie' - c[:exception_details] = true - c[:reload_classes] = false - c[:log_level] = Chef::Config[:log_level] - c[:log_stream] = Chef::Config[:log_location] -end - -Chef::Log.logger = Merb.logger -Chef::Log.info("Compiling routes... (totally normal to see 'Cannot find resource model')") - -Merb::Router.prepare do |r| - # RESTful routes - # r.resources :posts - - # This is the default route for /:controller/:action/:id - # This is fine for most cases. If you're heavily using resource-based - # routes, you may want to comment/remove this line to prevent - # clients from calling your create or destroy actions with a GET - - resources :nodes - resources :searches, :path => "search", :controller => "search" do - resources :entries, :controller => "search_entries" - end - - match("/cookbooks/_attribute_files").to(:controller => "cookbooks", :action => "attribute_files") - match("/cookbooks/_recipe_files").to(:controller => "cookbooks", :action => "recipe_files") - match("/cookbooks/_definition_files").to(:controller => "cookbooks", :action => "definition_files") - match("/cookbooks/_library_files").to(:controller => "cookbooks", :action => "library_files") - -# r.match("/cookbooks/:cookbook_id/templates").to(:controller => "cookbook_templates", :action => "index") - - resources :cookbooks do - resources :templates, :controller => "cookbook_templates" - resources :files, :controller => "cookbook_files" - resources :recipes, :controller => "cookbook_recipes" - resources :attributes, :controller => "cookbook_attributes" - resources :definitions, :controller => "cookbook_definitions" - resources :libraries, :controller => "cookbook_libraries" - end - - #r.resources :openid do |res| - # res.resources :register, :controller => "openid_register" - # res.resources :server, :controller => "openid_server" - #end - - resources :registrations, :controller => "openid_register" - resources :registrations, :controller => "openid_register", :member => { :validate => :post } - resources :registrations, :controller => "openid_register", :member => { :admin => :post } - - match("/openid/server").to(:controller => "openid_server", :action => "index").name(:openid_server) - match("/openid/server/server/xrds"). - to(:controller => "openid_server", :action => 'idp_xrds').name(:openid_server_xrds) - match("/openid/server/node/:id"). - to(:controller => "openid_server", :action => 'node_page').name(:openid_node) - match('/openid/server/node/:id/xrds'). - to(:controller => 'openid_server', :action => 'node_xrds').name(:openid_node_xrds) - match('/openid/server/decision').to(:controller => "openid_server", :action => "decision").name(:openid_server_decision) - match('/openid/consumer').to(:controller => 'openid_consumer', :action => 'index').name(:openid_consumer) - match('/openid/consumer/start').to(:controller => 'openid_consumer', :action => 'start').name(:openid_consumer_start) - match('/openid/consumer/complete').to(:controller => 'openid_consumer', :action => 'complete').name(:openid_consumer_complete) - match('/openid/consumer/logout').to(:controller => 'openid_consumer', :action => 'logout').name(:openid_consumer_logout) - - #r.default_routes - - # Change this for your home page to be available at / - match('/').to(:controller => 'nodes', :action =>'index').name(:top) -end diff --git a/chef-server/lib/tasks/install.rake b/chef-server/lib/tasks/install.rake new file mode 100644 index 0000000000..2c68243332 --- /dev/null +++ b/chef-server/lib/tasks/install.rake @@ -0,0 +1,7 @@ +require 'rubygems' +require 'rake/gempackagetask' + +task :install => :package do + sh %{sudo gem install pkg/#{GEM}-#{CHEF_SERVER_VERSION} --no-rdoc --no-ri} +end + diff --git a/chef-server/lib/tasks/package.rake b/chef-server/lib/tasks/package.rake new file mode 100644 index 0000000000..48cbecebdb --- /dev/null +++ b/chef-server/lib/tasks/package.rake @@ -0,0 +1,52 @@ +require 'rubygems' +require 'rake/gempackagetask' + +spec = Gem::Specification.new do |s| + s.name = GEM + s.version = CHEF_SERVER_VERSION + s.platform = Gem::Platform::RUBY + s.has_rdoc = true + s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'NOTICE'] + s.summary = SUMMARY + s.description = s.summary + s.author = AUTHOR + s.email = EMAIL + s.homepage = HOMEPAGE + + s.add_dependency "stomp" + s.add_dependency "stompserver" + s.add_dependency "ferret" + s.add_dependency "merb-core" + s.add_dependency "merb-haml" + s.add_dependency "merb-assets" + s.add_dependency "merb-helpers" + s.add_dependency "mongrel" + s.add_dependency "haml" + s.add_dependency "ruby-openid" + s.add_dependency "json" + s.add_dependency "syntax" + + s.bindir = "bin" + s.executables = %w( chef-server chef-indexer ) + s.files = %w(LICENSE NOTICE README.rdoc Rakefile) + + [ "README.txt", + "LICENSE", + "NOTICE", + "{app}/**/*", + "{config}/**/*", + "{doc}/**/*", + "{lib}/**/*", + "{log}/**/*", + "{merb}/**/*", + "{public}/**/*", + "{slices}/**/*", + "{spec}/**/*", + "{tasks}/**/*",].inject([]) { |m,dir| m << Dir.glob(dir) }.flatten +end + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + + + diff --git a/chef-server/lib/tasks/uninstall.rake b/chef-server/lib/tasks/uninstall.rake new file mode 100644 index 0000000000..8521d1e5ff --- /dev/null +++ b/chef-server/lib/tasks/uninstall.rake @@ -0,0 +1,7 @@ +require 'rubygems' +require 'rake/gempackagetask' + +task :uninstall do + sh %{sudo gem uninstall #{GEM} -x -v #{CHEF_SERVER_VERSION}} +end + diff --git a/chef-server/lib/views/nodes/index.html.haml b/chef-server/lib/views/nodes/index.html.haml deleted file mode 100644 index 9ee67e5e1d..0000000000 --- a/chef-server/lib/views/nodes/index.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- throw_content(:header, "Node List") -- @node_list.each do |node| - .node - %a{ :href => url(:node, { :id => node.gsub(/\./, "_") }) } - = node - - if session[:level] == :admin - = delete_button url(:node, { :id => node.gsub(/\./, "_") }), "Delete"
\ No newline at end of file diff --git a/chef-server/merb/merb-auth/setup.rb b/chef-server/merb/merb-auth/setup.rb new file mode 100644 index 0000000000..612f01d5a6 --- /dev/null +++ b/chef-server/merb/merb-auth/setup.rb @@ -0,0 +1,44 @@ +# This file is specifically setup for use with the merb-auth plugin. +# This file should be used to setup and configure your authentication stack. +# It is not required and may safely be deleted. +# +# To change the parameter names for the password or login field you may set either of these two options +# +# Merb::Plugins.config[:"merb-auth"][:login_param] = :email +# Merb::Plugins.config[:"merb-auth"][:password_param] = :my_password_field_name + +begin + # Sets the default class ofr authentication. This is primarily used for + # Plugins and the default strategies + Merb::Authentication.user_class = User + + + # Mixin the salted user mixin + require 'merb-auth-more/mixins/salted_user' + Merb::Authentication.user_class.class_eval{ include Merb::Authentication::Mixins::SaltedUser } + + # Setup the session serialization + class Merb::Authentication + + def fetch_user(session_user_id) + Merb::Authentication.user_class.get(session_user_id) + end + + def store_user(user) + user.nil? ? user : user.id + end + end + +rescue + Merb.logger.error <<-TEXT + + You need to setup some kind of user class with merb-auth. + Merb::Authentication.user_class = User + + If you want to fully customize your authentication you should use merb-core directly. + + See merb/merb-auth/setup.rb and strategies.rb to customize your setup + + TEXT +end + diff --git a/chef-server/merb/merb-auth/strategies.rb b/chef-server/merb/merb-auth/strategies.rb new file mode 100644 index 0000000000..fd6f20a0c7 --- /dev/null +++ b/chef-server/merb/merb-auth/strategies.rb @@ -0,0 +1,11 @@ +# This file is specifically for you to define your strategies +# +# You should declare you strategies directly and/or use +# Merb::Authentication.activate!(:label_of_strategy) +# +# To load and set the order of strategy processing + +Merb::Slices::config[:"merb-auth-slice-password"][:no_default_strategies] = true + +Merb::Authentication.activate!(:default_password_form) +Merb::Authentication.activate!(:default_basic_auth)
\ No newline at end of file diff --git a/chef-server/merb/session/session.rb b/chef-server/merb/session/session.rb new file mode 100644 index 0000000000..5d05316353 --- /dev/null +++ b/chef-server/merb/session/session.rb @@ -0,0 +1,9 @@ +module Merb + module Session + + # The Merb::Session module gets mixed into Merb::SessionContainer to allow + # app-level functionality; it will be included and methods will be available + # through request.session as instance methods. + + end +end
\ No newline at end of file diff --git a/chef-server/public/.htaccess b/chef-server/public/.htaccess new file mode 100644 index 0000000000..455e706f96 --- /dev/null +++ b/chef-server/public/.htaccess @@ -0,0 +1,17 @@ +# Sets the default handler for FastCGI scripts +AddHandler fastcgi-script .fcgi + +# If Apache2 is used together with mod_fcgid, +# uncomment the line below and comment in the line +# above to set the correct script handler +#AddHandler fcgid-script .fcgi + +RewriteEngine On + +RewriteRule ^$ index.html [QSA] +RewriteRule ^([^.]+)$ $1.html [QSA] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteRule ^(.*)$ merb.fcgi [QSA,L] + + +ErrorDocument 500 "<h2>Application Error</h2>Merb could not be reached" diff --git a/chef-server/public/favicon.ico b/chef-server/public/favicon.ico Binary files differnew file mode 100644 index 0000000000..c908d63b92 --- /dev/null +++ b/chef-server/public/favicon.ico diff --git a/chef-server/lib/public/images/merb.jpg b/chef-server/public/images/merb.jpg Binary files differindex a19dcf4048..a19dcf4048 100644 --- a/chef-server/lib/public/images/merb.jpg +++ b/chef-server/public/images/merb.jpg diff --git a/chef-server/public/javascripts/application.js b/chef-server/public/javascripts/application.js new file mode 100644 index 0000000000..246a8be4c1 --- /dev/null +++ b/chef-server/public/javascripts/application.js @@ -0,0 +1 @@ +// Common JavaScript code across your application goes here.
\ No newline at end of file diff --git a/chef-server/public/javascripts/jquery.js b/chef-server/public/javascripts/jquery.js new file mode 100644 index 0000000000..396646c842 --- /dev/null +++ b/chef-server/public/javascripts/jquery.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-01-13 12:50:31 -0500 (Tue, 13 Jan 2009) + * Revision: 6104 + */ +(function(){var l=this,g,x=l.jQuery,o=l.$,n=l.jQuery=l.$=function(D,E){return new n.fn.init(D,E)},C=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;n.fn=n.prototype={init:function(D,G){D=D||document;if(D.nodeType){this[0]=D;this.length=1;this.context=D;return this}if(typeof D==="string"){var F=C.exec(D);if(F&&(F[1]||!G)){if(F[1]){D=n.clean([F[1]],G)}else{var H=document.getElementById(F[3]);if(H){if(H.id!=F[3]){return n().find(D)}var E=n(H);E.context=document;E.selector=D;return E}D=[]}}else{return n(G).find(D)}}else{if(n.isFunction(D)){return n(document).ready(D)}}if(D.selector&&D.context){this.selector=D.selector;this.context=D.context}return this.setArray(n.makeArray(D))},selector:"",jquery:"1.3",size:function(){return this.length},get:function(D){return D===g?n.makeArray(this):this[D]},pushStack:function(E,G,D){var F=n(E);F.prevObject=this;F.context=this.context;if(G==="find"){F.selector=this.selector+(this.selector?" ":"")+D}else{if(G){F.selector=this.selector+"."+G+"("+D+")"}}return F},setArray:function(D){this.length=0;Array.prototype.push.apply(this,D);return this},each:function(E,D){return n.each(this,E,D)},index:function(D){return n.inArray(D&&D.jquery?D[0]:D,this)},attr:function(E,G,F){var D=E;if(typeof E==="string"){if(G===g){return this[0]&&n[F||"attr"](this[0],E)}else{D={};D[E]=G}}return this.each(function(H){for(E in D){n.attr(F?this.style:this,E,n.prop(this,D[E],F,H,E))}})},css:function(D,E){if((D=="width"||D=="height")&&parseFloat(E)<0){E=g}return this.attr(D,E,"curCSS")},text:function(E){if(typeof E!=="object"&&E!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(E))}var D="";n.each(E||this,function(){n.each(this.childNodes,function(){if(this.nodeType!=8){D+=this.nodeType!=1?this.nodeValue:n.fn.text([this])}})});return D},wrapAll:function(D){if(this[0]){var E=n(D,this[0].ownerDocument).clone();if(this[0].parentNode){E.insertBefore(this[0])}E.map(function(){var F=this;while(F.firstChild){F=F.firstChild}return F}).append(this)}return this},wrapInner:function(D){return this.each(function(){n(this).contents().wrapAll(D)})},wrap:function(D){return this.each(function(){n(this).wrapAll(D)})},append:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.appendChild(D)}})},prepend:function(){return this.domManip(arguments,true,function(D){if(this.nodeType==1){this.insertBefore(D,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this)})},after:function(){return this.domManip(arguments,false,function(D){this.parentNode.insertBefore(D,this.nextSibling)})},end:function(){return this.prevObject||n([])},push:[].push,find:function(D){if(this.length===1&&!/,/.test(D)){var F=this.pushStack([],"find",D);F.length=0;n.find(D,this[0],F);return F}else{var E=n.map(this,function(G){return n.find(D,G)});return this.pushStack(/[^+>] [^+>]/.test(D)?n.unique(E):E,"find",D)}},clone:function(E){var D=this.map(function(){if(!n.support.noCloneEvent&&!n.isXMLDoc(this)){var H=this.cloneNode(true),G=document.createElement("div");G.appendChild(H);return n.clean([G.innerHTML])[0]}else{return this.cloneNode(true)}});var F=D.find("*").andSelf().each(function(){if(this[h]!==g){this[h]=null}});if(E===true){this.find("*").andSelf().each(function(H){if(this.nodeType==3){return}var G=n.data(this,"events");for(var J in G){for(var I in G[J]){n.event.add(F[H],J,G[J][I],G[J][I].data)}}})}return D},filter:function(D){return this.pushStack(n.isFunction(D)&&n.grep(this,function(F,E){return D.call(F,E)})||n.multiFilter(D,n.grep(this,function(E){return E.nodeType===1})),"filter",D)},closest:function(D){var E=n.expr.match.POS.test(D)?n(D):null;return this.map(function(){var F=this;while(F&&F.ownerDocument){if(E?E.index(F)>-1:n(F).is(D)){return F}F=F.parentNode}})},not:function(D){if(typeof D==="string"){if(f.test(D)){return this.pushStack(n.multiFilter(D,this,true),"not",D)}else{D=n.multiFilter(D,this)}}var E=D.length&&D[D.length-1]!==g&&!D.nodeType;return this.filter(function(){return E?n.inArray(this,D)<0:this!=D})},add:function(D){return this.pushStack(n.unique(n.merge(this.get(),typeof D==="string"?n(D):n.makeArray(D))))},is:function(D){return !!D&&n.multiFilter(D,this).length>0},hasClass:function(D){return !!D&&this.is("."+D)},val:function(J){if(J===g){var D=this[0];if(D){if(n.nodeName(D,"option")){return(D.attributes.value||{}).specified?D.value:D.text}if(n.nodeName(D,"select")){var H=D.selectedIndex,K=[],L=D.options,G=D.type=="select-one";if(H<0){return null}for(var E=G?H:0,I=G?H+1:L.length;E<I;E++){var F=L[E];if(F.selected){J=n(F).val();if(G){return J}K.push(J)}}return K}return(D.value||"").replace(/\r/g,"")}return g}if(typeof J==="number"){J+=""}return this.each(function(){if(this.nodeType!=1){return}if(n.isArray(J)&&/radio|checkbox/.test(this.type)){this.checked=(n.inArray(this.value,J)>=0||n.inArray(this.name,J)>=0)}else{if(n.nodeName(this,"select")){var M=n.makeArray(J);n("option",this).each(function(){this.selected=(n.inArray(this.value,M)>=0||n.inArray(this.text,M)>=0)});if(!M.length){this.selectedIndex=-1}}else{this.value=J}}})},html:function(D){return D===g?(this[0]?this[0].innerHTML:null):this.empty().append(D)},replaceWith:function(D){return this.after(D).remove()},eq:function(D){return this.slice(D,+D+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(D){return this.pushStack(n.map(this,function(F,E){return D.call(F,E,F)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=n.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild,D=this.length>1?I.cloneNode(true):I;if(H){for(var G=0,E=this.length;G<E;G++){L.call(K(this[G],H),G>0?D.cloneNode(true):I)}}if(F){n.each(F,y)}}return this;function K(N,O){return M&&n.nodeName(N,"table")&&n.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};n.fn.init.prototype=n.fn;function y(D,E){if(E.src){n.ajax({url:E.src,async:false,dataType:"script"})}else{n.globalEval(E.text||E.textContent||E.innerHTML||"")}if(E.parentNode){E.parentNode.removeChild(E)}}function e(){return +new Date}n.extend=n.fn.extend=function(){var I=arguments[0]||{},G=1,H=arguments.length,D=false,F;if(typeof I==="boolean"){D=I;I=arguments[1]||{};G=2}if(typeof I!=="object"&&!n.isFunction(I)){I={}}if(H==G){I=this;--G}for(;G<H;G++){if((F=arguments[G])!=null){for(var E in F){var J=I[E],K=F[E];if(I===K){continue}if(D&&K&&typeof K==="object"&&!K.nodeType){I[E]=n.extend(D,J||(K.length!=null?[]:{}),K)}else{if(K!==g){I[E]=K}}}}}return I};var b=/z-?index|font-?weight|opacity|zoom|line-?height/i,p=document.defaultView||{},r=Object.prototype.toString;n.extend({noConflict:function(D){l.$=o;if(D){l.jQuery=x}return n},isFunction:function(D){return r.call(D)==="[object Function]"},isArray:function(D){return r.call(D)==="[object Array]"},isXMLDoc:function(D){return D.documentElement&&!D.body||D.tagName&&D.ownerDocument&&!D.ownerDocument.body},globalEval:function(F){F=n.trim(F);if(F){var E=document.getElementsByTagName("head")[0]||document.documentElement,D=document.createElement("script");D.type="text/javascript";if(n.support.scriptEval){D.appendChild(document.createTextNode(F))}else{D.text=F}E.insertBefore(D,E.firstChild);E.removeChild(D)}},nodeName:function(E,D){return E.nodeName&&E.nodeName.toUpperCase()==D.toUpperCase()},each:function(F,J,E){var D,G=0,H=F.length;if(E){if(H===g){for(D in F){if(J.apply(F[D],E)===false){break}}}else{for(;G<H;){if(J.apply(F[G++],E)===false){break}}}}else{if(H===g){for(D in F){if(J.call(F[D],D,F[D])===false){break}}}else{for(var I=F[0];G<H&&J.call(I,G,I)!==false;I=F[++G]){}}}return F},prop:function(G,H,F,E,D){if(n.isFunction(H)){H=H.call(G,E)}return typeof H==="number"&&F=="curCSS"&&!b.test(D)?H+"px":H},className:{add:function(D,E){n.each((E||"").split(/\s+/),function(F,G){if(D.nodeType==1&&!n.className.has(D.className,G)){D.className+=(D.className?" ":"")+G}})},remove:function(D,E){if(D.nodeType==1){D.className=E!==g?n.grep(D.className.split(/\s+/),function(F){return !n.className.has(E,F)}).join(" "):""}},has:function(E,D){return n.inArray(D,(E.className||E).toString().split(/\s+/))>-1}},swap:function(G,F,H){var D={};for(var E in F){D[E]=G.style[E];G.style[E]=F[E]}H.call(G);for(var E in F){G.style[E]=D[E]}},css:function(F,D,H){if(D=="width"||D=="height"){var J,E={position:"absolute",visibility:"hidden",display:"block"},I=D=="width"?["Left","Right"]:["Top","Bottom"];function G(){J=D=="width"?F.offsetWidth:F.offsetHeight;var L=0,K=0;n.each(I,function(){L+=parseFloat(n.curCSS(F,"padding"+this,true))||0;K+=parseFloat(n.curCSS(F,"border"+this+"Width",true))||0});J-=Math.round(L+K)}if(n(F).is(":visible")){G()}else{n.swap(F,E,G)}return Math.max(0,J)}return n.curCSS(F,D,H)},curCSS:function(H,E,F){var K,D=H.style;if(E=="opacity"&&!n.support.opacity){K=n.attr(D,"opacity");return K==""?"1":K}if(E.match(/float/i)){E=v}if(!F&&D&&D[E]){K=D[E]}else{if(p.getComputedStyle){if(E.match(/float/i)){E="float"}E=E.replace(/([A-Z])/g,"-$1").toLowerCase();var L=p.getComputedStyle(H,null);if(L){K=L.getPropertyValue(E)}if(E=="opacity"&&K==""){K="1"}}else{if(H.currentStyle){var I=E.replace(/\-(\w)/g,function(M,N){return N.toUpperCase()});K=H.currentStyle[E]||H.currentStyle[I];if(!/^\d+(px)?$/i.test(K)&&/^\d/.test(K)){var G=D.left,J=H.runtimeStyle.left;H.runtimeStyle.left=H.currentStyle.left;D.left=K||0;K=D.pixelLeft+"px";D.left=G;H.runtimeStyle.left=J}}}}return K},clean:function(E,J,H){J=J||document;if(typeof J.createElement==="undefined"){J=J.ownerDocument||J[0]&&J[0].ownerDocument||document}if(!H&&E.length===1&&typeof E[0]==="string"){var G=/^<(\w+)\s*\/?>$/.exec(E[0]);if(G){return[J.createElement(G[1])]}}var F=[],D=[],K=J.createElement("div");n.each(E,function(O,Q){if(typeof Q==="number"){Q+=""}if(!Q){return}if(typeof Q==="string"){Q=Q.replace(/(<(\w+)[^>]*?)\/>/g,function(S,T,R){return R.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?S:T+"></"+R+">"});var N=n.trim(Q).toLowerCase();var P=!N.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!N.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||N.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!N.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!N.indexOf("<td")||!N.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!N.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||!n.support.htmlSerialize&&[1,"div<div>","</div>"]||[0,"",""];K.innerHTML=P[1]+Q+P[2];while(P[0]--){K=K.lastChild}if(!n.support.tbody){var M=!N.indexOf("<table")&&N.indexOf("<tbody")<0?K.firstChild&&K.firstChild.childNodes:P[1]=="<table>"&&N.indexOf("<tbody")<0?K.childNodes:[];for(var L=M.length-1;L>=0;--L){if(n.nodeName(M[L],"tbody")&&!M[L].childNodes.length){M[L].parentNode.removeChild(M[L])}}}if(!n.support.leadingWhitespace&&/^\s/.test(Q)){K.insertBefore(J.createTextNode(Q.match(/^\s*/)[0]),K.firstChild)}Q=n.makeArray(K.childNodes)}if(Q.nodeType){F.push(Q)}else{F=n.merge(F,Q)}});if(H){for(var I=0;F[I];I++){if(n.nodeName(F[I],"script")&&(!F[I].type||F[I].type.toLowerCase()==="text/javascript")){D.push(F[I].parentNode?F[I].parentNode.removeChild(F[I]):F[I])}else{if(F[I].nodeType===1){F.splice.apply(F,[I+1,0].concat(n.makeArray(F[I].getElementsByTagName("script"))))}H.appendChild(F[I])}}return D}return F},attr:function(I,F,J){if(!I||I.nodeType==3||I.nodeType==8){return g}var G=!n.isXMLDoc(I),K=J!==g;F=G&&n.props[F]||F;if(I.tagName){var E=/href|src|style/.test(F);if(F=="selected"&&I.parentNode){I.parentNode.selectedIndex}if(F in I&&G&&!E){if(K){if(F=="type"&&n.nodeName(I,"input")&&I.parentNode){throw"type property can't be changed"}I[F]=J}if(n.nodeName(I,"form")&&I.getAttributeNode(F)){return I.getAttributeNode(F).nodeValue}if(F=="tabIndex"){var H=I.getAttributeNode("tabIndex");return H&&H.specified?H.value:I.nodeName.match(/^(a|area|button|input|object|select|textarea)$/i)?0:g}return I[F]}if(!n.support.style&&G&&F=="style"){return n.attr(I.style,"cssText",J)}if(K){I.setAttribute(F,""+J)}var D=!n.support.hrefNormalized&&G&&E?I.getAttribute(F,2):I.getAttribute(F);return D===null?g:D}if(!n.support.opacity&&F=="opacity"){if(K){I.zoom=1;I.filter=(I.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(J)+""=="NaN"?"":"alpha(opacity="+J*100+")")}return I.filter&&I.filter.indexOf("opacity=")>=0?(parseFloat(I.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}F=F.replace(/-([a-z])/ig,function(L,M){return M.toUpperCase()});if(K){I[F]=J}return I[F]},trim:function(D){return(D||"").replace(/^\s+|\s+$/g,"")},makeArray:function(F){var D=[];if(F!=null){var E=F.length;if(E==null||typeof F==="string"||n.isFunction(F)||F.setInterval){D[0]=F}else{while(E){D[--E]=F[E]}}}return D},inArray:function(F,G){for(var D=0,E=G.length;D<E;D++){if(G[D]===F){return D}}return -1},merge:function(G,D){var E=0,F,H=G.length;if(!n.support.getAll){while((F=D[E++])!=null){if(F.nodeType!=8){G[H++]=F}}}else{while((F=D[E++])!=null){G[H++]=F}}return G},unique:function(J){var E=[],D={};try{for(var F=0,G=J.length;F<G;F++){var I=n.data(J[F]);if(!D[I]){D[I]=true;E.push(J[F])}}}catch(H){E=J}return E},grep:function(E,I,D){var F=[];for(var G=0,H=E.length;G<H;G++){if(!D!=!I(E[G],G)){F.push(E[G])}}return F},map:function(D,I){var E=[];for(var F=0,G=D.length;F<G;F++){var H=I(D[F],F);if(H!=null){E[E.length]=H}}return E.concat.apply([],E)}});var B=navigator.userAgent.toLowerCase();n.browser={version:(B.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[0,"0"])[1],safari:/webkit/.test(B),opera:/opera/.test(B),msie:/msie/.test(B)&&!/opera/.test(B),mozilla:/mozilla/.test(B)&&!/(compatible|webkit)/.test(B)};n.each({parent:function(D){return D.parentNode},parents:function(D){return n.dir(D,"parentNode")},next:function(D){return n.nth(D,2,"nextSibling")},prev:function(D){return n.nth(D,2,"previousSibling")},nextAll:function(D){return n.dir(D,"nextSibling")},prevAll:function(D){return n.dir(D,"previousSibling")},siblings:function(D){return n.sibling(D.parentNode.firstChild,D)},children:function(D){return n.sibling(D.firstChild)},contents:function(D){return n.nodeName(D,"iframe")?D.contentDocument||D.contentWindow.document:n.makeArray(D.childNodes)}},function(D,E){n.fn[D]=function(F){var G=n.map(this,E);if(F&&typeof F=="string"){G=n.multiFilter(F,G)}return this.pushStack(n.unique(G),D,F)}});n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(D,E){n.fn[D]=function(){var F=arguments;return this.each(function(){for(var G=0,H=F.length;G<H;G++){n(F[G])[E](this)}})}});n.each({removeAttr:function(D){n.attr(this,D,"");if(this.nodeType==1){this.removeAttribute(D)}},addClass:function(D){n.className.add(this,D)},removeClass:function(D){n.className.remove(this,D)},toggleClass:function(E,D){if(typeof D!=="boolean"){D=!n.className.has(this,E)}n.className[D?"add":"remove"](this,E)},remove:function(D){if(!D||n.filter(D,[this]).length){n("*",this).add([this]).each(function(){n.event.remove(this);n.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){n(">*",this).remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(D,E){n.fn[D]=function(){return this.each(E,arguments)}});function j(D,E){return D[0]&&parseInt(n.curCSS(D[0],E,true),10)||0}var h="jQuery"+e(),u=0,z={};n.extend({cache:{},data:function(E,D,F){E=E==l?z:E;var G=E[h];if(!G){G=E[h]=++u}if(D&&!n.cache[G]){n.cache[G]={}}if(F!==g){n.cache[G][D]=F}return D?n.cache[G][D]:G},removeData:function(E,D){E=E==l?z:E;var G=E[h];if(D){if(n.cache[G]){delete n.cache[G][D];D="";for(D in n.cache[G]){break}if(!D){n.removeData(E)}}}else{try{delete E[h]}catch(F){if(E.removeAttribute){E.removeAttribute(h)}}delete n.cache[G]}},queue:function(E,D,G){if(E){D=(D||"fx")+"queue";var F=n.data(E,D);if(!F||n.isArray(G)){F=n.data(E,D,n.makeArray(G))}else{if(G){F.push(G)}}}return F},dequeue:function(G,F){var D=n.queue(G,F),E=D.shift();if(!F||F==="fx"){E=D[0]}if(E!==g){E.call(G)}}});n.fn.extend({data:function(D,F){var G=D.split(".");G[1]=G[1]?"."+G[1]:"";if(F===g){var E=this.triggerHandler("getData"+G[1]+"!",[G[0]]);if(E===g&&this.length){E=n.data(this[0],D)}return E===g&&G[1]?this.data(G[0]):E}else{return this.trigger("setData"+G[1]+"!",[G[0],F]).each(function(){n.data(this,D,F)})}},removeData:function(D){return this.each(function(){n.removeData(this,D)})},queue:function(D,E){if(typeof D!=="string"){E=D;D="fx"}if(E===g){return n.queue(this[0],D)}return this.each(function(){var F=n.queue(this,D,E);if(D=="fx"&&F.length==1){F[0].call(this)}})},dequeue:function(D){return this.each(function(){n.dequeue(this,D)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.1 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var N=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|[^[\]]+)+\]|\\.|[^ >+~,(\[]+)+|[>+~])(\s*,\s*)?/g,I=0,F=Object.prototype.toString;var E=function(ae,S,aa,V){aa=aa||[];S=S||document;if(S.nodeType!==1&&S.nodeType!==9){return[]}if(!ae||typeof ae!=="string"){return aa}var ab=[],ac,Y,ah,ag,Z,R,Q=true;N.lastIndex=0;while((ac=N.exec(ae))!==null){ab.push(ac[1]);if(ac[2]){R=RegExp.rightContext;break}}if(ab.length>1&&G.match.POS.exec(ae)){if(ab.length===2&&G.relative[ab[0]]){var U="",X;while((X=G.match.POS.exec(ae))){U+=X[0];ae=ae.replace(G.match.POS,"")}Y=E.filter(U,E(/\s$/.test(ae)?ae+"*":ae,S))}else{Y=G.relative[ab[0]]?[S]:E(ab.shift(),S);while(ab.length){var P=[];ae=ab.shift();if(G.relative[ae]){ae+=ab.shift()}for(var af=0,ad=Y.length;af<ad;af++){E(ae,Y[af],P)}Y=P}}}else{var ai=V?{expr:ab.pop(),set:D(V)}:E.find(ab.pop(),ab.length===1&&S.parentNode?S.parentNode:S);Y=E.filter(ai.expr,ai.set);if(ab.length>0){ah=D(Y)}else{Q=false}while(ab.length){var T=ab.pop(),W=T;if(!G.relative[T]){T=""}else{W=ab.pop()}if(W==null){W=S}G.relative[T](ah,W,M(S))}}if(!ah){ah=Y}if(!ah){throw"Syntax error, unrecognized expression: "+(T||ae)}if(F.call(ah)==="[object Array]"){if(!Q){aa.push.apply(aa,ah)}else{if(S.nodeType===1){for(var af=0;ah[af]!=null;af++){if(ah[af]&&(ah[af]===true||ah[af].nodeType===1&&H(S,ah[af]))){aa.push(Y[af])}}}else{for(var af=0;ah[af]!=null;af++){if(ah[af]&&ah[af].nodeType===1){aa.push(Y[af])}}}}}else{D(ah,aa)}if(R){E(R,S,aa,V)}return aa};E.matches=function(P,Q){return E(P,null,null,Q)};E.find=function(V,S){var W,Q;if(!V){return[]}for(var R=0,P=G.order.length;R<P;R++){var T=G.order[R],Q;if((Q=G.match[T].exec(V))){var U=RegExp.leftContext;if(U.substr(U.length-1)!=="\\"){Q[1]=(Q[1]||"").replace(/\\/g,"");W=G.find[T](Q,S);if(W!=null){V=V.replace(G.match[T],"");break}}}}if(!W){W=S.getElementsByTagName("*")}return{set:W,expr:V}};E.filter=function(S,ac,ad,T){var Q=S,Y=[],ah=ac,V,ab;while(S&&ac.length){for(var U in G.filter){if((V=G.match[U].exec(S))!=null){var Z=G.filter[U],R=null,X=0,aa,ag;ab=false;if(ah==Y){Y=[]}if(G.preFilter[U]){V=G.preFilter[U](V,ah,ad,Y,T);if(!V){ab=aa=true}else{if(V===true){continue}else{if(V[0]===true){R=[];var W=null,af;for(var ae=0;(af=ah[ae])!==g;ae++){if(af&&W!==af){R.push(af);W=af}}}}}}if(V){for(var ae=0;(ag=ah[ae])!==g;ae++){if(ag){if(R&&ag!=R[X]){X++}aa=Z(ag,V,X,R);var P=T^!!aa;if(ad&&aa!=null){if(P){ab=true}else{ah[ae]=false}}else{if(P){Y.push(ag);ab=true}}}}}if(aa!==g){if(!ad){ah=Y}S=S.replace(G.match[U],"");if(!ab){return[]}break}}}S=S.replace(/\s*,\s*/,"");if(S==Q){if(ab==null){throw"Syntax error, unrecognized expression: "+S}else{break}}Q=S}return ah};var G=E.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(P){return P.getAttribute("href")}},relative:{"+":function(T,Q){for(var R=0,P=T.length;R<P;R++){var S=T[R];if(S){var U=S.previousSibling;while(U&&U.nodeType!==1){U=U.previousSibling}T[R]=typeof Q==="string"?U||false:U===Q}}if(typeof Q==="string"){E.filter(Q,T,true)}},">":function(U,Q,V){if(typeof Q==="string"&&!/\W/.test(Q)){Q=V?Q:Q.toUpperCase();for(var R=0,P=U.length;R<P;R++){var T=U[R];if(T){var S=T.parentNode;U[R]=S.nodeName===Q?S:false}}}else{for(var R=0,P=U.length;R<P;R++){var T=U[R];if(T){U[R]=typeof Q==="string"?T.parentNode:T.parentNode===Q}}if(typeof Q==="string"){E.filter(Q,U,true)}}},"":function(S,Q,U){var R="done"+(I++),P=O;if(!Q.match(/\W/)){var T=Q=U?Q:Q.toUpperCase();P=L}P("parentNode",Q,R,S,T,U)},"~":function(S,Q,U){var R="done"+(I++),P=O;if(typeof Q==="string"&&!Q.match(/\W/)){var T=Q=U?Q:Q.toUpperCase();P=L}P("previousSibling",Q,R,S,T,U)}},find:{ID:function(Q,R){if(R.getElementById){var P=R.getElementById(Q[1]);return P?[P]:[]}},NAME:function(P,Q){return Q.getElementsByName?Q.getElementsByName(P[1]):null},TAG:function(P,Q){return Q.getElementsByTagName(P[1])}},preFilter:{CLASS:function(S,Q,R,P,U){S=" "+S[1].replace(/\\/g,"")+" ";for(var T=0;Q[T];T++){if(U^(" "+Q[T].className+" ").indexOf(S)>=0){if(!R){P.push(Q[T])}}else{if(R){Q[T]=false}}}return false},ID:function(P){return P[1].replace(/\\/g,"")},TAG:function(Q,P){for(var R=0;!P[R];R++){}return M(P[R])?Q[1]:Q[1].toUpperCase()},CHILD:function(P){if(P[1]=="nth"){var Q=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(P[2]=="even"&&"2n"||P[2]=="odd"&&"2n+1"||!/\D/.test(P[2])&&"0n+"+P[2]||P[2]);P[2]=(Q[1]+(Q[2]||1))-0;P[3]=Q[3]-0}P[0]="done"+(I++);return P},ATTR:function(Q){var P=Q[1];if(G.attrMap[P]){Q[1]=G.attrMap[P]}if(Q[2]==="~="){Q[4]=" "+Q[4]+" "}return Q},PSEUDO:function(T,Q,R,P,U){if(T[1]==="not"){if(T[3].match(N).length>1){T[3]=E(T[3],null,null,Q)}else{var S=E.filter(T[3],Q,R,true^U);if(!R){P.push.apply(P,S)}return false}}else{if(G.match.POS.test(T[0])){return true}}return T},POS:function(P){P.unshift(true);return P}},filters:{enabled:function(P){return P.disabled===false&&P.type!=="hidden"},disabled:function(P){return P.disabled===true},checked:function(P){return P.checked===true},selected:function(P){P.parentNode.selectedIndex;return P.selected===true},parent:function(P){return !!P.firstChild},empty:function(P){return !P.firstChild},has:function(R,Q,P){return !!E(P[3],R).length},header:function(P){return/h\d/i.test(P.nodeName)},text:function(P){return"text"===P.type},radio:function(P){return"radio"===P.type},checkbox:function(P){return"checkbox"===P.type},file:function(P){return"file"===P.type},password:function(P){return"password"===P.type},submit:function(P){return"submit"===P.type},image:function(P){return"image"===P.type},reset:function(P){return"reset"===P.type},button:function(P){return"button"===P.type||P.nodeName.toUpperCase()==="BUTTON"},input:function(P){return/input|select|textarea|button/i.test(P.nodeName)}},setFilters:{first:function(Q,P){return P===0},last:function(R,Q,P,S){return Q===S.length-1},even:function(Q,P){return P%2===0},odd:function(Q,P){return P%2===1},lt:function(R,Q,P){return Q<P[3]-0},gt:function(R,Q,P){return Q>P[3]-0},nth:function(R,Q,P){return P[3]-0==Q},eq:function(R,Q,P){return P[3]-0==Q}},filter:{CHILD:function(P,S){var V=S[1],W=P.parentNode;var U="child"+W.childNodes.length;if(W&&(!W[U]||!P.nodeIndex)){var T=1;for(var Q=W.firstChild;Q;Q=Q.nextSibling){if(Q.nodeType==1){Q.nodeIndex=T++}}W[U]=T-1}if(V=="first"){return P.nodeIndex==1}else{if(V=="last"){return P.nodeIndex==W[U]}else{if(V=="only"){return W[U]==1}else{if(V=="nth"){var Y=false,R=S[2],X=S[3];if(R==1&&X==0){return true}if(R==0){if(P.nodeIndex==X){Y=true}}else{if((P.nodeIndex-X)%R==0&&(P.nodeIndex-X)/R>=0){Y=true}}return Y}}}}},PSEUDO:function(V,R,S,W){var Q=R[1],T=G.filters[Q];if(T){return T(V,S,R,W)}else{if(Q==="contains"){return(V.textContent||V.innerText||"").indexOf(R[3])>=0}else{if(Q==="not"){var U=R[3];for(var S=0,P=U.length;S<P;S++){if(U[S]===V){return false}}return true}}}},ID:function(Q,P){return Q.nodeType===1&&Q.getAttribute("id")===P},TAG:function(Q,P){return(P==="*"&&Q.nodeType===1)||Q.nodeName===P},CLASS:function(Q,P){return P.test(Q.className)},ATTR:function(T,R){var P=G.attrHandle[R[1]]?G.attrHandle[R[1]](T):T[R[1]]||T.getAttribute(R[1]),U=P+"",S=R[2],Q=R[4];return P==null?false:S==="="?U===Q:S==="*="?U.indexOf(Q)>=0:S==="~="?(" "+U+" ").indexOf(Q)>=0:!R[4]?P:S==="!="?U!=Q:S==="^="?U.indexOf(Q)===0:S==="$="?U.substr(U.length-Q.length)===Q:S==="|="?U===Q||U.substr(0,Q.length+1)===Q+"-":false},POS:function(T,Q,R,U){var P=Q[2],S=G.setFilters[P];if(S){return S(T,R,Q,U)}}}};for(var K in G.match){G.match[K]=RegExp(G.match[K].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var D=function(Q,P){Q=Array.prototype.slice.call(Q);if(P){P.push.apply(P,Q);return P}return Q};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(J){D=function(T,S){var Q=S||[];if(F.call(T)==="[object Array]"){Array.prototype.push.apply(Q,T)}else{if(typeof T.length==="number"){for(var R=0,P=T.length;R<P;R++){Q.push(T[R])}}else{for(var R=0;T[R];R++){Q.push(T[R])}}}return Q}}(function(){var Q=document.createElement("form"),R="script"+(new Date).getTime();Q.innerHTML="<input name='"+R+"'/>";var P=document.documentElement;P.insertBefore(Q,P.firstChild);if(!!document.getElementById(R)){G.find.ID=function(T,U){if(U.getElementById){var S=U.getElementById(T[1]);return S?S.id===T[1]||S.getAttributeNode&&S.getAttributeNode("id").nodeValue===T[1]?[S]:g:[]}};G.filter.ID=function(U,S){var T=U.getAttributeNode&&U.getAttributeNode("id");return U.nodeType===1&&T&&T.nodeValue===S}}P.removeChild(Q)})();(function(){var P=document.createElement("div");P.appendChild(document.createComment(""));if(P.getElementsByTagName("*").length>0){G.find.TAG=function(Q,U){var T=U.getElementsByTagName(Q[1]);if(Q[1]==="*"){var S=[];for(var R=0;T[R];R++){if(T[R].nodeType===1){S.push(T[R])}}T=S}return T}}P.innerHTML="<a href='#'></a>";if(P.firstChild.getAttribute("href")!=="#"){G.attrHandle.href=function(Q){return Q.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var P=E;E=function(T,S,Q,R){S=S||document;if(!R&&S.nodeType===9){try{return D(S.querySelectorAll(T),Q)}catch(U){}}return P(T,S,Q,R)};E.find=P.find;E.filter=P.filter;E.selectors=P.selectors;E.matches=P.matches})()}if(document.documentElement.getElementsByClassName){G.order.splice(1,0,"CLASS");G.find.CLASS=function(P,Q){return Q.getElementsByClassName(P[1])}}function L(Q,W,V,Z,X,Y){for(var T=0,R=Z.length;T<R;T++){var P=Z[T];if(P){P=P[Q];var U=false;while(P&&P.nodeType){var S=P[V];if(S){U=Z[S];break}if(P.nodeType===1&&!Y){P[V]=T}if(P.nodeName===W){U=P;break}P=P[Q]}Z[T]=U}}}function O(Q,V,U,Y,W,X){for(var S=0,R=Y.length;S<R;S++){var P=Y[S];if(P){P=P[Q];var T=false;while(P&&P.nodeType){if(P[U]){T=Y[P[U]];break}if(P.nodeType===1){if(!X){P[U]=S}if(typeof V!=="string"){if(P===V){T=true;break}}else{if(E.filter(V,[P]).length>0){T=P;break}}}P=P[Q]}Y[S]=T}}}var H=document.compareDocumentPosition?function(Q,P){return Q.compareDocumentPosition(P)&16}:function(Q,P){return Q!==P&&(Q.contains?Q.contains(P):true)};var M=function(P){return P.documentElement&&!P.body||P.tagName&&P.ownerDocument&&!P.ownerDocument.body};n.find=E;n.filter=E.filter;n.expr=E.selectors;n.expr[":"]=n.expr.filters;E.selectors.filters.hidden=function(P){return"hidden"===P.type||n.css(P,"display")==="none"||n.css(P,"visibility")==="hidden"};E.selectors.filters.visible=function(P){return"hidden"!==P.type&&n.css(P,"display")!=="none"&&n.css(P,"visibility")!=="hidden"};E.selectors.filters.animated=function(P){return n.grep(n.timers,function(Q){return P===Q.elem}).length};n.multiFilter=function(R,P,Q){if(Q){R=":not("+R+")"}return E.matches(R,P)};n.dir=function(R,Q){var P=[],S=R[Q];while(S&&S!=document){if(S.nodeType==1){P.push(S)}S=S[Q]}return P};n.nth=function(T,P,R,S){P=P||1;var Q=0;for(;T;T=T[R]){if(T.nodeType==1&&++Q==P){break}}return T};n.sibling=function(R,Q){var P=[];for(;R;R=R.nextSibling){if(R.nodeType==1&&R!=Q){P.push(R)}}return P};return;l.Sizzle=E})();n.event={add:function(H,E,G,J){if(H.nodeType==3||H.nodeType==8){return}if(H.setInterval&&H!=l){H=l}if(!G.guid){G.guid=this.guid++}if(J!==g){var F=G;G=this.proxy(F);G.data=J}var D=n.data(H,"events")||n.data(H,"events",{}),I=n.data(H,"handle")||n.data(H,"handle",function(){return typeof n!=="undefined"&&!n.event.triggered?n.event.handle.apply(arguments.callee.elem,arguments):g});I.elem=H;n.each(E.split(/\s+/),function(L,M){var N=M.split(".");M=N.shift();G.type=N.slice().sort().join(".");var K=D[M];if(n.event.specialAll[M]){n.event.specialAll[M].setup.call(H,J,N)}if(!K){K=D[M]={};if(!n.event.special[M]||n.event.special[M].setup.call(H,J,N)===false){if(H.addEventListener){H.addEventListener(M,I,false)}else{if(H.attachEvent){H.attachEvent("on"+M,I)}}}}K[G.guid]=G;n.event.global[M]=true});H=null},guid:1,global:{},remove:function(J,G,I){if(J.nodeType==3||J.nodeType==8){return}var F=n.data(J,"events"),E,D;if(F){if(G===g||(typeof G==="string"&&G.charAt(0)==".")){for(var H in F){this.remove(J,H+(G||""))}}else{if(G.type){I=G.handler;G=G.type}n.each(G.split(/\s+/),function(L,N){var P=N.split(".");N=P.shift();var M=RegExp("(^|\\.)"+P.slice().sort().join(".*\\.")+"(\\.|$)");if(F[N]){if(I){delete F[N][I.guid]}else{for(var O in F[N]){if(M.test(F[N][O].type)){delete F[N][O]}}}if(n.event.specialAll[N]){n.event.specialAll[N].teardown.call(J,P)}for(E in F[N]){break}if(!E){if(!n.event.special[N]||n.event.special[N].teardown.call(J,P)===false){if(J.removeEventListener){J.removeEventListener(N,n.data(J,"handle"),false)}else{if(J.detachEvent){J.detachEvent("on"+N,n.data(J,"handle"))}}}E=null;delete F[N]}}})}for(E in F){break}if(!E){var K=n.data(J,"handle");if(K){K.elem=null}n.removeData(J,"events");n.removeData(J,"handle")}}},trigger:function(H,J,G,D){var F=H.type||H;if(!D){H=typeof H==="object"?H[h]?H:n.extend(n.Event(F),H):n.Event(F);if(F.indexOf("!")>=0){H.type=F=F.slice(0,-1);H.exclusive=true}if(!G){H.stopPropagation();if(this.global[F]){n.each(n.cache,function(){if(this.events&&this.events[F]){n.event.trigger(H,J,this.handle.elem)}})}}if(!G||G.nodeType==3||G.nodeType==8){return g}H.result=g;H.target=G;J=n.makeArray(J);J.unshift(H)}H.currentTarget=G;var I=n.data(G,"handle");if(I){I.apply(G,J)}if((!G[F]||(n.nodeName(G,"a")&&F=="click"))&&G["on"+F]&&G["on"+F].apply(G,J)===false){H.result=false}if(!D&&G[F]&&!H.isDefaultPrevented()&&!(n.nodeName(G,"a")&&F=="click")){this.triggered=true;try{G[F]()}catch(K){}}this.triggered=false;if(!H.isPropagationStopped()){var E=G.parentNode||G.ownerDocument;if(E){n.event.trigger(H,J,E,true)}}},handle:function(J){var I,D;J=arguments[0]=n.event.fix(J||l.event);var K=J.type.split(".");J.type=K.shift();I=!K.length&&!J.exclusive;var H=RegExp("(^|\\.)"+K.slice().sort().join(".*\\.")+"(\\.|$)");D=(n.data(this,"events")||{})[J.type];for(var F in D){var G=D[F];if(I||H.test(G.type)){J.handler=G;J.data=G.data;var E=G.apply(this,arguments);if(E!==g){J.result=E;if(E===false){J.preventDefault();J.stopPropagation()}}if(J.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(G){if(G[h]){return G}var E=G;G=n.Event(E);for(var F=this.props.length,I;F;){I=this.props[--F];G[I]=E[I]}if(!G.target){G.target=G.srcElement||document}if(G.target.nodeType==3){G.target=G.target.parentNode}if(!G.relatedTarget&&G.fromElement){G.relatedTarget=G.fromElement==G.target?G.toElement:G.fromElement}if(G.pageX==null&&G.clientX!=null){var H=document.documentElement,D=document.body;G.pageX=G.clientX+(H&&H.scrollLeft||D&&D.scrollLeft||0)-(H.clientLeft||0);G.pageY=G.clientY+(H&&H.scrollTop||D&&D.scrollTop||0)-(H.clientTop||0)}if(!G.which&&((G.charCode||G.charCode===0)?G.charCode:G.keyCode)){G.which=G.charCode||G.keyCode}if(!G.metaKey&&G.ctrlKey){G.metaKey=G.ctrlKey}if(!G.which&&G.button){G.which=(G.button&1?1:(G.button&2?3:(G.button&4?2:0)))}return G},proxy:function(E,D){D=D||function(){return E.apply(this,arguments)};D.guid=E.guid=E.guid||D.guid||this.guid++;return D},special:{ready:{setup:A,teardown:function(){}}},specialAll:{live:{setup:function(D,E){n.event.add(this,E[0],c)},teardown:function(F){if(F.length){var D=0,E=RegExp("(^|\\.)"+F[0]+"(\\.|$)");n.each((n.data(this,"events").live||{}),function(){if(E.test(this.type)){D++}});if(D<1){n.event.remove(this,F[0],c)}}}}}};n.Event=function(D){if(!this.preventDefault){return new n.Event(D)}if(D&&D.type){this.originalEvent=D;this.type=D.type;this.timeStamp=D.timeStamp}else{this.type=D}if(!this.timeStamp){this.timeStamp=e()}this[h]=true};function k(){return false}function t(){return true}n.Event.prototype={preventDefault:function(){this.isDefaultPrevented=t;var D=this.originalEvent;if(!D){return}if(D.preventDefault){D.preventDefault()}D.returnValue=false},stopPropagation:function(){this.isPropagationStopped=t;var D=this.originalEvent;if(!D){return}if(D.stopPropagation){D.stopPropagation()}D.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=t;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(E){var D=E.relatedTarget;while(D&&D!=this){try{D=D.parentNode}catch(F){D=this}}if(D!=this){E.type=E.data;n.event.handle.apply(this,arguments)}};n.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(E,D){n.event.special[D]={setup:function(){n.event.add(this,E,a,D)},teardown:function(){n.event.remove(this,E,a)}}});n.fn.extend({bind:function(E,F,D){return E=="unload"?this.one(E,F,D):this.each(function(){n.event.add(this,E,D||F,D&&F)})},one:function(F,G,E){var D=n.event.proxy(E||G,function(H){n(this).unbind(H,D);return(E||G).apply(this,arguments)});return this.each(function(){n.event.add(this,F,D,E&&G)})},unbind:function(E,D){return this.each(function(){n.event.remove(this,E,D)})},trigger:function(D,E){return this.each(function(){n.event.trigger(D,E,this)})},triggerHandler:function(D,F){if(this[0]){var E=n.Event(D);E.preventDefault();E.stopPropagation();n.event.trigger(E,F,this[0]);return E.result}},toggle:function(F){var D=arguments,E=1;while(E<D.length){n.event.proxy(F,D[E++])}return this.click(n.event.proxy(F,function(G){this.lastToggle=(this.lastToggle||0)%E;G.preventDefault();return D[this.lastToggle++].apply(this,arguments)||false}))},hover:function(D,E){return this.mouseenter(D).mouseleave(E)},ready:function(D){A();if(n.isReady){D.call(document,n)}else{n.readyList.push(D)}return this},live:function(F,E){var D=n.event.proxy(E);D.guid+=this.selector+F;n(document).bind(i(F,this.selector),this.selector,D);return this},die:function(E,D){n(document).unbind(i(E,this.selector),D?{guid:D.guid+this.selector+E}:null);return this}});function c(G){var D=RegExp("(^|\\.)"+G.type+"(\\.|$)"),F=true,E=[];n.each(n.data(this,"events").live||[],function(H,I){if(D.test(I.type)){var J=n(G.target).closest(I.data)[0];if(J){E.push({elem:J,fn:I})}}});n.each(E,function(){if(!G.isImmediatePropagationStopped()&&this.fn.call(this.elem,G,this.fn.data)===false){F=false}});return F}function i(E,D){return["live",E,D.replace(/\./g,"`").replace(/ /g,"|")].join(".")}n.extend({isReady:false,readyList:[],ready:function(){if(!n.isReady){n.isReady=true;if(n.readyList){n.each(n.readyList,function(){this.call(document,n)});n.readyList=null}n(document).triggerHandler("ready")}}});var w=false;function A(){if(w){return}w=true;if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);n.ready()},false)}else{if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);n.ready()}});if(document.documentElement.doScroll&&!l.frameElement){(function(){if(n.isReady){return}try{document.documentElement.doScroll("left")}catch(D){setTimeout(arguments.callee,0);return}n.ready()})()}}}n.event.add(l,"load",n.ready)}n.each(("blur,focus,load,resize,scroll,unload,click,dblclick,mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave,change,select,submit,keydown,keypress,keyup,error").split(","),function(E,D){n.fn[D]=function(F){return F?this.bind(D,F):this.trigger(D)}});n(l).bind("unload",function(){for(var D in n.cache){if(D!=1&&n.cache[D].handle){n.event.remove(n.cache[D].handle.elem)}}});(function(){n.support={};var E=document.documentElement,F=document.createElement("script"),J=document.createElement("div"),I="script"+(new Date).getTime();J.style.display="none";J.innerHTML=' <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>';var G=J.getElementsByTagName("*"),D=J.getElementsByTagName("a")[0];if(!G||!G.length||!D){return}n.support={leadingWhitespace:J.firstChild.nodeType==3,tbody:!J.getElementsByTagName("tbody").length,objectAll:!!J.getElementsByTagName("object")[0].getElementsByTagName("*").length,htmlSerialize:!!J.getElementsByTagName("link").length,style:/red/.test(D.getAttribute("style")),hrefNormalized:D.getAttribute("href")==="/a",opacity:D.style.opacity==="0.5",cssFloat:!!D.style.cssFloat,scriptEval:false,noCloneEvent:true,boxModel:null};F.type="text/javascript";try{F.appendChild(document.createTextNode("window."+I+"=1;"))}catch(H){}E.insertBefore(F,E.firstChild);if(l[I]){n.support.scriptEval=true;delete l[I]}E.removeChild(F);if(J.attachEvent&&J.fireEvent){J.attachEvent("onclick",function(){n.support.noCloneEvent=false;J.detachEvent("onclick",arguments.callee)});J.cloneNode(true).fireEvent("onclick")}n(function(){var K=document.createElement("div");K.style.width="1px";K.style.paddingLeft="1px";document.body.appendChild(K);n.boxModel=n.support.boxModel=K.offsetWidth===2;document.body.removeChild(K)})})();var v=n.support.cssFloat?"cssFloat":"styleFloat";n.props={"for":"htmlFor","class":"className","float":v,cssFloat:v,styleFloat:v,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",tabindex:"tabIndex"};n.fn.extend({_load:n.fn.load,load:function(F,I,J){if(typeof F!=="string"){return this._load(F)}var H=F.indexOf(" ");if(H>=0){var D=F.slice(H,F.length);F=F.slice(0,H)}var G="GET";if(I){if(n.isFunction(I)){J=I;I=null}else{if(typeof I==="object"){I=n.param(I);G="POST"}}}var E=this;n.ajax({url:F,type:G,dataType:"html",data:I,complete:function(L,K){if(K=="success"||K=="notmodified"){E.html(D?n("<div/>").append(L.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(D):L.responseText)}if(J){E.each(J,[L.responseText,K,L])}}});return this},serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?n.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type))}).map(function(D,E){var F=n(this).val();return F==null?null:n.isArray(F)?n.map(F,function(H,G){return{name:E.name,value:H}}):{name:E.name,value:F}}).get()}});n.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(D,E){n.fn[E]=function(F){return this.bind(E,F)}});var q=e();n.extend({get:function(D,F,G,E){if(n.isFunction(F)){G=F;F=null}return n.ajax({type:"GET",url:D,data:F,success:G,dataType:E})},getScript:function(D,E){return n.get(D,null,E,"script")},getJSON:function(D,E,F){return n.get(D,E,F,"json")},post:function(D,F,G,E){if(n.isFunction(F)){G=F;F={}}return n.ajax({type:"POST",url:D,data:F,success:G,dataType:E})},ajaxSetup:function(D){n.extend(n.ajaxSettings,D)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(L){L=n.extend(true,L,n.extend(true,{},n.ajaxSettings,L));var V,E=/=\?(&|$)/g,Q,U,F=L.type.toUpperCase();if(L.data&&L.processData&&typeof L.data!=="string"){L.data=n.param(L.data)}if(L.dataType=="jsonp"){if(F=="GET"){if(!L.url.match(E)){L.url+=(L.url.match(/\?/)?"&":"?")+(L.jsonp||"callback")+"=?"}}else{if(!L.data||!L.data.match(E)){L.data=(L.data?L.data+"&":"")+(L.jsonp||"callback")+"=?"}}L.dataType="json"}if(L.dataType=="json"&&(L.data&&L.data.match(E)||L.url.match(E))){V="jsonp"+q++;if(L.data){L.data=(L.data+"").replace(E,"="+V+"$1")}L.url=L.url.replace(E,"="+V+"$1");L.dataType="script";l[V]=function(W){U=W;H();K();l[V]=g;try{delete l[V]}catch(X){}if(G){G.removeChild(S)}}}if(L.dataType=="script"&&L.cache==null){L.cache=false}if(L.cache===false&&F=="GET"){var D=e();var T=L.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+D+"$2");L.url=T+((T==L.url)?(L.url.match(/\?/)?"&":"?")+"_="+D:"")}if(L.data&&F=="GET"){L.url+=(L.url.match(/\?/)?"&":"?")+L.data;L.data=null}if(L.global&&!n.active++){n.event.trigger("ajaxStart")}var P=/^(\w+:)?\/\/([^\/?#]+)/.exec(L.url);if(L.dataType=="script"&&F=="GET"&&P&&(P[1]&&P[1]!=location.protocol||P[2]!=location.host)){var G=document.getElementsByTagName("head")[0];var S=document.createElement("script");S.src=L.url;if(L.scriptCharset){S.charset=L.scriptCharset}if(!V){var N=false;S.onload=S.onreadystatechange=function(){if(!N&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){N=true;H();K();G.removeChild(S)}}}G.appendChild(S);return g}var J=false;var I=L.xhr();if(L.username){I.open(F,L.url,L.async,L.username,L.password)}else{I.open(F,L.url,L.async)}try{if(L.data){I.setRequestHeader("Content-Type",L.contentType)}if(L.ifModified){I.setRequestHeader("If-Modified-Since",n.lastModified[L.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}I.setRequestHeader("X-Requested-With","XMLHttpRequest");I.setRequestHeader("Accept",L.dataType&&L.accepts[L.dataType]?L.accepts[L.dataType]+", */*":L.accepts._default)}catch(R){}if(L.beforeSend&&L.beforeSend(I,L)===false){if(L.global&&!--n.active){n.event.trigger("ajaxStop")}I.abort();return false}if(L.global){n.event.trigger("ajaxSend",[I,L])}var M=function(W){if(I.readyState==0){if(O){clearInterval(O);O=null;if(L.global&&!--n.active){n.event.trigger("ajaxStop")}}}else{if(!J&&I&&(I.readyState==4||W=="timeout")){J=true;if(O){clearInterval(O);O=null}Q=W=="timeout"?"timeout":!n.httpSuccess(I)?"error":L.ifModified&&n.httpNotModified(I,L.url)?"notmodified":"success";if(Q=="success"){try{U=n.httpData(I,L.dataType,L)}catch(Y){Q="parsererror"}}if(Q=="success"){var X;try{X=I.getResponseHeader("Last-Modified")}catch(Y){}if(L.ifModified&&X){n.lastModified[L.url]=X}if(!V){H()}}else{n.handleError(L,I,Q)}K();if(L.async){I=null}}}};if(L.async){var O=setInterval(M,13);if(L.timeout>0){setTimeout(function(){if(I){if(!J){M("timeout")}if(I){I.abort()}}},L.timeout)}}try{I.send(L.data)}catch(R){n.handleError(L,I,null,R)}if(!L.async){M()}function H(){if(L.success){L.success(U,Q)}if(L.global){n.event.trigger("ajaxSuccess",[I,L])}}function K(){if(L.complete){L.complete(I,Q)}if(L.global){n.event.trigger("ajaxComplete",[I,L])}if(L.global&&!--n.active){n.event.trigger("ajaxStop")}}return I},handleError:function(E,G,D,F){if(E.error){E.error(G,D,F)}if(E.global){n.event.trigger("ajaxError",[G,E,F])}},active:0,httpSuccess:function(E){try{return !E.status&&location.protocol=="file:"||(E.status>=200&&E.status<300)||E.status==304||E.status==1223}catch(D){}return false},httpNotModified:function(F,D){try{var G=F.getResponseHeader("Last-Modified");return F.status==304||G==n.lastModified[D]}catch(E){}return false},httpData:function(I,G,F){var E=I.getResponseHeader("content-type"),D=G=="xml"||!G&&E&&E.indexOf("xml")>=0,H=D?I.responseXML:I.responseText;if(D&&H.documentElement.tagName=="parsererror"){throw"parsererror"}if(F&&F.dataFilter){H=F.dataFilter(H,G)}if(typeof H==="string"){if(G=="script"){n.globalEval(H)}if(G=="json"){H=l["eval"]("("+H+")")}}return H},param:function(D){var F=[];function G(H,I){F[F.length]=encodeURIComponent(H)+"="+encodeURIComponent(I)}if(n.isArray(D)||D.jquery){n.each(D,function(){G(this.name,this.value)})}else{for(var E in D){if(n.isArray(D[E])){n.each(D[E],function(){G(E,this)})}else{G(E,n.isFunction(D[E])?D[E]():D[E])}}}return F.join("&").replace(/%20/g,"+")}});var m={},d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function s(E,D){var F={};n.each(d.concat.apply([],d.slice(0,D)),function(){F[this]=E});return F}n.fn.extend({show:function(I,K){if(I){return this.animate(s("show",3),I,K)}else{for(var G=0,E=this.length;G<E;G++){var D=n.data(this[G],"olddisplay");this[G].style.display=D||"";if(n.css(this[G],"display")==="none"){var F=this[G].tagName,J;if(m[F]){J=m[F]}else{var H=n("<"+F+" />").appendTo("body");J=H.css("display");if(J==="none"){J="block"}H.remove();m[F]=J}this[G].style.display=n.data(this[G],"olddisplay",J)}}return this}},hide:function(G,H){if(G){return this.animate(s("hide",3),G,H)}else{for(var F=0,E=this.length;F<E;F++){var D=n.data(this[F],"olddisplay");if(!D&&D!=="none"){n.data(this[F],"olddisplay",n.css(this[F],"display"))}this[F].style.display="none"}return this}},_toggle:n.fn.toggle,toggle:function(F,E){var D=typeof F==="boolean";return n.isFunction(F)&&n.isFunction(E)?this._toggle.apply(this,arguments):F==null||D?this.each(function(){var G=D?F:n(this).is(":hidden");n(this)[G?"show":"hide"]()}):this.animate(s("toggle",3),F,E)},fadeTo:function(D,F,E){return this.animate({opacity:F},D,E)},animate:function(H,E,G,F){var D=n.speed(E,G,F);return this[D.queue===false?"each":"queue"](function(){var J=n.extend({},D),L,K=this.nodeType==1&&n(this).is(":hidden"),I=this;for(L in H){if(H[L]=="hide"&&K||H[L]=="show"&&!K){return J.complete.call(this)}if((L=="height"||L=="width")&&this.style){J.display=n.css(this,"display");J.overflow=this.style.overflow}}if(J.overflow!=null){this.style.overflow="hidden"}J.curAnim=n.extend({},H);n.each(H,function(N,R){var Q=new n.fx(I,J,N);if(/toggle|show|hide/.test(R)){Q[R=="toggle"?K?"show":"hide":R](H)}else{var P=R.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),S=Q.cur(true)||0;if(P){var M=parseFloat(P[2]),O=P[3]||"px";if(O!="px"){I.style[N]=(M||1)+O;S=((M||1)/Q.cur(true))*S;I.style[N]=S+O}if(P[1]){M=((P[1]=="-="?-1:1)*M)+S}Q.custom(S,M,O)}else{Q.custom(S,R,"")}}});return true})},stop:function(E,D){var F=n.timers;if(E){this.queue([])}this.each(function(){for(var G=F.length-1;G>=0;G--){if(F[G].elem==this){if(D){F[G](true)}F.splice(G,1)}}});if(!D){this.dequeue()}return this}});n.each({slideDown:s("show",1),slideUp:s("hide",1),slideToggle:s("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(D,E){n.fn[D]=function(F,G){return this.animate(E,F,G)}});n.extend({speed:function(F,G,E){var D=typeof F==="object"?F:{complete:E||!E&&G||n.isFunction(F)&&F,duration:F,easing:E&&G||G&&!n.isFunction(G)&&G};D.duration=n.fx.off?0:typeof D.duration==="number"?D.duration:n.fx.speeds[D.duration]||n.fx.speeds._default;D.old=D.complete;D.complete=function(){if(D.queue!==false){n(this).dequeue()}if(n.isFunction(D.old)){D.old.call(this)}};return D},easing:{linear:function(F,G,D,E){return D+E*F},swing:function(F,G,D,E){return((-Math.cos(F*Math.PI)/2)+0.5)*E+D}},timers:[],timerId:null,fx:function(E,D,F){this.options=D;this.elem=E;this.prop=F;if(!D.orig){D.orig={}}}});n.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(n.fx.step[this.prop]||n.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(E){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var D=parseFloat(n.css(this.elem,this.prop,E));return D&&D>-10000?D:parseFloat(n.curCSS(this.elem,this.prop))||0},custom:function(H,G,F){this.startTime=e();this.start=H;this.end=G;this.unit=F||this.unit||"px";this.now=this.start;this.pos=this.state=0;var D=this;function E(I){return D.step(I)}E.elem=this.elem;n.timers.push(E);if(E()&&n.timerId==null){n.timerId=setInterval(function(){var J=n.timers;for(var I=0;I<J.length;I++){if(!J[I]()){J.splice(I--,1)}}if(!J.length){clearInterval(n.timerId);n.timerId=null}},13)}},show:function(){this.options.orig[this.prop]=n.attr(this.elem.style,this.prop);this.options.show=true;this.custom(this.prop=="width"||this.prop=="height"?1:0,this.cur());n(this.elem).show()},hide:function(){this.options.orig[this.prop]=n.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(G){var F=e();if(G||F>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var D=true;for(var E in this.options.curAnim){if(this.options.curAnim[E]!==true){D=false}}if(D){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(n.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){n(this.elem).hide()}if(this.options.hide||this.options.show){for(var H in this.options.curAnim){n.attr(this.elem.style,H,this.options.orig[H])}}}if(D){this.options.complete.call(this.elem)}return false}else{var I=F-this.startTime;this.state=I/this.options.duration;this.pos=n.easing[this.options.easing||(n.easing.swing?"swing":"linear")](this.state,I,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};n.extend(n.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(D){n.attr(D.elem.style,"opacity",D.now)},_default:function(D){if(D.elem.style&&D.elem.style[D.prop]!=null){D.elem.style[D.prop]=D.now+D.unit}else{D.elem[D.prop]=D.now}}}});if(document.documentElement.getBoundingClientRect){n.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return n.offset.bodyOffset(this[0])}var F=this[0].getBoundingClientRect(),I=this[0].ownerDocument,E=I.body,D=I.documentElement,K=D.clientTop||E.clientTop||0,J=D.clientLeft||E.clientLeft||0,H=F.top+(self.pageYOffset||n.boxModel&&D.scrollTop||E.scrollTop)-K,G=F.left+(self.pageXOffset||n.boxModel&&D.scrollLeft||E.scrollLeft)-J;return{top:H,left:G}}}else{n.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return n.offset.bodyOffset(this[0])}n.offset.initialized||n.offset.initialize();var I=this[0],F=I.offsetParent,E=I,N=I.ownerDocument,L,G=N.documentElement,J=N.body,K=N.defaultView,D=K.getComputedStyle(I,null),M=I.offsetTop,H=I.offsetLeft;while((I=I.parentNode)&&I!==J&&I!==G){L=K.getComputedStyle(I,null);M-=I.scrollTop,H-=I.scrollLeft;if(I===F){M+=I.offsetTop,H+=I.offsetLeft;if(n.offset.doesNotAddBorder&&!(n.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(I.tagName))){M+=parseInt(L.borderTopWidth,10)||0,H+=parseInt(L.borderLeftWidth,10)||0}E=F,F=I.offsetParent}if(n.offset.subtractsBorderForOverflowNotVisible&&L.overflow!=="visible"){M+=parseInt(L.borderTopWidth,10)||0,H+=parseInt(L.borderLeftWidth,10)||0}D=L}if(D.position==="relative"||D.position==="static"){M+=J.offsetTop,H+=J.offsetLeft}if(D.position==="fixed"){M+=Math.max(G.scrollTop,J.scrollTop),H+=Math.max(G.scrollLeft,J.scrollLeft)}return{top:M,left:H}}}n.offset={initialize:function(){if(this.initialized){return}var K=document.body,E=document.createElement("div"),G,F,M,H,L,D,I=K.style.marginTop,J='<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"cellpadding="0"cellspacing="0"><tr><td></td></tr></table>';L={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(D in L){E.style[D]=L[D]}E.innerHTML=J;K.insertBefore(E,K.firstChild);G=E.firstChild,F=G.firstChild,H=G.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(F.offsetTop!==5);this.doesAddBorderForTableAndCells=(H.offsetTop===5);G.style.overflow="hidden",G.style.position="relative";this.subtractsBorderForOverflowNotVisible=(F.offsetTop===-5);K.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(K.offsetTop===0);K.style.marginTop=I;K.removeChild(E);this.initialized=true},bodyOffset:function(D){n.offset.initialized||n.offset.initialize();var F=D.offsetTop,E=D.offsetLeft;if(n.offset.doesNotIncludeMarginInBodyOffset){F+=parseInt(n.curCSS(D,"marginTop",true),10)||0,E+=parseInt(n.curCSS(D,"marginLeft",true),10)||0}return{top:F,left:E}}};n.fn.extend({position:function(){var H=0,G=0,E;if(this[0]){var F=this.offsetParent(),I=this.offset(),D=/^body|html$/i.test(F[0].tagName)?{top:0,left:0}:F.offset();I.top-=j(this,"marginTop");I.left-=j(this,"marginLeft");D.top+=j(F,"borderTopWidth");D.left+=j(F,"borderLeftWidth");E={top:I.top-D.top,left:I.left-D.left}}return E},offsetParent:function(){var D=this[0].offsetParent||document.body;while(D&&(!/^body|html$/i.test(D.tagName)&&n.css(D,"position")=="static")){D=D.offsetParent}return n(D)}});n.each(["Left","Top"],function(E,D){var F="scroll"+D;n.fn[F]=function(G){if(!this[0]){return null}return G!==g?this.each(function(){this==l||this==document?l.scrollTo(!E?G:n(l).scrollLeft(),E?G:n(l).scrollTop()):this[F]=G}):this[0]==l||this[0]==document?self[E?"pageYOffset":"pageXOffset"]||n.boxModel&&document.documentElement[F]||document.body[F]:this[0][F]}});n.each(["Height","Width"],function(G,E){var D=G?"Left":"Top",F=G?"Right":"Bottom";n.fn["inner"+E]=function(){return this[E.toLowerCase()]()+j(this,"padding"+D)+j(this,"padding"+F)};n.fn["outer"+E]=function(I){return this["inner"+E]()+j(this,"border"+D+"Width")+j(this,"border"+F+"Width")+(I?j(this,"margin"+D)+j(this,"margin"+F):0)};var H=E.toLowerCase();n.fn[H]=function(I){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+E]||document.body["client"+E]:this[0]==document?Math.max(document.documentElement["client"+E],document.body["scroll"+E],document.documentElement["scroll"+E],document.body["offset"+E],document.documentElement["offset"+E]):I===g?(this.length?n.css(this[0],H):null):this.css(H,typeof I==="string"?I:I+"px")}})})();
\ No newline at end of file diff --git a/chef-server/public/merb.fcgi b/chef-server/public/merb.fcgi new file mode 100755 index 0000000000..9804e0f36d --- /dev/null +++ b/chef-server/public/merb.fcgi @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'merb-core' + +# this is Merb.root, change this if you have some funky setup. +merb_root = File.expand_path(File.dirname(__FILE__) / '../') + +# If the fcgi process runs as apache, make sure +# we have an inlinedir set for Rubyinline action-args to work +unless ENV["INLINEDIR"] || ENV["HOME"] + tmpdir = merb_root / "tmp" + unless File.directory?(tmpdir) + Dir.mkdir(tmpdir) + end + ENV["INLINEDIR"] = tmpdir +end + +# start merb with the fcgi adapter, add options or change the log dir here +Merb.start(:adapter => 'fcgi', + :merb_root => merb_root, + :log_file => merb_root /'log'/'merb.log')
\ No newline at end of file diff --git a/chef-server/public/robots.txt b/chef-server/public/robots.txt new file mode 100644 index 0000000000..f85a11b3b8 --- /dev/null +++ b/chef-server/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-Agent: * +# Disallow: /
\ No newline at end of file diff --git a/chef-server/public/stylesheets/master.css b/chef-server/public/stylesheets/master.css new file mode 100644 index 0000000000..c4fa676047 --- /dev/null +++ b/chef-server/public/stylesheets/master.css @@ -0,0 +1,119 @@ +body { + font-family: Arial, Verdana, sans-serif; + font-size: 12px; + background-color: #fff; +} +* { + margin: 0px; + padding: 0px; + text-decoration: none; +} +html { + height: 100%; + margin-bottom: 1px; +} +#container { + width: 80%; + text-align: left; + background-color: #fff; + margin-right: auto; + margin-left: auto; +} +#header-container { + width: 100%; + padding-top: 15px; +} +#header-container h1, #header-container h2 { + margin-left: 6px; + margin-bottom: 6px; +} +.spacer { + width: 100%; + height: 15px; +} +hr { + border: 0px; + color: #ccc; + background-color: #cdcdcd; + height: 1px; + width: 100%; + text-align: left; +} +h1 { + font-size: 28px; + color: #c55; + background-color: #fff; + font-family: Arial, Verdana, sans-serif; + font-weight: 300; +} +h2 { + font-size: 15px; + color: #999; + font-family: Arial, Verdana, sans-serif; + font-weight: 300; + background-color: #fff; +} +h3 { + color: #4d9b12; + font-size: 15px; + text-align: left; + font-weight: 300; + padding: 5px; + margin-top: 5px; +} + +#left-container { + float: left; + width: 250px; + background-color: #FFFFFF; + color: black; +} + +#left-container h3 { + color: #c55; +} + +#main-container { + margin: 5px 5px 5px 260px; + padding: 15px; + border-left: 1px solid silver; + min-height: 400px; +} +p { + color: #000; + background-color: #fff; + line-height: 20px; + padding: 5px; +} +a { + color: #4d9b12; + background-color: #fff; + text-decoration: none; +} +a:hover { + color: #4d9b12; + background-color: #fff; + text-decoration: underline; +} +#footer-container { + clear: both; + font-size: 12px; + font-family: Verdana, Arial, sans-serif; +} +.right { + float: right; + font-size: 100%; + margin-top: 5px; + color: #999; + background-color: #fff; +} +.left { + float: left; + font-size: 100%; + margin-top: 5px; + color: #999; + background-color: #fff; +} +#main-container ul { + margin-left: 3.0em; +}
\ No newline at end of file diff --git a/chef-server/spec/spec.opts b/chef-server/spec/spec.opts index c9c9b4ddf0..e69de29bb2 100644 --- a/chef-server/spec/spec.opts +++ b/chef-server/spec/spec.opts @@ -1,3 +0,0 @@ ---color ---loadby -mtime diff --git a/chef-server/spec/spec_helper.rb b/chef-server/spec/spec_helper.rb index c60b8e0613..36ebcd57e2 100644 --- a/chef-server/spec/spec_helper.rb +++ b/chef-server/spec/spec_helper.rb @@ -1,37 +1,25 @@ -# Author:: Adam Jacob (<adam@opscode.com>) -# Copyright:: Copyright (c) 2008 Opscode, Inc. -# License:: GNU General Public License version 2 or later -# -# This program and entire repository is free software; you can -# redistribute it and/or modify it under the terms of the GNU -# General Public License as published by the Free Software -# Foundation; either version 2 of the License, or any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# +require "rubygems" -$:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) +# Add the local gems dir if found within the app root; any dependencies loaded +# hereafter will try to load from the local gems before loading system gems. +if (local_gem_dir = File.join(File.dirname(__FILE__), '..', 'gems')) && $BUNDLE.nil? + $BUNDLE = true; Gem.clear_paths; Gem.path.unshift(local_gem_dir) +end -require 'chef' +require "merb-core" +require "spec" # Satisfies Autotest and anyone else not using the Rake tasks -chef_lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) -Dir[ - File.expand_path( - File.join( - chef_lib_path, 'chef', '**', '*.rb' - ) - ) -].sort.each do |lib| - lib_short_path = lib.match("^#{chef_lib_path}#{File::SEPARATOR}(.+)$")[1] - require lib_short_path -end -Dir[File.join(File.dirname(__FILE__), 'lib', '**', '*.rb')].sort.each { |lib| require lib } +# this loads all plugins required in your init file so don't add them +# here again, Merb will do it for you +Merb.start_environment(:testing => true, :adapter => 'runner', :environment => ENV['MERB_ENV'] || 'test') -Chef::Config.log_level(:error) +Spec::Runner.configure do |config| + config.include(Merb::Test::ViewHelper) + config.include(Merb::Test::RouteHelper) + config.include(Merb::Test::ControllerHelper) + + config.before(:all) do + DataMapper.auto_migrate! if Merb.orm == :datamapper + end + +end diff --git a/chef-server/spec/unit/search_index_spec.rb b/chef-server/spec/unit/search_index_spec.rb deleted file mode 100644 index 0422fada37..0000000000 --- a/chef-server/spec/unit/search_index_spec.rb +++ /dev/null @@ -1,136 +0,0 @@ -# -# Author:: Adam Jacob (<adam@opscode.com>) -# Copyright:: Copyright (c) 2008 Opscode, Inc. -# License:: Apache License, Version 2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) - -describe Chef::SearchIndex, "initialize method" do - it "should create a new Chef::SearchIndex object" do - mf = mock("Ferret::Index::Index", :null_object => true) - Ferret::Index::Index.stub!(:new).and_return(mf) - Chef::SearchIndex.new.should be_kind_of(Chef::SearchIndex) - end - - it "should create a Ferret Indexer" do - mf = mock("Ferret::Index::Index", :null_object => true) - Ferret::Index::Index.should_receive(:new).and_return(mf) - Chef::SearchIndex.new - end -end - -describe Chef::SearchIndex, "create_index_object method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - @fakeobj = mock("ToIndex", :null_object => true) - @the_pigeon = { :index_name => "bird", :id => "pigeon" } - @fakeobj.stub!(:respond_to?).with(:to_index).and_return(true) - @fakeobj.stub!(:to_index).and_return(@the_pigeon) - Ferret::Index::Index.stub!(:new).and_return(@mf) - end - - def do_create_index_object - index = Chef::SearchIndex.new - index.create_index_object(@fakeobj) - end - - it "should call to_index if the passed object responds to it" do - @fakeobj.should_receive(:respond_to?).with(:to_index).and_return(true) - @fakeobj.should_receive(:to_index).and_return(@the_pigeon) - do_create_index_object - end - - it "should use a hash if the passed argument does not have to_index (but is a hash)" do - @fakeobj.stub!(:respond_to?).with(:to_index).and_return(false) - @fakeobj.should_receive(:kind_of?).with(Hash).and_return(true) - do_create_index_object - end - - it "should raise SearchIndex exception if the hash does not contain an :id field" do - @the_pigeon.delete(:id) - lambda { do_create_index_object }.should raise_error(Chef::Exception::SearchIndex) - end - - it "should raise SearchIndex exception if the hash does not contain an :index_name field" do - @the_pigeon.delete(:index_name) - lambda { do_create_index_object }.should raise_error(Chef::Exception::SearchIndex) - end - - it "should raise SearchIndex exception if the hash new_object cannot be indexed" do - @fakeobj.stub!(:respond_to?).and_return(false) - @fakeobj.stub!(:kind_of?).and_return(false) - lambda { do_create_index_object }.should raise_error(Chef::Exception::SearchIndex) - end - - it "should turn index hash keys in to symbols if it has strings" do - @the_pigeon["john"] = "and_yoko" - do_create_index_object.should have_key(:john) - end -end - -describe Chef::SearchIndex, "add method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - @fakeobj = mock("ToIndex", :null_object => true) - @the_pigeon = { :index_name => "bird", :id => "pigeon" } - @fakeobj.stub!(:respond_to?).with(:to_index).and_return(true) - @fakeobj.stub!(:to_index).and_return(@the_pigeon) - Ferret::Index::Index.stub!(:new).and_return(@mf) - end - - def do_add - index = Chef::SearchIndex.new - index.add(@fakeobj) - end - - it "should send the resulting hash to the index" do - @mf.should_receive(:add_document).with(@the_pigeon) - do_add - end -end - -describe Chef::SearchIndex, "delete method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - @fakeobj = mock("ToIndex", :null_object => true) - @the_pigeon = { :index_name => "bird", :id => "pigeon" } - @fakeobj.stub!(:respond_to?).with(:to_index).and_return(true) - @fakeobj.stub!(:to_index).and_return(@the_pigeon) - Ferret::Index::Index.stub!(:new).and_return(@mf) - end - - def do_delete(object) - index = Chef::SearchIndex.new - index.delete(object) - end - - it "should delete the resulting hash to the index" do - @mf.should_receive(:delete).with(@the_pigeon[:id]) - do_delete(@fakeobj) - end -end - -describe Chef::SearchIndex, "commit method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - Ferret::Index::Index.stub!(:new).and_return(@mf) - end - - it "should commit index to disk" do - @mf.should_receive(:commit) - Chef::SearchIndex.new.commit - end -end diff --git a/chef-server/spec/unit/search_spec.rb b/chef-server/spec/unit/search_spec.rb deleted file mode 100644 index 4681c23fe2..0000000000 --- a/chef-server/spec/unit/search_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -# -# Author:: Adam Jacob (<adam@opscode.com>) -# Copyright:: Copyright (c) 2008 Opscode, Inc. -# License:: Apache License, Version 2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) - -describe Chef::Search, "initialize method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - Ferret::Index::Index.stub!(:new).and_return(@mf) - end - - it "should build a Chef::Search object" do - Chef::Search.new.should be_a_kind_of(Chef::Search) - end - - it "should build a Ferret search backend" do - Ferret::Index::Index.should_receive(:new).and_return(@mf) - Chef::Search.new - end -end - -describe Chef::Search, "search method" do - before(:each) do - @mf = mock("Ferret::Index::Index", :null_object => true) - end - - def do_search(type, query, &block) - Ferret::Index::Index.stub!(:new).and_return(@mf) - cs = Chef::Search.new - if Kernel.block_given? - cs.search(type, query, &block) - else - cs.search(type, query) - end - end - - it "should build the search query from the type and query provided" do - Ferret::Index::Index.stub!(:new).and_return(@mf) - cs = Chef::Search.new - cs.should_receive(:build_search_query).with(:node, "tag:monkey") - cs.search(:node, "tag:monkey") - end - - it "should call search_each with the custom block if a block is given" do - cp = lambda { |n,u| "noting to do here" } - @mf.should_receive(:search_each).with("index_name:node AND (tag:monkey)", { :limit => :all }, &cp) - do_search(:node, "tag:monkey", &cp) - end - - it "should call search_each if a block is not given" do - @mf.should_receive(:search_each).with("index_name:node AND (tag:monkey)", {:limit => :all}) - do_search(:node, "tag:monkey") - end - - it "should return the search results" do - @mf.should_receive(:search_each).with("index_name:node AND (tag:monkey)", :limit => :all).and_return(true) - do_search(:node, "tag:monkey").should eql([]) - end - - it "should expand a splat query to id:splat" do - @mf.should_receive(:search_each).with("index_name:node AND (id:*)", :limit => :all).and_return(true) - do_search(:node, '*') - end -end
\ No newline at end of file diff --git a/chef-server/tasks/doc.thor b/chef-server/tasks/doc.thor new file mode 100644 index 0000000000..77e3222711 --- /dev/null +++ b/chef-server/tasks/doc.thor @@ -0,0 +1,149 @@ +$: << File.join("doc") +require 'rubygems' +require 'rdoc/rdoc' +require 'fileutils' +require 'erb' + +module Merb + + class GemNotFoundException < Exception + end + + module DocMethods + def setup_gem_path + if File.directory?(gems_dir = File.join(File.dirname(__FILE__), 'gems')) + $BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir) + end + end + + def get_more + libs = [] + more_library = find_library("merb-more") + File.open("#{more_library}/lib/merb-more.rb").read.each_line do |line| + if line['require'] + libs << line.gsub("require '", '').gsub("'\n", '') + end + end + return libs + end + + def generate_documentation(file_list, destination, arguments = []) + output_dir = File.join("/../doc", "rdoc", destination) + FileUtils.rm_rf(output_dir) + + arguments += [ + "--fmt", "merb", + "--op", output_dir + ] + RDoc::RDoc.new.document(arguments + file_list) + AdvancedDoc.new.index + end + + def find_library(directory_snippet) + gem_dir = nil + Gem.path.find do |path| + dir = Dir.glob("#{path}/gems/#{directory_snippet}*") + dir.empty? ? false : gem_dir = dir.last + end + raise GemNotFoundException if gem_dir.nil? + return gem_dir + end + + def get_file_list(directory_snippet) + gem_dir = find_library(directory_snippet) + files = Dir.glob("#{gem_dir}/**/lib/**/*.rb") + files += ["#{gem_dir}/README"] if File.exists?("#{gem_dir}/README") + return files + end + end + + class AdvancedDoc < Thor + + group 'core' + include DocMethods + + def initialize + super + setup_gem_path + end + + desc 'index', "Regenerate the index file for your framework documentation" + def index + @directories = Dir.entries(File.join(File.dirname(__FILE__) + "/../", "doc", "rdoc")) + @directories.delete(".") + @directories.delete("..") + @directories.delete("generators") + @directories.delete("index.html") + index_template = File.read(File.join("doc", "rdoc", "generators", "template", "merb", "index.html.erb")) + + File.open(File.join("doc", "rdoc", "index.html"), "w") do |file| + file.write(ERB.new(index_template).result(binding)) + end + end + + desc 'plugins', 'Generate the rdoc for each merb-plugins seperatly' + def plugins + libs = ["merb_activerecord", "merb_builder", "merb_jquery", "merb_laszlo", "merb_parts", "merb_screw_unit", "merb_sequel", "merb_stories", "merb_test_unit"] + + libs.each do |lib| + options[:gem] = lib + gem + end + end + + desc 'more', 'Generate the rdoc for each merb-more gem seperatly' + def more + libs = get_more + libs.each do |lib| + options[:gem] = lib + gem + end + end + + desc 'core', 'Generate the rdoc for merb-core' + def core + options[:gem] = "merb-core" + gem + end + + desc 'gem', 'Generate the rdoc for a specific gem' + method_options "--gem" => :required + def gem + file_list = get_file_list(options[:gem]) + readme = File.join(find_library("merb-core"), "README") + generate_documentation(file_list, options[:gem], ["-m", readme]) + rescue GemNotFoundException + puts "Can not find the gem in the gem path #{options[:gem]}" + end + + end + + class Doc < Thor + + include DocMethods + + def initialize + super + setup_gem_path + + end + + desc 'stack', 'Generate the rdoc for merb-core, merb-more merged together' + def stack + libs = ["merb"] + + file_list = [] + libs.each do |gem_name| + begin + file_list += get_file_list(gem_name) + rescue GemNotFoundException + puts "Could not find #{gem_name} in #{Gem.path}. Continuing with out it." + end + end + readme = File.join(find_library("merb"), "README") + generate_documentation(file_list, "stack", ["-m", readme]) + end + + end + +end
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/app_script.rb b/chef-server/tasks/merb.thor/app_script.rb new file mode 100644 index 0000000000..fb0e1163f5 --- /dev/null +++ b/chef-server/tasks/merb.thor/app_script.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + +# This was added by Merb's bundler + +require "rubygems" +require File.join(File.dirname(__FILE__), "common") + +gems_dir = File.join(File.dirname(__FILE__), '..', 'gems') + +if File.directory?(gems_dir) + $BUNDLE = true + Gem.clear_paths + Gem.path.replace([File.expand_path(gems_dir)]) + ENV["PATH"] = "#{File.dirname(__FILE__)}:#{ENV["PATH"]}" + + gem_file = File.join(gems_dir, "specifications", "<%= spec.name %>-*.gemspec") + + if local_gem = Dir[gem_file].last + version = File.basename(local_gem)[/-([\.\d]+)\.gemspec$/, 1] + end +end + +version ||= "<%= Gem::Requirement.default %>" + +if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then + version = $1 + ARGV.shift +end + +gem '<%= @spec.name %>', version +load '<%= bin_file_name %>'
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/common.rb b/chef-server/tasks/merb.thor/common.rb new file mode 100644 index 0000000000..9b722a1943 --- /dev/null +++ b/chef-server/tasks/merb.thor/common.rb @@ -0,0 +1,68 @@ +# This was added via Merb's bundler + +require "rubygems" +require "rubygems/source_index" + +module Gem + BUNDLED_SPECS = File.join(Dir.pwd, "gems", "specifications") + MAIN_INDEX = Gem::SourceIndex.from_gems_in(BUNDLED_SPECS) + FALLBACK_INDEX = Gem::SourceIndex.from_installed_gems + + def self.source_index + MultiSourceIndex.new + end + + def self.searcher + MultiPathSearcher.new + end + + class ArbitrarySearcher < GemPathSearcher + def initialize(source_index) + @source_index = source_index + super() + end + + def init_gemspecs + @source_index.map { |_, spec| spec }.sort { |a,b| + (a.name <=> b.name).nonzero? || (b.version <=> a.version) + } + end + end + + class MultiPathSearcher + def initialize + @main_searcher = ArbitrarySearcher.new(MAIN_INDEX) + @fallback_searcher = ArbitrarySearcher.new(FALLBACK_INDEX) + end + + def find(path) + try = @main_searcher.find(path) + return try if try + @fallback_searcher.find(path) + end + + def find_all(path) + try = @main_searcher.find_all(path) + return try unless try.empty? + @fallback_searcher.find_all(path) + end + end + + class MultiSourceIndex + # Used by merb.thor to confirm; not needed when MSI is in use + def load_gems_in(*args) + end + + def search(*args) + try = MAIN_INDEX.search(*args) + return try unless try.empty? + FALLBACK_INDEX.search(*args) + end + + def find_name(*args) + try = MAIN_INDEX.find_name(*args) + return try unless try.empty? + FALLBACK_INDEX.find_name(*args) + end + end +end
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/gem_ext.rb b/chef-server/tasks/merb.thor/gem_ext.rb new file mode 100644 index 0000000000..6b605d7c8c --- /dev/null +++ b/chef-server/tasks/merb.thor/gem_ext.rb @@ -0,0 +1,125 @@ +require "erb" + +Gem.pre_install_hooks.push(proc do |installer| + unless File.file?(installer.bin_dir / "common.rb") + FileUtils.mkdir_p(installer.bin_dir) + FileUtils.cp(File.dirname(__FILE__) / "common.rb", installer.bin_dir / "common.rb") + end + + include ColorfulMessages + name = installer.spec.name + if $GEMS && versions = ($GEMS.assoc(name) || [])[1] + dep = Gem::Dependency.new(name, versions) + unless dep.version_requirements.satisfied_by?(installer.spec.version) + error "Cannot install #{installer.spec.full_name} " \ + "for #{$INSTALLING}; " \ + "you required #{dep}" + ::Thor::Tasks::Merb::Gem.rollback_trans + exit! + end + end + success "Installing #{installer.spec.full_name}" +end) + +class ::Gem::Uninstaller + def self._with_silent_ui + + ui = Gem::DefaultUserInteraction.ui + def ui.say(str) + puts "- #{str}" + end + + yield + + class << Gem::DefaultUserInteraction.ui + remove_method :say + end + end + + def self._uninstall(source_index, name, op, version) + unless source_index.find_name(name, "#{op} #{version}").empty? + uninstaller = Gem::Uninstaller.new( + name, + :version => "#{op} #{version}", + :install_dir => Dir.pwd / "gems", + :all => true, + :ignore => true + ) + _with_silent_ui { uninstaller.uninstall } + end + end + + def self._uninstall_others(source_index, name, version) + _uninstall(source_index, name, "<", version) + _uninstall(source_index, name, ">", version) + end +end + +Gem.post_install_hooks.push(proc do |installer| + source_index = installer.instance_variable_get("@source_index") + ::Gem::Uninstaller._uninstall_others( + source_index, installer.spec.name, installer.spec.version + ) +end) + +class ::Gem::DependencyInstaller + alias old_fg find_gems_with_sources + + def find_gems_with_sources(dep) + if @source_index.any? { |_, installed_spec| + installed_spec.satisfies_requirement?(dep) + } + return [] + end + + old_fg(dep) + end +end + +class ::Gem::SpecFetcher + alias old_fetch fetch + def fetch(dependency, all = false, matching_platform = true) + idx = Gem::SourceIndex.from_installed_gems + + reqs = dependency.version_requirements.requirements + + if reqs.size == 1 && reqs[0][0] == "=" + dep = idx.search(dependency).sort.last + end + + if dep + file = dep.loaded_from.dup + file.gsub!(/specifications/, "cache") + file.gsub!(/gemspec$/, "gem") + spec = ::Gem::Format.from_file_by_path(file).spec + [[spec, file]] + else + old_fetch(dependency, all, matching_platform) + end + end +end + +class ::Gem::Installer + def app_script_text(bin_file_name) + template = File.read(File.dirname(__FILE__) / "app_script.rb") + erb = ERB.new(template) + erb.result(binding) + end +end + +class ::Gem::Specification + def recursive_dependencies(from, index = Gem.source_index) + specs = self.runtime_dependencies.map do |dep| + spec = index.search(dep).last + unless spec + from_name = from.is_a?(::Gem::Specification) ? from.full_name : from.to_s + wider_net = index.find_name(dep.name).last + ThorUI.error "Needed #{dep} for #{from_name}, but could not find it" + ThorUI.error "Found #{wider_net.full_name}" if wider_net + ::Thor::Tasks::Merb::Gem.rollback_trans + end + spec + end + specs + specs.map {|s| s.recursive_dependencies(self, index)}.flatten.uniq + end +end
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/main.thor b/chef-server/tasks/merb.thor/main.thor new file mode 100644 index 0000000000..a4c5e10337 --- /dev/null +++ b/chef-server/tasks/merb.thor/main.thor @@ -0,0 +1,150 @@ +require "rubygems" +require "rubygems/source_index" +require "rubygems/dependency_installer" +require "rubygems/uninstaller" +require "fileutils" +require File.join(File.dirname(__FILE__), "utils") +require File.join(File.dirname(__FILE__), "gem_ext") +require File.join(File.dirname(__FILE__), "ops") + +$INSTALLING = [] + +module Merb + + class Gem < Thor + extend ColorfulMessages + + def initialize + dirs = [Dir.pwd, File.dirname(__FILE__) / ".."] + root = dirs.find {|d| File.file?(d / "config" / "dependencies.rb")} + + if root + @depsrb = root / "config" / "dependencies.rb" + else + self.class.error "dependencies.rb was not found" + exit! + end + + FileUtils.mkdir_p(Dir.pwd / "gems") + + @list = Collector.collect(File.read(@depsrb)) + @idx = ::Gem::SourceIndex.new.load_gems_in("gems/specifications") + end + + def list + require "pp" + pp @list + end + + desc "redeploy", "Syncs up gems/cache with gems/gems. All gems in the cache " \ + "that are not already installed will be installed from the " \ + "cache. All installed gems that are not in the cache will " \ + "be uninstalled." + def redeploy + gem_dir = Dir.pwd / "gems" / "gems" + cache_dir = Dir.pwd / "gems" / "cache" + + gems = Dir[gem_dir / "*"].map! {|n| File.basename(n)} + cache = Dir[cache_dir / "*.gem"].map! {|n| File.basename(n, ".gem")} + new_gems = cache - gems + outdated = gems - cache + idx = ::Gem::SourceIndex.new + idx.load_gems_in(Dir.pwd / "gems" / "specifications") + + new_gems.each do |g| + installer = ::Gem::Installer.new(cache_dir / "#{g}.gem", + :bin_dir => Dir.pwd / "bin", + :install_dir => Dir.pwd / "gems", + :ignore_dependencies => true, + :user_install => false, + :wrappers => true, + :source_index => idx) + + installer.install + end + + outdated.each do |g| + /(.*)\-(.*)/ =~ g + name, version = $1, $2 + uninstaller = ::Gem::Uninstaller.new(name, + :version => version, + :bin_dir => Dir.pwd / "bin", + :install_dir => Dir.pwd / "gems", + :ignore => true, + :executables => true + ) + uninstaller.uninstall + end + end + + desc "confirm", "Confirm the current setup. merb:gem:install will " \ + "automatically run this task before committing the " \ + "changes it makes." + def confirm(gems = @list) + ::Gem.path.replace([Dir.pwd / "gems"]) + ::Gem.source_index.load_gems_in(Dir.pwd / "gems" / "specifications") + + self.class.info "Confirming configuration..." + + ::Gem.loaded_specs.clear + + begin + gems.each do |name, versions| + versions ||= [] + ::Gem.activate name, *versions + end + rescue ::Gem::LoadError => e + self.class.error "Configuration could not be confirmed: #{e.message}" + self.class.rollback_trans + end + self.class.info "Confirmed" + end + + desc 'install', 'Sync up your bundled gems with the list in config/dependencies.rb' + def install(*gems) + if gems.empty? + gems = @list + else + gems = gems.map {|desc| name, *versions = desc.split(" ") } + end + + $GEMS = gems + + self.class.begin_trans + + gems.each do |name, versions| + dep = ::Gem::Dependency.new(name, versions || []) + unless @idx.search(dep).empty? + next + end + + rescue_failures do + $INSTALLING = dep + _install(dep) + end + end + + gem_dir = Dir.pwd / "gems" / "gems" + installed_gems = Dir[gem_dir / "*"].map! {|n| File.basename(n)} + + list = full_list.map {|x| x.full_name}.compact + + (installed_gems - list).each do |g| + /^(.*)\-(.*)$/ =~ g + name, version = $1, $2 + uninstaller = ::Gem::Uninstaller.new(name, + :version => version, + :bin_dir => (Dir.pwd / "bin").to_s, + :install_dir => (Dir.pwd / "gems").to_s, + :ignore => true, + :executables => true + ) + uninstaller.uninstall + end + + confirm(gems) + + self.class.commit_trans + end + end +end
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/ops.rb b/chef-server/tasks/merb.thor/ops.rb new file mode 100644 index 0000000000..c758af23af --- /dev/null +++ b/chef-server/tasks/merb.thor/ops.rb @@ -0,0 +1,93 @@ +module Thor::Tasks + module Merb + class Collector + attr_reader :dependencies + + def self.collect(str) + collector = new + collector.instance_eval(str) + collector.dependencies + end + + def initialize + @dependencies = [] + end + + def dependency(name, *versions) + versions.pop if versions.last.is_a?(Hash) + @dependencies << [name, versions] + end + end + + class Gem < Thor + def full_list + @idx.load_gems_in("gems/specifications") + + @list.map do |name, versions| + dep = ::Gem::Dependency.new(name, versions) + spec = @idx.search(dep).last + unless spec + self.class.error "A required dependency #{dep} was not found" + self.class.rollback_trans + end + deps = spec.recursive_dependencies(dep, @idx) + [spec] + deps + end.flatten.uniq + end + + def rescue_failures(error = StandardError, prc = nil) + begin + yield + rescue error => e + if prc + prc.call(e) + else + puts e.message + puts e.backtrace + end + self.class.rollback_trans + end + end + + def self.begin_trans + note "Beginning transaction" + FileUtils.cp_r(Dir.pwd / "gems", Dir.pwd / ".original_gems") + end + + def self.commit_trans + note "Committing transaction" + FileUtils.rm_rf(Dir.pwd / ".original_gems") + end + + def self.rollback_trans + if File.exist?(Dir.pwd / ".original_gems") + note "Rolling back transaction" + FileUtils.rm_rf(Dir.pwd / "gems") + FileUtils.mv(Dir.pwd / ".original_gems", Dir.pwd / "gems") + end + exit! + end + + private + def _install(dep) + @idx.load_gems_in("gems/specifications") + return if @idx.search(dep).last + + installer = ::Gem::DependencyInstaller.new( + :bin_dir => Dir.pwd / "bin", + :install_dir => Dir.pwd / "gems", + :user_install => false) + + begin + installer.install dep.name, dep.version_requirements + rescue ::Gem::GemNotFoundException => e + puts "Cannot find #{dep}" + rescue ::Gem::RemoteFetcher::FetchError => e + puts e.message + puts "Retrying..." + retry + end + end + end + end +end
\ No newline at end of file diff --git a/chef-server/tasks/merb.thor/utils.rb b/chef-server/tasks/merb.thor/utils.rb new file mode 100644 index 0000000000..1b1caa6978 --- /dev/null +++ b/chef-server/tasks/merb.thor/utils.rb @@ -0,0 +1,40 @@ +class String + def /(other) + (Pathname.new(self) + other).to_s + end +end + +module ColorfulMessages + + # red + def error(*messages) + puts messages.map { |msg| "\033[1;31m#{msg}\033[0m" } + end + + # yellow + def warning(*messages) + puts messages.map { |msg| "\033[1;33m#{msg}\033[0m" } + end + + # green + def success(*messages) + puts messages.map { |msg| "\033[1;32m#{msg}\033[0m" } + end + + alias_method :message, :success + + # magenta + def note(*messages) + puts messages.map { |msg| "\033[1;35m#{msg}\033[0m" } + end + + # blue + def info(*messages) + puts messages.map { |msg| "\033[1;34m#{msg}\033[0m" } + end + +end + +module ThorUI + extend ColorfulMessages +end diff --git a/chef/Rakefile b/chef/Rakefile index 7c7f28f412..faa66847a4 100644 --- a/chef/Rakefile +++ b/chef/Rakefile @@ -49,6 +49,10 @@ task :install => :package do sh %{sudo gem install pkg/#{GEM}-#{CHEF_VERSION} --no-rdoc --no-ri} end +task :uninstall do + sh %{sudo gem uninstall #{GEM} -x -v #{CHEF_VERSION} } +end + desc "create a gemspec file" task :make_spec do File.open("#{GEM}.gemspec", "w") do |file| diff --git a/chef/bin/chef-client b/chef/bin/chef-client index 479a592125..32a3c45fe0 100755 --- a/chef/bin/chef-client +++ b/chef/bin/chef-client @@ -30,39 +30,26 @@ config = { } opts = OptionParser.new do |opts| opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)" - opts.on("-c CONFIG", "--config CONFIG", "The Chef Config file to use") do |c| - config[:config_file] = c - end - opts.on("-u USER", "--user USER", "User to change uid to before daemonizing") do |u| - config[:user] = u - end - opts.on("-g GROUP", "--group GROUP", "Group to change gid to before daemonizing") do |g| - config[:group] = g - end - opts.on("-d", "--daemonize", "Daemonize the process") do |d| - config[:daemonize] = true - end - opts.on("-i SECONDS", "--interval SECONDS", "Run chef-client peridoically, in seconds") do |i| - config[:interval] = i - end - opts.on("-j JSON_ATTRIBS", "--json-attributes JSON_ATTRIBS", "Load attributes from a JSON file") do |j| - config[:json_attribs] = j - end - opts.on("-N NODE_NAME", "--node-name NODE_NAME", "The node name for this client") do |n| - config[:node_name] = n - end - opts.on("-s SECONDS", "--splay SECONDS", "The splay time for running at intervals, in seconds") do |s| - config[:splay] = s - end - opts.on("-l LEVEL", "--loglevel LEVEL", "Set the log level (debug, info, warn, error, fatal)") do |l| - config[:log_level] = l.to_sym - end - opts.on("-L LOGLOCATION", "--logfile LOGLOCATION", "Set the log file location, defaults to STDOUT - recommended for daemonizing") do |lf| - config[:log_location] = lf - end - opts.on("-t TOKEN", "--token TOKEN", "Set the openid validation token") do |t| - config[:validation_token] = t + client_option_hash = { + :config_file=> { :short=>"-c CONFIG", :long=>"--config CONFIG", :description=>"The Chef Config file to use", :proc=>nil }, + :user=> { :short=>"-u USER", :long=>"--user USER", :description=>"User to change uid to before daemonizing", :proc=>nil }, + :group=> { :short=>"-g GROUP", :long=>"--group GROUP", :description=>"Group to change gid to before daemonizing", :proc=>nil }, + :daemonize=> { :short=>"-d", :long=>"--daemonize", :description=>"Daemonize the process", :proc=> lambda { |p| true} }, + :interval=> { :short=>"-i SECONDS", :long=>"--interval SECONDS", :description=>"Run chef-client periodically, in seconds", :proc=>nil }, + :json_attribs=>{ :short=>"-j JSON_ATTRIBS", :long=>"--json-attributes JSON_ATTRIBS", :description=>"Load attributes from a JSON file", :proc=>nil }, + :node_name=> { :short=>"-N NODE_NAME", :long=>"--node-name NODE_NAME", :description=>"The node name for this client", :proc=>nil }, + :splay=> { :short=>"-s SECONDS", :long=>"--splay SECONDS", :description=>"The splay time for running at intervals, in seconds", :proc=>nil }, + :log_level=> { :short=>"-l LEVEL", :long=>"--loglevel LEVEL", :description=>"Set the log level (debug, info, warn, error, fatal)", :proc=>lambda { |p| p.to_sym} }, + :log_location=>{ :short=>"-L LOGLOCATION", :long=>"--logfile LOGLOCATION", :description=>"Set the log file location, defaults to STDOUT - recommended for daemonizing", :proc=>nil }, + :validation_token=>{ :short=>"-t TOKEN", :long=>"--token TOKEN", :description=>"Set the openid validation token", :proc=>nil }, + } + + client_option_hash.each do |opt_key, opt_val| + opts.on(opt_val[:short],opt_val[:long],opt_val[:description]) do |c| + config[opt_key] = (opt_val[:proc] && opt_val[:proc].call(c)) || c + end end + opts.on_tail("-h", "--help", "Show this message") do puts opts exit 0 diff --git a/chef/config/server.rb b/chef/config/server.rb index 74c12df403..671024217a 100644 --- a/chef/config/server.rb +++ b/chef/config/server.rb @@ -8,10 +8,8 @@ node_path File.join(File.dirname(__FILE__), "..", "examples", "config", "nod file_store_path File.join(File.dirname(__FILE__), "..", "examples", "store") openid_store_path File.join(File.dirname(__FILE__), "..", "examples", "openid-db") openid_cstore_path File.join(File.dirname(__FILE__), "..", "examples", "openid-cstore") -merb_log_path File.join(File.dirname(__FILE__), "..", "examples", "logs") search_index_path File.join(File.dirname(__FILE__), "..", "examples", "search_index") - # openid_providers [ "localhost:4001", "openid.hjksolutions.com" ] Chef::Log::Formatter.show_time = false diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb index befaa3f35c..325156b421 100644 --- a/chef/lib/chef/config.rb +++ b/chef/lib/chef/config.rb @@ -44,15 +44,13 @@ class Chef :user => nil, :group => nil, :json_attribs => nil, - :cookbook_path => [ "/etc/chef/site-cookbook", "/etc/chef/cookbook" ], + :cookbook_path => [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ], :validation_token => nil, - :merb_root => "/var/chef/merb", - :node_path => "/etc/chef/node", + :node_path => "/var/chef/node", :file_store_path => "/var/chef/store", :search_index_path => "/var/chef/search_index", :log_level => :info, :log_location => STDOUT, - :merb_log_path => "/var/log/chef/merb.log", :openid_providers => nil, :ssl_verify_mode => :verify_none, :ssl_client_cert => "", @@ -95,6 +93,23 @@ class Chef yield @configuration end + # Manages the chef secret session key + # === Returns + # <newkey>:: A new or retrieved session key + # + def manage_secret_key + newkey = nil + if Chef::FileCache.has_key?("chef_server_cookie_id") + newkey = Chef::FileCache.load("chef_server_cookie_id") + else + chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + newkey = "" + 1.upto(40) { |i| newkey << chars[rand(chars.size-1)] } + Chef::FileCache.store("chef_server_cookie_id", newkey) + end + newkey + end + # Get the value of a configuration option # # === Parameters diff --git a/chef/lib/chef/rest.rb b/chef/lib/chef/rest.rb index 68ba9a3987..bac6788e52 100644 --- a/chef/lib/chef/rest.rb +++ b/chef/lib/chef/rest.rb @@ -166,7 +166,7 @@ class Chef http_retries = 1 # TODO - Figure out how to test this block - I really have no idea how - # to do it wouthout actually calling http.request... + # to do it without actually calling http.request... begin res = http.request(req) do |response| if raw diff --git a/chefserverslice/LICENSE b/chefserverslice/LICENSE new file mode 100644 index 0000000000..11069edd79 --- /dev/null +++ b/chefserverslice/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/chefserverslice/NOTICE b/chefserverslice/NOTICE new file mode 100644 index 0000000000..02f03a496d --- /dev/null +++ b/chefserverslice/NOTICE @@ -0,0 +1,10 @@ +Chef NOTICE +=========== + +Developed at HJK Solutions (http://www.hjksolutions.com). + +Contributors and Copyright holders: + + * Copyright 2008, Adam Jacob <adam@opscode.com> + * Copyright 2008, Arjuna Christensen <aj@hjksolutions.com> + * Copyright 2008, Ezra Zygmuntowicz <ezra@engineyard.com> diff --git a/chefserverslice/README.rdoc b/chefserverslice/README.rdoc new file mode 100644 index 0000000000..a7a4ea8dea --- /dev/null +++ b/chefserverslice/README.rdoc @@ -0,0 +1,94 @@ += chef + +* http://www.opscode.com/chef + +== DESCRIPTION: + +Chef is a systems management framework masquerading as a configuration management tool. + +I'm in ur netwerk, cookin up yer servers. :) + +== REQUIREMENTS: + +chef: + +* ruby-openid +* json +* erubis +* extlib +* stomp +* ohai + +chef-server and the chefserverslice (merb slice), same requires as chef above, plus: + +* stompserver +* ferret +* merb-core +* merb-haml +* mongrel +* haml +* ruby-openid +* syntax + +Interim Note: + +Once the {chef,chef-server,chefserverslice}.gem set is installed, do the following: + + > cd /Library/Ruby/Gems/1.8/gems/chef-server<xxx> # or wherever your gems land + > sudo rake slices:chefserverslice:install + +This installs the chefserverslice into the chef-server merb application + +External Servers: + +* stompserver (for easy stomp mq testing) +* CouchDB + +== INSTALL: + +Install all of the above. To fire up a develpment environment, do the following: + + * Start CouchDB with 'couchdb' + * Start stompserver with 'stompserver' + * Start chef-indexer with: + + chef-indexer -l debug + + * Start chef-server: + + chef-server -N -c 2 + + * Test run chef to begin node registration: + + sudo ./bin/chef-client + + * Validate the node registration: + + Visit http://localhost:4000 + Login, enter an openid URL (see http://openid.net/get/) + Registrations, click Validate + + * Test run chef with: + + chef-client + +== LICENSE: + +Chef - A configuration management system + +Author:: Adam Jacob (<adam@opscode.com>) +Copyright:: Copyright (c) 2008 Opscode, Inc. +License:: Apache License, Version 2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/chefserverslice/Rakefile b/chefserverslice/Rakefile new file mode 100644 index 0000000000..4f094d0a5f --- /dev/null +++ b/chefserverslice/Rakefile @@ -0,0 +1,69 @@ +require 'rubygems' +require 'rake/gempackagetask' + +require 'merb-core' +require 'merb-core/tasks/merb' + +GEM_NAME = "chefserverslice" +CHEF_SERVER_VERSION="0.5.5" +AUTHOR = "Opscode" +EMAIL = "chef@opscode.com" +HOMEPAGE = "http://wiki.opscode.com/display/chef" +SUMMARY = "A systems integration framework, built to bring the benefits of configuration management to your entire infrastructure." + +spec = Gem::Specification.new do |s| + s.rubyforge_project = 'merb' + s.name = GEM_NAME + s.version = CHEF_SERVER_VERSION + s.platform = Gem::Platform::RUBY + s.has_rdoc = true + s.extra_rdoc_files = ["README.rdoc", "LICENSE", 'NOTICE'] + s.summary = SUMMARY + s.description = s.summary + s.author = AUTHOR + s.email = EMAIL + s.homepage = HOMEPAGE + + ["merb-slices", + "stomp", + "stompserver", + "ferret", + "merb-core", + "merb-haml", + "merb-assets", + "merb-helpers", + "mongrel", + "haml", + "ruby-openid", + "json", + "syntax",].each { |g| s.add_dependency g} + + s.require_path = 'lib' + s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{config,lib,spec,app,public,stubs}/**/*") +end + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + +desc "Install the gem" +task :install do + Merb::RakeHelper.install(GEM_NAME, :version => CHEF_SERVER_VERSION) +end + +desc "Uninstall the gem" +task :uninstall do + Merb::RakeHelper.uninstall(GEM_NAME, :version => CHEF_SERVER_VERSION) +end + +desc "Create a gemspec file" +task :gemspec do + File.open("#{GEM_NAME}.gemspec", "w") do |file| + file.puts spec.to_ruby + end +end + +require 'spec/rake/spectask' +require 'merb-core/test/tasks/spectasks' +desc 'Default: run spec examples' +task :default => 'spec' diff --git a/chefserverslice/TODO b/chefserverslice/TODO new file mode 100644 index 0000000000..fc5ab78c0e --- /dev/null +++ b/chefserverslice/TODO @@ -0,0 +1,15 @@ +TODO: + +- Fix Chefserverslice.description and Chefserverslice.version +- Fix LICENSE with your name +- Fix Rakefile with your name and contact info +- Add your code to lib/chefserverslice.rb +- Add your Merb rake tasks to lib/chefserverslice/merbtasks.rb + +Remove anything that you don't need: + +- app/controllers/main.rb Chefserverslice::Main controller +- app/views/layout/chefserver.html.erb +- spec/controllers/main_spec.rb controller specs +- public/* any public files +- stubs/* any stub files
\ No newline at end of file diff --git a/chef-server/lib/controllers/application.rb b/chefserverslice/app/controllers/application.rb index dd300ee2d8..271914141c 100644 --- a/chef-server/lib/controllers/application.rb +++ b/chefserverslice/app/controllers/application.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -19,8 +20,33 @@ require "chef" / "mixin" / "checksum" require "chef" / "cookbook_loader" -class Application < Merb::Controller +class Chefserverslice::Application < Merb::Controller + controller_for_slice + + # Generate the absolute url for a slice - takes the slice's :path_prefix into account. + # + # @param slice_name<Symbol> + # The name of the slice - in identifier_sym format (underscored). + # @param *args<Array[Symbol,Hash]> + # There are several possibilities regarding arguments: + # - when passing a Hash only, the :default route of the current + # slice will be used + # - when a Symbol is passed, it's used as the route name + # - a Hash with additional params can optionally be passed + # + # @return <String> A uri based on the requested slice. + # + # @example absolute_slice_url(:awesome, :format => 'html') + # @example absolute_slice_url(:forum, :posts, :format => 'xml') + def absolute_slice_url(slice_name, *args) + options = extract_options_from_args!(args) || {} + protocol = options.delete(:protocol) || request.protocol + host = options.delete(:host) || request.host + + protocol + "://" + host + slice_url(slice_name,*args) + end + def fix_up_node_id if params.has_key?(:id) params[:id].gsub!(/_/, '.') @@ -149,4 +175,4 @@ class Application < Merb::Controller files end -end
\ No newline at end of file +end diff --git a/chef-server/lib/controllers/cookbook_attributes.rb b/chefserverslice/app/controllers/cookbook_attributes.rb index 49a72c5953..01e8c090e0 100644 --- a/chef-server/lib/controllers/cookbook_attributes.rb +++ b/chefserverslice/app/controllers/cookbook_attributes.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -18,7 +19,7 @@ require 'chef' / 'mixin' / 'checksum' -class CookbookAttributes < Application +class Chefserverslice::CookbookAttributes < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbook_definitions.rb b/chefserverslice/app/controllers/cookbook_definitions.rb index c9252f2d96..999f290873 100644 --- a/chef-server/lib/controllers/cookbook_definitions.rb +++ b/chefserverslice/app/controllers/cookbook_definitions.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -18,7 +19,7 @@ require 'chef' / 'mixin' / 'checksum' -class CookbookDefinitions < Application +class Chefserverslice::CookbookDefinitions < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbook_files.rb b/chefserverslice/app/controllers/cookbook_files.rb index 9f24100510..abe0315b93 100644 --- a/chef-server/lib/controllers/cookbook_files.rb +++ b/chefserverslice/app/controllers/cookbook_files.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -20,7 +21,7 @@ require 'chef' / 'mixin' / 'checksum' require 'chef' / 'cookbook_loader' require 'chef' / 'mixin' / 'find_preferred_file' -class CookbookFiles < Application +class Chefserverslice::CookbookFiles < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbook_libraries.rb b/chefserverslice/app/controllers/cookbook_libraries.rb index b1f9dbcd73..e3e528b689 100644 --- a/chef-server/lib/controllers/cookbook_libraries.rb +++ b/chefserverslice/app/controllers/cookbook_libraries.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -19,7 +20,7 @@ require 'chef' / 'mixin' / 'checksum' require 'chef' / 'cookbook_loader' -class CookbookLibraries < Application +class Chefserverslice::CookbookLibraries < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbook_recipes.rb b/chefserverslice/app/controllers/cookbook_recipes.rb index 18698591d3..abba278db6 100644 --- a/chef-server/lib/controllers/cookbook_recipes.rb +++ b/chefserverslice/app/controllers/cookbook_recipes.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -18,7 +19,7 @@ require 'chef' / 'mixin' / 'checksum' -class CookbookRecipes < Application +class Chefserverslice::CookbookRecipes < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbook_templates.rb b/chefserverslice/app/controllers/cookbook_templates.rb index 6435203db9..1d2e4913eb 100644 --- a/chef-server/lib/controllers/cookbook_templates.rb +++ b/chefserverslice/app/controllers/cookbook_templates.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -20,7 +21,7 @@ require 'chef' / 'mixin' / 'checksum' require 'chef' / 'cookbook_loader' require 'chef' / 'mixin' / 'find_preferred_file' -class CookbookTemplates < Application +class Chefserverslice::CookbookTemplates < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/cookbooks.rb b/chefserverslice/app/controllers/cookbooks.rb index d96a78d9ee..33dddc5900 100644 --- a/chef-server/lib/controllers/cookbooks.rb +++ b/chefserverslice/app/controllers/cookbooks.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -18,7 +19,7 @@ require 'chef' / 'cookbook_loader' -class Cookbooks < Application +class Chefserverslice::Cookbooks < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/exceptions.rb b/chefserverslice/app/controllers/exceptions.rb index 5d1f097693..b0d5934eeb 100644 --- a/chef-server/lib/controllers/exceptions.rb +++ b/chefserverslice/app/controllers/exceptions.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -16,7 +17,7 @@ # limitations under the License. # -class Exceptions < Application +class Chefserverslice::Exceptions < Chefserverslice::Application provides :html, :json @@ -35,4 +36,4 @@ class Exceptions < Application display params end -end
\ No newline at end of file +end diff --git a/chefserverslice/app/controllers/main.rb b/chefserverslice/app/controllers/main.rb new file mode 100644 index 0000000000..897b6de172 --- /dev/null +++ b/chefserverslice/app/controllers/main.rb @@ -0,0 +1,7 @@ +class Chefserverslice::Main < Chefserverslice::Application + + def index + render + end + +end diff --git a/chef-server/lib/controllers/nodes.rb b/chefserverslice/app/controllers/nodes.rb index 9757057691..4f0a48e17f 100644 --- a/chef-server/lib/controllers/nodes.rb +++ b/chefserverslice/app/controllers/nodes.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -18,7 +19,7 @@ require 'chef' / 'node' -class Nodes < Application +class Chefserverslice::Nodes < Chefserverslice::Application provides :html, :json @@ -38,7 +39,7 @@ class Nodes < Application raise NotFound, "Cannot load node #{params[:id]}" end if params[:ajax] == "true" - render JSON.pretty_generate(@node) + render JSON.pretty_generate(@node), :layout=>false else display @node end @@ -86,7 +87,7 @@ class Nodes < Application @status = 202 display @node else - redirect(url(:nodes), {:message => { :notice => "Node #{params[:id]} deleted succesfully" }, :permanent => true}) + redirect(slice_url(:nodes), {:message => { :notice => "Node #{params[:id]} deleted succesfully" }, :permanent => true}) end end diff --git a/chef-server/lib/controllers/openid_consumer.rb b/chefserverslice/app/controllers/openid_consumer.rb index a3a873a371..b239cd329f 100644 --- a/chef-server/lib/controllers/openid_consumer.rb +++ b/chefserverslice/app/controllers/openid_consumer.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -21,7 +22,7 @@ require 'pathname' require "openid" require 'openid/store/filesystem' -class OpenidConsumer < Application +class Chefserverslice::OpenidConsumer < Chefserverslice::Application provides :html, :json @@ -36,8 +37,8 @@ class OpenidConsumer < Application rescue OpenID::OpenIDError => e raise BadRequest, "Discovery failed for #{params[:openid_identifier]}: #{e}" end - return_to = absolute_url(:openid_consumer_complete) - realm = absolute_url(:openid_consumer) + return_to = absolute_slice_url(:openid_consumer_complete) + realm = absolute_slice_url(:openid_consumer) if oidreq.send_redirect?(realm, return_to, params[:immediate]) return redirect(oidreq.redirect_url(realm, return_to, params[:immediate])) @@ -49,7 +50,7 @@ class OpenidConsumer < Application def complete # FIXME - url_for some action is not necessarily the current URL. - current_url = absolute_url(:openid_consumer_complete) + current_url = absolute_slice_url(:openid_consumer_complete) parameters = params.reject{|k,v| k == "controller" || k == "action"} oidresp = consumer.complete(parameters, current_url) case oidresp.status @@ -70,7 +71,7 @@ class OpenidConsumer < Application else session[:level] = :admin end - redirect_back_or_default(absolute_url(:nodes)) + redirect_back_or_default(absolute_slice_url(:nodes)) return "Verification of #{oidresp.display_identifier} succeeded." when OpenID::Consumer::SETUP_NEEDED return "Immediate request failed - Setup Needed" @@ -78,14 +79,14 @@ class OpenidConsumer < Application return "OpenID transaction cancelled." else end - redirect absolute_url(:openid_consumer) + redirect absolute_slice_url(:openid_consumer) end def logout session[:openid] = nil if session.has_key?(:openid) session[:level] = nil if session.has_key?(:level) session[:node_name] = nil if session.has_key?(:node_name) - redirect url(:top) + redirect slice_url(:top) end private diff --git a/chef-server/lib/controllers/openid_register.rb b/chefserverslice/app/controllers/openid_register.rb index 40f5ef707e..b987cc0468 100644 --- a/chef-server/lib/controllers/openid_register.rb +++ b/chefserverslice/app/controllers/openid_register.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -19,7 +20,7 @@ require 'openid' require 'chef' / 'openid_registration' -class OpenidRegister < Application +class Chefserverslice::OpenidRegister < Chefserverslice::Application provides :html, :json @@ -80,7 +81,7 @@ class OpenidRegister < Application end r.destroy if content_type == :html - redirect url(:registrations) + redirect slice_url(:registrations) else display({ :message => "Deleted registration for #{params[:id]}"}) end @@ -94,7 +95,7 @@ class OpenidRegister < Application end r.validated = r.validated ? false : true r.save - redirect url(:registrations) + redirect slice_url(:registrations) end def admin @@ -105,6 +106,6 @@ class OpenidRegister < Application end r.admin = r.admin ? false : true r.save - redirect url(:registrations) + redirect slice_url(:registrations) end end diff --git a/chef-server/lib/controllers/openid_server.rb b/chefserverslice/app/controllers/openid_server.rb index f12fea766a..9fa38d0cb1 100644 --- a/chef-server/lib/controllers/openid_server.rb +++ b/chefserverslice/app/controllers/openid_server.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -30,11 +31,11 @@ require 'json' require 'chef' / 'openid_registration' #end -class OpenidServer < Application +class Chefserverslice::OpenidServer < Chefserverslice::Application provides :html, :json - include Merb::OpenidServerHelper + include Merb::Chefserverslice::OpenidServerHelper include OpenID::Server layout nil @@ -60,12 +61,12 @@ class OpenidServer < Application elsif self.is_authorized(identity, oidreq.trust_root) oidresp = oidreq.answer(true, nil, identity) elsif oidreq.immediate - server_url = url :openid_server + server_url = slice_url :openid_server oidresp = oidreq.answer(false, server_url) else if content_type != 'application/json' session[:last_oidreq] = oidreq - response = { :action => url(:openid_server_decision) } + response = { :action => slice_url(:openid_server_decision) } return response.to_json else return show_decision_page(oidreq) @@ -105,11 +106,11 @@ class OpenidServer < Application end # content negotiation failed, so just render the user page - xrds_url = absolute_url(:openid_node_xrds, :id => params[:id]) + xrds_url = absolute_slice_url(:openid_node_xrds, :id => params[:id]) identity_page = <<EOS <html><head> <meta http-equiv="X-XRDS-Location" content="#{xrds_url}" /> -<link rel="openid.server" href="#{absolute_url(:openid_node, :id => params[:id])}" /> +<link rel="openid.server" href="#{absolute_slice_url(:openid_node, :id => params[:id])}" /> </head><body><p>OpenID identity page for registration #{params[:id]}</p> </body></html> EOS @@ -173,7 +174,7 @@ EOS def server if @server.nil? - server_url = absolute_url(:openid_server) + server_url = absolute_slice_url(:openid_server) dir = Chef::Config[:openid_store_path] store = OpenID::Store::Filesystem.new(dir) @server = Server.new(store, server_url) @@ -205,7 +206,7 @@ EOS <XRD> <Service priority="0"> #{type_str} - <URI>#{absolute_url(:openid_server)}</URI> + <URI>#{absolute_slice_url(:openid_server)}</URI> </Service> </XRD> </xrds:XRDS> diff --git a/chef-server/lib/controllers/search.rb b/chefserverslice/app/controllers/search.rb index 2e7be63bbd..051eb312e6 100644 --- a/chef-server/lib/controllers/search.rb +++ b/chefserverslice/app/controllers/search.rb @@ -1,5 +1,6 @@ - # +# # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -19,7 +20,7 @@ require 'chef' / 'search' require 'chef' / 'queue' -class Search < Application +class Chefserverslice::Search < Chefserverslice::Application provides :html, :json diff --git a/chef-server/lib/controllers/search_entries.rb b/chefserverslice/app/controllers/search_entries.rb index f841f0b2ee..71c78bd540 100644 --- a/chef-server/lib/controllers/search_entries.rb +++ b/chefserverslice/app/controllers/search_entries.rb @@ -1,5 +1,6 @@ # # Author:: Adam Jacob (<adam@opscode.com>) +# Author:: Christopher Brown (<cb@opscode.com>) # Copyright:: Copyright (c) 2008 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -19,7 +20,7 @@ require 'chef' / 'search' require 'chef' / 'queue' -class SearchEntries < Application +class Chefserverslice::SearchEntries < Chefserverslice::Application provides :html, :json diff --git a/chefserverslice/app/helpers/application_helper.rb b/chefserverslice/app/helpers/application_helper.rb new file mode 100644 index 0000000000..2ceec91243 --- /dev/null +++ b/chefserverslice/app/helpers/application_helper.rb @@ -0,0 +1,64 @@ +module Merb + module Chefserverslice + module ApplicationHelper + + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path relative to the public directory, with added segments. + def image_path(*segments) + public_path_for(:image, *segments) + end + + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path relative to the public directory, with added segments. + def javascript_path(*segments) + public_path_for(:javascript, *segments) + end + + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path relative to the public directory, with added segments. + def stylesheet_path(*segments) + public_path_for(:stylesheet, *segments) + end + + # Construct a path relative to the public directory + # + # @param <Symbol> The type of component. + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path relative to the public directory, with added segments. + def public_path_for(type, *segments) + ::Chefserverslice.public_path_for(type, *segments) + end + + # Construct an app-level path. + # + # @param <Symbol> The type of component. + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path within the host application, with added segments. + def app_path_for(type, *segments) + ::Chefserverslice.app_path_for(type, *segments) + end + + # Construct a slice-level path. + # + # @param <Symbol> The type of component. + # @param *segments<Array[#to_s]> Path segments to append. + # + # @return <String> + # A path within the slice source (Gem), with added segments. + def slice_path_for(type, *segments) + ::Chefserverslice.slice_path_for(type, *segments) + end + + end + end +end diff --git a/chefserverslice/app/helpers/cookbook_attributes_helper.rb b/chefserverslice/app/helpers/cookbook_attributes_helper.rb new file mode 100644 index 0000000000..3138acfec1 --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_attributes_helper.rb @@ -0,0 +1,7 @@ +module Merb + module Chefserverslice + module CookbookAttributesHelper + + end + end +end # Merb diff --git a/chefserverslice/app/helpers/cookbook_definitions_helper.rb b/chefserverslice/app/helpers/cookbook_definitions_helper.rb new file mode 100644 index 0000000000..7fadb94de3 --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_definitions_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module CookbookDefinitionsHelper + + end + + end +end # Merb diff --git a/chefserverslice/app/helpers/cookbook_files_helper.rb b/chefserverslice/app/helpers/cookbook_files_helper.rb new file mode 100644 index 0000000000..4147b5251d --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_files_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module CookbookFilesHelper + + end + + end +end # Merb diff --git a/chefserverslice/app/helpers/cookbook_libraries_helper.rb b/chefserverslice/app/helpers/cookbook_libraries_helper.rb new file mode 100644 index 0000000000..1cbb944744 --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_libraries_helper.rb @@ -0,0 +1,7 @@ +module Merb + module Chefserverslice + module CookbookLibrariesHelper + + end + end +end # Merb diff --git a/chefserverslice/app/helpers/cookbook_recipes_helper.rb b/chefserverslice/app/helpers/cookbook_recipes_helper.rb new file mode 100644 index 0000000000..00b73785df --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_recipes_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module CookbookRecipesHelper + + end + + end +end # Merb diff --git a/chefserverslice/app/helpers/cookbook_templates_helper.rb b/chefserverslice/app/helpers/cookbook_templates_helper.rb new file mode 100644 index 0000000000..fa6542314d --- /dev/null +++ b/chefserverslice/app/helpers/cookbook_templates_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module CookbookTemplatesHelper + + end + + end +end # Merb diff --git a/chef-server/lib/helpers/cookbooks_helper.rb b/chefserverslice/app/helpers/cookbooks_helper.rb index 25b74f8d8c..5117464662 100644 --- a/chef-server/lib/helpers/cookbooks_helper.rb +++ b/chefserverslice/app/helpers/cookbooks_helper.rb @@ -17,13 +17,15 @@ # module Merb - module CookbooksHelper - def syntax_highlight(code) - converter = Syntax::Convertors::HTML.for_syntax "ruby" - if File.exists?(code) - converter.convert(File.read(code)) - else - converter.convert(code) + module Chefserverslice + module CookbooksHelper + def syntax_highlight(code) + converter = Syntax::Convertors::HTML.for_syntax "ruby" + if File.exists?(code) + converter.convert(File.read(code)) + else + converter.convert(code) + end end end end diff --git a/chefserverslice/app/helpers/exceptions_helper.rb b/chefserverslice/app/helpers/exceptions_helper.rb new file mode 100644 index 0000000000..dde648c331 --- /dev/null +++ b/chefserverslice/app/helpers/exceptions_helper.rb @@ -0,0 +1,6 @@ +module Merb + module Chefserverslice + module ExceptionsHelper + end + end +end # Merb diff --git a/chef-server/lib/helpers/nodes_helper.rb b/chefserverslice/app/helpers/global_helpers.rb index 89c47e93d1..9b0f0fe8fb 100644 --- a/chef-server/lib/helpers/nodes_helper.rb +++ b/chefserverslice/app/helpers/global_helpers.rb @@ -17,21 +17,22 @@ # module Merb - module NodesHelper - def recipe_list(node) - response = "" - node.recipes.each do |recipe| - response << "<li>#{recipe}</li>" + module Chefserverslice + + module GlobalHelpers + # helpers defined here available to all views. + def resource_collection(collection) + html = "<ul>" + collection.each do |resource| + html << "<li><b>#{resource.class}</b></li>" + end + html << "</ul>" + html end - response - end - - def attribute_list(node) - response = "" - node.each_attribute do |k,v| - response << "<li><b>#{k}</b>: #{v}</li>" + + def node_escape(node) + node.gsub(/\./, '_') end - response end end -end
\ No newline at end of file +end diff --git a/chef-server/lib/helpers/global_helpers.rb b/chefserverslice/app/helpers/nodes_helper.rb index 1495996120..bbd7b20879 100644 --- a/chef-server/lib/helpers/global_helpers.rb +++ b/chefserverslice/app/helpers/nodes_helper.rb @@ -17,19 +17,24 @@ # module Merb - module GlobalHelpers - # helpers defined here available to all views. - def resource_collection(collection) - html = "<ul>" - collection.each do |resource| - html << "<li><b>#{resource.class}</b></li>" + module Chefserverslice + module NodesHelper + def recipe_list(node) + response = "" + node.recipes.each do |recipe| + response << "<li>#{recipe}</li>" + end + response + end + + def attribute_list(node) + response = "" + node.each_attribute do |k,v| + response << "<li><b>#{k}</b>: #{v}</li>" + end + response end - html << "</ul>" - html end - def node_escape(node) - node.gsub(/\./, '_') - end end end diff --git a/chefserverslice/app/helpers/openid_consumer_helper.rb b/chefserverslice/app/helpers/openid_consumer_helper.rb new file mode 100644 index 0000000000..1a7270f8a0 --- /dev/null +++ b/chefserverslice/app/helpers/openid_consumer_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module OpenidConsumerHelper + + end + end + +end # Merb diff --git a/chefserverslice/app/helpers/openid_register_helper.rb b/chefserverslice/app/helpers/openid_register_helper.rb new file mode 100644 index 0000000000..2b26dfbcc1 --- /dev/null +++ b/chefserverslice/app/helpers/openid_register_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module OpenidRegisterHelper + + end + end + +end # Merb diff --git a/chefserverslice/app/helpers/openid_server_helper.rb b/chefserverslice/app/helpers/openid_server_helper.rb new file mode 100644 index 0000000000..7bedb9e9ba --- /dev/null +++ b/chefserverslice/app/helpers/openid_server_helper.rb @@ -0,0 +1,6 @@ +module Merb + module Chefserverslice + module OpenidServerHelper + end + end +end # Merb diff --git a/chef-server/lib/helpers/openid_server_helpers.rb b/chefserverslice/app/helpers/openid_server_helpers.rb index bf8814382e..f1d3710fd2 100644 --- a/chef-server/lib/helpers/openid_server_helpers.rb +++ b/chefserverslice/app/helpers/openid_server_helpers.rb @@ -17,11 +17,13 @@ # module Merb - module OpenidServerHelper + module Chefserverslice + module OpenidServerHelper + + def url_for_user + url(:openid_node, :username => session[:username]) + end - def url_for_user - url(:openid_node, :username => session[:username]) end - end end diff --git a/chefserverslice/app/helpers/search_entries_helper.rb b/chefserverslice/app/helpers/search_entries_helper.rb new file mode 100644 index 0000000000..6c7ebf5df4 --- /dev/null +++ b/chefserverslice/app/helpers/search_entries_helper.rb @@ -0,0 +1,8 @@ +module Merb + module Chefserverslice + module SearchEntriesHelper + + end + end + +end # Merb diff --git a/chefserverslice/app/helpers/search_helper.rb b/chefserverslice/app/helpers/search_helper.rb new file mode 100644 index 0000000000..1c03acb7d0 --- /dev/null +++ b/chefserverslice/app/helpers/search_helper.rb @@ -0,0 +1,7 @@ +module Merb + module Chefserverslice + module SearchHelper + + end + end +end # Merb diff --git a/chef-server/lib/views/cookbook_templates/index.html.haml b/chefserverslice/app/views/cookbook_templates/index.html.haml index 7f8f76506f..7f8f76506f 100644 --- a/chef-server/lib/views/cookbook_templates/index.html.haml +++ b/chefserverslice/app/views/cookbook_templates/index.html.haml diff --git a/chef-server/lib/views/cookbooks/_attribute_file.html.haml b/chefserverslice/app/views/cookbooks/_attribute_file.html.haml index eb91921bf0..eb91921bf0 100644 --- a/chef-server/lib/views/cookbooks/_attribute_file.html.haml +++ b/chefserverslice/app/views/cookbooks/_attribute_file.html.haml diff --git a/chef-server/lib/views/cookbooks/_syntax_highlight.html.haml b/chefserverslice/app/views/cookbooks/_syntax_highlight.html.haml index 1f769d05f1..1f769d05f1 100644 --- a/chef-server/lib/views/cookbooks/_syntax_highlight.html.haml +++ b/chefserverslice/app/views/cookbooks/_syntax_highlight.html.haml diff --git a/chef-server/lib/views/cookbooks/attribute_files.html.haml b/chefserverslice/app/views/cookbooks/attribute_files.html.haml index c0751560f4..c0751560f4 100644 --- a/chef-server/lib/views/cookbooks/attribute_files.html.haml +++ b/chefserverslice/app/views/cookbooks/attribute_files.html.haml diff --git a/chef-server/lib/views/cookbooks/index.html.haml b/chefserverslice/app/views/cookbooks/index.html.haml index f94dcb37c9..51874ac2fd 100644 --- a/chef-server/lib/views/cookbooks/index.html.haml +++ b/chefserverslice/app/views/cookbooks/index.html.haml @@ -4,4 +4,4 @@ %table %tr %td - %a{ :href => url(:cookbook, { :id => cookbook.name }) }= cookbook.name + %a{ :href => slice_url(:cookbook, { :id => cookbook.name }) }= cookbook.name diff --git a/chef-server/lib/views/cookbooks/show.html.haml b/chefserverslice/app/views/cookbooks/show.html.haml index c9249609c1..c9249609c1 100644 --- a/chef-server/lib/views/cookbooks/show.html.haml +++ b/chefserverslice/app/views/cookbooks/show.html.haml diff --git a/chef-server/lib/views/exceptions/bad_request.json.erb b/chefserverslice/app/views/exceptions/bad_request.json.erb index f266cf99b9..f266cf99b9 100644 --- a/chef-server/lib/views/exceptions/bad_request.json.erb +++ b/chefserverslice/app/views/exceptions/bad_request.json.erb diff --git a/chef-server/lib/views/exceptions/internal_server_error.html.erb b/chefserverslice/app/views/exceptions/internal_server_error.html.erb index aadbfad350..aadbfad350 100644 --- a/chef-server/lib/views/exceptions/internal_server_error.html.erb +++ b/chefserverslice/app/views/exceptions/internal_server_error.html.erb diff --git a/chef-server/lib/views/exceptions/not_acceptable.html.erb b/chefserverslice/app/views/exceptions/not_acceptable.html.erb index f632712bb2..f632712bb2 100644 --- a/chef-server/lib/views/exceptions/not_acceptable.html.erb +++ b/chefserverslice/app/views/exceptions/not_acceptable.html.erb diff --git a/chef-server/lib/views/exceptions/not_found.html.erb b/chefserverslice/app/views/exceptions/not_found.html.erb index 388c72c31d..388c72c31d 100644 --- a/chef-server/lib/views/exceptions/not_found.html.erb +++ b/chefserverslice/app/views/exceptions/not_found.html.erb diff --git a/chef-server/lib/views/layout/application.html.haml b/chefserverslice/app/views/layout/chefserverslice.html.haml index a17ea69a63..b108486504 100644 --- a/chef-server/lib/views/layout/application.html.haml +++ b/chefserverslice/app/views/layout/chefserverslice.html.haml @@ -4,29 +4,29 @@ %head %title Chef Server %meta{"http-equiv" => "content-type", :content => "text/html; charset=utf-8" } - %link{:rel => "stylesheet", :href => "/stylesheets/master.css", :type => "text/css", :media => "screen", :charset => "utf-8" } + %link{:rel => "stylesheet", :href => "/stylesheets/master-highlight.css", :type => "text/css", :media => "screen", :charset => "utf-8" } = js_include_tag "jquery-1.3.1.min", "jquery.jeditable.mini", "jquery.livequery" = js_include_tag "chef" %body .header - %a{:href => url(:searches) } Search + %a{:href => slice_url(:searches) } Search | - %a{:href => url(:nodes) } Nodes + %a{:href => slice_url(:nodes) } Nodes | - %a{:href => url(:cookbooks) } Cookbooks + %a{:href => slice_url(:cookbooks) } Cookbooks | - %a{:href => url(:registrations)} Registrations + %a{:href => slice_url(:registrations)} Registrations - if session[:openid] | - %a{:href => url(:openid_consumer_logout)}= "Logout #{h session[:openid]}" + %a{:href => slice_url(:openid_consumer_logout)}= "Logout #{h session[:openid]}" = "(#{session[:level].to_s})" - else | - %a{:href => url(:openid_consumer)} Login + %a{:href => slice_url(:openid_consumer)} Login %h3= catch_content :header - unless message.empty? = message[:error] = message[:notice] = catch_content :for_layout -
\ No newline at end of file + diff --git a/chefserverslice/app/views/main/index.html.erb b/chefserverslice/app/views/main/index.html.erb new file mode 100644 index 0000000000..755c73de7f --- /dev/null +++ b/chefserverslice/app/views/main/index.html.erb @@ -0,0 +1 @@ +<strong><%= slice.description %></strong> (v. <%= slice.version %>)
\ No newline at end of file diff --git a/chef-server/lib/views/nodes/_action.html.haml b/chefserverslice/app/views/nodes/_action.html.haml index 8adb39338d..8adb39338d 100644 --- a/chef-server/lib/views/nodes/_action.html.haml +++ b/chefserverslice/app/views/nodes/_action.html.haml diff --git a/chef-server/lib/views/nodes/_node.html.haml b/chefserverslice/app/views/nodes/_node.html.haml index c5c73fbf66..c5c73fbf66 100644 --- a/chef-server/lib/views/nodes/_node.html.haml +++ b/chefserverslice/app/views/nodes/_node.html.haml diff --git a/chef-server/lib/views/nodes/_resource.html.haml b/chefserverslice/app/views/nodes/_resource.html.haml index 7b9776b816..7b9776b816 100644 --- a/chef-server/lib/views/nodes/_resource.html.haml +++ b/chefserverslice/app/views/nodes/_resource.html.haml diff --git a/chef-server/lib/views/nodes/compile.html.haml b/chefserverslice/app/views/nodes/compile.html.haml index 5656447ea8..5656447ea8 100644 --- a/chef-server/lib/views/nodes/compile.html.haml +++ b/chefserverslice/app/views/nodes/compile.html.haml diff --git a/chefserverslice/app/views/nodes/index.html.haml b/chefserverslice/app/views/nodes/index.html.haml new file mode 100644 index 0000000000..dce11d0c71 --- /dev/null +++ b/chefserverslice/app/views/nodes/index.html.haml @@ -0,0 +1,7 @@ +- throw_content(:header, "Node List") +- @node_list.each do |node| + .node + %a{ :href => slice_url(:node, { :id => node.gsub(/\./, "_") }) } + = node + - if session[:level] == :admin + = delete_button slice_url(:node, { :id => node.gsub(/\./, "_") }), "Delete" diff --git a/chef-server/lib/views/nodes/show.html.haml b/chefserverslice/app/views/nodes/show.html.haml index d355974c49..d355974c49 100644 --- a/chef-server/lib/views/nodes/show.html.haml +++ b/chefserverslice/app/views/nodes/show.html.haml diff --git a/chef-server/lib/views/openid_consumer/index.html.haml b/chefserverslice/app/views/openid_consumer/index.html.haml index e2a143631e..dcc0546066 100644 --- a/chef-server/lib/views/openid_consumer/index.html.haml +++ b/chefserverslice/app/views/openid_consumer/index.html.haml @@ -6,7 +6,7 @@ -if session[:success] .success= h session[:success] #verify-form - %form{ :method => "get", "accept-charset" => "UTF-8", :action => url(:openid_consumer_start) } + %form{ :method => "get", "accept-charset" => "UTF-8", :action => slice_url(:openid_consumer_start) } Identifier: %input.openid{ :type => "text", :name => "openid_identifier" }/ %input{ :type => "submit", :value => "Verify"}/ diff --git a/chef-server/lib/views/openid_consumer/start.html.haml b/chefserverslice/app/views/openid_consumer/start.html.haml index 75ed9a9257..75ed9a9257 100644 --- a/chef-server/lib/views/openid_consumer/start.html.haml +++ b/chefserverslice/app/views/openid_consumer/start.html.haml diff --git a/chef-server/lib/views/openid_login/index.html.haml b/chefserverslice/app/views/openid_login/index.html.haml index 521c735f78..521c735f78 100644 --- a/chef-server/lib/views/openid_login/index.html.haml +++ b/chefserverslice/app/views/openid_login/index.html.haml diff --git a/chef-server/lib/views/openid_register/index.html.haml b/chefserverslice/app/views/openid_register/index.html.haml index 50efd02096..cb52e4c196 100644 --- a/chef-server/lib/views/openid_register/index.html.haml +++ b/chefserverslice/app/views/openid_register/index.html.haml @@ -3,16 +3,16 @@ - @registered_nodes.each do |node| %tr %td - %a{ :href => url(:registration, { :id => node.name }) } + %a{ :href => slice_url(:registration, { :id => node.name }) } = h node.name %td - if session[:level] == :admin - %form{ :method => "post", :action => url(:validate_registration, { :id => node.name })} + %form{ :method => "post", :action => slice_url(:validate_registration, { :id => node.name })} - submit_name = node.validated ? "Invalidate" : "Validate" %input{ :type => "submit", :name => submit_name, :value => submit_name } - %form{ :method => "post", :action => url(:admin_registration, { :id => node.name })} + %form{ :method => "post", :action => slice_url(:admin_registration, { :id => node.name })} - submit_name = node.admin ? "Remove Admin Rights" : "Admin" %input{ :type => "submit", :name => submit_name, :value => submit_name } - %form{ :method => "post", :action => url(:registration, { :id => node.name })} + %form{ :method => "post", :action => slice_url(:registration, { :id => node.name })} %input{ :type => "hidden", :name => "_method", :value => "delete" } %input{ :type => "submit", :name => "Delete", :value => "Delete" } diff --git a/chef-server/lib/views/openid_register/show.html.haml b/chefserverslice/app/views/openid_register/show.html.haml index 604b9cd960..58f55a3579 100644 --- a/chef-server/lib/views/openid_register/show.html.haml +++ b/chefserverslice/app/views/openid_register/show.html.haml @@ -1,6 +1,6 @@ - throw_content(:header, "Registered OpenID Node #{@registered_node.name}") %ol %li - %a{ :href => url(:openid_node , { :id => @registered_node.name.gsub(/\./, "_") }) } OpenID URL + %a{ :href => slice_url(:openid_node , { :id => @registered_node.name.gsub(/\./, "_") }) } OpenID URL %li= "Validated: #{@registered_node.validated}" %li= "Admin: #{@registered_node.admin}" diff --git a/chef-server/lib/views/openid_server/decide.html.haml b/chefserverslice/app/views/openid_server/decide.html.haml index 8082a5068d..8082a5068d 100644 --- a/chef-server/lib/views/openid_server/decide.html.haml +++ b/chefserverslice/app/views/openid_server/decide.html.haml diff --git a/chef-server/lib/views/search/_search_form.html.haml b/chefserverslice/app/views/search/_search_form.html.haml index 99303e1714..5396e65123 100644 --- a/chef-server/lib/views/search/_search_form.html.haml +++ b/chefserverslice/app/views/search/_search_form.html.haml @@ -1,4 +1,4 @@ -%form{ :method => "get", :action => url(:search, { :id => index_name }) } +%form{ :method => "get", :action => slice_url(:search, { :id => index_name }) } Q: %input{ :type => "text", :name => "q" } A: diff --git a/chef-server/lib/views/search/index.html.haml b/chefserverslice/app/views/search/index.html.haml index 81edcfc847..75e4572481 100644 --- a/chef-server/lib/views/search/index.html.haml +++ b/chefserverslice/app/views/search/index.html.haml @@ -4,6 +4,6 @@ %table %tr %td - %a{ :href => url(:search, { :id => index }) }= index + %a{ :href => slice_url(:search, { :id => index }) }= index %td = partial(:search_form, :index_name => index) diff --git a/chef-server/lib/views/search/show.html.haml b/chefserverslice/app/views/search/show.html.haml index e464b458b7..e464b458b7 100644 --- a/chef-server/lib/views/search/show.html.haml +++ b/chefserverslice/app/views/search/show.html.haml diff --git a/chef-server/lib/views/search_entries/index.html.haml b/chefserverslice/app/views/search_entries/index.html.haml index c890cccf55..c890cccf55 100644 --- a/chef-server/lib/views/search_entries/index.html.haml +++ b/chefserverslice/app/views/search_entries/index.html.haml diff --git a/chef-server/lib/views/search_entries/show.html.haml b/chefserverslice/app/views/search_entries/show.html.haml index c680b45447..c680b45447 100644 --- a/chef-server/lib/views/search_entries/show.html.haml +++ b/chefserverslice/app/views/search_entries/show.html.haml diff --git a/chefserverslice/bin/chef-server b/chefserverslice/bin/chef-server new file mode 100755 index 0000000000..a68cf1b202 --- /dev/null +++ b/chefserverslice/bin/chef-server @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby +# +# ./chef-server - Serving up piping hot infrastructure! +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Based on the 'merb' command, by Ezra + +require "rubygems" +require "merb-core" +require "chef" + +if ARGV[0] && ARGV[0] =~ /^[^-]/ + ARGV.push "-H" +end +unless %w[-a --adapter -i --irb-console -r --script-runner].any? { |o| ARGV.index(o) } + ARGV.push *%w[-a mongrel] +end +ARGV.push *[ "-I", File.join(File.dirname(__FILE__), "..", "lib", "init.rb") ] + +if index = ARGV.index("-C") + config = ARGV[index+1] + ARGV.delete("-C") + ARGV.delete(config) + Chef::Config.from_file(File.expand_path(config)) +else + Chef::Config.from_file( + File.join("/etc", "chef", "server.rb") + ) +end + +Merb.start diff --git a/chefserverslice/config/init.rb b/chefserverslice/config/init.rb new file mode 100644 index 0000000000..f52cf62472 --- /dev/null +++ b/chefserverslice/config/init.rb @@ -0,0 +1,44 @@ +# +# ==== Standalone Chefserver configuration +# +# This configuration/environment file is only loaded by bin/slice, which can be +# used during development of the slice. It has no effect on this slice being +# loaded in a host application. To run your slice in standalone mode, just +# run 'slice' from its directory. The 'slice' command is very similar to +# the 'merb' command, and takes all the same options, including -i to drop +# into an irb session for example. +# +# The usual Merb configuration directives and init.rb setup methods apply, +# including use_orm and before_app_loads/after_app_loads. +# +# If you need need different configurations for different environments you can +# even create the specific environment file in config/environments/ just like +# in a regular Merb application. +# +# In fact, a slice is no different from a normal # Merb application - it only +# differs by the fact that seamlessly integrates into a so called 'host' +# application, which in turn can override or finetune the slice implementation +# code and views. +# + +merb_gems_version = "1.0.9" +dependency "merb-haml", merb_gems_version +dependency "merb-assets", merb_gems_version +dependency "merb-helpers", merb_gems_version +dependency "chef", :immediate=>true + +use_template_engine :haml + +Merb::Config.use do |c| + # BUGBUG [cb] For some reason, this next line + # causes a merb slice to vomit around openid + # c[:fork_for_class_load] = false + c[:session_id_key] = '_chef_server_session_id' + c[:session_secret_key] = Chef::Config.manage_secret_key + c[:session_store] = 'cookie' + c[:exception_details] = true + c[:reload_classes] = false + c[:log_level] = Chef::Config[:log_level] + c[:log_stream] = Chef::Config[:log_location] +end + diff --git a/chefserverslice/config/router.rb b/chefserverslice/config/router.rb new file mode 100644 index 0000000000..cdd2e88b0d --- /dev/null +++ b/chefserverslice/config/router.rb @@ -0,0 +1,5 @@ +# This file is here so slice can be testing as a stand alone application. + +#Merb::Router.prepare do +# ... +#end
\ No newline at end of file diff --git a/chefserverslice/lib/chefserverslice.rb b/chefserverslice/lib/chefserverslice.rb new file mode 100644 index 0000000000..78b5fabcbf --- /dev/null +++ b/chefserverslice/lib/chefserverslice.rb @@ -0,0 +1,134 @@ +if defined?(Merb::Plugins) + $:.unshift File.dirname(__FILE__) + + dependency 'merb-slices', :immediate => true + dependency 'chef', :immediate=>true + + require 'syntax/convertors/html' + + Merb::Plugins.add_rakefiles "chefserverslice/merbtasks", "chefserverslice/slicetasks", "chefserverslice/spectasks" + + # Register the Slice for the current host application + Merb::Slices::register(__FILE__) + + Merb.disable :json + + # Slice configuration - set this in a before_app_loads callback. + # By default a Slice uses its own layout, so you can switch to + # the main application layout or no layout at all if needed. + # + # Configuration options: + # :layout - the layout to use; defaults to :chefserverslice + # :mirror - which path component types to use on copy operations; defaults to all + Merb::Slices::config[:chefserverslice][:layout] ||= :chefserverslice + + # All Slice code is expected to be namespaced inside a module + module Chefserverslice + # Slice metadata + self.description = "Chefserverslice.. serving up some piping hot infrastructure!" + self.version = Chef::VERSION + self.author = "Opscode" + + # Stub classes loaded hook - runs before LoadClasses BootLoader + # right after a slice's classes have been loaded internally. + def self.loaded + Chef::Queue.connect + + # create the couch design docs for nodes and openid registrations + Chef::Node.create_design_document + Chef::OpenIDRegistration.create_design_document + + Chef::Log.logger = Merb.logger + Chef::Log.info("Compiling routes... (totally normal to see 'Cannot find resource model')") + end + + # Initialization hook - runs before AfterAppLoads BootLoader + def self.init + end + + # Activation hook - runs after AfterAppLoads BootLoader + def self.activate + end + + # Deactivation hook - triggered by Merb::Slices.deactivate(Chefserver) + def self.deactivate + end + + # Setup routes inside the host application + # + # @param scope<Merb::Router::Behaviour> + # Routes will be added within this scope (namespace). In fact, any + # router behaviour is a valid namespace, so you can attach + # routes at any level of your router setup. + # + # @note prefix your named routes with :chefserverslice_ + # to avoid potential conflicts with global named routes. + def self.setup_router(scope) + + scope.resources :nodes + + scope.resources :searches, :path => "search", :controller => "search" do + scope.resources :entries, :controller => "search_entries" + end + + scope.match("/cookbooks/_attribute_files").to(:controller => "cookbooks", :action => "attribute_files") + scope.match("/cookbooks/_recipe_files").to(:controller => "cookbooks", :action => "recipe_files") + scope.match("/cookbooks/_definition_files").to(:controller => "cookbooks", :action => "definition_files") + scope.match("/cookbooks/_library_files").to(:controller => "cookbooks", :action => "library_files") + + scope.match("/cookbooks/:cookbook_id/templates").to(:controller => "cookbook_templates", :action => "index") + scope.match("/cookbooks/:cookbook_id/libraries").to(:controller => "cookbook_libraries", :action => "index") + scope.match("/cookbooks/:cookbook_id/definitions").to(:controller => "cookbook_definitions", :action => "index") + scope.match("/cookbooks/:cookbook_id/recipes").to(:controller => "cookbook_recipes", :action => "index") + scope.match("/cookbooks/:cookbook_id/attributes").to(:controller => "cookbook_attributes", :action => "index") + + scope.resources :cookbooks + scope.resources :registrations, :controller => "openid_register" + scope.resources :registrations, :controller => "openid_register", :member => { :validate => :post } + scope.resources :registrations, :controller => "openid_register", :member => { :admin => :post } + + scope.match("/openid/server").to(:controller => "openid_server", :action => "index").name(:openid_server) + scope.match("/openid/server/server/xrds"). + to(:controller => "openid_server", :action => 'idp_xrds').name(:openid_server_xrds) + scope.match("/openid/server/node/:id"). + to(:controller => "openid_server", :action => 'node_page').name(:openid_node) + scope.match('/openid/server/node/:id/xrds'). + to(:controller => 'openid_server', :action => 'node_xrds').name(:openid_node_xrds) + scope.match('/openid/server/decision').to(:controller => "openid_server", :action => "decision").name(:openid_server_decision) + scope.match('/login').to(:controller=>'openid_consumer', :action=>'index').name(:openid_consumer) + scope.match('/logout').to(:controller => 'openid_consumer', :action => 'logout').name(:openid_consumer_logout) + scope.match('/openid/consumer').to(:controller => 'openid_consumer', :action => 'index').name(:openid_consumer) + scope.match('/openid/consumer/start').to(:controller => 'openid_consumer', :action => 'start').name(:openid_consumer_start) + scope.match('/openid/consumer/complete').to(:controller => 'openid_consumer', :action => 'complete').name(:openid_consumer_complete) + scope.match('/openid/consumer/logout').to(:controller => 'openid_consumer', :action => 'logout').name(:openid_consumer_logout) + + # the slice is mounted at /chefserverslice - note that it comes before default_routes + scope.match('/').to(:controller => 'nodes', :action =>'index').name(:top) + # enable slice-level default routes by default + # scope.default_routes + end + + end + + # Setup the slice layout for Chefserverslice + # + # Use Chefserverslice.push_path and Chefserverslice.push_app_path + # to set paths to chefserver-level and app-level paths. Example: + # + # Chefserverslice.push_path(:application, Chefserverslice.root) + # Chefserverslice.push_app_path(:application, Merb.root / 'slices' / 'chefserverslice') + # ... + # + # Any component path that hasn't been set will default to Chefserverslice.root + # + # Or just call setup_default_structure! to setup a basic Merb MVC structure. + Chefserverslice.setup_default_structure! + + # freaky path fix for javascript and stylesheets + unless Chefserverslice.standalone? + Chefserverslice.public_components.each do |component| + Chefserverslice.push_app_path(component, Merb.dir_for(:public) / "#{component}s", nil) + end + end +end + diff --git a/chefserverslice/lib/chefserverslice/merbtasks.rb b/chefserverslice/lib/chefserverslice/merbtasks.rb new file mode 100644 index 0000000000..491812dfd2 --- /dev/null +++ b/chefserverslice/lib/chefserverslice/merbtasks.rb @@ -0,0 +1,103 @@ +namespace :slices do + namespace :chefserverslice do + + desc "Install Chefserver" + task :install => [:preflight, :setup_directories, :copy_assets, :migrate] + + desc "Test for any dependencies" + task :preflight do # see slicetasks.rb + end + + desc "Setup directories" + task :setup_directories do + puts "Creating directories for host application" + Chefserverslice.mirrored_components.each do |type| + if File.directory?(Chefserverslice.dir_for(type)) + if !File.directory?(dst_path = Chefserverslice.app_dir_for(type)) + relative_path = dst_path.relative_path_from(Merb.root) + puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}" + mkdir_p(dst_path) + end + end + end + end + + # desc "Copy stub files to host application" + # task :stubs do + # puts "Copying stubs for Chefserverslice - resolves any collisions" + # copied, preserved = Chefserverslice.mirror_stubs! + # puts "- no files to copy" if copied.empty? && preserved.empty? + # copied.each { |f| puts "- copied #{f}" } + # preserved.each { |f| puts "! preserved override as #{f}" } + # end + + # desc "Copy stub files and views to host application" + # task :patch => [ "stubs", "freeze:views" ] + + desc "Copy public assets to host application" + task :copy_assets do + puts "Copying assets for Chefserverslice - resolves any collisions" + copied, preserved = Chefserverslice.mirror_public! + puts "- no files to copy" if copied.empty? && preserved.empty? + copied.each { |f| puts "- copied #{f}" } + preserved.each { |f| puts "! preserved override as #{f}" } + end + + desc "Migrate the database" + task :migrate do # see slicetasks.rb + end + + desc "Freeze Chefserverslice into your app (only chefserverslice/app)" + task :freeze => [ "freeze:app" ] + + namespace :freeze do + + # desc "Freezes Chefserverslice by installing the gem into application/gems" + # task :gem do + # ENV["GEM"] ||= "chefserverslice" + # Rake::Task['slices:install_as_gem'].invoke + # end + + desc "Freezes Chefserverslice by copying all files from chefserverslice/app to your application" + task :app do + puts "Copying all chefserverslice/app files to your application - resolves any collisions" + copied, preserved = Chefserverslice.mirror_app! + puts "- no files to copy" if copied.empty? && preserved.empty? + copied.each { |f| puts "- copied #{f}" } + preserved.each { |f| puts "! preserved override as #{f}" } + end + + desc "Freeze all views into your application for easy modification" + task :views do + puts "Copying all view templates to your application - resolves any collisions" + copied, preserved = Chefserverslice.mirror_files_for :view + puts "- no files to copy" if copied.empty? && preserved.empty? + copied.each { |f| puts "- copied #{f}" } + preserved.each { |f| puts "! preserved override as #{f}" } + end + + desc "Freeze all models into your application for easy modification" + task :models do + puts "Copying all models to your application - resolves any collisions" + copied, preserved = Chefserverslice.mirror_files_for :model + puts "- no files to copy" if copied.empty? && preserved.empty? + copied.each { |f| puts "- copied #{f}" } + preserved.each { |f| puts "! preserved override as #{f}" } + end + + desc "Freezes Chefserverslice as a gem and copies over chefserver/app" + task :app_with_gem => [:gem, :app] + + desc "Freezes Chefserverslice by unpacking all files into your application" + task :unpack do + puts "Unpacking Chefserverslice files to your application - resolves any collisions" + copied, preserved = Chefserverslice.unpack_slice! + puts "- no files to copy" if copied.empty? && preserved.empty? + copied.each { |f| puts "- copied #{f}" } + preserved.each { |f| puts "! preserved override as #{f}" } + end + + end + + end +end diff --git a/chefserverslice/lib/chefserverslice/slicetasks.rb b/chefserverslice/lib/chefserverslice/slicetasks.rb new file mode 100644 index 0000000000..187323a299 --- /dev/null +++ b/chefserverslice/lib/chefserverslice/slicetasks.rb @@ -0,0 +1,20 @@ +namespace :slices do + namespace :chefserverslice do + + # add your own chefserver tasks here + + # # Uncomment the following lines and edit the pre defined tasks + # + # # implement this to test for structural/code dependencies + # # like certain directories or availability of other files + # desc "Test for any dependencies" + # task :preflight do + # end + # + # # implement this to perform any database related setup steps + # desc "Migrate the database" + # task :migrate do + # end + + end +end diff --git a/chefserverslice/lib/chefserverslice/spectasks.rb b/chefserverslice/lib/chefserverslice/spectasks.rb new file mode 100644 index 0000000000..08d59ae17f --- /dev/null +++ b/chefserverslice/lib/chefserverslice/spectasks.rb @@ -0,0 +1,53 @@ +namespace :slices do + namespace :chefserverslice do + + desc "Run slice specs within the host application context" + task :spec => [ "spec:explain", "spec:default" ] + + namespace :spec do + + slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')) + + task :explain do + puts "\nNote: By running Chefserverslice specs inside the application context any\n" + + "overrides could break existing specs. This isn't always a problem,\n" + + "especially in the case of views. Use these spec tasks to check how\n" + + "well your application conforms to the original slice implementation." + end + + Spec::Rake::SpecTask.new('default') do |t| + t.spec_opts = ["--format", "specdoc", "--colour"] + t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort + end + + desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel" + Spec::Rake::SpecTask.new('model') do |t| + t.spec_opts = ["--format", "specdoc", "--colour"] + if(ENV['MODEL']) + t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort + else + t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort + end + end + + desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest" + Spec::Rake::SpecTask.new('request') do |t| + t.spec_opts = ["--format", "specdoc", "--colour"] + if(ENV['REQUEST']) + t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort + else + t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort + end + end + + desc "Run all specs and output the result in html" + Spec::Rake::SpecTask.new('html') do |t| + t.spec_opts = ["--format", "html"] + t.libs = ['lib', 'server/lib' ] + t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort + end + + end + + end +end diff --git a/chef-server/lib/public/images/indicator.gif b/chefserverslice/public/images/indicator.gif Binary files differindex 085ccaecaf..085ccaecaf 100644 --- a/chef-server/lib/public/images/indicator.gif +++ b/chefserverslice/public/images/indicator.gif diff --git a/chefserverslice/public/images/merb.jpg b/chefserverslice/public/images/merb.jpg Binary files differnew file mode 100644 index 0000000000..a19dcf4048 --- /dev/null +++ b/chefserverslice/public/images/merb.jpg diff --git a/chef-server/lib/public/javascripts/chef.js b/chefserverslice/public/javascripts/chef.js index bd82f1aeb7..bd82f1aeb7 100644 --- a/chef-server/lib/public/javascripts/chef.js +++ b/chefserverslice/public/javascripts/chef.js diff --git a/chef-server/lib/public/javascripts/jquery-1.3.1.min.js b/chefserverslice/public/javascripts/jquery-1.3.1.min.js index c327fae812..c327fae812 100644 --- a/chef-server/lib/public/javascripts/jquery-1.3.1.min.js +++ b/chefserverslice/public/javascripts/jquery-1.3.1.min.js diff --git a/chef-server/lib/public/javascripts/jquery.jeditable.mini.js b/chefserverslice/public/javascripts/jquery.jeditable.mini.js index fdc46ff456..fdc46ff456 100644 --- a/chef-server/lib/public/javascripts/jquery.jeditable.mini.js +++ b/chefserverslice/public/javascripts/jquery.jeditable.mini.js diff --git a/chef-server/lib/public/javascripts/jquery.livequery.js b/chefserverslice/public/javascripts/jquery.livequery.js index dde8ad8e32..dde8ad8e32 100644 --- a/chef-server/lib/public/javascripts/jquery.livequery.js +++ b/chefserverslice/public/javascripts/jquery.livequery.js diff --git a/chefserverslice/public/javascripts/master.js b/chefserverslice/public/javascripts/master.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/chefserverslice/public/javascripts/master.js diff --git a/chef-server/lib/public/stylesheets/master.css b/chefserverslice/public/stylesheets/master-highlight.css index ccbda31932..ccbda31932 100644 --- a/chef-server/lib/public/stylesheets/master.css +++ b/chefserverslice/public/stylesheets/master-highlight.css diff --git a/chefserverslice/spec/chefserverslice_spec.rb b/chefserverslice/spec/chefserverslice_spec.rb new file mode 100644 index 0000000000..a2be6409df --- /dev/null +++ b/chefserverslice/spec/chefserverslice_spec.rb @@ -0,0 +1,20 @@ +require File.dirname(__FILE__) + '/spec_helper' + +describe "Chefserverslice (module)" do + +# Implement your Chefserverslice specs here + +# To spec Chefserverslice you need to hook it up to the router like this: + + before :all do + Merb::Router.prepare { add_slice(:Chefserverslice) } if standalone? + end + + after :all do + Merb::Router.reset! if standalone? + end + + + it "should have proper specs" + +end diff --git a/chefserverslice/spec/requests/cookbook_attributes_spec.rb b/chefserverslice/spec/requests/cookbook_attributes_spec.rb new file mode 100644 index 0000000000..58dd810ff5 --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_attributes_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_attributes" do + before(:each) do + @response = request("/cookbook_attributes") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbook_definitions_spec.rb b/chefserverslice/spec/requests/cookbook_definitions_spec.rb new file mode 100644 index 0000000000..c2a2e02ec5 --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_definitions_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_definitions" do + before(:each) do + @response = request("/cookbook_definitions") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbook_files_spec.rb b/chefserverslice/spec/requests/cookbook_files_spec.rb new file mode 100644 index 0000000000..2de03afd95 --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_files_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_files" do + before(:each) do + @response = request("/cookbook_files") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbook_libraries_spec.rb b/chefserverslice/spec/requests/cookbook_libraries_spec.rb new file mode 100644 index 0000000000..5fec545351 --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_libraries_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_libraries" do + before(:each) do + @response = request("/cookbook_libraries") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbook_recipes_spec.rb b/chefserverslice/spec/requests/cookbook_recipes_spec.rb new file mode 100644 index 0000000000..4a56542bea --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_recipes_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_recipes" do + before(:each) do + @response = request("/cookbook_recipes") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbook_templates_spec.rb b/chefserverslice/spec/requests/cookbook_templates_spec.rb new file mode 100644 index 0000000000..8495a0fba1 --- /dev/null +++ b/chefserverslice/spec/requests/cookbook_templates_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbook_templates" do + before(:each) do + @response = request("/cookbook_templates") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/cookbooks_spec.rb b/chefserverslice/spec/requests/cookbooks_spec.rb new file mode 100644 index 0000000000..596e351e26 --- /dev/null +++ b/chefserverslice/spec/requests/cookbooks_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/cookbooks" do + before(:each) do + @response = request("/cookbooks") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/exceptions_spec.rb b/chefserverslice/spec/requests/exceptions_spec.rb new file mode 100644 index 0000000000..cf020a681c --- /dev/null +++ b/chefserverslice/spec/requests/exceptions_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/exceptions" do + before(:each) do + @response = request("/exceptions") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/main_spec.rb b/chefserverslice/spec/requests/main_spec.rb new file mode 100644 index 0000000000..93f291a6c8 --- /dev/null +++ b/chefserverslice/spec/requests/main_spec.rb @@ -0,0 +1,28 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/chefserverslice/" do + + before(:all) do + mount_slice + end + + describe "GET /" do + + before(:each) do + @response = request("/chefserverslice/") + end + + it "should be successful" do + @response.status.should be_successful + end + + # This is just an example of what you can do + # You can also use the other webrat methods to click links, + # fill up forms etc... + it "should render the default slice layout" do + @response.should have_tag(:title, :content => "Chef Server") + end + + end + +end diff --git a/chefserverslice/spec/requests/nodes_spec.rb b/chefserverslice/spec/requests/nodes_spec.rb new file mode 100644 index 0000000000..b8eb4b9096 --- /dev/null +++ b/chefserverslice/spec/requests/nodes_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/nodes" do + before(:each) do + @response = request("/nodes") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/openid_consumer_spec.rb b/chefserverslice/spec/requests/openid_consumer_spec.rb new file mode 100644 index 0000000000..85d409cc7d --- /dev/null +++ b/chefserverslice/spec/requests/openid_consumer_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/openid_consumer" do + before(:each) do + @response = request("/openid_consumer") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/openid_register_spec.rb b/chefserverslice/spec/requests/openid_register_spec.rb new file mode 100644 index 0000000000..338943b4fe --- /dev/null +++ b/chefserverslice/spec/requests/openid_register_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/openid_register" do + before(:each) do + @response = request("/openid_register") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/openid_server_spec.rb b/chefserverslice/spec/requests/openid_server_spec.rb new file mode 100644 index 0000000000..40a3173cf8 --- /dev/null +++ b/chefserverslice/spec/requests/openid_server_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/openid_server" do + before(:each) do + @response = request("/openid_server") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/search_entries_spec.rb b/chefserverslice/spec/requests/search_entries_spec.rb new file mode 100644 index 0000000000..eca550559e --- /dev/null +++ b/chefserverslice/spec/requests/search_entries_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/search_entries" do + before(:each) do + @response = request("/search_entries") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/requests/search_spec.rb b/chefserverslice/spec/requests/search_spec.rb new file mode 100644 index 0000000000..cda43c17ea --- /dev/null +++ b/chefserverslice/spec/requests/search_spec.rb @@ -0,0 +1,7 @@ +require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb') + +describe "/search" do + before(:each) do + @response = request("/search") + end +end
\ No newline at end of file diff --git a/chefserverslice/spec/spec_helper.rb b/chefserverslice/spec/spec_helper.rb new file mode 100644 index 0000000000..f6fea74f1f --- /dev/null +++ b/chefserverslice/spec/spec_helper.rb @@ -0,0 +1,57 @@ +require 'rubygems' +require 'merb-core' +require 'merb-slices' + +# Add chefserverslice.rb to the search path +Merb::Plugins.config[:merb_slices][:auto_register] = false +Merb::Plugins.config[:merb_slices][:search_path] = File.join(File.dirname(__FILE__), '..', 'lib', 'chefserverslice.rb') + +# Require chefserver.rb explicitly so any dependencies are loaded +require Merb::Plugins.config[:merb_slices][:search_path] + +# Using Merb.root below makes sure that the correct root is set for +# - testing standalone, without being installed as a gem and no host application +# - testing from within the host application; its root will be used +Merb.start_environment( + :testing => true, + :adapter => 'runner', + :environment => ENV['MERB_ENV'] || 'test', + :session_store => 'memory' +) + +module Merb + module Test + module SliceHelper + + # The absolute path to the current slice + def current_slice_root + @current_slice_root ||= File.expand_path(File.join(File.dirname(__FILE__), '..')) + end + + # Whether the specs are being run from a host application or standalone + def standalone? + Merb.root == ::Chefserverslice.root + end + + end + end +end + +Spec::Runner.configure do |config| + config.include(Merb::Test::ViewHelper) + config.include(Merb::Test::RouteHelper) + config.include(Merb::Test::ControllerHelper) + config.include(Merb::Test::SliceHelper) +end + +# You can add your own helpers here +# +Merb::Test.add_helpers do + def mount_slice + Merb::Router.prepare { add_slice(:Chefserverslice, "chefserverslice") } if standalone? + end + + def dismount_slice + Merb::Router.reset! if standalone? + end +end diff --git a/chefserverslice/stubs/app/controllers/application.rb b/chefserverslice/stubs/app/controllers/application.rb new file mode 100644 index 0000000000..09a81cee52 --- /dev/null +++ b/chefserverslice/stubs/app/controllers/application.rb @@ -0,0 +1,2 @@ +class Chefserverslice::Application < Merb::Controller +end diff --git a/chefserverslice/stubs/app/controllers/main.rb b/chefserverslice/stubs/app/controllers/main.rb new file mode 100644 index 0000000000..265b349e75 --- /dev/null +++ b/chefserverslice/stubs/app/controllers/main.rb @@ -0,0 +1,2 @@ +class Chefserverslice::Main < Chefserverslice::Application +end diff --git a/example-repository/config/server.rb b/example-repository/config/server.rb index f5557daa4a..53f7c02bda 100644 --- a/example-repository/config/server.rb +++ b/example-repository/config/server.rb @@ -16,10 +16,8 @@ template_url "http://127.0.0.1:4000" remotefile_url "http://127.0.0.1:4000" search_url "http://127.0.0.1:4000" cookbook_path [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ] -node_path "/var/chef/nodes" openid_store_path "/var/chef/openid/store" openid_cstore_path "/var/chef/openid/cstore" -merb_log_path "/var/log/chef-server.log" search_index_path "/var/chef/search_index" Chef::Log::Formatter.show_time = false |