summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules6
-rw-r--r--README.rdoc22
-rw-r--r--Rakefile11
-rw-r--r--chef-server-slice/app/controllers/application.rb2
-rw-r--r--chef-server-slice/app/controllers/nodes.rb10
-rw-r--r--chef-server-slice/app/controllers/openid_consumer.rb6
-rw-r--r--chef-server-slice/app/controllers/openid_register.rb2
-rw-r--r--chef-server-slice/app/controllers/search_entries.rb4
-rw-r--r--chef-server-slice/app/helpers/cookbooks_helper.rb4
-rw-r--r--chef-server-slice/app/helpers/global_helpers.rb1
-rw-r--r--chef-server-slice/app/helpers/nodes_helper.rb31
-rw-r--r--chef-server-slice/app/helpers/search_helper.rb33
-rw-r--r--chef-server-slice/app/views/cookbooks/_attribute_file.html.haml2
-rw-r--r--chef-server-slice/app/views/cookbooks/_syntax_highlight.html.haml2
-rw-r--r--chef-server-slice/app/views/cookbooks/attribute_files.html.haml4
-rw-r--r--chef-server-slice/app/views/cookbooks/index.html.haml17
-rw-r--r--chef-server-slice/app/views/cookbooks/show.html.haml68
-rw-r--r--chef-server-slice/app/views/layout/chef_server_slice.html.haml64
-rw-r--r--chef-server-slice/app/views/nodes/_node.html.haml38
-rw-r--r--chef-server-slice/app/views/nodes/compile.html.haml5
-rw-r--r--chef-server-slice/app/views/nodes/index.html.haml24
-rw-r--r--chef-server-slice/app/views/nodes/show.html.haml8
-rw-r--r--chef-server-slice/app/views/openid_consumer/index.html.haml42
-rw-r--r--chef-server-slice/app/views/openid_register/index.html.haml37
-rw-r--r--chef-server-slice/app/views/openid_register/show.html.haml13
-rw-r--r--chef-server-slice/app/views/search/index.html.haml18
-rw-r--r--chef-server-slice/app/views/search/show.html.haml27
-rw-r--r--chef-server-slice/app/views/status/index.html.haml30
-rw-r--r--chef-server-slice/config/init.rb2
-rw-r--r--chef-server-slice/public/facebox/README.txt4
-rw-r--r--chef-server-slice/public/facebox/b.pngbin0 -> 84 bytes
-rw-r--r--chef-server-slice/public/facebox/bl.pngbin0 -> 124 bytes
-rw-r--r--chef-server-slice/public/facebox/br.pngbin0 -> 124 bytes
-rwxr-xr-xchef-server-slice/public/facebox/closelabel.gifbin0 -> 979 bytes
-rw-r--r--chef-server-slice/public/facebox/facebox.css95
-rw-r--r--chef-server-slice/public/facebox/facebox.js319
-rwxr-xr-xchef-server-slice/public/facebox/loading.gifbin0 -> 2767 bytes
-rw-r--r--chef-server-slice/public/facebox/tl.pngbin0 -> 132 bytes
-rw-r--r--chef-server-slice/public/facebox/tr.pngbin0 -> 125 bytes
-rw-r--r--chef-server-slice/public/images/avatar.pngbin0 -> 3214 bytes
-rw-r--r--chef-server-slice/public/images/toggle-collapse.gifbin0 -> 176 bytes
-rw-r--r--chef-server-slice/public/images/toggle-expand.gifbin0 -> 181 bytes
-rw-r--r--chef-server-slice/public/javascripts/chef.js70
-rw-r--r--chef-server-slice/public/javascripts/jquery.editinline.js108
-rw-r--r--chef-server-slice/public/javascripts/jquery.localscroll.js104
-rw-r--r--chef-server-slice/public/javascripts/jquery.scrollTo.js150
-rw-r--r--chef-server-slice/public/javascripts/master.js0
-rw-r--r--chef-server-slice/public/stylesheets/base.css336
-rw-r--r--chef-server-slice/public/stylesheets/chef.css50
-rw-r--r--chef-server-slice/public/stylesheets/master-highlight.css193
-rw-r--r--chef-server-slice/public/stylesheets/themes/bec-green/style.css290
-rw-r--r--chef-server-slice/public/stylesheets/themes/bec/style.css301
-rw-r--r--chef-server-slice/public/stylesheets/themes/blue/style.css280
-rw-r--r--chef-server-slice/public/stylesheets/themes/default/style.css267
-rw-r--r--chef-server-slice/public/stylesheets/themes/djime-cerulean/style.css298
-rw-r--r--chef-server-slice/public/stylesheets/themes/kathleene/style.css272
-rw-r--r--chef-server-slice/public/stylesheets/themes/orange/style.css263
-rw-r--r--chef-server-slice/public/stylesheets/themes/reidb-greenish/style.css301
-rwxr-xr-xchef-server/bin/chef-indexer4
-rw-r--r--chef-server/config/init.rb1
-rw-r--r--chef-server/contrib/el/chef-indexer.config10
-rw-r--r--chef-server/contrib/el/chef-indexer.init76
-rw-r--r--chef-server/contrib/el/chef-server.config22
-rw-r--r--chef-server/contrib/el/chef-server.init77
-rw-r--r--chef-server/lib/chef/search.rb2
-rwxr-xr-xchef/bin/chef-client2
-rwxr-xr-xchef/bin/chef-solo2
-rw-r--r--chef/contrib/el/chef-client.config16
-rw-r--r--chef/contrib/el/chef-client.init74
-rw-r--r--chef/lib/chef/config.rb4
-rw-r--r--chef/lib/chef/couchdb.rb18
-rw-r--r--chef/lib/chef/daemon.rb4
-rw-r--r--chef/lib/chef/mixin/command.rb98
-rw-r--r--chef/lib/chef/provider/cron.rb8
-rw-r--r--chef/lib/chef/provider/package/yum-dump-json.py67
-rw-r--r--chef/lib/chef/provider/package/yum.rb127
-rw-r--r--chef/spec/unit/couchdb_spec.rb40
-rw-r--r--chef/spec/unit/mixin/command_spec.rb85
-rw-r--r--chef/spec/unit/mixin/template_spec.rb3
-rw-r--r--chef/spec/unit/provider/cron_spec.rb31
-rw-r--r--chef/spec/unit/provider/package/yum_spec.rb116
-rw-r--r--features/data/cookbooks/execute_commands/recipes/4k.rb25
-rw-r--r--features/data/cookbooks/execute_commands/recipes/debug.rb22
-rw-r--r--features/data/cookbooks/execute_commands/recipes/default.rb22
-rw-r--r--features/execute_commands.feature30
-rw-r--r--features/steps/run_client.rb16
86 files changed, 4539 insertions, 697 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..e0f7431c5e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "chef-server-slice/public/javascripts/jsoneditor"]
+ path = chef-server-slice/public/javascripts/jsoneditor
+ url = git://github.com/iterationlabs/jsoneditor.git
+[submodule "chef-server-slice/public/web-app-theme"]
+ path = chef-server-slice/public/web-app-theme
+ url = git://github.com/pilu/web-app-theme.git
diff --git a/README.rdoc b/README.rdoc
index a7a4ea8dea..3cd1d8ffc2 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -4,9 +4,7 @@
== DESCRIPTION:
-Chef is a systems management framework masquerading as a configuration management tool.
-
-I'm in ur netwerk, cookin up yer servers. :)
+Chef is a systems integration framework masquerading as a configuration management tool.
== REQUIREMENTS:
@@ -19,7 +17,7 @@ chef:
* stomp
* ohai
-chef-server and the chefserverslice (merb slice), same requires as chef above, plus:
+chef-server and the chef-server-slice (merb slice), same requires as chef above, plus:
* stompserver
* ferret
@@ -30,15 +28,6 @@ chef-server and the chefserverslice (merb slice), same requires as chef above, p
* 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)
@@ -46,7 +35,7 @@ External Servers:
== INSTALL:
-Install all of the above. To fire up a develpment environment, do the following:
+Install all of the above. To fire up a development environment, do the following:
* Start CouchDB with 'couchdb'
* Start stompserver with 'stompserver'
@@ -57,10 +46,13 @@ Install all of the above. To fire up a develpment environment, do the following
* Start chef-server:
chef-server -N -c 2
+ OR
+ (in chef-server-slice)
+ slice -N -c 2
* Test run chef to begin node registration:
- sudo ./bin/chef-client
+ chef-client
* Validate the node registration:
diff --git a/Rakefile b/Rakefile
index 01669283c5..781675e2b7 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,15 +2,22 @@ gems = %w[chef chef-server-slice chef-server]
require 'rubygems'
require 'cucumber/rake/task'
+namespace :git do
+ desc "Initialise and update the Git submodules"
+ task :submodule_update do
+ sh "git submodule update --init"
+ end
+end
+
desc "Build the chef gems"
-task :gem do
+task :gem => "git:submodule_update" do
gems.each do |dir|
Dir.chdir(dir) { sh "rake package" }
end
end
desc "Install the chef gems"
-task :install do
+task :install => "git:submodule_update" do
gems.each do |dir|
Dir.chdir(dir) { sh "rake install" }
end
diff --git a/chef-server-slice/app/controllers/application.rb b/chef-server-slice/app/controllers/application.rb
index a87ed92a5d..de03021917 100644
--- a/chef-server-slice/app/controllers/application.rb
+++ b/chef-server-slice/app/controllers/application.rb
@@ -107,7 +107,7 @@ class ChefServerSlice::Application < Merb::Controller
case content_type
when :html
store_location
- redirect url(:openid_consumer)
+ redirect slice_url(:openid_consumer), :message => { :error => "You don't have access to that, please login."}
else
raise Unauthorized, "You must authenticate first!"
end
diff --git a/chef-server-slice/app/controllers/nodes.rb b/chef-server-slice/app/controllers/nodes.rb
index 52ee441121..03b343767a 100644
--- a/chef-server-slice/app/controllers/nodes.rb
+++ b/chef-server-slice/app/controllers/nodes.rb
@@ -38,8 +38,8 @@ class ChefServerSlice::Nodes < ChefServerSlice::Application
rescue Net::HTTPServerException => e
raise NotFound, "Cannot load node #{params[:id]}"
end
- if params[:ajax] == "true"
- render JSON.pretty_generate(@node), :layout=>false
+ if request.xhr?
+ render JSON.pretty_generate(@node), :layout => false
else
display @node
end
@@ -57,7 +57,7 @@ class ChefServerSlice::Nodes < ChefServerSlice::Application
end
def update
- if params[:ajax]
+ if request.xhr?
@node = JSON.parse(params[:value])
else
@node = params.has_key?("inflated_object") ? params["inflated_object"] : nil
@@ -66,8 +66,8 @@ class ChefServerSlice::Nodes < ChefServerSlice::Application
if @node
@status = 202
@node.save
- if params[:ajax]
- partial("nodes/node", :node => @node)
+ if request.xhr?
+ partial :node, :node => @node
else
display @node
end
diff --git a/chef-server-slice/app/controllers/openid_consumer.rb b/chef-server-slice/app/controllers/openid_consumer.rb
index 98abe8e74a..f2f232eae5 100644
--- a/chef-server-slice/app/controllers/openid_consumer.rb
+++ b/chef-server-slice/app/controllers/openid_consumer.rb
@@ -26,7 +26,11 @@ class ChefServerSlice::OpenidConsumer < ChefServerSlice::Application
provides :html, :json
def index
- render
+ if request.xhr?
+ render :layout => false
+ else
+ render
+ end
end
def start
diff --git a/chef-server-slice/app/controllers/openid_register.rb b/chef-server-slice/app/controllers/openid_register.rb
index bc4ff618f8..d337790c1f 100644
--- a/chef-server-slice/app/controllers/openid_register.rb
+++ b/chef-server-slice/app/controllers/openid_register.rb
@@ -25,6 +25,8 @@ class ChefServerSlice::OpenidRegister < ChefServerSlice::Application
provides :html, :json
before :fix_up_node_id
+ before :login_required, :only => [ :update, :destroy, :validate, :admin ]
+ before :authorized_node, :only => [ :update, :destroy, :validate, :admin ]
def index
@headers['X-XRDS-Location'] = Chef::Config[:openid_url] + "/openid/server/server/xrds"
diff --git a/chef-server-slice/app/controllers/search_entries.rb b/chef-server-slice/app/controllers/search_entries.rb
index 371a024dc3..dfbc724ad2 100644
--- a/chef-server-slice/app/controllers/search_entries.rb
+++ b/chef-server-slice/app/controllers/search_entries.rb
@@ -26,7 +26,7 @@ class ChefServerSlice::SearchEntries < ChefServerSlice::Application
def index
@s = Chef::Search.new
- @entries = @s.search(params[:search_id], "?*")
+ @entries = @s.search(params[:search_id])
display @entries
end
@@ -57,7 +57,7 @@ class ChefServerSlice::SearchEntries < ChefServerSlice::Application
def destroy
@s = Chef::Search.new
- @entries = @s.search(params[:id], "?*")
+ @entries = @s.search(params[:id])
@entries.each do |entry|
Chef::Queue.send_msg(:queue, :remove, entry)
end
diff --git a/chef-server-slice/app/helpers/cookbooks_helper.rb b/chef-server-slice/app/helpers/cookbooks_helper.rb
index 49d5010824..76efc7c765 100644
--- a/chef-server-slice/app/helpers/cookbooks_helper.rb
+++ b/chef-server-slice/app/helpers/cookbooks_helper.rb
@@ -22,9 +22,9 @@ module Merb
def syntax_highlight(code)
converter = Syntax::Convertors::HTML.for_syntax "ruby"
if File.exists?(code)
- converter.convert(File.read(code))
+ converter.convert(File.read(code), false)
else
- converter.convert(code)
+ converter.convert(code, false)
end
end
end
diff --git a/chef-server-slice/app/helpers/global_helpers.rb b/chef-server-slice/app/helpers/global_helpers.rb
index 77e51fca8a..645343a6a7 100644
--- a/chef-server-slice/app/helpers/global_helpers.rb
+++ b/chef-server-slice/app/helpers/global_helpers.rb
@@ -33,6 +33,7 @@ module Merb
def node_escape(node)
node.gsub(/\./, '_')
end
+
end
end
end
diff --git a/chef-server-slice/app/helpers/nodes_helper.rb b/chef-server-slice/app/helpers/nodes_helper.rb
index 258aa944bc..3409f44f09 100644
--- a/chef-server-slice/app/helpers/nodes_helper.rb
+++ b/chef-server-slice/app/helpers/nodes_helper.rb
@@ -1,5 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Copyright:: Copyright (c) 2008 Opscode, Inc.
# License:: Apache License, Version 2.0
#
@@ -22,7 +23,7 @@ module Merb
def recipe_list(node)
response = ""
node.recipes.each do |recipe|
- response << "<li>#{recipe}</li>"
+ response << "<li>#{recipe}</li>\n"
end
response
end
@@ -30,11 +31,35 @@ module Merb
def attribute_list(node)
response = ""
node.each_attribute do |k,v|
- response << "<li><b>#{k}</b>: #{v}</li>"
+ response << "<li><b>#{k}</b>: #{v}</li>\n"
end
response
end
+
+ # Recursively build a tree of lists.
+ def build_tree(node)
+ list = "<dl>"
+ list << "\n<!-- Beginning of Node Tree -->"
+ walk = lambda do |key,value|
+ case value
+ when Hash, Array
+ list << "\n<!-- Beginning of Enumerable obj -->"
+ list << "\n<dt>#{key}</dt>"
+ list << "<dd>"
+ list << "\t<dl>\n"
+ value.each(&walk)
+ list << "\t</dl>\n"
+ list << "</dd>"
+ list << "\n<!-- End of Enumerable obj -->"
+
+ else
+ list << "\n<dt>#{key}</dt>"
+ list << "<dd>#{value}</dd>"
+ end
+ end
+ node.attribute.sort{ |a,b| a[0] <=> b[0] }.each(&walk)
+ list << "</dl>"
+ end
end
-
end
end
diff --git a/chef-server-slice/app/helpers/search_helper.rb b/chef-server-slice/app/helpers/search_helper.rb
index a3e5846918..d7de6e6d70 100644
--- a/chef-server-slice/app/helpers/search_helper.rb
+++ b/chef-server-slice/app/helpers/search_helper.rb
@@ -1,7 +1,38 @@
module Merb
module ChefServerSlice
module SearchHelper
-
+ def output_path(attributes)
+ res = Hash.new
+ attributes.each do |path|
+ parts = path.split("/")
+ unless parts[0].nil?
+ parts.shift if parts[0].length == 0
+ end
+ res[path] = ohai_walk(parts)
+ end
+ res
+ end
+
+ def ohai_walk(path)
+ unless path[0]
+ @@ohai.to_json
+ else
+ ohai_walk_r(@@ohai, path)
+ end
+ end
+
+ def ohai_walk_r(ohai, path)
+ hop = (ohai.is_a?(Array) ? path.shift.to_i : path.shift)
+ if ohai[hop]
+ if path[0]
+ ohai_walk_r(ohai[hop], path)
+ else
+ ohai[hop].to_json
+ end
+ else
+ nil
+ end
+ end
end
end
end # Merb
diff --git a/chef-server-slice/app/views/cookbooks/_attribute_file.html.haml b/chef-server-slice/app/views/cookbooks/_attribute_file.html.haml
deleted file mode 100644
index eb91921bf0..0000000000
--- a/chef-server-slice/app/views/cookbooks/_attribute_file.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%h3= name
-%pre= contents \ No newline at end of file
diff --git a/chef-server-slice/app/views/cookbooks/_syntax_highlight.html.haml b/chef-server-slice/app/views/cookbooks/_syntax_highlight.html.haml
deleted file mode 100644
index 1f769d05f1..0000000000
--- a/chef-server-slice/app/views/cookbooks/_syntax_highlight.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%h3= name
-%div.ruby= syntax_highlight(contents) \ No newline at end of file
diff --git a/chef-server-slice/app/views/cookbooks/attribute_files.html.haml b/chef-server-slice/app/views/cookbooks/attribute_files.html.haml
deleted file mode 100644
index c0751560f4..0000000000
--- a/chef-server-slice/app/views/cookbooks/attribute_files.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-- throw_content(:header, "All Attributes Files")
-- @attribute_files.each do |af_hash|
- %h2= "#{af_hash[:cookbook]}"
- = partial(:attribute_file, :name => af_hash[:name], :contents => af_hash[:contents]) \ No newline at end of file
diff --git a/chef-server-slice/app/views/cookbooks/index.html.haml b/chef-server-slice/app/views/cookbooks/index.html.haml
index 51874ac2fd..d8e9eabda6 100644
--- a/chef-server-slice/app/views/cookbooks/index.html.haml
+++ b/chef-server-slice/app/views/cookbooks/index.html.haml
@@ -1,7 +1,10 @@
-- throw_content(:header, "Cookbooks")
-- @cl.each do |cookbook|
- .index
- %table
- %tr
- %td
- %a{ :href => slice_url(:cookbook, { :id => cookbook.name }) }= cookbook.name
+.block#block-tables
+ .content
+ %h2.title Cookbooks
+ .inner
+ %table.table
+ %tr
+ %th.first Cookbook Name
+ - @cl.sort{ |a,b| a.name.to_s <=> b.name.to_s }.each_with_index do |cookbook, index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td= link_to cookbook.name, slice_url(:cookbook, { :id => cookbook.name }) \ No newline at end of file
diff --git a/chef-server-slice/app/views/cookbooks/show.html.haml b/chef-server-slice/app/views/cookbooks/show.html.haml
index c9249609c1..03b39d55ab 100644
--- a/chef-server-slice/app/views/cookbooks/show.html.haml
+++ b/chef-server-slice/app/views/cookbooks/show.html.haml
@@ -1,28 +1,40 @@
-- throw_content(:header, "Cookbook #{h @cookbook.name}")
-.cookbook
-- if @cookbook.lib_files.length > 0
- %h2 Library Files
- - @cookbook.lib_files.each do |f|
- = partial(:syntax_highlight, :name => File.basename(f), :contents => f)
-
-- if @cookbook.attribute_files.length > 0
- %h2 Attribute Files
- - @cookbook.attribute_files.each do |f|
- = partial(:syntax_highlight, :name => File.basename(f), :contents => f)
-
-- if @cookbook.definition_files.length > 0
- %h2 Definition Files
- - @cookbook.definition_files.each do |f|
- = partial(:syntax_highlight, :name => File.basename(f), :contents => f)
-
-- if @cookbook.recipe_files.length > 0
- %h2 Recipe Files
- - @cookbook.recipe_files.each do |f|
- = partial(:syntax_highlight, :name => File.basename(f), :contents => f)
-
-- if @cookbook.template_files.length > 0
- %h2 Template Files
- - @cookbook.template_files.each do |f|
- %h3= File.basename(f)
- %div= syntax_highlight(f)
-
+.block#block-text
+ .content
+ %h2.title= "Cookbook #{h @cookbook.name}"
+ .inner
+ .accordion
+ - unless @cookbook.lib_files.empty?
+ %h2.head= link_to "Library Files", "#"
+ .files
+ - @cookbook.lib_files.each do |f|
+ .code
+ %h4.head= link_to File.basename(f), "#"
+ %pre.ruby= syntax_highlight(f)
+ - unless @cookbook.attribute_files.empty?
+ %h2.head= link_to "Attribute Files", "#"
+ .files
+ - @cookbook.attribute_files.each do |f|
+ .code
+ %h4.head= link_to File.basename(f), "#"
+ %pre.ruby= syntax_highlight(f)
+ - unless @cookbook.definition_files.empty?
+ %h2.head= link_to "Definition Files", "#"
+ .files
+ - @cookbook.definition_files.each do |f|
+ .code
+ %h4.head= link_to File.basename(f), "#"
+ %pre.ruby= syntax_highlight(f)
+ - unless @cookbook.recipe_files.empty?
+ %h2.head= link_to "Recipe Files", "#"
+ .files
+ - @cookbook.recipe_files.each do |f|
+ .code
+ %h4.head= link_to File.basename(f), "#"
+ %pre.ruby= syntax_highlight(f)
+ - unless @cookbook.template_files.empty?
+ %h2.head= link_to "Template Files", "#"
+ .files
+ - @cookbook.template_files.each do |f|
+ .code
+ %h4.head= link_to File.basename(f), "#"
+ %pre.ruby= syntax_highlight(f) \ No newline at end of file
diff --git a/chef-server-slice/app/views/layout/chef_server_slice.html.haml b/chef-server-slice/app/views/layout/chef_server_slice.html.haml
index e4e353926b..d06372abfd 100644
--- a/chef-server-slice/app/views/layout/chef_server_slice.html.haml
+++ b/chef-server-slice/app/views/layout/chef_server_slice.html.haml
@@ -2,31 +2,45 @@
!!!
%html
%head
+ %meta{ "http-equiv" => "content-type", :content => "text/html; charset=utf-8" }
%title Chef Server
- %meta{"http-equiv" => "content-type", :content => "text/html; 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"
+ = css_include_tag "base", "themes/djime-cerulean/style", "chef", "/facebox/facebox.css"
+ = js_include_tag "jquery-1.3.1.min", "jquery.jeditable.mini", "jquery.livequery", "jquery.localscroll", "jquery.scrollTo"
+ = js_include_tag "/facebox/facebox.js"
= js_include_tag "chef"
+
%body
- .header
- %a{:href => slice_url(:searches) } Search
- |
- %a{:href => slice_url(:nodes) } Nodes
- |
- %a{:href => slice_url(:status) } Status
- |
- %a{:href => slice_url(:cookbooks) } Cookbooks
- |
- %a{:href => slice_url(:registrations)} Registrations
- - if session[:openid]
- |
- %a{:href => slice_url(:openid_consumer_logout)}= "Logout #{h session[:openid]}"
- = "(#{session[:level].to_s})"
- - else
- |
- %a{:href => slice_url(:openid_consumer)} Login
- %h3= catch_content :header
- - unless message.empty?
- = message[:error]
- = message[:notice]
- = catch_content :for_layout
+ #container
+ #header
+ %h1= link_to "Chef Server", slice_url(:top)
+ #user-navigation
+ %ul
+ - if session[:openid]
+ %li= link_to "Logout #{h session[:openid]} (#{session[:level].to_s})", slice_url(:openid_consumer_logout), :method => "get", :confirm => "Are you sure you want to logout?"
+ - else
+ %li= link_to "Login", slice_url(:openid_consumer), :rel => "facebox"
+ .clear
+ #main-navigation
+ %ul
+ %li= link_to "Search", slice_url(:searches)
+ %li= link_to "Status", slice_url(:status)
+ %li= link_to "Nodes", slice_url(:nodes)
+ %li= link_to "Cookbooks", slice_url(:cookbooks)
+ %li= link_to "Registrations", slice_url(:registrations)
+ .clear
+ #wrapper
+ #main
+ - unless message.empty?
+ .block#block-messages
+ .content
+ %h2.title Error Messages
+ .inner
+ .flash
+ - message.each do |type, msg|
+ %div{:class => "message #{type}"}
+ %p= msg
+ = catch_content :for_layout
+ #footer
+ .block
+ %p Copyright &copy; 2009 Opscode
+ .clear
diff --git a/chef-server-slice/app/views/nodes/_node.html.haml b/chef-server-slice/app/views/nodes/_node.html.haml
index c5c73fbf66..a63b37e735 100644
--- a/chef-server-slice/app/views/nodes/_node.html.haml
+++ b/chef-server-slice/app/views/nodes/_node.html.haml
@@ -1,16 +1,22 @@
-- if session[:level] == :admin
- .edit_area#node{:to_update => node.name}
- %h2 Recipes
- %ol
- = recipe_list(node)
- %h2 Attributes
- %ol
- = attribute_list(node)
-- else
- #node
- %h2 Recipes
- %ol
- = recipe_list(node)
- %h2 Attributes
- %ol
- = attribute_list(node) \ No newline at end of file
+%div{ :id => 'node', :class => "#{session[:level] == :admin ? 'edit_area' : 'non_edit_area'}", :to_update => @node.name}
+ %table.table
+ %tr
+ %th.first Recipes
+ %th.last &nbsp
+ - if @node.recipes.empty?
+ %tr
+ %td{:colspan => 2} This node has no recipes applied, double click to add one.
+ - else
+ - @node.recipes.each_with_index do |recipe, index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td{:colspan => 2}= recipe
+ %table.table
+ %tr
+ %th.first Attributes
+ %th.last &nbsp
+ - if @node.attribute.empty?
+ %tr
+ %td{:colspan => 2} This node has no attributes, double click to add one.
+ - else
+ %tr
+ %td{:colspan => 2}= build_tree(@node) \ No newline at end of file
diff --git a/chef-server-slice/app/views/nodes/compile.html.haml b/chef-server-slice/app/views/nodes/compile.html.haml
deleted file mode 100644
index 5656447ea8..0000000000
--- a/chef-server-slice/app/views/nodes/compile.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-= partial(:node, :node => @output[:node])
-.resource_collection
- %h1 Resource Collection
- - @output[:collection].each do |resource|
- = partial(:resource, :resource => resource)
diff --git a/chef-server-slice/app/views/nodes/index.html.haml b/chef-server-slice/app/views/nodes/index.html.haml
index dce11d0c71..3d260cb6f4 100644
--- a/chef-server-slice/app/views/nodes/index.html.haml
+++ b/chef-server-slice/app/views/nodes/index.html.haml
@@ -1,7 +1,17 @@
-- 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"
+.block#block-tables
+ .content
+ %h2.title Node List
+ .inner
+ %table.table
+ %tr
+ %th.first{:colspan => 2} Node Name
+ %th Control
+ %th.last &nbsp
+ - if @node_list.empty?
+ %tr
+ %td{:colspan => 4}= "You appear to have no nodes - try connecting one, or validating an existing #{link_to('registration', slice_url(:registrations))}"
+ - else
+ - @node_list.each_with_index do |node, index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td{:colspan => 2}= link_to node, slice_url(:node, { :id => node.gsub(/\./, "_" ) } )
+ %td= link_to "Delete", slice_url(:node, { :id => node.gsub(/\./, "_") }), :method => "delete", :confirm => "Are you sure? There is no undo." \ No newline at end of file
diff --git a/chef-server-slice/app/views/nodes/show.html.haml b/chef-server-slice/app/views/nodes/show.html.haml
index d355974c49..a028f989b7 100644
--- a/chef-server-slice/app/views/nodes/show.html.haml
+++ b/chef-server-slice/app/views/nodes/show.html.haml
@@ -1,3 +1,5 @@
-- throw_content(:header, "Node #{h @node.name}")
-.node
- = partial(:node, :node => @node) \ No newline at end of file
+.block#block-tables
+ .content
+ %h2.title= "Node #{h @node.name}"
+ .inner
+ = partial :node, :node => @node \ No newline at end of file
diff --git a/chef-server-slice/app/views/openid_consumer/index.html.haml b/chef-server-slice/app/views/openid_consumer/index.html.haml
index 6ff300b8cc..7580a5bf07 100644
--- a/chef-server-slice/app/views/openid_consumer/index.html.haml
+++ b/chef-server-slice/app/views/openid_consumer/index.html.haml
@@ -1,25 +1,17 @@
-- throw_content(:header, "OpenID Relaying Party")
--if session[:alert]
- .alert= h session[:alert]
--if session[:error]
- .error= h session[:error]
--if session[:success]
- .success= h session[:success]
-#verify-form
- %form{ :method => "get", "accept-charset" => "UTF-8", :action => slice_url(:openid_consumer_login) }
- Identifier:
- %input.openid{ :type => "text", :name => "openid_identifier" }/
- %input{ :type => "submit", :value => "Verify"}/
- %br
- %input#immediate{ :name => "immediate", :type => "checkbox" }/
- %label{:for => "immediate"} Use immediate mode
- %br
- %input#immediate{ :name => "use_sreg", :type => "checkbox" }/
- %label{:for => "use_sreg"} Request registration data
- %br
- %input#immediate{ :name => "use_pape", :type => "checkbox" }/
- %label{:for => "use_pape"} Request phishing-resistent auth policy
- %br
- %input#immediate{ :name => "force_post", :type => "checkbox" }/
- %label{:for => "force_post"} Force the transaction to POST
- %br
+.block#block-forms
+ .content
+ %h2.title Login
+ .inner
+ %form.form{ :method => "get", "accept-charset" => "UTF-8", :action => slice_url(:openid_consumer_start) }
+ .group
+ .text_field= text_field :openid_identifier
+ .group
+ .check_box= check_box :name => "immediate", :label => "Use immediate mode", :value => "0"
+ .group
+ .check_box= check_box :name => "use_sreg", :label => "Request registration data", :value => "0"
+ .group
+ .check_box= check_box :name => "use_pape", :label => "Request phishing-resistent auth policy", :value => "0"
+ .group
+ .check_box= check_box :name => "force_post", :label => "Force the transaction to POST", :value => "0"
+ .group.navform
+ .button= submit "Login &rarr;"
diff --git a/chef-server-slice/app/views/openid_register/index.html.haml b/chef-server-slice/app/views/openid_register/index.html.haml
index cb52e4c196..4a54ed7512 100644
--- a/chef-server-slice/app/views/openid_register/index.html.haml
+++ b/chef-server-slice/app/views/openid_register/index.html.haml
@@ -1,18 +1,19 @@
-- throw_content(:header, "Registered OpenID Nodes List")
-%table
-- @registered_nodes.each do |node|
- %tr
- %td
- %a{ :href => slice_url(:registration, { :id => node.name }) }
- = h node.name
- %td
- - if session[:level] == :admin
- %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 => 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 => slice_url(:registration, { :id => node.name })}
- %input{ :type => "hidden", :name => "_method", :value => "delete" }
- %input{ :type => "submit", :name => "Delete", :value => "Delete" }
+.block#block-tables
+ .content
+ %h2.title Registrations
+ .inner
+ %table.table
+ %tr
+ %th.first Registration Name
+ %th &nbsp
+ %th Control
+ %th.last &nbsp;
+ - @registered_nodes.sort{ |a,b| a.name.to_s <=> b.name.to_s }.each_with_index do |node, index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td{:colspan => 2}= link_to(node.name, slice_url(:registration, { :id => node.name }))
+ %td
+ = link_to((node.validated ? "Invalidate" : "Validate"), slice_url(:validate_registration, { :id => node.name}), :method => "post", :confirm => "Are you sure you want to toggle this registrations validation?")
+ |
+ = link_to((node.admin ? "Remove Admin Rights" : "Make Admin"), slice_url(:admin_registration, { :id => node.name}), :method => "post", :confirm => "Are you sure you want to toggle this registrations administrator privileges?")
+ |
+ = link_to("Delete", slice_url(:registration, { :id => node.name} ), :method => "delete", :confirm => "Are you sure you want to delete this registration") \ No newline at end of file
diff --git a/chef-server-slice/app/views/openid_register/show.html.haml b/chef-server-slice/app/views/openid_register/show.html.haml
index 58f55a3579..63d84d8364 100644
--- a/chef-server-slice/app/views/openid_register/show.html.haml
+++ b/chef-server-slice/app/views/openid_register/show.html.haml
@@ -1,6 +1,7 @@
-- throw_content(:header, "Registered OpenID Node #{@registered_node.name}")
-%ol
- %li
- %a{ :href => slice_url(:openid_node , { :id => @registered_node.name.gsub(/\./, "_") }) } OpenID URL
- %li= "Validated: #{@registered_node.validated}"
- %li= "Admin: #{@registered_node.admin}"
+.block#block-text
+ .content
+ %h2.title= "Registration #{@registered_node.name}"
+ .inner
+ = link_to "OpenID URL", slice_url(:openid_node, { :id => @registered_node.name.gsub(/\./, "_")})
+ %br= "Validated: #{@registered_node.validated}"
+ %br= "Admin: #{@registered_node.admin}" \ No newline at end of file
diff --git a/chef-server-slice/app/views/search/index.html.haml b/chef-server-slice/app/views/search/index.html.haml
index 75e4572481..8f3b199592 100644
--- a/chef-server-slice/app/views/search/index.html.haml
+++ b/chef-server-slice/app/views/search/index.html.haml
@@ -1,9 +1,9 @@
-- throw_content(:header, "Search Indexes")
-- @search_indexes.each do |index|
- .index
- %table
- %tr
- %td
- %a{ :href => slice_url(:search, { :id => index }) }= index
- %td
- = partial(:search_form, :index_name => index)
+.block#block-forms
+ .content
+ %h2.title Search Indexes
+ .inner
+ - @search_indexes.sort{ |a,b| a.to_s <=> b.to_s}.each do |index|
+ %form.form{ :method => "get", "accept-charset" => "UTF-8", :action => slice_url(:search, { :id => index})}
+ .group.label= text_field :name => "q", :label => "Query"
+ .group.label= text_field :name => "a", :label => "Attributes"
+ .group= submit "Search #{index}" \ No newline at end of file
diff --git a/chef-server-slice/app/views/search/show.html.haml b/chef-server-slice/app/views/search/show.html.haml
index e464b458b7..f20f9fa2ea 100644
--- a/chef-server-slice/app/views/search/show.html.haml
+++ b/chef-server-slice/app/views/search/show.html.haml
@@ -1,13 +1,14 @@
-- throw_content(:header, "Search Results")
-.search
- = partial(:search_form, :index_name => params[:id])
-.query
- %h2= "Search Query was #{params[:q] ? params[:q] : '*'}"
-- @results.each do |result|
- .search_result
- %h3= "#{h result[:index_name]} (#{h result[:id]})"
- %table
- - result.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |k, v|
- %tr.attr_group
- %td.attr_name= k
- %td.attr_value= v.kind_of?(Array) ? v.join(",") : v
+.block#block-tables
+ .content
+ %h2.title Search Results
+ .inner
+ - @results.each do |result|
+ %table.table.search{:width => '400px'}
+ %tr
+ %th.first Attribute
+ %th.last Value
+ - @results.each do |result|
+ - result.sort { |a,b| a[0].to_s <=> b[0].to_s}.each_with_index do |h,index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td= "#{h[0]} (#{h[1].class})"
+ %td= h[1] \ No newline at end of file
diff --git a/chef-server-slice/app/views/status/index.html.haml b/chef-server-slice/app/views/status/index.html.haml
index eff064752c..dd1fce34cc 100644
--- a/chef-server-slice/app/views/status/index.html.haml
+++ b/chef-server-slice/app/views/status/index.html.haml
@@ -1,8 +1,22 @@
-- throw_content(:header, "Status")
-- @node_list.each do |node|
- .node
- %h3= node
- = get_info(node)
- %br
- %strong Recipes:
- = recipe_list(node)
+.block#block-tables
+ .content
+ %h2.title Status
+ .inner
+ %table.table
+ %tr
+ %th.first Node Name
+ %th &nbsp
+ %th State
+ %th.last &nbsp
+ - if @node_list.empty?
+ %tr
+ %td{:colspan => 4}= "You appear to have no nodes - try connecting one, or validating an existing #{link_to('registration', slice_url(:registrations))}"
+ - else
+ - @node_list.each_with_index do |node, index|
+ %tr{:class => "#{index % 2 == 1 ? 'odd' : 'even'}"}
+ %td{:colspan => 2}= node
+ %td
+ = get_info(node)
+ %br
+ %strong Recipes:
+ = recipe_list(node) \ No newline at end of file
diff --git a/chef-server-slice/config/init.rb b/chef-server-slice/config/init.rb
index 588d47c81f..064df28b93 100644
--- a/chef-server-slice/config/init.rb
+++ b/chef-server-slice/config/init.rb
@@ -21,7 +21,7 @@
# code and views.
#
-merb_gems_version = "> 1.0"
+merb_gems_version = " > 1.0"
dependency "merb-haml", merb_gems_version
dependency "merb-assets", merb_gems_version
dependency "merb-helpers", merb_gems_version
diff --git a/chef-server-slice/public/facebox/README.txt b/chef-server-slice/public/facebox/README.txt
new file mode 100644
index 0000000000..d4fc2d5e81
--- /dev/null
+++ b/chef-server-slice/public/facebox/README.txt
@@ -0,0 +1,4 @@
+Please visit http://famspam.com/facebox/ or open index.html in your favorite browser.
+
+Need help? Join our Google Groups mailing list:
+ http://groups.google.com/group/facebox/
diff --git a/chef-server-slice/public/facebox/b.png b/chef-server-slice/public/facebox/b.png
new file mode 100644
index 0000000000..f184e6269b
--- /dev/null
+++ b/chef-server-slice/public/facebox/b.png
Binary files differ
diff --git a/chef-server-slice/public/facebox/bl.png b/chef-server-slice/public/facebox/bl.png
new file mode 100644
index 0000000000..f6271859d5
--- /dev/null
+++ b/chef-server-slice/public/facebox/bl.png
Binary files differ
diff --git a/chef-server-slice/public/facebox/br.png b/chef-server-slice/public/facebox/br.png
new file mode 100644
index 0000000000..31f204fc45
--- /dev/null
+++ b/chef-server-slice/public/facebox/br.png
Binary files differ
diff --git a/chef-server-slice/public/facebox/closelabel.gif b/chef-server-slice/public/facebox/closelabel.gif
new file mode 100755
index 0000000000..87b4f8bd69
--- /dev/null
+++ b/chef-server-slice/public/facebox/closelabel.gif
Binary files differ
diff --git a/chef-server-slice/public/facebox/facebox.css b/chef-server-slice/public/facebox/facebox.css
new file mode 100644
index 0000000000..97ebe3cab3
--- /dev/null
+++ b/chef-server-slice/public/facebox/facebox.css
@@ -0,0 +1,95 @@
+#facebox .b {
+ background:url(/facebox/b.png);
+}
+
+#facebox .tl {
+ background:url(/facebox/tl.png);
+}
+
+#facebox .tr {
+ background:url(/facebox/tr.png);
+}
+
+#facebox .bl {
+ background:url(/facebox/bl.png);
+}
+
+#facebox .br {
+ background:url(/facebox/br.png);
+}
+
+#facebox {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 100;
+ text-align: left;
+}
+
+#facebox .popup {
+ position: relative;
+}
+
+#facebox table {
+ border-collapse: collapse;
+}
+
+#facebox td {
+ border-bottom: 0;
+ padding: 0;
+}
+
+#facebox .body {
+ padding: 10px;
+ background: #fff;
+ width: 370px;
+}
+
+#facebox .loading {
+ text-align: center;
+}
+
+#facebox .image {
+ text-align: center;
+}
+
+#facebox img {
+ border: 0;
+ margin: 0;
+}
+
+#facebox .footer {
+ border-top: 1px solid #DDDDDD;
+ padding-top: 5px;
+ margin-top: 10px;
+ text-align: right;
+}
+
+#facebox .tl, #facebox .tr, #facebox .bl, #facebox .br {
+ height: 10px;
+ width: 10px;
+ overflow: hidden;
+ padding: 0;
+}
+
+#facebox_overlay {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ height:100%;
+ width:100%;
+}
+
+.facebox_hide {
+ z-index:-100;
+}
+
+.facebox_overlayBG {
+ background-color: #000;
+ z-index: 99;
+}
+
+* html #facebox_overlay { /* ie6 hack */
+ position: absolute;
+ height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
diff --git a/chef-server-slice/public/facebox/facebox.js b/chef-server-slice/public/facebox/facebox.js
new file mode 100644
index 0000000000..cbbb450b10
--- /dev/null
+++ b/chef-server-slice/public/facebox/facebox.js
@@ -0,0 +1,319 @@
+/*
+ * Facebox (for jQuery)
+ * version: 1.2 (05/05/2008)
+ * @requires jQuery v1.2 or later
+ *
+ * Examples at http://famspam.com/facebox/
+ *
+ * Licensed under the MIT:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2007, 2008 Chris Wanstrath [ chris@ozmm.org ]
+ *
+ * Usage:
+ *
+ * jQuery(document).ready(function() {
+ * jQuery('a[rel*=facebox]').facebox()
+ * })
+ *
+ * <a href="#terms" rel="facebox">Terms</a>
+ * Loads the #terms div in the box
+ *
+ * <a href="terms.html" rel="facebox">Terms</a>
+ * Loads the terms.html page in the box
+ *
+ * <a href="terms.png" rel="facebox">Terms</a>
+ * Loads the terms.png image in the box
+ *
+ *
+ * You can also use it programmatically:
+ *
+ * jQuery.facebox('some html')
+ *
+ * The above will open a facebox with "some html" as the content.
+ *
+ * jQuery.facebox(function($) {
+ * $.get('blah.html', function(data) { $.facebox(data) })
+ * })
+ *
+ * The above will show a loading screen before the passed function is called,
+ * allowing for a better ajaxy experience.
+ *
+ * The facebox function can also display an ajax page or image:
+ *
+ * jQuery.facebox({ ajax: 'remote.html' })
+ * jQuery.facebox({ image: 'dude.jpg' })
+ *
+ * Want to close the facebox? Trigger the 'close.facebox' document event:
+ *
+ * jQuery(document).trigger('close.facebox')
+ *
+ * Facebox also has a bunch of other hooks:
+ *
+ * loading.facebox
+ * beforeReveal.facebox
+ * reveal.facebox (aliased as 'afterReveal.facebox')
+ * init.facebox
+ *
+ * Simply bind a function to any of these hooks:
+ *
+ * $(document).bind('reveal.facebox', function() { ...stuff to do after the facebox and contents are revealed... })
+ *
+ */
+(function($) {
+ $.facebox = function(data, klass) {
+ $.facebox.loading()
+
+ if (data.ajax) fillFaceboxFromAjax(data.ajax)
+ else if (data.image) fillFaceboxFromImage(data.image)
+ else if (data.div) fillFaceboxFromHref(data.div)
+ else if ($.isFunction(data)) data.call($)
+ else $.facebox.reveal(data, klass)
+ }
+
+ /*
+ * Public, $.facebox methods
+ */
+
+ $.extend($.facebox, {
+ settings: {
+ opacity : 0,
+ overlay : true,
+ loadingImage : '/facebox/loading.gif',
+ closeImage : '/facebox/closelabel.gif',
+ imageTypes : [ 'png', 'jpg', 'jpeg', 'gif' ],
+ faceboxHtml : '\
+ <div id="facebox" style="display:none;"> \
+ <div class="popup"> \
+ <table> \
+ <tbody> \
+ <tr> \
+ <td class="tl"/><td class="b"/><td class="tr"/> \
+ </tr> \
+ <tr> \
+ <td class="b"/> \
+ <td class="body"> \
+ <div class="content"> \
+ </div> \
+ <div class="footer"> \
+ <a href="#" class="close"> \
+ <img src="/facebox/closelabel.gif" title="close" class="close_image" /> \
+ </a> \
+ </div> \
+ </td> \
+ <td class="b"/> \
+ </tr> \
+ <tr> \
+ <td class="bl"/><td class="b"/><td class="br"/> \
+ </tr> \
+ </tbody> \
+ </table> \
+ </div> \
+ </div>'
+ },
+
+ loading: function() {
+ init()
+ if ($('#facebox .loading').length == 1) return true
+ showOverlay()
+
+ $('#facebox .content').empty()
+ $('#facebox .body').children().hide().end().
+ append('<div class="loading"><img src="'+$.facebox.settings.loadingImage+'"/></div>')
+
+ $('#facebox').css({
+ top: getPageScroll()[1] + (getPageHeight() / 10),
+ left: 385.5
+ }).show()
+
+ $(document).bind('keydown.facebox', function(e) {
+ if (e.keyCode == 27) $.facebox.close()
+ return true
+ })
+ $(document).trigger('loading.facebox')
+ },
+
+ reveal: function(data, klass) {
+ $(document).trigger('beforeReveal.facebox')
+ if (klass) $('#facebox .content').addClass(klass)
+ $('#facebox .content').append(data)
+ $('#facebox .loading').remove()
+ $('#facebox .body').children().fadeIn('normal')
+ $('#facebox').css('left', $(window).width() / 2 - ($('#facebox table').width() / 2))
+ $(document).trigger('reveal.facebox').trigger('afterReveal.facebox')
+ },
+
+ close: function() {
+ $(document).trigger('close.facebox')
+ return false
+ }
+ })
+
+ /*
+ * Public, $.fn methods
+ */
+
+ $.fn.facebox = function(settings) {
+ init(settings)
+
+ function clickHandler() {
+ $.facebox.loading(true)
+
+ // support for rel="facebox.inline_popup" syntax, to add a class
+ // also supports deprecated "facebox[.inline_popup]" syntax
+ var klass = this.rel.match(/facebox\[?\.(\w+)\]?/)
+ if (klass) klass = klass[1]
+
+ fillFaceboxFromHref(this.href, klass)
+ return false
+ }
+
+ return this.click(clickHandler)
+ }
+
+ /*
+ * Private methods
+ */
+
+ // called one time to setup facebox on this page
+ function init(settings) {
+ if ($.facebox.settings.inited) return true
+ else $.facebox.settings.inited = true
+
+ $(document).trigger('init.facebox')
+ makeCompatible()
+
+ var imageTypes = $.facebox.settings.imageTypes.join('|')
+ $.facebox.settings.imageTypesRegexp = new RegExp('\.' + imageTypes + '$', 'i')
+
+ if (settings) $.extend($.facebox.settings, settings)
+ $('body').append($.facebox.settings.faceboxHtml)
+
+ var preload = [ new Image(), new Image() ]
+ preload[0].src = $.facebox.settings.closeImage
+ preload[1].src = $.facebox.settings.loadingImage
+
+ $('#facebox').find('.b:first, .bl, .br, .tl, .tr').each(function() {
+ preload.push(new Image())
+ preload.slice(-1).src = $(this).css('background-image').replace(/url\((.+)\)/, '$1')
+ })
+
+ $('#facebox .close').click($.facebox.close)
+ $('#facebox .close_image').attr('src', $.facebox.settings.closeImage)
+ }
+
+ // getPageScroll() by quirksmode.com
+ function getPageScroll() {
+ var xScroll, yScroll;
+ if (self.pageYOffset) {
+ yScroll = self.pageYOffset;
+ xScroll = self.pageXOffset;
+ } else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
+ yScroll = document.documentElement.scrollTop;
+ xScroll = document.documentElement.scrollLeft;
+ } else if (document.body) {// all other Explorers
+ yScroll = document.body.scrollTop;
+ xScroll = document.body.scrollLeft;
+ }
+ return new Array(xScroll,yScroll)
+ }
+
+ // Adapted from getPageSize() by quirksmode.com
+ function getPageHeight() {
+ var windowHeight
+ if (self.innerHeight) { // all except Explorer
+ windowHeight = self.innerHeight;
+ } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+ windowHeight = document.documentElement.clientHeight;
+ } else if (document.body) { // other Explorers
+ windowHeight = document.body.clientHeight;
+ }
+ return windowHeight
+ }
+
+ // Backwards compatibility
+ function makeCompatible() {
+ var $s = $.facebox.settings
+
+ $s.loadingImage = $s.loading_image || $s.loadingImage
+ $s.closeImage = $s.close_image || $s.closeImage
+ $s.imageTypes = $s.image_types || $s.imageTypes
+ $s.faceboxHtml = $s.facebox_html || $s.faceboxHtml
+ }
+
+ // Figures out what you want to display and displays it
+ // formats are:
+ // div: #id
+ // image: blah.extension
+ // ajax: anything else
+ function fillFaceboxFromHref(href, klass) {
+ // div
+ if (href.match(/#/)) {
+ var url = window.location.href.split('#')[0]
+ var target = href.replace(url,'')
+ $.facebox.reveal($(target).clone().show(), klass)
+
+ // image
+ } else if (href.match($.facebox.settings.imageTypesRegexp)) {
+ fillFaceboxFromImage(href, klass)
+ // ajax
+ } else {
+ fillFaceboxFromAjax(href, klass)
+ }
+ }
+
+ function fillFaceboxFromImage(href, klass) {
+ var image = new Image()
+ image.onload = function() {
+ $.facebox.reveal('<div class="image"><img src="' + image.src + '" /></div>', klass)
+ }
+ image.src = href
+ }
+
+ function fillFaceboxFromAjax(href, klass) {
+ $.get(href, function(data) { $.facebox.reveal(data, klass) })
+ }
+
+ function skipOverlay() {
+ return $.facebox.settings.overlay == false || $.facebox.settings.opacity === null
+ }
+
+ function showOverlay() {
+ if (skipOverlay()) return
+
+ if ($('facebox_overlay').length == 0)
+ $("body").append('<div id="facebox_overlay" class="facebox_hide"></div>')
+
+ $('#facebox_overlay').hide().addClass("facebox_overlayBG")
+ .css('opacity', $.facebox.settings.opacity)
+ .click(function() { $(document).trigger('close.facebox') })
+ .fadeIn(200)
+ return false
+ }
+
+ function hideOverlay() {
+ if (skipOverlay()) return
+
+ $('#facebox_overlay').fadeOut(200, function(){
+ $("#facebox_overlay").removeClass("facebox_overlayBG")
+ $("#facebox_overlay").addClass("facebox_hide")
+ $("#facebox_overlay").remove()
+ })
+
+ return false
+ }
+
+ /*
+ * Bindings
+ */
+
+ $(document).bind('close.facebox', function() {
+ $(document).unbind('keydown.facebox')
+ $('#facebox').fadeOut(function() {
+ $('#facebox .content').removeClass().addClass('content')
+ hideOverlay()
+ $('#facebox .loading').remove()
+ })
+ })
+
+})(jQuery);
diff --git a/chef-server-slice/public/facebox/loading.gif b/chef-server-slice/public/facebox/loading.gif
new file mode 100755
index 0000000000..f864d5fd38
--- /dev/null
+++ b/chef-server-slice/public/facebox/loading.gif
Binary files differ
diff --git a/chef-server-slice/public/facebox/tl.png b/chef-server-slice/public/facebox/tl.png
new file mode 100644
index 0000000000..d99c8f6c6e
--- /dev/null
+++ b/chef-server-slice/public/facebox/tl.png
Binary files differ
diff --git a/chef-server-slice/public/facebox/tr.png b/chef-server-slice/public/facebox/tr.png
new file mode 100644
index 0000000000..e99b6ec831
--- /dev/null
+++ b/chef-server-slice/public/facebox/tr.png
Binary files differ
diff --git a/chef-server-slice/public/images/avatar.png b/chef-server-slice/public/images/avatar.png
new file mode 100644
index 0000000000..66488481ae
--- /dev/null
+++ b/chef-server-slice/public/images/avatar.png
Binary files differ
diff --git a/chef-server-slice/public/images/toggle-collapse.gif b/chef-server-slice/public/images/toggle-collapse.gif
new file mode 100644
index 0000000000..f0979304ac
--- /dev/null
+++ b/chef-server-slice/public/images/toggle-collapse.gif
Binary files differ
diff --git a/chef-server-slice/public/images/toggle-expand.gif b/chef-server-slice/public/images/toggle-expand.gif
new file mode 100644
index 0000000000..03fa8360dd
--- /dev/null
+++ b/chef-server-slice/public/images/toggle-expand.gif
Binary files differ
diff --git a/chef-server-slice/public/javascripts/chef.js b/chef-server-slice/public/javascripts/chef.js
index bd82f1aeb7..0245bf79d2 100644
--- a/chef-server-slice/public/javascripts/chef.js
+++ b/chef-server-slice/public/javascripts/chef.js
@@ -1,5 +1,6 @@
//
// Author:: Adam Jacob (<adam@opscode.com>)
+// Author:: AJ Christensen (<aj@junglist.gen.nz>)
// Copyright:: Copyright (c) 2008 Opscode, Inc.
// License:: Apache License, Version 2.0
//
@@ -16,21 +17,8 @@
// limitations under the License.
//
-$(document).ready(function() {
- $(".edit_area").editable(location.href + ".json", {
- type : 'textarea',
- cancel : 'Cancel',
- submit : 'Save',
- indicator : "<img src='/images/indicator.gif'>",
- target : location.href + "?ajax=true",
- loadtype : "GET",
- loadurl : location.href + ".json?ajax=true",
- tooltip : 'Click to edit...',
- method : "PUT"
- });
-});
-
-$(document).ready(function() {
+$(document).ready(function(){
+ // livequery hidden form for link_to ajax magic
$('a[method]').livequery(function(){
var message = $(this).attr('confirm');
var method = $(this).attr('method');
@@ -53,4 +41,56 @@ $(document).ready(function() {
}
});
});
+
+ $("dd:has(dl)").livequery(function(){
+ $(this).hide().prev("dt").addClass("collapsed");
+ });
+ $("dd:not(:has(dl))").livequery(function(){
+ $(this).addClass("inline").prev().addClass("inline");
+ });
+ $("dt.collapsed").livequery(function(){
+ $(this).click(function() {
+ $(this).toggleClass("collapsed").next().toggle();
+ });
+ });
+
+ // editable table for the node show view
+ $(".edit_area").editable(location.href + ".json", {
+ target : location.href,
+ method : "PUT",
+ submit : "Save",
+ cancel : "Cancel",
+ indicator : "Saving..",
+ loadurl : location.href,
+ tooltip : "Click to edit",
+ type : "textarea",
+ event : "dblclick",
+ height : 300
+ });
+
+
+ //alert("blah" + $('#json_tree_source').text());
+ //var json = $('#json_tree_source').text();
+ //$('#attribute_tree_view').append(TreeView($('#json_tree_source').text()));
+
+ // accordion for the cookbooks show view
+ $('.accordion .head').click(function() {
+ $(this).next().toggle('slow');
+ return false;
+ }).next().hide();
+
+ // global facebox callback
+ $('a[rel*=facebox]').facebox();
+
+ /*
+ JSONEditor.prototype.ADD_IMG = '/images/add.png';
+ JSONEditor.prototype.DELETE_IMG = '/images/delete.png';
+ var attrib_editor = new JSONEditor($("#attrib_json_edit"), 400, 300);
+ attrib_editor.doTruncation(true);
+ attrib_editor.showFunctionButtons();
+
+ var recipe_editor = new JSONEditor($("#recipe_json_edit"), 400, 300);
+ recipe_editor.doTruncation(true);
+ recipe_editor.showFunctionButtons();
+ */
}); \ No newline at end of file
diff --git a/chef-server-slice/public/javascripts/jquery.editinline.js b/chef-server-slice/public/javascripts/jquery.editinline.js
new file mode 100644
index 0000000000..e97abe1d15
--- /dev/null
+++ b/chef-server-slice/public/javascripts/jquery.editinline.js
@@ -0,0 +1,108 @@
+// 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.
+
+(function($) {
+
+ function startEditing(elem, options) {
+ var editable = $(elem);
+ var origHtml = editable.html();
+ var origText = options.populate($.trim(editable.text()));
+
+ if (!options.begin.apply(elem, [origText])) {
+ return;
+ }
+
+ var input = options.createInput.apply(elem, [origText])
+ .addClass("editinline").val(origText)
+ .dblclick(function() { return false; })
+ .keydown(function(evt) {
+ switch (evt.keyCode) {
+ case 13: { // return
+ if (!input.is("textarea")) applyChange(evt.keyCode);
+ break;
+ }
+ case 27: { // escape
+ cancelChange(evt.keyCode);
+ break;
+ }
+ case 9: { // tab
+ if (!input.is("textarea")) {
+ applyChange(evt.keyCode);
+ return false;
+ }
+ }
+ }
+ });
+
+ function applyChange(keyCode) {
+ var newText = input.val();
+ if (newText == origText) {
+ cancelChange(keyCode);
+ return true;
+ }
+ if ((!options.allowEmpty && !newText.length) ||
+ !options.validate.apply(elem, [newText])) {
+ input.addClass("invalid");
+ return false;
+ }
+ input.remove();
+ tools.remove();
+ options.accept.apply(elem, [newText, origText]);
+ editable.removeClass("editinline-container");
+ options.end.apply(elem, [keyCode]);
+ return true;
+ }
+
+ function cancelChange(keyCode) {
+ options.cancel.apply(elem, [origText]);
+ editable.html(origHtml).removeClass("editinline-container");
+ options.end.apply(elem, [keyCode]);
+ }
+
+ var tools = $("<span class='editinline-tools'></span>");
+ $("<button type='button' class='apply'></button>")
+ .text(options.acceptLabel).click(applyChange).appendTo(tools);
+ $("<button type='button' class='cancel'></button>")
+ .text(options.cancelLabel).click(cancelChange).appendTo(tools)
+
+ editable.html("").append(tools).append(input)
+ .addClass("editinline-container");
+ options.prepareInput.apply(elem, [input[0]]);
+ input.each(function() { this.focus(); this.select(); });
+ }
+
+ $.fn.makeEditable = function(options) {
+ options = $.extend({
+ allowEmpty: true,
+ acceptLabel: "",
+ cancelLabel: "",
+ toolTip: "Double click to edit",
+
+ // callbacks
+ begin: function() { return true },
+ accept: function(newValue, oldValue) {},
+ cancel: function(oldValue) {},
+ createInput: function(value) { return $("<input type='text'>") },
+ prepareInput: function(input) {},
+ end: function(keyCode) {},
+ populate: function(value) { return value },
+ validate: function() { return true }
+ }, options || {});
+
+ return this.each(function() {
+ $(this).attr("title", options.toolTip).dblclick(function() {
+ startEditing(this, options);
+ });
+ });
+ }
+
+})(jQuery);
diff --git a/chef-server-slice/public/javascripts/jquery.localscroll.js b/chef-server-slice/public/javascripts/jquery.localscroll.js
new file mode 100644
index 0000000000..596e1ba200
--- /dev/null
+++ b/chef-server-slice/public/javascripts/jquery.localscroll.js
@@ -0,0 +1,104 @@
+/**
+ * jQuery.LocalScroll
+ * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * Date: 6/3/2008
+ *
+ * @projectDescription Animated scrolling navigation, using anchors.
+ * http://flesler.blogspot.com/2007/10/jquerylocalscroll-10.html
+ * @author Ariel Flesler
+ * @version 1.2.6
+ *
+ * @id jQuery.fn.localScroll
+ * @param {Object} settings Hash of settings, it is passed in to jQuery.ScrollTo, none is required.
+ * @return {jQuery} Returns the same jQuery object, for chaining.
+ *
+ * @example $('ul.links').localScroll();
+ *
+ * @example $('ul.links').localScroll({ filter:'.animated', duration:400, axis:'x' });
+ *
+ * @example $.localScroll({ target:'#pane', axis:'xy', queue:true, event:'mouseover' });
+ *
+ * Notes:
+ * - The plugin requires jQuery.ScrollTo.
+ * - The hash of settings, is passed to jQuery.ScrollTo, so the settings are valid for that plugin as well.
+ * - jQuery.localScroll can be used if the desired links, are all over the document, it accepts the same settings.
+ * - If the setting 'lazy' is set to true, then the binding will still work for later added anchors.
+ * - The setting 'speed' is deprecated, use 'duration' instead.
+ * - If onBefore returns false, the event is ignored.
+ **/
+;(function( $ ){
+ var URI = location.href.replace(/#.*/,'');//local url without hash
+
+ var $localScroll = $.localScroll = function( settings ){
+ $('body').localScroll( settings );
+ };
+
+ //Many of these defaults, belong to jQuery.ScrollTo, check it's demo for an example of each option.
+ //@see http://www.freewebs.com/flesler/jQuery.ScrollTo/
+ $localScroll.defaults = {//the defaults are public and can be overriden.
+ duration:1000, //how long to animate.
+ axis:'y',//which of top and left should be modified.
+ event:'click',//on which event to react.
+ stop:true//avoid queuing animations
+ /*
+ lock:false,//ignore events if already animating
+ lazy:false,//if true, links can be added later, and will still work.
+ target:null, //what to scroll (selector or element). Keep it null if want to scroll the whole window.
+ filter:null, //filter some anchors out of the matched elements.
+ hash: false//if true, the hash of the selected link, will appear on the address bar.
+ */
+ };
+
+ //if the URL contains a hash, it will scroll to the pointed element
+ $localScroll.hash = function( settings ){
+ settings = $.extend( {}, $localScroll.defaults, settings );
+ settings.hash = false;//can't be true
+ if( location.hash )
+ setTimeout(function(){ scroll( 0, location, settings ); }, 0 );//better wrapped with a setTimeout
+ };
+
+ $.fn.localScroll = function( settings ){
+ settings = $.extend( {}, $localScroll.defaults, settings );
+
+ return ( settings.persistent || settings.lazy )
+ ? this.bind( settings.event, function( e ){//use event delegation, more links can be added later.
+ var a = $([e.target, e.target.parentNode]).filter(filter)[0];//if a valid link was clicked.
+ a && scroll( e, a, settings );//do scroll.
+ })
+ : this.find('a,area')//bind concretely, to each matching link
+ .filter( filter ).bind( settings.event, function(e){
+ scroll( e, this, settings );
+ }).end()
+ .end();
+
+ function filter(){//is this a link that points to an anchor and passes a possible filter ? href is checked to avoid a bug in FF.
+ return !!this.href && !!this.hash && this.href.replace(this.hash,'') == URI && (!settings.filter || $(this).is( settings.filter ));
+ };
+ };
+
+ function scroll( e, link, settings ){
+ var id = link.hash.slice(1),
+ elem = document.getElementById(id) || document.getElementsByName(id)[0];
+ if ( elem ){
+ e && e.preventDefault();
+ var $target = $( settings.target || $.scrollTo.window() );//if none specified, then the window.
+
+ if( settings.lock && $target.is(':animated') ||
+ settings.onBefore && settings.onBefore.call(link, e, elem, $target) === false ) return;
+
+ if( settings.stop )
+ $target.queue('fx',[]).stop();//remove all its animations
+ $target
+ .scrollTo( elem, settings )//do scroll
+ .trigger('notify.serialScroll',[elem]);//notify serialScroll about this change
+ if( settings.hash )
+ $target.queue(function(){
+ location = link.hash;
+ // make sure this function is released
+ $(this).dequeue();
+ });
+ }
+ };
+
+})( jQuery ); \ No newline at end of file
diff --git a/chef-server-slice/public/javascripts/jquery.scrollTo.js b/chef-server-slice/public/javascripts/jquery.scrollTo.js
new file mode 100644
index 0000000000..688d58e55d
--- /dev/null
+++ b/chef-server-slice/public/javascripts/jquery.scrollTo.js
@@ -0,0 +1,150 @@
+/**
+ * jQuery.ScrollTo
+ * Copyright (c) 2007-2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * Date: 2/19/2008
+ *
+ * @projectDescription Easy element scrolling using jQuery.
+ * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
+ * Tested with jQuery 1.2.1. On FF 2.0.0.11, IE 6, Opera 9.22 and Safari 3 beta. on Windows.
+ *
+ * @author Ariel Flesler
+ * @version 1.3.3
+ *
+ * @id jQuery.scrollTo
+ * @id jQuery.fn.scrollTo
+ * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
+ * The different options for target are:
+ * - A number position (will be applied to all axes).
+ * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
+ * - A jQuery/DOM element ( logically, child of the element to scroll )
+ * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
+ * - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
+ * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
+ * @param {Object} settings Hash of settings, optional.
+ * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
+ * @option {Number} duration The OVERALL length of the animation.
+ * @option {String} easing The easing method for the animation.
+ * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
+ * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
+ * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
+ * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
+ * @option {Function} onAfter Function to be called after the scrolling ends.
+ * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
+ * @return {jQuery} Returns the same jQuery object, for chaining.
+ *
+ * @example $('div').scrollTo( 340 );
+ *
+ * @example $('div').scrollTo( '+=340px', { axis:'y' } );
+ *
+ * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
+ *
+ * @example var second_child = document.getElementById('container').firstChild.nextSibling;
+ * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
+ * alert('scrolled!!');
+ * }});
+ *
+ * @example $('div').scrollTo( { top: 300, left:'+=200' }, { offset:-20 } );
+ *
+ * Notes:
+ * - jQuery.scrollTo will make the whole window scroll, it accepts the same arguments as jQuery.fn.scrollTo.
+ * - If you are interested in animated anchor navigation, check http://jquery.com/plugins/project/LocalScroll.
+ * - The options margin, offset and over are ignored, if the target is not a jQuery object or a DOM element.
+ * - The option 'queue' won't be taken into account, if only 1 axis is given.
+ */
+;(function( $ ){
+
+ var $scrollTo = $.scrollTo = function( target, duration, settings ){
+ $scrollTo.window().scrollTo( target, duration, settings );
+ };
+
+ $scrollTo.defaults = {
+ axis:'y',
+ duration:1
+ };
+
+ //returns the element that needs to be animated to scroll the window
+ $scrollTo.window = function(){
+ return $( $.browser.safari ? 'body' : 'html' );
+ };
+
+ $.fn.scrollTo = function( target, duration, settings ){
+ if( typeof duration == 'object' ){
+ settings = duration;
+ duration = 0;
+ }
+ settings = $.extend( {}, $scrollTo.defaults, settings );
+ duration = duration || settings.speed || settings.duration;//speed is still recognized for backwards compatibility
+ settings.queue = settings.queue && settings.axis.length > 1;//make sure the settings are given right
+ if( settings.queue )
+ duration /= 2;//let's keep the overall speed, the same.
+ settings.offset = both( settings.offset );
+ settings.over = both( settings.over );
+
+ return this.each(function(){
+ var elem = this, $elem = $(elem),
+ t = target, toff, attr = {},
+ win = $elem.is('html,body');
+ switch( typeof t ){
+ case 'number'://will pass the regex
+ case 'string':
+ if( /^([+-]=)?\d+(px)?$/.test(t) ){
+ t = both( t );
+ break;//we are done
+ }
+ t = $(t,this);// relative selector, no break!
+ case 'object':
+ if( t.is || t.style )//DOM/jQuery
+ toff = (t = $(t)).offset();//get the real position of the target
+ }
+ $.each( settings.axis.split(''), function( i, axis ){
+ var Pos = axis == 'x' ? 'Left' : 'Top',
+ pos = Pos.toLowerCase(),
+ key = 'scroll' + Pos,
+ act = elem[key],
+ Dim = axis == 'x' ? 'Width' : 'Height',
+ dim = Dim.toLowerCase();
+
+ if( toff ){//jQuery/DOM
+ attr[key] = toff[pos] + ( win ? 0 : act - $elem.offset()[pos] );
+
+ if( settings.margin ){//if it's a dom element, reduce the margin
+ attr[key] -= parseInt(t.css('margin'+Pos)) || 0;
+ attr[key] -= parseInt(t.css('border'+Pos+'Width')) || 0;
+ }
+
+ attr[key] += settings.offset[pos] || 0;//add/deduct the offset
+
+ if( settings.over[pos] )//scroll to a fraction of its width/height
+ attr[key] += t[dim]() * settings.over[pos];
+ }else
+ attr[key] = t[pos];//remove the unnecesary 'px'
+
+ if( /^\d+$/.test(attr[key]) )//number or 'number'
+ attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max(Dim) );//check the limits
+
+ if( !i && settings.queue ){//queueing each axis is required
+ if( act != attr[key] )//don't waste time animating, if there's no need.
+ animate( settings.onAfterFirst );//intermediate animation
+ delete attr[key];//don't animate this axis again in the next iteration.
+ }
+ });
+ animate( settings.onAfter );
+
+ function animate( callback ){
+ $elem.animate( attr, duration, settings.easing, callback && function(){
+ callback.call(this, target);
+ });
+ };
+ function max( Dim ){
+ var el = win ? $.browser.opera ? document.body : document.documentElement : elem;
+ return el['scroll'+Dim] - el['client'+Dim];
+ };
+ });
+ };
+
+ function both( val ){
+ return typeof val == 'object' ? val : { top:val, left:val };
+ };
+
+})( jQuery ); \ No newline at end of file
diff --git a/chef-server-slice/public/javascripts/master.js b/chef-server-slice/public/javascripts/master.js
deleted file mode 100644
index e69de29bb2..0000000000
--- a/chef-server-slice/public/javascripts/master.js
+++ /dev/null
diff --git a/chef-server-slice/public/stylesheets/base.css b/chef-server-slice/public/stylesheets/base.css
new file mode 100644
index 0000000000..5775ac3a16
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/base.css
@@ -0,0 +1,336 @@
+* {margin:0;padding:0}
+.clear { clear: both; height: 0; }
+
+h1 { margin: 15px 0; font-size: 22px; font-weight: normal; }
+h2 { font-size: 22px; margin: 15px 0; font-weight: normal;}
+h3 { font-size: 18px; margin: 10px 0; font-weight: normal;}
+h4 { font-size: 16px; margin: 10px 0; font-weight: normal;}
+hr {height: 1px; border: 0; }
+p { margin: 15px 0;}
+a img { border: none; }
+
+body {
+ font-size: 12px;
+ font-family: sans-serif;
+}
+
+#container {
+ min-width: 960px;
+}
+
+#header, #wrapper {
+ padding: 0 20px;
+}
+
+#header {
+ position: relative;
+ padding-top: 1px;
+}
+
+#header h1 {
+ margin: 0;
+ padding: 10px 0;
+ font-size: 30px;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ text-decoration: none;
+}
+
+#main {
+ width: 70%;
+ float: left;
+}
+
+.actions-bar {
+ padding: 10px 1px;
+}
+
+.actions-bar .actions {
+ float: left;
+}
+
+
+.actions-bar .pagination {
+ float: right;
+ padding: 1px 0;
+}
+
+#sidebar {
+ width: 25%;
+ float: right;
+}
+
+#sidebar h3 {
+ padding: 10px 15px;
+ margin: 0;
+ font-size: 13px;
+}
+
+#sidebar .block {
+ margin-bottom: 20px;
+ padding-bottom: 10px;
+}
+
+#sidebar .block .content {
+ padding: 0 15px;
+}
+
+#sidebar ul.navigation li a:link, #sidebar ul.navigation li a:visited {
+ display: block;
+ padding: 10px 15px;
+}
+
+#sidebar .block .sidebar-block, #sidebar .notice {
+ padding:10px;
+}
+
+#wrapper {
+ padding-top: 20px;
+}
+
+#main .block {
+ margin-bottom: 20px;
+ padding-top: 1px;
+}
+
+#main .block .content .inner {
+ padding: 0 15px 15px;
+}
+
+#main .main p.first {
+ margin-top: 0;
+}
+
+#user-navigation {
+ position: absolute;
+ top: 0px;
+ right: 20px;
+}
+
+#main-navigation {
+ width: 100%;
+}
+
+#user-navigation ul, #main-navigation ul, .secondary-navigation ul, #sidebar ul.navigation {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+}
+
+#user-navigation ul li, #main-navigation ul li, .secondary-navigation ul li {
+ float: left;
+}
+
+#main-navigation ul li {
+ margin-right: 5px;
+}
+
+#user-navigation ul li {
+ padding: 5px 10px;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+}
+
+#main-navigation ul li a {
+ font-size: 15px;
+ display: block;
+ padding: 8px 15px;
+}
+
+.secondary-navigation {
+ font-size: 13px;
+ border-bottom-width: 10px;
+ border-bottom-style: solid;
+}
+
+.secondary-navigation ul li a {
+ display: block;
+ padding: 10px 15px;
+}
+
+#footer {
+ padding-bottom: 20px;
+}
+
+/* pagination */
+
+.pagination a, .pagination span {
+ padding: 2px 5px;
+ margin-right: 5px;
+ display: block;
+ float: left;
+ border-style: solid;
+ border-width: 1px;
+}
+
+.pagination span.current {
+ font-weight: bold;
+}
+
+.pagination a {
+ text-decoration: none;
+}
+
+/* tables */
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 15px;
+}
+
+.table th {
+ padding: 10px;
+ font-weight: bold;
+ text-align: left;
+}
+
+.table th.first {
+ width: 30px;
+}
+
+.table th.last {
+ width: 200px;
+}
+
+.table .checkbox {
+ margin-left: 10px;
+}
+
+.table td {
+ padding: 10px;
+}
+
+.table td.last {
+ text-align: right;
+}
+
+/* forms */
+
+input.checkbox {
+ margin: 0;
+ padding: 0;
+}
+
+.form .group {
+ margin-bottom: 15px;
+}
+
+.form div.left {
+ width: 20%;
+ float: left;
+}
+
+.form div.right {
+ width: 75%;
+ float: right;
+}
+
+.form .columns .column {
+ width: 48%;
+}
+
+.form .columns .left {
+ float: left;
+}
+
+.form .columns .right {
+ float: right;
+}
+
+.form label.label, .form input.text_field, .form textarea.text_area {
+ font-size: 1.2em;
+ padding: 1px 0;
+ margin: 0;
+}
+
+.form label.right {
+ text-align: right;
+}
+
+.form input.checkbox, .form input.radio {
+ margin-right: 5px;
+}
+
+.form label.checkbox, .form label.radio {
+ line-height: 1.5em;
+}
+
+.form label.label {
+ display: block;
+ padding-bottom: 2px;
+ font-weight: bold;
+}
+
+.form div.fieldWithErrors label.label {
+ display: inline;
+}
+
+.form .fieldWithErrors .error {
+ color: red;
+}
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border-width: 1px;
+ border-style: solid;
+}
+
+/* lists */
+
+ul.list {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+}
+
+ul.list li {
+ clear: left;
+ padding-bottom: 5px;
+}
+
+ul.list li .left {
+ float: left;
+}
+
+ul.list li .left .avatar {
+ width: 50px;
+ height: 50px;
+}
+
+ul.list li .item {
+ margin-left: 80px;
+}
+
+ul.list li .item .avatar {
+ float: left;
+ margin: 0 5px 5px 0;
+ width: 30px;
+ height: 30px;
+}
+
+/* box */
+
+#box {
+ width: 500px;
+ margin: 50px auto;
+}
+
+#box .block {
+ margin-bottom: 20px;
+}
+
+#box .block h2 {
+ padding: 10px 15px;
+ margin: 0;
+}
+
+#box .block .content {
+ padding: 10px 20px;
+}
+
+
diff --git a/chef-server-slice/public/stylesheets/chef.css b/chef-server-slice/public/stylesheets/chef.css
new file mode 100644
index 0000000000..1efc8a84c1
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/chef.css
@@ -0,0 +1,50 @@
+.ruby .normal {}
+.ruby .comment { color: #005; font-style: italic; }
+.ruby .keyword { color: #A00; font-weight: bold; }
+.ruby .method { color: #077; }
+.ruby .class { color: #074; }
+.ruby .module { color: #050; }
+.ruby .punct { color: #447; font-weight: bold; }
+.ruby .symbol { color: #099; }
+.ruby .string { color: #944; background: #FFE; }
+.ruby .char { color: #F07; }
+.ruby .ident { color: #004; }
+.ruby .constant { color: #07F; }
+.ruby .regex { color: #B66; background: #FEF; }
+.ruby .number { color: #F99; }
+.ruby .attribute { color: #7BB; }
+.ruby .global { color: #7FB; }
+.ruby .expr { color: #227; }
+.ruby .escape { color: #277; }
+
+.files {
+ padding-left: 20px;
+}
+
+.code {
+ overflow: auto;
+ font-size: 0.8em;
+}
+
+.search td {
+ /*overflow: auto;*/
+ font-size: 0.8em;
+}
+
+dl dt { font-weight: bold; }
+.content td dl { margin: 0; padding: 0; }
+.content td dt {
+ background: transparent url(/images/toggle-collapse.gif) 0 3px no-repeat;
+ clear: left; color: #333; cursor: pointer; line-height: 1em;
+ margin-left: -12px; padding-left: 14px;
+}
+.content td dd { margin: 0;
+ padding: 0 0 0 1em;
+}
+.content td dt.collapsed {
+ background-image: url(/images/toggle-expand.gif);
+}
+.content td dt.inline { background-image: none; cursor: default;
+ float: left; margin-left: 0; padding-left: 2px; padding-right: .5em;
+ padding-top: 2px;
+} \ No newline at end of file
diff --git a/chef-server-slice/public/stylesheets/master-highlight.css b/chef-server-slice/public/stylesheets/master-highlight.css
deleted file mode 100644
index ccbda31932..0000000000
--- a/chef-server-slice/public/stylesheets/master-highlight.css
+++ /dev/null
@@ -1,193 +0,0 @@
-body {
- font-family: Verdana, Arial, sans-serif;
- font-size: 11px;
- background-color: #fff;
- margin: ;
-}
-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;
- margin-right: 3.0em;
-}
-#main-container ol {
- margin-left: 3.0em;
- margin-right: 3.0em;
-}
-
-div.resource_collection {
- border: 1px solid #999;
- float: left;
- clear: both;
- margin: 10px;
- padding: 5px;
-}
-
-div.resource {
- border-top: 1px solid #999;
- float: left;
- clear: both;
-}
-
-div.attr_group {
- float: left;
- clear: both;
-}
-
-td.attr_name {
- font-weight: bold;
- margin-right: 10px;
- border-right: 1px solid #999;
-}
-
-td.attr_value {
- padding-left: 5px;
- border-top: 1px solid #999;
-}
-
-div.node {
- float: left;
- clear: both;
- margin: 10px;
- padding: 5px;
- border: 1px solid #999;
- width: 95%;
-}
-li {
- overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
- white-space: pre-wrap; /* css-3 */
- white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
- white-space: -pre-wrap; /* Opera 4-6 */
- white-space: -o-pre-wrap; /* Opera 7 */
- /* width: 99%; */
- word-wrap: break-word; /* Internet Explorer 5.5+ */
-}
-
-textarea {
- font-size: 10px;
- font-family: monospace;
- border: 1px solid #999;
-}
-
-.ruby .normal {}
-.ruby .comment { color: #005; font-style: italic; }
-.ruby .keyword { color: #A00; font-weight: bold; }
-.ruby .method { color: #077; }
-.ruby .class { color: #074; }
-.ruby .module { color: #050; }
-.ruby .punct { color: #447; font-weight: bold; }
-.ruby .symbol { color: #099; }
-.ruby .string { color: #944; background: #FFE; }
-.ruby .char { color: #F07; }
-.ruby .ident { color: #004; }
-.ruby .constant { color: #07F; }
-.ruby .regex { color: #B66; background: #FEF; }
-.ruby .number { color: #F99; }
-.ruby .attribute { color: #7BB; }
-.ruby .global { color: #7FB; }
-.ruby .expr { color: #227; }
-.ruby .escape { color: #277; }
diff --git a/chef-server-slice/public/stylesheets/themes/bec-green/style.css b/chef-server-slice/public/stylesheets/themes/bec-green/style.css
new file mode 100644
index 0000000000..225b6d4e35
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/bec-green/style.css
@@ -0,0 +1,290 @@
+a:link, a:visited, a:hover, a:active { color: #33f; }
+h1, h2, h3 {color:#444}
+
+body {
+ color: #222;
+ background: #e5e5e5;
+ font-family: "Bitstream Vera Sans", verdana, sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+}
+
+p {
+ font-size: 14px;
+ line-height: 20px;
+}
+
+input.checkbox {
+ vertical-align:middle;
+}
+
+#header h1 {
+ font-size: 28px;
+ padding: 5px 0;
+ margin: 5px 0;
+}
+
+.hightlight {
+ background-color: #ffc;
+}
+.small {
+ font-size: 11px;
+}
+.gray {
+ color: #999;
+}
+#header {
+ background: #48625B;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#main {
+ background: #e5e5e5;
+ width: 69%;
+}
+
+#main .block {
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ padding: 0;
+ margin-bottom:20px;
+ padding-bottom: 20px;
+ background: #fff;
+}
+
+#main .block h2.title {
+ margin: 0 0 20px 0;
+ background-color: #E9FAE6;
+ padding: 5px 5px 5px 15px;
+ font-size:18px;
+}
+
+.main_container {
+ padding:10px;
+}
+
+/* #sidebar .block { background: #FFF; padding-bottom:0px; } */
+
+#sidebar .notice {
+ background-color: #ffc;
+ padding: 0 10px;
+ border-bottom:1px solid #ddd;
+ border-right:1px solid #ddd;
+ border-top:1px solid #fff;
+ border-left:1px solid #fff;
+}
+#sidebar .notice h2 {
+ font-size:16px;
+ margin: 5px 0;
+ border-bottom:1px solid #aaa;
+}
+#sidebar .notice p {
+ font-size:12px;
+}
+
+#sidebar .block {
+ padding-bottom: 0;
+}
+
+#sidebar .block .content {
+ padding: 0 10px;
+}
+
+
+#sidebar h3 {
+ background: #c7d8d8;
+ border-bottom:1px solid #999;
+ padding: 5px 10px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ font-size:14px;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #E9FAE6;
+ color: #444;
+ font-size:14px;
+ text-decoration:underline;
+}
+#sidebar ul.navigation li.last a {
+ border-bottom: none;
+}
+
+#sidebar ul.navigation li a:link,#sidebar ul.navigation li a:visited {
+ padding: 5px 10px;
+ color:#444;
+ text-decoration: none;
+}
+#sidebar ul.navigation li a:hover {
+ text-decoration:underline;
+}
+#sidebar .block .sidebar-block h4 {
+ border-bottom: 1px solid #bbb;
+}
+#main-navigation ul li {
+ background: #30423E;
+}
+
+#main-navigation ul li:hover {
+ background: #23302D;
+}
+
+#main-navigation ul li.active {
+ background: #e5e5e5;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+#main-navigation ul li a {
+ font-size: 14px;
+ padding: 4px 10px;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #333;
+}
+#user-navigation ul li a:hover {
+ text-decoration: underline;
+}
+.secondary-navigation {
+ background: #48625B;
+ border-bottom-color: #30423e;
+ border-bottom: 5px solid #30423e;
+}
+
+.secondary-navigation ul li.active {
+ background-color: #30423e;
+}
+
+.secondary-navigation ul li:hover {
+ background-color: #23302d;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #30423e;
+ color: #FFF;
+ border: 1px solid #30423e;
+ -moz-border-radius:5px;
+}
+
+.pagination a {
+ color: #364B69;
+ border: 1px solid #ddd;
+ -moz-border-radius:5px;
+}
+
+.pagination a:hover {
+ color: #444;
+ background: #E9FAE6;
+}
+
+/* tables */
+
+.table th {
+ background: #48625B;
+ color: #FFF;
+ font-weight:normal;
+ padding:3px;
+}
+
+.table th a.toggle {
+ display: block;
+ width: 12px;
+ height: 12px;
+ background: transparent url('images/tick.png') center no-repeat;
+ text-indent: -9999px;
+ -moz-outline: none;
+}
+
+.table th.first {
+ width: 30px;
+ text-align: center;
+}
+
+.table td {
+ border-bottom: 1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text, .form textarea.textarea {
+ border: 1px solid #ddd;
+ padding: 5px;
+ width: 95%;
+}
+
+.form .navform {
+ padding:10px;
+ background-color: #E9FAE6;
+ font-size:14px;
+ border-bottom:1px solid #ddd;
+ border-right:1px solid #ddd;
+ border-top:1px solid #eee;
+ border-left:1px solid #eee;
+}
+.form .navform input {
+ font-size:14px;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin:0 auto 5px;
+ width:80%;
+}
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #ddf;
+ background-color: #eef;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ margin: 3px 10px 0 0;
+}
+
+ul.list li .left {
+ padding: 5px 5px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #48625B;
+ color: #FFF;
+}
diff --git a/chef-server-slice/public/stylesheets/themes/bec/style.css b/chef-server-slice/public/stylesheets/themes/bec/style.css
new file mode 100644
index 0000000000..c94474866a
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/bec/style.css
@@ -0,0 +1,301 @@
+a:link, a:visited, a:hover, a:active { color: #33f; }
+h1, h2, h3 {color:#444}
+
+body {
+ color: #333;
+ background: #e5e5e5;
+ font-family: Verdana, Arial, "Bitstream Vera Sans", sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+}
+
+p {
+ font-size: 12px;
+ line-height: 20px;
+}
+
+input.checkbox {
+ vertical-align:middle;
+}
+
+#header h1 {
+ font-size: 28px;
+ padding: 5px 0;
+ margin: 5px 0;
+}
+
+.hightlight {
+ background-color: #ffc;
+}
+.small {
+ font-size: 11px;
+}
+.gray {
+ color: #999;
+}
+#header {
+ background: #006666;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#main {
+ background: #e5e5e5;
+ width: 73%;
+}
+
+#main .block {
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ padding: 0;
+ margin-bottom:20px;
+ padding-bottom: 20px;
+ background: #fff;
+}
+
+#main .block h2.title {
+ margin: 0 0 20px 0;
+ background-color: #E6FAFA;
+ padding: 5px 5px 5px 15px;
+ font-size:18px;
+}
+
+.main_container {
+ padding:10px;
+}
+
+#footer .block p {
+ color:#aaa;
+ font-size:11px;
+}
+
+/* #sidebar .block { background: #FFF; padding-bottom:0px; } */
+
+#sidebar .notice {
+ background-color: #ffc;
+ padding: 0 10px;
+ border-bottom:1px solid #ddd;
+ border-right:1px solid #ddd;
+ border-top:1px solid #fff;
+ border-left:1px solid #fff;
+}
+#sidebar .notice h2 {
+ font-size:16px;
+ margin: 5px 0;
+ border-bottom:1px solid #aaa;
+}
+#sidebar .notice p {
+ font-size:12px;
+}
+
+#sidebar .block {
+ padding-bottom: 0;
+}
+
+#sidebar .block .content {
+ padding: 0 10px;
+}
+
+
+#sidebar h3 {
+ background: #c7d8d8;
+ border-bottom:1px solid #999;
+ padding: 5px 10px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ font-size:12px;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #e1efef;
+ color: #444;
+ font-size:12px;
+ text-decoration:underline;
+}
+#sidebar ul.navigation li.last a {
+ border-bottom: none;
+}
+
+#sidebar ul.navigation li a:link,#sidebar ul.navigation li a:visited {
+ padding: 5px 10px;
+ color:#444;
+ text-decoration: none;
+}
+#sidebar ul.navigation li a:hover {
+ text-decoration:underline;
+}
+#sidebar .block .sidebar-block h4 {
+ border-bottom: 1px solid #bbb;
+}
+#main-navigation ul li {
+ background: #008c8c;
+}
+
+#main-navigation ul li:hover {
+ background: #00b2b2;
+}
+
+#main-navigation ul li.active {
+ background: #f0f0ee;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+#main-navigation ul li a {
+ font-size: 12px;
+ padding: 4px 10px;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #364b69;
+}
+#user-navigation ul li a:hover {
+ text-decoration: underline;
+}
+.secondary-navigation {
+ background: #006666;
+ border-bottom-color: #008c8c;
+ border-bottom: 5px solid #008c8c;
+}
+
+.secondary-navigation ul li.active {
+ background-color: #008c8c;
+}
+
+.secondary-navigation ul li:hover {
+ background-color: #00b2b2;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #008c8c;
+ color: #FFF;
+ border: 1px solid #008c8c;
+ -moz-border-radius:5px;
+}
+
+.pagination a {
+ color: #364B69;
+ border: 1px solid #ddd;
+ -moz-border-radius:5px;
+ font-size:11px;
+}
+
+.pagination a:hover {
+ color: #444;
+ background: #E6FAFA;
+}
+
+/* tables */
+
+.table th {
+ background: #006666;
+ color: #FFF;
+ font-weight:normal;
+ padding:3px;
+}
+
+.table th a.toggle {
+ display: block;
+ width: 12px;
+ height: 12px;
+ background: transparent url('images/tick.png') center no-repeat;
+ text-indent: -9999px;
+ -moz-outline: none;
+}
+
+.table th.first {
+ width: 30px;
+ text-align: center;
+}
+
+.table td {
+ border-bottom: 1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text, .form textarea.textarea {
+ border: 1px solid #ddd;
+ padding: 5px;
+ width:99%;
+}
+
+.form .navform {
+ padding:10px;
+ background-color: #f1f8f8;
+ font-size:14px;
+ border-bottom:1px solid #ddd;
+ border-right:1px solid #ddd;
+ border-top:1px solid #eee;
+ border-left:1px solid #eee;
+}
+.form .navform input {
+ font-size:14px;
+}
+
+.description {
+ color:#aaa;
+ font-family:Georgia, serif;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin:0 auto 5px;
+ width:80%;
+}
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #ddf;
+ background-color: #eef;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ margin: 3px 10px 0 0;
+}
+
+ul.list li .left {
+ padding: 5px 5px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #006666;
+ color: #FFF;
+}
diff --git a/chef-server-slice/public/stylesheets/themes/blue/style.css b/chef-server-slice/public/stylesheets/themes/blue/style.css
new file mode 100644
index 0000000000..cce8f4bdf0
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/blue/style.css
@@ -0,0 +1,280 @@
+.small { font-size:12px; }
+.gray { color:#999999; }
+.hightlight { background-color:#FFFFCC; }
+
+a:link, a:visited, a:hover, a:active, h1, h2, h3 { color: #2F427A; }
+a { -moz-outline: none; }
+
+body {
+ color: #222;
+ background: #f0f0ee;
+ font-family: helvetica, arial, sans-serif;
+ font-size: 14px;
+}
+
+hr {
+ background: #f0f0ee;
+ color: #f0f0ee;
+}
+
+#header {
+ background: #2F427A;
+}
+
+#header h1 {
+ padding: 20px 0;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#user-navigation {
+ top: auto;
+ bottom: 5px;
+ right: 25px;
+}
+
+#main .block .content {
+ background: #FFF;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+#sidebar .block {
+ background: #FFF;
+}
+
+#sidebar .notice {
+ background: #FFFFCC;
+}
+
+#sidebar h3 {
+ background: #2F427A;
+ color: #FFF;
+ border-bottom: 10px solid #262626;
+ font-size: 15px;
+}
+
+#main-navigation ul li {
+ padding-left: 15px;
+}
+
+#main-navigation ul li a {
+ padding: 8px 0;
+}
+
+#main-navigation ul li.active {
+ padding: 0;
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active {
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active a {
+ padding: 8px 15px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ background: #FFF;
+ border-bottom: 1px solid #F0F0EE;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #316291;
+ color: #FFF;
+}
+
+#main-navigation {
+ background: #262626;
+}
+
+#main-navigation ul li {
+ background: #262626;
+ margin-right: 0;
+}
+
+#main-navigation ul li.active {
+ background: #f0f0ee;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+.secondary-navigation li a:hover {
+ background: #5C637A;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #262626;
+}
+
+.secondary-navigation {
+ background: #2F427A;
+ border-bottom-color: #262626;
+ font-size: 15px;
+}
+
+.secondary-navigation ul li.active, .secondary-navigation ul li.active a:hover {
+ background-color: #262626;
+}
+
+#footer .block {
+ color: #FFF;
+ background: #262626;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 10px;
+}
+
+/* pagination */
+
+.pagination span, .pagination a {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ padding-top: 4px;
+}
+
+.pagination span.current {
+ background: #262626;
+ color: #FFF;
+ border-color: #262626;
+}
+
+.pagination a {
+ color: #262626;
+ border-color: #262626;
+}
+
+.pagination a:hover {
+ color: #FFF;
+ background: #262626;
+}
+
+/* tables */
+
+.table th {
+ background: #262626;
+ color: #FFF;
+}
+
+.table td {
+ border-bottom:1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border: 1px solid #262626;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #262626;
+ padding: 2px 5px;
+ border: 1px solid #262626;
+ cursor: pointer;
+}
+
+.form .description {
+ font-style: italic;
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin: 0 auto 15px;
+
+}
+
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #1FDF00;
+ background-color: #BBFFB6;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #2F427A;
+ color: #FFF;
+}
+
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li, .secondary-navigation, #main .block, #sidebar .block, #sidebar h3, ul.list li,
+#footer .block, .form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a, .secondary-navigation ul li.first, .table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block, #sidebar ul.navigation, ul.list li, #footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+} \ No newline at end of file
diff --git a/chef-server-slice/public/stylesheets/themes/default/style.css b/chef-server-slice/public/stylesheets/themes/default/style.css
new file mode 100644
index 0000000000..e7a5ee1271
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/default/style.css
@@ -0,0 +1,267 @@
+.small { font-size:11px; }
+.gray { color:#999999; }
+.hightlight { background-color:#FFFFCC; }
+
+a:link, a:visited, a:hover, a:active, h1, h2, h3 { color: #7A1818; }
+a { -moz-outline: none; }
+
+body {
+ color: #222;
+ background: #f0f0ee;
+ font-family: helvetica, arial, sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+ color: #f0f0ee;
+}
+
+#header {
+ background: #7A1818;
+}
+
+#header h1 {
+ padding: 20px 0;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#user-navigation {
+ top: auto;
+ bottom: 5px;
+ right: 25px;
+}
+
+#main .block .content {
+ background: #FFF;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+#sidebar .block {
+ background: #FFF;
+}
+
+#sidebar h3 {
+ background: #7A1818;
+ color: #FFF;
+ border-bottom: 10px solid #262626;
+}
+
+#main-navigation ul li {
+ padding-left: 15px;
+}
+
+#main-navigation ul li a {
+ padding: 8px 0;
+}
+
+#main-navigation ul li.active {
+ padding: 0;
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active {
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active a {
+ padding: 8px 15px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ background: #FFF;
+ border-bottom: 1px solid #F0F0EE;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #470E0E;
+ color: #FFF;
+}
+
+#main-navigation {
+ background: #262626;
+}
+
+#main-navigation ul li {
+ background: #262626;
+ margin-right: 0;
+}
+
+#main-navigation ul li.active {
+ background: #f0f0ee;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+.secondary-navigation li a:hover {
+ background: #470E0E;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #262626;
+}
+
+.secondary-navigation {
+ background: #7A1818;
+ border-bottom-color: #262626;
+}
+
+.secondary-navigation ul li.active, .secondary-navigation ul li.active a:hover {
+ background-color: #262626;
+}
+
+#footer .block {
+ color: #FFF;
+ background: #262626;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 10px;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #262626;
+ color: #FFF;
+ border-color: #262626;
+}
+
+.pagination a {
+ color: #262626;
+ border-color: #262626;
+}
+
+.pagination a:hover {
+ color: #FFF;
+ background: #262626;
+}
+
+/* tables */
+
+.table th {
+ background: #262626;
+ color: #FFF;
+}
+
+.table td {
+ border-bottom:1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border: 1px solid #262626;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #262626;
+ padding: 2px 5px;
+ border: 1px solid #262626;
+ cursor: pointer;
+}
+
+.form .description {
+ font-style: italic;
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin: 0 auto 15px;
+
+}
+
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #1FDF00;
+ background-color: #BBFFB6;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #7A1818;
+ color: #FFF;
+}
+
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li, .secondary-navigation, #main .block, #sidebar .block, #sidebar h3, ul.list li,
+#footer .block, .form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a, .secondary-navigation ul li.first, .table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block, #sidebar ul.navigation, ul.list li, #footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+} \ No newline at end of file
diff --git a/chef-server-slice/public/stylesheets/themes/djime-cerulean/style.css b/chef-server-slice/public/stylesheets/themes/djime-cerulean/style.css
new file mode 100644
index 0000000000..9b050c6785
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/djime-cerulean/style.css
@@ -0,0 +1,298 @@
+/**
+ * Cerulean web-app-theme made for Djime: http://github.com/mikl/djime/
+ *
+ * Please note that we're using CSSEdit's @group comment syntax.
+ *
+ * Colour sheme:
+ * Cerulean: #007BA7
+ * Bright blue: #01B8DE
+ * Near-white: #F7F7F8
+ * Silver grey: #C2C8D1
+ * Dark blue: #001C26
+ *
+ * http://www.colourlovers.com/palette/646252/Cerulean_touch
+ */
+
+/* @group General styles */
+
+.small { font-size:11px; }
+.gray { color:#999; }
+.hightlight { background-color:#ffc; }
+
+a:link, a:visited, a:hover, a:active, h1, h2, h3 { color: #007BA7; }
+
+body {
+ color: #222;
+ background: #C2C8D1;
+ font-family: "Helvetica Neue",Helvetica,Arial,"Bitstream Vera Sans",sans-serif;
+}
+
+hr {
+ background: #EEF0F0;
+ color: #EEF0F0;
+}
+
+/* @end */
+
+/* @group Header */
+
+#header {
+ background: #007BA7;
+}
+
+#header h1 {
+ padding: 20px 0;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #F7F7F8;
+}
+
+/* @end */
+
+#user-navigation {
+ top: auto;
+ bottom: 5px;
+ right: 25px;
+}
+
+#main .block .content {
+ background: #F7F7F8;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+
+
+/* @group Main navigation */
+
+#main-navigation ul li {
+ padding-left: 0;
+}
+
+#main-navigation ul li a {
+ padding: 8px 0;
+}
+
+#main-navigation ul li a {
+ padding: 8px 15px;
+}
+
+#main-navigation {
+ background-color: #005573;
+}
+
+#main-navigation ul li a:hover {
+ background-color: #001C26;
+}
+
+#main-navigation ul li.active a {
+ background-color: #C2C8D1;
+ background: -webkit-gradient(linear, left top, left bottom, from(#C2C8D1), to(#C2C8D1), color-stop(0.5, #F7F7F8), color-stop(0.5, #F7F7F8));
+
+}
+
+/* @end */
+
+/* @group Secondary navigation */
+
+.secondary-navigation li a:hover {
+ background: #005573;
+}
+
+.secondary-navigation {
+ background: #007BA7;
+ border-bottom-width: 7px;
+ border-bottom-color: #005573;
+}
+
+.secondary-navigation ul li.active, .secondary-navigation ul li.active a:hover {
+ background-color: #005573;
+}
+
+/* @end */
+
+/* @group Sidebar */
+
+#sidebar .block {
+ background: #F7F7F8;
+}
+
+#sidebar h3 {
+ background: #007BA7;
+ color: #F7F7F8;
+ border-bottom: 7px solid #005573;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ background: #F7F7F8;
+ border-bottom: 1px solid #EEF0F0;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #005573;
+ color: #F7F7F8;
+}
+
+/* @end */
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #F7F7F8;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #001C26;
+}
+
+#footer .block {
+ color: #F7F7F8;
+ background: #005573;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 10px;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #005573;
+ color: #F7F7F8;
+ border-color: #005573;
+}
+
+.pagination a,
+.pagination span {
+ color: #001C26;
+ border-color: #005573;
+}
+
+.pagination a:hover {
+ color: #F7F7F8;
+ background: #005573;
+}
+
+/* tables */
+
+.table th {
+ background: #C2C8D1;
+ color: #001C26;
+}
+
+.table td {
+ border-bottom:1px solid #EEF0F0;
+}
+
+/* forms */
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border: 1px solid #001C26;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #001C26;
+ padding: 2px 5px;
+ border: 1px solid #001C26;
+ cursor: pointer;
+}
+
+.form .description {
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* @group Flash messages */
+
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin: 0 auto 15px;
+}
+
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #e0d300;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #8ec4df;
+ background-color: #dffaff;
+}
+
+/* @end */
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #EEF0F0;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #EEF0F0;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #F7F7F8;
+}
+
+#box .block h2 {
+ background: #005573;
+ color: #F7F7F8;
+}
+
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li, #main-navigation li a, .secondary-navigation, #main .block, #sidebar .block, #sidebar h3, ul.list li,
+#footer .block, .form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a, .secondary-navigation ul li.first, .table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block, #sidebar ul.navigation, ul.list li, #footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+} \ No newline at end of file
diff --git a/chef-server-slice/public/stylesheets/themes/kathleene/style.css b/chef-server-slice/public/stylesheets/themes/kathleene/style.css
new file mode 100644
index 0000000000..e68a545431
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/kathleene/style.css
@@ -0,0 +1,272 @@
+.small { font-size:11px; }
+.gray { color:#999999; }
+.hightlight { background-color:#FFFFCC; }
+
+a:link, a:visited, a:hover, a:active, h1, h2, h3 { color: #AF0000; }
+a { -moz-outline: none; }
+
+body {
+ color: #222;
+ background: #f0f0ee;
+ font-family: helvetica, arial, sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+ color: #f0f0ee;
+}
+
+#header {
+ background: #AF0000;
+}
+
+#header h1 {
+ padding: 20px 0;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#user-navigation {
+ top: auto;
+ bottom: 5px;
+ right: 25px;
+}
+
+#main .block .content {
+ background: #FFF;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+#sidebar .block {
+ background: #FFF;
+}
+
+#sidebar h3 {
+ background: #AF0000;
+ color: #FFF;
+ border-bottom: 5px solid #2a0000;
+}
+
+#main-navigation ul li {
+ padding-left: 15px;
+}
+
+#main-navigation ul li a {
+ padding: 8px 0;
+}
+
+#main-navigation ul li.active {
+ padding: 0;
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active {
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active a {
+ padding: 8px 15px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ background: #FFF;
+ border-bottom: 1px solid #F0F0EE;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #470E0E;
+ color: #FFF;
+}
+
+#main-navigation {
+ background: #2a0000;
+}
+
+#main-navigation ul li {
+ background: #2a0000;
+ margin-right: 0;
+}
+
+#main-navigation ul li.active {
+ background: #f0f0ee;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+.secondary-navigation li a:hover {
+ background: #470E0E;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #2a0000;
+}
+
+.secondary-navigation {
+ background: #AF0000;
+ border-bottom-color: #2a0000;
+}
+
+.secondary-navigation ul li.active, .secondary-navigation ul li.active a:hover {
+ background-color: #2a0000;
+}
+
+#footer .block {
+ color: #FFF;
+ background: #2a0000;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 10px;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #2a0000;
+ color: #FFF;
+ border-color: #2a0000;
+}
+
+.pagination a {
+ color: #2a0000;
+ border-color: #2a0000;
+}
+
+.pagination a:hover {
+ color: #FFF;
+ background: #2a0000;
+}
+
+/* tables */
+
+.table th {
+ background: #100000;
+ border-bottom: 3px solid #700000;
+ color: #FFF;
+}
+
+.table td {
+ border-bottom:1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border: 1px solid #2a0000;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #2a0000;
+ padding: 2px 5px;
+ border: 1px solid #2a0000;
+ cursor: pointer;
+}
+
+.form .description {
+ font-style: italic;
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin: 0 auto 15px;
+
+}
+
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #1FDF00;
+ background-color: #BBFFB6;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #AF0000;
+ color: #FFF;
+}
+
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li, .secondary-navigation, #main .block, #sidebar .block, #sidebar h3, ul.list li,
+#footer .block, .form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a, .secondary-navigation ul li.first, .table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block, #sidebar ul.navigation, ul.list li, #footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+}
+
+.secondary-navigation {
+ border-bottom-width: 5px;
+}
diff --git a/chef-server-slice/public/stylesheets/themes/orange/style.css b/chef-server-slice/public/stylesheets/themes/orange/style.css
new file mode 100644
index 0000000000..90e7d8de58
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/orange/style.css
@@ -0,0 +1,263 @@
+.small { font-size:11px; }
+.gray { color:#999999; }
+.hightlight { background-color:#FFFFCC; }
+
+a:link, a:visited, a:hover, a:active, h1, h2, h3 { color: #ff7900; }
+a { -moz-outline: none; }
+
+body {
+ color: #222;
+ background: #f0f0ee;
+ font-family: helvetica, arial, sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+ color: #f0f0ee;
+}
+
+#header {
+ background: #ff7900;
+}
+
+#header h1 a:link, #header h1 a:active, #header h1 a:hover, #header h1 a:visited {
+ color: #FFF;
+}
+
+#user-navigation {
+ top: auto;
+ bottom: 5px;
+ right: 25px;
+}
+
+#main .block .content {
+ background: #FFF;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+#sidebar .block {
+ background: #FFF;
+}
+
+#sidebar h3 {
+ background: #ff7900;
+ color: #FFF;
+ border-bottom: 10px solid #262626;
+}
+
+#main-navigation ul li {
+ padding-left: 15px;
+}
+
+#main-navigation ul li a {
+ padding: 8px 0;
+}
+
+#main-navigation ul li.active {
+ padding: 0;
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active {
+ margin-left: 15px;
+}
+
+#main-navigation ul li.active a {
+ padding: 8px 15px;
+}
+
+#sidebar ul li a:link, #sidebar ul li a:visited {
+ background: #FFF;
+ border-bottom: 1px solid #F0F0EE;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover, #sidebar ul li a:active {
+ background: #863800;
+ color: #FFF;
+}
+
+#main-navigation {
+ background: #262626;
+}
+
+#main-navigation ul li {
+ background: #262626;
+ margin-right: 0;
+}
+
+#main-navigation ul li.active {
+ background: #f0f0ee;
+}
+
+#main-navigation ul li a:link, #main-navigation ul li a:visited, #main-navigation ul li a:hover, #main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited, .secondary-navigation ul li a:hover, .secondary-navigation ul li a:active,
+#user-navigation ul li a:link, #user-navigation ul li a:visited, #user-navigation ul li a:hover, #user-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+.secondary-navigation li a:hover {
+ background: #863800;
+}
+
+#main-navigation ul li.active a:link, #main-navigation ul li.active a:visited, #main-navigation ul li.active a:hover, #main-navigation ul li.active a:active {
+ color: #262626;
+}
+
+.secondary-navigation {
+ background: #ff7900;
+ border-bottom-color: #262626;
+}
+
+.secondary-navigation ul li.active, .secondary-navigation ul li.active a:hover {
+ background-color: #262626;
+}
+
+#footer .block {
+ color: #FFF;
+ background: #262626;
+ width: 70%;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 10px;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #262626;
+ color: #FFF;
+ border-color: #262626;
+}
+
+.pagination a {
+ color: #262626;
+ border-color: #262626;
+}
+
+.pagination a:hover {
+ color: #FFF;
+ background: #262626;
+}
+
+/* tables */
+
+.table th {
+ background: #262626;
+ color: #FFF;
+}
+
+.table td {
+ border-bottom:1px solid #F0F0EE;
+}
+
+/* forms */
+
+.form input.text, .form textarea {
+ width: 100%;
+ border: 1px solid #262626;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #262626;
+ padding: 2px 5px;
+ border: 1px solid #262626;
+ cursor: pointer;
+}
+
+.form .description {
+ font-style: italic;
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin:0 auto 5px;
+
+}
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #1FDF00;
+ background-color: #BBFFB6;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ background: #ff7900;
+ color: #FFF;
+}
+
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li, .secondary-navigation, #main .block, #sidebar .block, #sidebar h3, ul.list li,
+#footer .block, .form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a, .secondary-navigation ul li.first, .table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block, #sidebar ul.navigation, ul.list li, #footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+} \ No newline at end of file
diff --git a/chef-server-slice/public/stylesheets/themes/reidb-greenish/style.css b/chef-server-slice/public/stylesheets/themes/reidb-greenish/style.css
new file mode 100644
index 0000000000..23e5245eb4
--- /dev/null
+++ b/chef-server-slice/public/stylesheets/themes/reidb-greenish/style.css
@@ -0,0 +1,301 @@
+.small { font-size:11px; }
+.gray { color:#a2b0b6; }
+.hightlight { background-color:#d6e7c7; }
+
+a:link, a:visited, a:hover, a:active { color: #81B953; }
+h1, h2, h3 { color: #3B5526; }
+a { -moz-outline: none; }
+
+body {
+ color: #222;
+ background: #e4ebe4;
+ font-family: helvetica, arial, sans-serif;
+}
+
+hr {
+ background: #f0f0ee;
+ color: #f0f0ee;
+}
+
+#header {
+ background: #c9deb7;
+
+}
+
+#header h1{
+ padding: 20px 0;
+ font-weight: bold;
+
+}
+
+#header h1 a:link, #header h1 a:active,
+#header h1 a:hover, #header h1 a:visited {
+ color: #3B5526;
+}
+
+#main .block .content {
+ background: #FFF;
+ padding-top: 1px;
+}
+
+#main .block .content h2 {
+ margin-left: 15px;
+}
+
+#main .content { border: 1px solid #81B953;}
+
+#sidebar .block {
+ background: #FFF;
+ border: none;
+}
+
+#sidebar h3 {
+ padding: 8px 12px;
+ background: #3B5526;
+ color: #FFF;
+ font-weight: bold;
+ border-bottom: 5px solid #81B953;
+}
+
+
+#sidebar ul li a:link,
+#sidebar ul li a:visited {
+ background: #FFF;
+ border-bottom: 1px solid #F0F0EE;
+ text-decoration: none;
+}
+
+#sidebar ul li a:hover,
+#sidebar ul li a:active {
+ background: #D3E8C1;
+ color: #FFF;
+}
+
+#main-navigation {
+ background: #44721e;
+ margin-right: 20px;
+ padding: 7px 7px 0 7px;
+}
+
+#main-navigation ul li {
+ background: #91B96F;
+ margin-right: 7px;
+}
+
+#main-navigation ul li a {
+ padding: 10px 10px 5px 10px;
+}
+
+#main-navigation ul li.active {
+ background: #e4ebe4;
+ border: 1px solid #91B96F;
+ border-bottom: none;
+ font-weight: bold;
+}
+
+#main-navigation ul li a:hover,
+#main-navigation ul li a:link,
+#main-navigation ul li a:visited,
+#main-navigation ul li a:active,
+.secondary-navigation ul li a:link, .secondary-navigation ul li a:visited,
+.secondary-navigation ul li a:hover, .secondary-navigation ul li a:active {
+ text-decoration: none;
+ color: #FFF;
+}
+
+#user-navigation ul li a:link,
+#user-navigation ul li a:visited,
+#user-navigation ul li a:active {
+ color: #3B5526;
+}
+
+#user-navigation ul li a:hover { color: #fff; }
+
+#main-navigation ul li.active a:link,
+#main-navigation ul li.active a:visited,
+#main-navigation ul li.active a:hover,
+#main-navigation ul li.active a:active {
+ color: #262626;
+}
+
+.secondary-navigation {
+ background: #3B5526;
+ border-bottom: 5px solid #81b953;
+}
+
+.secondary-navigation ul li a {
+display:block;
+padding: 8px 12px;
+}
+
+.secondary-navigation ul li.active {background: #81b953; font-weight: bold;}
+.secondary-navigation ul li.active a:hover {
+ background-color: #81B953;
+}
+
+.secondary-navigation li a:hover {
+ background: #81B953;
+}
+
+#footer .block {
+ color: #FFF;
+ background: #3B5526;
+}
+
+#footer .block p {
+ margin: 0;
+ padding: 5px;
+}
+
+/* pagination */
+
+.pagination span.current {
+ background: #262626;
+ color: #FFF;
+ border-color: #262626;
+}
+
+.pagination a {
+ color: #262626;
+ border-color: #262626;
+}
+
+.pagination a:hover {
+ color: #FFF;
+ background: #262626;
+}
+
+/* tables */
+
+
+.table th {
+ background: #253618;
+ color: #FFF;
+}
+
+.table tr th { padding: 5px; }
+
+.table td {
+ border-bottom:1px solid #F0F0EE;
+}
+.table tr.odd {background: #ebfadf;}
+.table tr.even {background: #d3e8c1;}
+
+
+/* forms */
+
+.form input.text_field, .form textarea.text_area {
+ width: 100%;
+ border: 1px solid #262626;
+}
+
+.form input.button {
+ background: #EEE;
+ color: #262626;
+ padding: 2px 5px;
+ border: 1px solid #262626;
+ cursor: pointer;
+}
+
+.form .description {
+ font-style: italic;
+ color: #8C8C8C;
+ font-size: .9em;
+}
+
+/* flash-messages */
+.flash .message {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ text-align:center;
+ margin: 0 auto 15px;
+
+}
+
+.flash .message p {
+ margin:8px;
+}
+.flash .error {
+ border: 1px solid #fbb;
+ background-color: #fdd;
+}
+.flash .warning {
+ border: 1px solid #fffaaa;
+ background-color: #ffffcc;
+}
+.flash .notice {
+ border: 1px solid #1FDF00;
+ background-color: #BBFFB6;
+}
+
+/* lists */
+
+ul.list li {
+ border-bottom-color: #F0F0EE;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+ul.list li .item .avatar {
+ border-color: #F0F0EE;
+ border-width: 1px;
+ border-style: solid;
+ padding: 2px;
+}
+
+/* box */
+
+#box .block {
+ background: #FFF;
+}
+
+#box .block h2 {
+ color: #fff;
+ background: #3B5526;
+ border-bottom: 5px solid #81b953;
+}
+
+#box .block .content { border: 1px solid #81b953; border}
+
+/* login */
+
+#block-login { }
+#block-login h2 { background: #3B5526;border-bottom: 5px solid #81b953;}
+
+/* rounded borders */
+
+#main, #main-navigation, #main-navigation li,
+.secondary-navigation, #main .block, #sidebar .block,
+#sidebar h3, ul.list li, #footer .block,
+.form input.button, #box .block, #box .block h2 {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation li.first a,
+.secondary-navigation ul li.first,
+.table th.first, .table th.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+.table th.last {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+}
+
+.secondary-navigation ul li.first {
+ -moz-border-radius-topleft: 4px;
+ -webkit-border-top-left-radius: 4px;
+}
+
+#sidebar, #sidebar .block, #main .block,
+#sidebar ul.navigation, ul.list li,
+#footer .block, .form input.button, #box .block {
+ -moz-border-radius-bottomleft: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+} \ No newline at end of file
diff --git a/chef-server/bin/chef-indexer b/chef-server/bin/chef-indexer
index f36e09a8c3..74eb5efca5 100755
--- a/chef-server/bin/chef-indexer
+++ b/chef-server/bin/chef-indexer
@@ -31,7 +31,7 @@ config = {
:config_file => "/etc/chef/server.rb",
}
opts = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)"
+ opts.banner = "Usage: #{$0} (options)"
opts.on("-c CONFIG", "--config CONFIG", "The Chef Config file to use") do |c|
config[:config_file] = c
end
@@ -64,6 +64,8 @@ end
Chef::Config.from_file(config[:config_file])
Chef::Config.configure { |c| c.merge!(config) }
+Chef::Daemon.change_privilege
+
if Chef::Config[:daemonize]
unless Chef::Config[:log_location].is_a? IO
Chef::Log.init(Chef::Config[:log_location])
diff --git a/chef-server/config/init.rb b/chef-server/config/init.rb
index 1d091484e2..aedc9b6753 100644
--- a/chef-server/config/init.rb
+++ b/chef-server/config/init.rb
@@ -23,4 +23,5 @@ end
Merb::BootLoader.after_app_loads do
# This will get executed after your app's classes have been loaded.
+ OpenID::Util.logger = Merb.logger
end
diff --git a/chef-server/contrib/el/chef-indexer.config b/chef-server/contrib/el/chef-indexer.config
new file mode 100644
index 0000000000..1be9c9f0d2
--- /dev/null
+++ b/chef-server/contrib/el/chef-indexer.config
@@ -0,0 +1,10 @@
+#
+# Chef Server Config File
+#
+
+log_level :info
+search_index_path "/var/lib/chef/search_index"
+
+Chef::Log::Formatter.show_time = false
+
+pid_file "/var/run/chef/chef-indexer.pid"
diff --git a/chef-server/contrib/el/chef-indexer.init b/chef-server/contrib/el/chef-indexer.init
new file mode 100644
index 0000000000..94b5b6ecfd
--- /dev/null
+++ b/chef-server/contrib/el/chef-indexer.init
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Startup script for chef-indexer
+#
+# chkconfig: - 75 25
+# description: Server component of the Chef systems integration framework.
+# processname: chef-indexer
+#
+# config: /etc/sysconfig/chef-indexer
+# pidfile: /var/run/chef/chef-indexer.pid
+
+# Source function library
+. /etc/init.d/functions
+
+[ -f /etc/sysconfig/chef-indexer ] && . /etc/sysconfig/chef-indexer
+
+prog="chef-indexer"
+PIDFILE=/var/run/chef/chef-indexer.pid
+LOCKFILE=/var/lock/subsys/$prog
+CONFIG=/etc/chef/indexer.rb
+USER="chef"
+GROUP="chef"
+LOGFILE=/var/log/chef/chef-indexer.log
+OPTIONS=
+
+start() {
+ echo -n "Starting $prog:"
+ daemon chef-indexer -d -c "$CONFIG" -u "$USER" -g "$GROUP" -L "$LOGFILE" "$OPTIONS" ">/dev/null"
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch ${LOCKFILE}
+ return $RETVAL
+}
+
+stop() {
+ echo -n "Stopping $prog: "
+ if [ -f $PIDFILE ]; then
+ killproc chef-indexer
+ RETVAL=$?
+ if [ $RETVAL -ne 0 ]; then
+ failure;
+ fi;
+ else
+ RETVAL=1
+ failure;
+ fi
+ rm -f $LOCKFILE
+ echo
+ return $RETVAL
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f $LOCKFILE ]; then
+ stop
+ start
+ fi
+ ;;
+ status)
+ status chef-indexer
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|condrestart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/chef-server/contrib/el/chef-server.config b/chef-server/contrib/el/chef-server.config
new file mode 100644
index 0000000000..6d2a7794e0
--- /dev/null
+++ b/chef-server/contrib/el/chef-server.config
@@ -0,0 +1,22 @@
+#
+# Chef Server Config File
+#
+
+log_level :info
+ssl_verify_mode :verify_none
+registration_url "http://cserver:4000"
+openid_url "http://cserver:4001"
+template_url "http://cserver:4000"
+remotefile_url "http://cserver:4000"
+search_url "http://cserver:4000"
+cookbook_path [ "/var/lib/chef/site-cookbooks", "/var/lib/chef/cookbooks" ]
+
+merb_root "/var/lib/chef/merb"
+node_path "/etc/chef/node"
+file_store_path "/var/lib/chef/store"
+search_index_path "/var/lib/chef/search_index"
+openid_store_path "/var/lib/chef/openid/db"
+openid_cstore_path "/var/lib/chef/openid/cstore"
+file_cache_path "/var/lib/chef/cache"
+
+Chef::Log::Formatter.show_time = false
diff --git a/chef-server/contrib/el/chef-server.init b/chef-server/contrib/el/chef-server.init
new file mode 100644
index 0000000000..5e5e9231d2
--- /dev/null
+++ b/chef-server/contrib/el/chef-server.init
@@ -0,0 +1,77 @@
+#!/bin/bash
+# Startup script for chef-server
+#
+# chkconfig: - 65 35
+# description: Server component of the Chef systems integration framework.
+# processname: chef-server
+#
+# config: /etc/sysconfig/chef-server
+# pidfile: /var/run/chef/chef-server.pid
+
+# Source function library
+. /etc/init.d/functions
+
+[ -f /etc/sysconfig/chef-server ] && . /etc/sysconfig/chef-server
+
+prog="chef-server"
+PIDFILE=/var/run/chef/chef-server.pid
+LOCKFILE=/var/lock/subsys/$prog
+CONFIG=/etc/chef/server.rb
+USER="chef"
+GROUP="chef"
+CLUSTER_NODES=2
+LOGFILE=/var/log/chef/chef-server-merb.log
+OPTIONS=
+
+start() {
+ echo -n "Starting $prog:"
+ daemon chef-server -d -c "$CLUSTER_NODES" -C "$CONFIG" -u "$USER" -G "$GROUP" -L "$LOGFILE" -P "$PIDFILE" "$OPTIONS" ">/dev/null"
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch ${LOCKFILE}
+ return $RETVAL
+}
+
+stop() {
+ echo -n "Stopping $prog: "
+ if [ -f $PIDFILE ]; then
+ killproc chef-server
+ RETVAL=$?
+ if [ $RETVAL -ne 0 ]; then
+ failure;
+ fi;
+ else
+ RETVAL=1
+ failure;
+ fi
+ rm -f $LOCKFILE
+ echo
+ return $RETVAL
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f $LOCKFILE ]; then
+ stop
+ start
+ fi
+ ;;
+ status)
+ status chef-server
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|condrestart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/chef-server/lib/chef/search.rb b/chef-server/lib/chef/search.rb
index a26669f76c..3ecd89cbdd 100644
--- a/chef-server/lib/chef/search.rb
+++ b/chef-server/lib/chef/search.rb
@@ -27,7 +27,7 @@ class Chef
@index = Ferret::Index::Index.new(:path => Chef::Config[:search_index_path])
end
- def search(type, query, attributes, &block)
+ def search(type, query="*", attributes=[], &block)
search_query = build_search_query(type, query)
start_time = Time.now
results = []
diff --git a/chef/bin/chef-client b/chef/bin/chef-client
index df06e068fd..1c1b38bd19 100755
--- a/chef/bin/chef-client
+++ b/chef/bin/chef-client
@@ -79,6 +79,8 @@ end
Chef::Config.from_file(config[:config_file])
Chef::Config.configure { |c| c.merge!(config) }
+Chef::Daemon.change_privilege
+
if Chef::Config[:daemonize]
unless Chef::Config[:log_location].is_a? IO
Chef::Log.init(Chef::Config[:log_location])
diff --git a/chef/bin/chef-solo b/chef/bin/chef-solo
index 5543faf9c3..ecc2c57f8a 100755
--- a/chef/bin/chef-solo
+++ b/chef/bin/chef-solo
@@ -34,7 +34,7 @@ config = {
Chef::Config[:solo] = true
opts = OptionParser.new do |opts|
- opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)"
+ opts.banner = "Usage: #{$0} (options)"
opts.on("-c CONFIG", "--config CONFIG", "The Chef Config file to use") do |c|
config[:config_file] = c
end
diff --git a/chef/contrib/el/chef-client.config b/chef/contrib/el/chef-client.config
new file mode 100644
index 0000000000..5be5f35114
--- /dev/null
+++ b/chef/contrib/el/chef-client.config
@@ -0,0 +1,16 @@
+#
+# Chef Client Config File
+#
+
+log_level :info
+ssl_verify_mode :verify_none
+registration_url "http://127.0.0.1:4000"
+openid_url "http://127.0.0.1:4001"
+template_url "http://127.0.0.1:4000"
+remotefile_url "http://127.0.0.1:4000"
+search_url "http://127.0.0.1:4000"
+
+pid_file "/var/run/chef/chef-client.pid"
+
+#interval 1800
+#splay 0
diff --git a/chef/contrib/el/chef-client.init b/chef/contrib/el/chef-client.init
new file mode 100644
index 0000000000..332e9606d9
--- /dev/null
+++ b/chef/contrib/el/chef-client.init
@@ -0,0 +1,74 @@
+#!/bin/bash
+# Startup script for chef-client
+#
+# chkconfig: - 98 02
+# description: Client component of the Chef systems integration framework.
+# processname: chef-client
+#
+# config: /etc/sysconfig/chef-client
+# pidfile: /var/run/chef/chef-client.pid
+
+# Source function library
+. /etc/init.d/functions
+
+[ -f /etc/sysconfig/chef-client ] && . /etc/sysconfig/chef-client
+
+prog="chef-client"
+pidfile=${PIDFILE-/var/run/chef/chef-client.pid}
+lockfile=${LOCKFILE-/var/lock/subsys/$prog}
+config=${CONFIG-/etc/chef/client.rb}
+logfile=${LOGFILE-/var/log/chef/chef-client.log}
+OPTIONS=
+
+start() {
+ echo -n "Starting $prog:"
+ daemon chef-client -d -c "$config" -L "$logfile" "$OPTIONS" ">/dev/null"
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch ${lockfile}
+ return $RETVAL
+}
+
+stop() {
+ echo -n "Stopping $prog: "
+ if [ -f $pidfile ]; then
+ killproc chef-client
+ RETVAL=$?
+ if [ $RETVAL -ne 0 ]; then
+ failure;
+ fi;
+ else
+ RETVAL=1
+ failure;
+ fi
+ rm -f $lockfile
+ echo
+ return $RETVAL
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f $lockfile ]; then
+ stop
+ start
+ fi
+ ;;
+ status)
+ status chef-client
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|condrestart|status}"
+ exit 1
+esac
+
+exit $RETVAL
diff --git a/chef/lib/chef/config.rb b/chef/lib/chef/config.rb
index 9c33ccff53..f9fda13cf0 100644
--- a/chef/lib/chef/config.rb
+++ b/chef/lib/chef/config.rb
@@ -41,8 +41,6 @@ class Chef
:interval => nil,
:splay => nil,
:solo => false,
- :user => nil,
- :group => nil,
:json_attribs => nil,
:cookbook_path => [ "/var/chef/site-cookbooks", "/var/chef/cookbooks" ],
:validation_token => nil,
@@ -62,8 +60,8 @@ class Chef
:template_url => "http://localhost:4000",
:remotefile_url => "http://localhost:4000",
:search_url => "http://localhost:4000",
- :couchdb_database => "chef",
:couchdb_version => nil,
+ :couchdb_database => "chef",
:openid_store_couchdb => false,
:openid_cstore_couchdb => false,
:openid_store_path => "/var/chef/openid/db",
diff --git a/chef/lib/chef/couchdb.rb b/chef/lib/chef/couchdb.rb
index 2c488b2ec2..a374205021 100644
--- a/chef/lib/chef/couchdb.rb
+++ b/chef/lib/chef/couchdb.rb
@@ -25,11 +25,10 @@ require 'json'
class Chef
class CouchDB
include Chef::Mixin::ParamsValidate
-
+
def initialize(url=nil)
url ||= Chef::Config[:couchdb_url]
@rest = Chef::REST.new(url)
- Chef::Config[:couchdb_version] ||= @rest.run_request(:GET, URI.parse(@rest.url + "/"), false, 10, false)["version"].gsub(/-.+/,"").to_f
end
def create_db
@@ -145,13 +144,8 @@ class Chef
end
end
- private
-
- def safe_name(name)
- name.gsub(/\./, "_")
- end
-
def view_uri(design, view)
+ Chef::Config[:couchdb_version] ||= @rest.run_request(:GET, URI.parse(@rest.url + "/"), false, 10, false)["version"].gsub(/-.+/,"").to_f
case Chef::Config[:couchdb_version]
when 0.9
"#{Chef::Config[:couchdb_database]}/_design/#{design}/_view/#{view}"
@@ -159,6 +153,12 @@ class Chef
"#{Chef::Config[:couchdb_database]}/_view/#{design}/#{view}"
end
end
-
+
+ private
+
+ def safe_name(name)
+ name.gsub(/\./, "_")
+ end
+
end
end
diff --git a/chef/lib/chef/daemon.rb b/chef/lib/chef/daemon.rb
index 7ab66ab710..2b6e6db3ea 100644
--- a/chef/lib/chef/daemon.rb
+++ b/chef/lib/chef/daemon.rb
@@ -40,12 +40,12 @@ class Chef
exit if fork
Process.setsid
exit if fork
- change_privilege
Chef::Log.info("Forked, in #{Process.pid}. Priveleges: #{Process.euid} #{Process.egid}")
File.umask 0000
$stdin.reopen("/dev/null")
$stdout.reopen("/dev/null", "a")
$stdout.reopen($stdout)
+ save_pid_file
at_exit { remove_pid_file }
rescue NotImplementedError => e
Chef.fatal!("There is no fork: #{e.message}")
@@ -167,4 +167,4 @@ class Chef
end
end
end
-end \ No newline at end of file
+end
diff --git a/chef/lib/chef/mixin/command.rb b/chef/lib/chef/mixin/command.rb
index d787059548..f0c76b09e9 100644
--- a/chef/lib/chef/mixin/command.rb
+++ b/chef/lib/chef/mixin/command.rb
@@ -113,35 +113,11 @@ class Chef
end
exec_processing_block = lambda do |pid, stdin, stdout, stderr|
- stdout.sync = true
- stderr.sync = true
-
Chef::Log.debug("---- Begin output of #{args[:command]} ----")
-
- stdout_finished = false
- stderr_finished = false
-
- while !stdout_finished || !stderr_finished
- ready = IO.select([stdout, stderr], nil, nil, 1.0)
- if ready && ready.first.include?(stdout)
- line = stdout.gets
- if line
- command_output << "STDOUT: #{line.strip}\n"
- Chef::Log.debug("STDOUT: #{line.strip}")
- else
- stdout_finished = true
- end
- end
- if ready && ready.first.include?(stderr)
- line = stderr.gets
- if line
- command_output << "STDERR: #{line.strip}\n"
- Chef::Log.debug("STDERR: #{line.strip}")
- else
- stderr_finished = true
- end
- end
- end
+ Chef::Log.debug("STDOUT: #{stdout.string.chomp!}")
+ Chef::Log.debug("STDERR: #{stderr.string.chomp!}")
+ command_output << "STDOUT: #{stdout.string.chomp!}"
+ command_output << "STDERR: #{stderr.string.chomp!}"
Chef::Log.debug("---- End output of #{args[:command]} ----")
end
@@ -154,10 +130,6 @@ class Chef
status = nil
- # I don't understand what this :waitlast argument is doing, but setting it to true is causing the block in popen4
- # not to wait until the command is finished to execute, kind of the opposite of what I would guess from the name
- args[:waitlast] ||= true
-
Dir.chdir(args[:cwd]) do
if args[:timeout]
begin
@@ -200,8 +172,13 @@ class Chef
#
# Thanks Ara!
def popen4(cmd, args={}, &b)
-
+
# Waitlast - this is magic.
+ #
+ # Do we wait for the child process to die before we yield
+ # to the block, or after? That is the magic of waitlast.
+ #
+ # By default, we are waiting before we yield the block.
args[:waitlast] ||= false
args[:user] ||= nil
@@ -298,11 +275,58 @@ class Chef
# wants to do must be done - it's dead. If it isn't,
# it's because something totally skanky is happening,
# and we don't care.
+ o = StringIO.new
+ e = StringIO.new
+
pi[0].close
- pi[1].fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
- pi[2].fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
- results = Process.waitpid2(cid).last
- b[cid, *pi]
+
+ stdout = pi[1]
+ stderr = pi[2]
+
+ stdout.sync = true
+ stderr.sync = true
+
+ stdout.fcntl(Fcntl::F_SETFL, pi[1].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
+ stderr.fcntl(Fcntl::F_SETFL, pi[2].fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
+
+
+ stdout_finished = false
+ stderr_finished = false
+
+ results = nil
+
+ while !stdout_finished || !stderr_finished
+ begin
+ ready = IO.select([stdout, stderr], nil, nil, 1.0)
+ rescue Errno::EAGAIN
+ results = Process.waitpid2(cid, Process::WNOHANG)
+ if results
+ stdout_finished = true
+ stderr_finished = true
+ end
+ end
+
+ if ready && ready.first.include?(stdout)
+ line = results ? stdout.gets(nil) : stdout.gets
+ if line
+ o.write(line)
+ else
+ stdout_finished = true
+ end
+ end
+ if ready && ready.first.include?(stderr)
+ line = results ? stderr.gets(nil) : stderr.gets
+ if line
+ e.write(line)
+ else
+ stderr_finished = true
+ end
+ end
+ end
+ results = Process.waitpid2(cid).last unless results
+ o.rewind
+ e.rewind
+ b[cid, pi[0], o, e]
results
end
ensure
diff --git a/chef/lib/chef/provider/cron.rb b/chef/lib/chef/provider/cron.rb
index 88fd75e2d3..9e633053ce 100644
--- a/chef/lib/chef/provider/cron.rb
+++ b/chef/lib/chef/provider/cron.rb
@@ -64,7 +64,12 @@ class Chef
status = popen4("crontab -l -u #{@new_resource.user}") do |pid, stdin, stdout, stderr|
stdout.each_line do |line|
if cron_found
- crontab << "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
+ cronline = "#{@new_resource.minute} #{@new_resource.hour} #{@new_resource.day} #{@new_resource.month} #{@new_resource.weekday} #{@new_resource.command}\n"
+ if (line == cronline)
+ Chef::Log.debug("Skipping existing cron entry '#{@new_resource.name}'")
+ return
+ end
+ crontab << cronline
cron_found = false
next
end
@@ -76,6 +81,7 @@ class Chef
end
end
+
status = popen4("crontab -u #{@new_resource.user} -", :waitlast => true) do |pid, stdin, stdout, stderr|
crontab.each { |line| stdin.puts "#{line}" }
stdin.close
diff --git a/chef/lib/chef/provider/package/yum-dump-json.py b/chef/lib/chef/provider/package/yum-dump-json.py
new file mode 100644
index 0000000000..115265e5aa
--- /dev/null
+++ b/chef/lib/chef/provider/package/yum-dump-json.py
@@ -0,0 +1,67 @@
+#
+# Author:: Matthew Kent (<mkent@magoazul.com>)
+# Copyright:: Copyright (c) 2009 Matthew Kent
+# 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.
+#
+
+# yum-dump-json.py
+# Inspired by yumhelper.py by David Lutterkort
+#
+# Produce a list of installed and available packages using yum and dump the
+# result as json to stdout.
+#
+# This invokes yum just as the command line would which makes it subject to
+# all the caching related configuration paramaters in yum.conf.
+#
+# Can be run as non root, but that won't update the cache.
+
+import os
+import sys
+import yum
+import json
+
+y = yum.YumBase()
+# Only want json in output
+y.doConfigSetup(debuglevel=0, errorlevel=0)
+
+# yum assumes it can update the cache directory. Disable this for non root
+# users.
+y.conf.cache = os.geteuid() != 0
+
+y.doTsSetup()
+y.doRpmDBSetup()
+
+db = y.doPackageLists('all')
+
+y.closeRpmDB()
+
+combined = {}
+
+for pkg in db.installed:
+ combined[pkg.name] = {}
+ combined[pkg.name]["installed"] = { "epoch": pkg.epoch,
+ "version": pkg.version,
+ "release": pkg.release,
+ "arch": pkg.arch }
+for pkg in db.available:
+ if not combined.has_key(pkg.name):
+ combined[pkg.name] = {}
+ combined[pkg.name]["available"] = { "epoch": pkg.epoch,
+ "version": pkg.version,
+ "release": pkg.release,
+ "arch": pkg.arch }
+print json.write( combined )
+
+sys.exit(0)
diff --git a/chef/lib/chef/provider/package/yum.rb b/chef/lib/chef/provider/package/yum.rb
index 7e4a6e96ba..5eab817f0e 100644
--- a/chef/lib/chef/provider/package/yum.rb
+++ b/chef/lib/chef/provider/package/yum.rb
@@ -19,63 +19,114 @@
require 'chef/provider/package'
require 'chef/mixin/command'
require 'chef/resource/package'
+require 'singleton'
class Chef
class Provider
class Package
class Yum < Chef::Provider::Package
+ class YumCache
+ include Chef::Mixin::Command
+ include Singleton
+
+ def initialize
+ @created_at = Time.now
+ load_data
+ end
+
+ def stale?
+ interval = Chef::Config[:interval].to_f
+
+ # run once mode
+ if interval == 0
+ return false
+ elsif (Time.now - @created_at) > interval
+ return true
+ end
+
+ false
+ end
+
+ def refresh
+ if @data.empty?
+ reload
+ elsif stale?
+ reload
+ end
+ end
+
+ def load_data
+ parsed = String.new
+ helper = ::File.join(::File.dirname(__FILE__), 'yum-dump-json.py')
+ status = popen4("python #{helper}", :waitlast => true) do |pid, stdin, stdout, stderr|
+ stdout.each do |line|
+ parsed << line
+ end
+ end
+
+ unless status.exitstatus == 0
+ raise Chef::Exceptions::Package, "yum failed - #{status.inspect}!"
+ end
+
+ @data = JSON.parse(parsed)
+ end
+ alias :reload :load_data
+
+ def version(package_name, type)
+ if (x = @data[package_name])
+ if (y = x[type])
+ return "#{y["version"]}-#{y["release"]}"
+ end
+ end
+
+ nil
+ end
+
+ def installed_version(package_name)
+ version(package_name, "installed")
+ end
+
+ def candidate_version(package_name)
+ version(package_name, "available")
+ end
+
+ def flush
+ @data.clear
+ end
+ end
+
+ def initialize(node, new_resource)
+ @yum = YumCache.instance
+ super(node, new_resource)
+ end
+
def load_current_resource
@current_resource = Chef::Resource::Package.new(@new_resource.name)
@current_resource.package_name(@new_resource.package_name)
Chef::Log.debug("Checking yum info for #{@new_resource.package_name}")
- status = popen4("yum info -q -y #{@new_resource.package_name}") do |pid, stdin, stdout, stderr|
- package_type = nil
- installed_version = nil
- candidate_version = nil
- stdout.each do |line|
- case line
- when /^Installed Packages$/
- package_type = :installed
- when /^Available Packages$/
- package_type = :available
- when /^Version\s*: (.+)$/
- if package_type == :installed
- installed_version = $1
- elsif package_type == :available
- candidate_version = $1
- end
- when /^Release: (.+)$/
- if package_type == :installed
- installed_version += "-#{$1}"
- Chef::Log.debug("Installed release is #{installed_version}")
- elsif package_type == :available
- candidate_version += "-#{$1}"
- Chef::Log.debug("Candidate version is #{candidate_version}")
- end
- end
- end
-
- @current_resource.version(installed_version)
- if candidate_version
- @candidate_version = candidate_version
- else
- @candidate_version = installed_version
- end
- end
+
+ @yum.refresh
- unless status.exitstatus == 0
- raise Chef::Exceptions::Package, "yum failed - #{status.inspect}!"
+ installed_version = @yum.installed_version(@new_resource.package_name)
+ @candidate_version = @yum.candidate_version(@new_resource.package_name)
+
+ @current_resource.version(installed_version)
+ if candidate_version
+ @candidate_version = candidate_version
+ else
+ @candidate_version = installed_version
end
@current_resource
end
-
+
def install_package(name, version)
run_command(
:command => "yum -q -y install #{name}-#{version}"
)
+ @yum.flush
end
def upgrade_package(name, version)
@@ -84,6 +135,7 @@ class Chef
run_command(
:command => "yum -q -y update #{name}-#{version}"
)
+ @yum.flush
else
install_package(name, version)
end
@@ -93,6 +145,7 @@ class Chef
run_command(
:command => "yum -q -y remove #{name}-#{version}"
)
+ @yum.flush
end
def purge_package(name, version)
diff --git a/chef/spec/unit/couchdb_spec.rb b/chef/spec/unit/couchdb_spec.rb
index fe7a80235f..62ba85e33d 100644
--- a/chef/spec/unit/couchdb_spec.rb
+++ b/chef/spec/unit/couchdb_spec.rb
@@ -31,15 +31,11 @@ describe Chef::CouchDB, "new" do
Chef::REST.should_receive(:new).with("http://monkey")
Chef::CouchDB.new
end
-
+
it "should create a new Chef::REST object from a provided url" do
Chef::REST.should_receive(:new).with("http://monkeypants")
Chef::CouchDB.new("http://monkeypants")
end
-
- it "should parse the CouchDB version number" do
-
- end
end
describe Chef::CouchDB, "create_db" do
@@ -252,23 +248,27 @@ describe Chef::CouchDB, "has_key?" do
end
end
-describe Chef::CouchDB, "safe_name" do
+describe Chef::CouchDB, "view_uri" do
before do
- @couchdb = mock("Chef::CouchDB", :null_object => true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
+ @mock_rest = mock("Chef::REST", :null_object => true, :url => "http://monkeypants")
+ Chef::REST.stub!(:new).and_return(@mock_rest)
+ @couchdb = Chef::CouchDB.new("http://localhost")
end
- it "should convert the name to a safe name" do
- @couchdb.should_receive(:safe_name).with("asdf.lol.com").and_return("asdf_lol_com")
- @couchdb.safe_name("asdf.lol.com")
- end
-end
-
-describe Chef::CouchDB, "view_uri" do
- before do
- @couchdb = mock("Chef::CouchDB", :null_object => true)
- Chef::CouchDB.stub!(:new).and_return(@couchdb)
- Chef::Config.stub!(:[]).with(:couchdb_database).and_return("chef")
+ describe "when the couchdb version is unknown" do
+ it "should set the couchdb version appropriately" do
+ ov = Chef::Config[:couchdb_version]
+ Chef::Config[:couchdb_version] = nil
+ @mock_rest.should_receive(:run_request).with(
+ :GET,
+ URI.parse("http://monkeypants/"),
+ false,
+ 10,
+ false
+ ).and_return({ "version" => "0.9" })
+ @couchdb.view_uri("nodes", "all")
+ Chef::Config[:couchdb_version] = ov
+ end
end
describe "on couchdb 0.8" do
@@ -292,4 +292,4 @@ describe Chef::CouchDB, "view_uri" do
@couchdb.view_uri("nodes", "all")
end
end
-end \ No newline at end of file
+end
diff --git a/chef/spec/unit/mixin/command_spec.rb b/chef/spec/unit/mixin/command_spec.rb
deleted file mode 100644
index d35712bab5..0000000000
--- a/chef/spec/unit/mixin/command_spec.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-#
-# Author:: Matthew Landauer (<matthew@openaustralia.org>)
-# Copyright:: Copyright (c) 2008 Matthew Landauer
-# 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::Mixin::Command do
- before :each do
- Chef::Log.init
- end
-
- # Reset the logger for other tests
- after :each do
- Chef::Log.init
- end
-
- it "should log the command's standard output at debug log level" do
- command = "ruby -e 'puts 5'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 5").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should log the command's standard error at debug log level" do
- command = "ruby -e 'STDERR.puts 5'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 5").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should log the command's standard out and error at the same time" do
- command = "ruby -e 'STDERR.puts 1; puts 2; STDERR.puts 3; puts 4'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 1").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 2").ordered
- Chef::Log.should_receive(:debug).with("STDERR: 3").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 4").ordered
- Chef::Log.should_receive(:debug).with("---- End output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("Ran #{command} returned 0").ordered
- Chef::Mixin::Command.run_command(:command => command)
- end
-
- it "should throw an exception if the command returns a bad exit value" do
- command = "ruby -e 'puts 1; exit 1'"
- Chef::Log.level :debug
- # Stub out Chef::Log.debug to avoid messages going to console
- Chef::Log.stub!(:debug)
- lambda {Chef::Mixin::Command.run_command(:command => command)}.should raise_error(Chef::Exceptions::Exec, "#{command} returned 1, expected 0")
- end
-
- it "should include the command output in the exception if the log level is not at debug" do
- command = "ruby -e 'puts 1; exit 1'"
- Chef::Log.level :info
- lambda {Chef::Mixin::Command.run_command(:command => command)}.should raise_error(Chef::Exceptions::Exec, "#{command} returned 1, expected 0\n---- Begin output of #{command} ----\nSTDOUT: 1\n---- End output of #{command} ----\n")
- end
-
- it "should log the output as the command is executing" do
- command = "ruby -e 'STDOUT.sync = true; puts 1; sleep 2; puts 2'"
- Chef::Log.should_receive(:debug).with("Executing #{command}").ordered
- Chef::Log.should_receive(:debug).with("---- Begin output of #{command} ----").ordered
- Chef::Log.should_receive(:debug).with("STDOUT: 1").ordered
- lambda {Chef::Mixin::Command.run_command(:command => command, :timeout => 1)}.should raise_error(Timeout::Error)
- end
-end
diff --git a/chef/spec/unit/mixin/template_spec.rb b/chef/spec/unit/mixin/template_spec.rb
index 86e0b59232..05fee6820a 100644
--- a/chef/spec/unit/mixin/template_spec.rb
+++ b/chef/spec/unit/mixin/template_spec.rb
@@ -31,7 +31,8 @@ describe Chef::Mixin::Template, "render_template" do
end
it "should return a file" do
- @template.render_template("abcdef", {}).should be_kind_of(File)
+ f = @template.render_template("abcdef", {})
+ @template.render_template("abcdef", {}).should be_kind_of(Tempfile)
end
describe "when an exception is raised in the template" do
diff --git a/chef/spec/unit/provider/cron_spec.rb b/chef/spec/unit/provider/cron_spec.rb
index d09d14b93c..eda942e14b 100644
--- a/chef/spec/unit/provider/cron_spec.rb
+++ b/chef/spec/unit/provider/cron_spec.rb
@@ -128,7 +128,7 @@ describe Chef::Provider::Cron, "action_create" do
@provider.action_create
end
- it "should update the cron entry if it exists" do
+ it "should update the cron entry if it exists and has changed" do
@status = mock("Status", :exitstatus => 0)
@stdin = mock("STDIN", :null_object => true)
@stdout = mock("STDOUT", :null_object => true)
@@ -142,7 +142,36 @@ describe Chef::Provider::Cron, "action_create" do
@provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
Chef::Log.should_receive(:info).with("Updated cron '#{@new_resource.name}'")
@provider.action_create
+ end
+
+ it "should not update the cron entry if it exists and has not changed" do
+ resource = mock("Chef::Resource::Cron",
+ :null_object => true,
+ :name => "foo",
+ :minute => "30",
+ :hour => "*",
+ :day => "*",
+ :month => "*",
+ :weekday => "*",
+ :command => "/bin/true"
+ )
+ provider = Chef::Provider::Cron.new(@node, resource)
+
+ @status = mock("Status", :exitstatus => 0)
+ @stdin = mock("STDIN", :null_object => true)
+ @stdout = mock("STDOUT", :null_object => true)
+ @stderr = mock("STDERR", :null_object => true)
+ @pid = mock("PID", :null_object => true)
+ @stdout.stub!(:each_line).and_yield("# Chef Name: bar").
+ and_yield("* 10 * * * /bin/false").
+ and_yield("# Chef Name: foo\n").
+ and_yield("30 * * * * /bin/true\n")
+ provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ Chef::Log.should_not_receive(:info).with("Updated cron '#{@new_resource.name}'")
+ Chef::Log.should_receive(:debug).with("Skipping existing cron entry '#{@new_resource.name}'")
+ provider.cron_exists = true
+ provider.action_create
end
end
diff --git a/chef/spec/unit/provider/package/yum_spec.rb b/chef/spec/unit/provider/package/yum_spec.rb
index 6a554c812f..2e8ddb9971 100644
--- a/chef/spec/unit/provider/package/yum_spec.rb
+++ b/chef/spec/unit/provider/package/yum_spec.rb
@@ -36,55 +36,16 @@ describe Chef::Provider::Package::Yum, "load_current_resource" do
:updated => nil
)
@status = mock("Status", :exitstatus => 0)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
Chef::Resource::Package.stub!(:new).and_return(@current_resource)
- @provider.stub!(:popen4).and_return(@status)
- @stdin = mock("STDIN", :null_object => true)
- @stdout = mock("STDOUT", :null_object => true)
- @stdout.stub!(:each).and_yield("Installed Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5").
- and_yield("Size : 7.8 M").
- and_yield("Repo : installed").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.").
- and_yield("").
- and_yield("Available Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5_2.3").
- and_yield("Size : 2.7 M").
- and_yield("Repo : updates").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.")
- @stdout_available = mock("STDOUT AVAILABLE", :null_object => true)
- @stdout_available.stub!(:each).and_yield("Available Packages").
- and_yield("Name : cups").
- and_yield("Arch : i386").
- and_yield("Epoch : 1").
- and_yield("Version: 1.2.4").
- and_yield("Release: 11.18.el5_2.3").
- and_yield("Size : 2.7 M").
- and_yield("Repo : updates").
- and_yield("Summary: Common Unix Printing System").
- and_yield("Description:").
- and_yield("The Common UNIX Printing System provides a portable printing layer for").
- and_yield("UNIX® operating systems. It has been developed by Easy Software Products").
- and_yield("to promote a standard printing solution for all UNIX vendors and users.").
- and_yield("CUPS provides the System V and Berkeley command-line interfaces.")
@stderr = mock("STDERR", :null_object => true)
@pid = mock("PID", :null_object => true)
end
@@ -99,45 +60,22 @@ describe Chef::Provider::Package::Yum, "load_current_resource" do
@provider.load_current_resource
end
- it "should run yum info with the package name" do
- @provider.should_receive(:popen4).with("yum info -q -y #{@new_resource.package_name}").and_return(@status)
- @provider.load_current_resource
- end
-
- it "should read stdout on yum info" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
- @stdout.should_receive(:each).and_return(true)
- @provider.load_current_resource
- end
-
it "should set the installed version to nil on the current resource if no installed package" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout_available, @stderr).and_return(@status)
+ @yum_cache.stub!(:installed_version).and_return(nil)
@current_resource.should_receive(:version).with(nil).and_return(true)
@provider.load_current_resource
end
- it "should set the installed version if yum info has one" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ it "should set the installed version if yum has one" do
@current_resource.should_receive(:version).with("1.2.4-11.18.el5").and_return(true)
@provider.load_current_resource
end
it "should set the candidate version if yum info has one" do
- @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
@provider.load_current_resource
@provider.candidate_version.should eql("1.2.4-11.18.el5_2.3")
end
- it "should raise an exception if yum info fails" do
- @status.should_receive(:exitstatus).and_return(1)
- lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package)
- end
-
- it "should not raise an exception if yum info succeeds" do
- @status.should_receive(:exitstatus).and_return(0)
- lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::Package)
- end
-
it "should return the current resouce" do
@provider.load_current_resource.should eql(@current_resource)
end
@@ -154,6 +92,14 @@ describe Chef::Provider::Package::Yum, "install_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end
@@ -169,13 +115,21 @@ describe Chef::Provider::Package::Yum, "upgrade_package" do
before(:each) do
@node = mock("Chef::Node", :null_object => true)
- @new_resource = mock("Chef::Resource::Package",
+ @new_resource = mock("Chef::Resource::Package",
:null_object => true,
:name => "emacs",
:version => nil,
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@current_resource = mock("Chef::Resource::Package",
:null_object => true,
:name => "emacs",
@@ -214,6 +168,14 @@ describe Chef::Provider::Package::Yum, "remove_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end
@@ -235,6 +197,14 @@ describe Chef::Provider::Package::Yum, "purge_package" do
:package_name => "emacs",
:updated => nil
)
+ @yum_cache = mock(
+ 'Chef::Provider::Yum::YumCache',
+ :refresh => true,
+ :flush => true,
+ :installed_version => "1.2.4-11.18.el5",
+ :candidate_version => "1.2.4-11.18.el5_2.3"
+ )
+ Chef::Provider::Package::Yum::YumCache.stub!(:instance).and_return(@yum_cache)
@provider = Chef::Provider::Package::Yum.new(@node, @new_resource)
end
diff --git a/features/data/cookbooks/execute_commands/recipes/4k.rb b/features/data/cookbooks/execute_commands/recipes/4k.rb
new file mode 100644
index 0000000000..bc810bd979
--- /dev/null
+++ b/features/data/cookbooks/execute_commands/recipes/4k.rb
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+execute "create-4k-file" do
+ command "dd if=/dev/random of=#{node[:tmpdir]}/execute-4k.txt bs=1024 count=5"
+end
+
+execute "read-4k-file" do
+ command "cat #{node[:tmpdir]}/execute-4k.txt"
+end
diff --git a/features/data/cookbooks/execute_commands/recipes/debug.rb b/features/data/cookbooks/execute_commands/recipes/debug.rb
new file mode 100644
index 0000000000..5b76380508
--- /dev/null
+++ b/features/data/cookbooks/execute_commands/recipes/debug.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+execute "debug-log" do
+ command "ruby -e 'puts \"whats up\"; STDERR.puts \"doc!\"'"
+end
+
diff --git a/features/data/cookbooks/execute_commands/recipes/default.rb b/features/data/cookbooks/execute_commands/recipes/default.rb
new file mode 100644
index 0000000000..6a6f98361f
--- /dev/null
+++ b/features/data/cookbooks/execute_commands/recipes/default.rb
@@ -0,0 +1,22 @@
+#
+# Cookbook Name:: execute_commands
+# Recipe:: default
+#
+# Copyright 2009, Opscode
+#
+# 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.
+#
+
+execute "create-mastodon" do
+ command "echo wewt > #{node[:tmpdir]}/mastodon_rocks"
+end
diff --git a/features/execute_commands.feature b/features/execute_commands.feature
new file mode 100644
index 0000000000..2034cec79e
--- /dev/null
+++ b/features/execute_commands.feature
@@ -0,0 +1,30 @@
+Feature: Execute Commands
+ In order to utilize the plethora of useful command line utilities
+ As a Developer
+ I want to execute commands from within chef
+
+ Scenario: Execute a command
+ Given a validated node
+ And it includes the recipe 'execute_commands'
+ When I run the chef-client
+ Then the run should exit '0'
+ And a file named 'mastodon_rocks' should exist
+
+ Scenario: Execute a command with more than 4k of output
+ Given a validated node
+ And it includes the recipe 'execute_commands::4k'
+ When I run the chef-client
+ Then the run should exit '0'
+ And a file named 'execute-4k.txt' should exist
+
+ Scenario: Execute a command at the debug log level
+ Given a validated node
+ And it includes the recipe 'execute_commands::debug'
+ When I run the chef-client at log level 'debug'
+ Then the run should exit '0'
+ And 'stdout' should have 'DEBUG: Executing ruby -e .puts "whats up"; STDERR.puts "doc!".'
+ And 'stdout' should have 'DEBUG: ---- Begin output of ruby -e .puts "whats up"; STDERR.puts "doc!". ----'
+ And 'stdout' should have 'DEBUG: STDOUT: whats up'
+ And 'stdout' should have 'DEBUG: STDERR: doc!'
+ And 'stdout' should have 'DEBUG: ---- End output of ruby -e .puts "whats up"; STDERR.puts "doc!". ----'
+ And 'stdout' should have 'DEBUG: Ran ruby -e .puts "whats up"; STDERR.puts "doc!". returned 0'
diff --git a/features/steps/run_client.rb b/features/steps/run_client.rb
index 4afee9aa1d..ba7f01326a 100644
--- a/features/steps/run_client.rb
+++ b/features/steps/run_client.rb
@@ -20,16 +20,20 @@
# When
###
When /^I run the chef\-client$/ do
- log_level = ENV["LOG_LEVEL"] ? ENV["LOG_LEVEL"] : "error"
+ @log_level ||= ENV["LOG_LEVEL"] ? ENV["LOG_LEVEL"] : "error"
status = Chef::Mixin::Command.popen4(
- "chef-client -l #{log_level} -c #{File.expand_path(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'client.rb'))}", :waitlast => true) do |p, i, o, e|
- i.close
+ "chef-client -l #{@log_level} -c #{File.expand_path(File.join(File.dirname(__FILE__), '..', 'data', 'config', 'client.rb'))}") do |p, i, o, e|
@stdout = o.gets(nil)
@stderr = e.gets(nil)
end
@status = status
end
+When /^I run the chef\-client at log level '(.+)'$/ do |log_level|
+ @log_level = log_level
+ When "I run the chef-client"
+end
+
###
# Then
###
@@ -46,10 +50,10 @@ end
def print_output
puts "--- run stdout:"
puts @stdout
- puts "--- run stderr"
+ puts "--- run stderr:"
puts @stderr
end
-Then /^stdout should have '(.+)'$/ do |to_match|
- @stdout.should match(/#{to_match}/m)
+Then /^'(.+)' should have '(.+)'$/ do |which, to_match|
+ self.instance_variable_get("@#{which}".to_sym).should match(/#{to_match}/m)
end