diff options
-rw-r--r-- | Rakefile | 6 | ||||
-rw-r--r-- | chef-server-api/app/controllers/application.rb | 2 | ||||
-rw-r--r-- | chef-server-api/app/controllers/nodes.rb | 16 | ||||
-rw-r--r-- | chef-server-api/app/controllers/roles.rb | 4 | ||||
-rw-r--r-- | chef-server-webui/app/controllers/application.rb | 56 | ||||
-rw-r--r-- | chef-server-webui/app/controllers/nodes.rb | 9 | ||||
-rw-r--r-- | chef-server-webui/app/views/nodes/show.html.haml | 2 | ||||
-rw-r--r-- | chef/lib/chef/node.rb | 40 | ||||
-rw-r--r-- | chef/lib/chef/role.rb | 16 | ||||
-rw-r--r-- | chef/spec/unit/node_spec.rb | 45 | ||||
-rw-r--r-- | cucumber.yml | 1 | ||||
-rw-r--r-- | features/api/nodes/list_nodes_api.feature | 6 | ||||
-rw-r--r-- | features/api/roles/list_roles_api.feature | 8 | ||||
-rw-r--r-- | features/steps/fixture_steps.rb | 4 | ||||
-rw-r--r-- | features/steps/response_steps.rb | 6 |
15 files changed, 162 insertions, 59 deletions
@@ -251,6 +251,12 @@ namespace :features do end end + namespace :nodes do + Cucumber::Rake::Task.new("sync") do |t| + t.profile = "api_nodes_sync" + end + end + namespace :cookbooks do desc "Run cucumber tests for the cookbooks portion of the REST API" Cucumber::Rake::Task.new(:cookbooks) do |t| diff --git a/chef-server-api/app/controllers/application.rb b/chef-server-api/app/controllers/application.rb index 7b3797730c..c7fdd28ef8 100644 --- a/chef-server-api/app/controllers/application.rb +++ b/chef-server-api/app/controllers/application.rb @@ -183,7 +183,7 @@ class ChefServerApi::Application < Merb::Controller def specific_cookbooks(node_name, cl) valid_cookbooks = Hash.new begin - node = Chef::Node.load(node_name) + node = Chef::Node.cdb_load(node_name) recipes, default_attrs, override_attrs = node.run_list.expand('couchdb') rescue Net::HTTPServerException recipes = [] diff --git a/chef-server-api/app/controllers/nodes.rb b/chef-server-api/app/controllers/nodes.rb index a3aa90a281..28ebccce2c 100644 --- a/chef-server-api/app/controllers/nodes.rb +++ b/chef-server-api/app/controllers/nodes.rb @@ -34,7 +34,7 @@ class ChefServerApi::Nodes < ChefServerApi::Application def show begin - @node = Chef::Node.load(params[:id]) + @node = Chef::Node.cdb_load(params[:id]) rescue Chef::Exceptions::CouchDBNotFound => e raise NotFound, "Cannot load node #{params[:id]}" end @@ -46,19 +46,19 @@ class ChefServerApi::Nodes < ChefServerApi::Application @node = params["inflated_object"] exists = true begin - Chef::Node.load(@node.name) + Chef::Node.cdb_load(@node.name) rescue Chef::Exceptions::CouchDBNotFound exists = false end raise Forbidden, "Node already exists" if exists self.status = 201 - @node.save + @node.cdb_save display({ :uri => absolute_slice_url(:node, escape_node_id(@node.name)) }) end def update begin - @node = Chef::Node.load(params[:id]) + @node = Chef::Node.cdb_load(params[:id]) rescue Chef::Exceptions::CouchDBNotFound => e raise NotFound, "Cannot load node #{params[:id]}" end @@ -66,25 +66,25 @@ class ChefServerApi::Nodes < ChefServerApi::Application updated = params['inflated_object'] @node.run_list.reset(updated.run_list) @node.attribute = updated.attribute - @node.save + @node.cdb_save @node.couchdb_rev = nil display(@node) end def destroy begin - @node = Chef::Node.load(params[:id]) + @node = Chef::Node.cdb_load(params[:id]) rescue Chef::Exceptions::CouchDBNotFound => e raise NotFound, "Cannot load node #{params[:id]}" end - @node.destroy + @node.cdb_destroy @node.couchdb_rev = nil display @node end def cookbooks begin - @node = Chef::Node.load(params[:id]) + @node = Chef::Node.cdb_load(params[:id]) rescue Chef::Exceptions::CouchDBNotFound => e raise NotFound, "Cannot load node #{params[:id]}" end diff --git a/chef-server-api/app/controllers/roles.rb b/chef-server-api/app/controllers/roles.rb index 7b99222fe2..107c1cba45 100644 --- a/chef-server-api/app/controllers/roles.rb +++ b/chef-server-api/app/controllers/roles.rb @@ -8,8 +8,8 @@ class ChefServerApi::Roles < ChefServerApi::Application # GET /roles def index - @role_list = Chef::Role.list(true) - display(@role_list.collect { |r| absolute_slice_url(:role, :id => r.name) }) + @role_list = Chef::Role.cdb_list(true) + display(@role_list.inject({}) { |r,role| r[role.name] = absolute_slice_url(:role, role.name); r }) end # GET /roles/:id diff --git a/chef-server-webui/app/controllers/application.rb b/chef-server-webui/app/controllers/application.rb index d5109edc86..61784a4539 100644 --- a/chef-server-webui/app/controllers/application.rb +++ b/chef-server-webui/app/controllers/application.rb @@ -72,23 +72,23 @@ class ChefServerWebui::Application < Merb::Controller end def authorized_node - if session[:level] == :admin - Chef::Log.debug("Authorized as Administrator") - true - elsif session[:level] == :node - Chef::Log.debug("Authorized as node") - if session[:node_name] == params[:id].gsub(/\./, '_') - true - else - raise( - Unauthorized, - "You are not the correct node for this action: #{session[:node_name]} instead of #{params[:id]}" - ) - end - else - Chef::Log.debug("Unauthorized") - raise Unauthorized, "You are not allowed to take this action." - end + # if session[:level] == :admin + # Chef::Log.debug("Authorized as Administrator") + # true + # elsif session[:level] == :node + # Chef::Log.debug("Authorized as node") + # if session[:node_name] == params[:id].gsub(/\./, '_') + # true + # else + # raise( + # Unauthorized, + # "You are not the correct node for this action: #{session[:node_name]} instead of #{params[:id]}" + # ) + # end + # else + # Chef::Log.debug("Unauthorized") + # raise Unauthorized, "You are not allowed to take this action." + # end end # Store the URI of the current request in the session. @@ -166,7 +166,7 @@ class ChefServerWebui::Application < Merb::Controller valid_cookbooks = Hash.new begin node = Chef::Node.load(node_name) - recipes, default_attrs, override_attrs = node.run_list.expand('couchdb') + recipes, default_attrs, override_attrs = node.run_list.expand rescue Net::HTTPServerException recipes = [] end @@ -211,18 +211,22 @@ class ChefServerWebui::Application < Merb::Controller end def get_available_recipes - cl = Chef::CookbookLoader.new - available_recipes = cl.sort{ |a,b| a.name.to_s <=> b.name.to_s }.inject([]) do |result, element| - element.recipes.sort.each do |r| - if r =~ /^(.+)::default$/ - result << $1 + r = Chef::REST.new(Chef::Config[:chef_server_url]) + result = Array.new + cookbooks = r.get_rest("cookbooks") + cookbooks.keys.sort.each do |key| + cb = r.get_rest(cookbooks[key]) + cb["recipes"].each do |recipe| + recipe["name"] =~ /(.+)\.rb/ + r_name = $1; + if r_name == "default" + result << key else - result << r + result << "#{key}::#{r_name}" end end - result end - available_recipes + result end end diff --git a/chef-server-webui/app/controllers/nodes.rb b/chef-server-webui/app/controllers/nodes.rb index d95e7fd262..dc8f9a3735 100644 --- a/chef-server-webui/app/controllers/nodes.rb +++ b/chef-server-webui/app/controllers/nodes.rb @@ -44,7 +44,7 @@ class ChefServerWebui::Nodes < ChefServerWebui::Application def new @node = Chef::Node.new @available_recipes = get_available_recipes - @available_roles = Chef::Role.list.sort + @available_roles = Chef::Role.list.keys.sort @run_list = @node.run_list render end @@ -56,7 +56,7 @@ class ChefServerWebui::Nodes < ChefServerWebui::Application raise NotFound, "Cannot load node #{params[:id]}" end @available_recipes = get_available_recipes - @available_roles = Chef::Role.list.sort + @available_roles = Chef::Role.list.keys.sort @run_list = @node.run_list render end @@ -72,7 +72,7 @@ class ChefServerWebui::Nodes < ChefServerWebui::Application rescue @node.attribute = JSON.parse(params[:attributes]) @available_recipes = get_available_recipes - @available_roles = Chef::Role.list.sort + @available_roles = Chef::Role.list.keys.sort @run_list = params[:for_node] @_message = { :error => $! } render :new @@ -90,11 +90,12 @@ class ChefServerWebui::Nodes < ChefServerWebui::Application @node.run_list.reset(params[:for_node] ? params[:for_node] : []) @node.attribute = JSON.parse(params[:attributes]) @node.save + Chef::Log.error("I made it here") @_message = { :notice => "Updated Node" } render :show rescue @available_recipes = get_available_recipes - @available_roles = Chef::Role.list.sort + @available_roles = Chef::Role.list.keys.sort @run_list = Chef::RunList.new @run_list.reset(params[:for_node]) render :edit diff --git a/chef-server-webui/app/views/nodes/show.html.haml b/chef-server-webui/app/views/nodes/show.html.haml index 121cbcedf4..4e94551adf 100644 --- a/chef-server-webui/app/views/nodes/show.html.haml +++ b/chef-server-webui/app/views/nodes/show.html.haml @@ -25,7 +25,7 @@ %td= type .left.accordion %h3.head= link_to("Recipes", "#") - - full_recipe_list, default_attrs, override_attrs = @node.run_list.expand("couchdb") + - full_recipe_list, default_attrs, override_attrs = @node.run_list.expand() %div %span.description.form.help This is the list of recipes, fully expanded, as they will be applied to the node in question. diff --git a/chef/lib/chef/node.rb b/chef/lib/chef/node.rb index 91a6685946..61ebaac050 100644 --- a/chef/lib/chef/node.rb +++ b/chef/lib/chef/node.rb @@ -371,22 +371,49 @@ class Chef end # Load a node by name from CouchDB - def self.load(name) + def self.cdb_load(name) couchdb = Chef::CouchDB.new couchdb.load("node", name) end + + # Load a node by name + def self.load(name) + r = Chef::REST.new(Chef::Config[:chef_server_url]) + r.get_rest("nodes/#{escape_node_id(name)}") + end # Remove this node from the CouchDB - def destroy + def cdb_destroy @couchdb.delete("node", @name, @couchdb_rev) end + + # Remove this node via the REST API + def destroy + r = Chef::REST.new(Chef::Config[:chef_server_url]) + r.delete_rest("nodes/#{Chef::Node.escape_node_id(@name)}") + end # Save this node to the CouchDB - def save + def cdb_save results = @couchdb.store("node", @name, self) @couchdb_rev = results["rev"] end + # Save this node via the REST API + def save + r = Chef::REST.new(Chef::Config[:chef_server_url]) + begin + r.put_rest("nodes/#{Chef::Node.escape_node_id(@name)}", self) + rescue Net::HTTPServerException => e + if e.response.code == "404" + r.post_rest("nodes", self) + else + raise e + end + end + self + end + # Set up our CouchDB design document def self.create_design_document couchdb = Chef::CouchDB.new @@ -397,6 +424,13 @@ class Chef def to_s "node[#{@name}]" end + + private + + def self.escape_node_id(arg=nil) + arg.gsub(/\./, '_') + end + end end diff --git a/chef/lib/chef/role.rb b/chef/lib/chef/role.rb index 1efb98d559..1f4dffcfdc 100644 --- a/chef/lib/chef/role.rb +++ b/chef/lib/chef/role.rb @@ -146,7 +146,7 @@ class Chef # List all the Chef::Role objects in the CouchDB. If inflate is set to true, you will get # the full list of all Roles, fully inflated. - def self.list(inflate=false) + def self.cdb_list(inflate=false) couchdb = Chef::CouchDB.new rs = couchdb.list("roles", inflate) if inflate @@ -155,6 +155,20 @@ class Chef rs["rows"].collect { |r| r["key"] } end end + + # Get the list of all roles from the API. + def self.list(inflate=false) + r = Chef::REST.new(Chef::Config[:chef_server_url]) + if inflate + response = Hash.new + Chef::Search::Query.new.search(:role) do |n| + response[n.name] = n + end + response + else + r.get_rest("roles") + end + end # Load a role by name from CouchDB def self.load(name) diff --git a/chef/spec/unit/node_spec.rb b/chef/spec/unit/node_spec.rb index a86364c0b1..f04500337b 100644 --- a/chef/spec/unit/node_spec.rb +++ b/chef/spec/unit/node_spec.rb @@ -295,6 +295,43 @@ describe Chef::Node do r["one"].should == "http://foo" end end + + describe "load" do + it "should load a node by name" do + @rest.should_receive(:get_rest).with("nodes/monkey").and_return("foo") + Chef::Node.load("monkey").should == "foo" + end + end + + describe "destroy" do + it "should destroy a node" do + @rest.should_receive(:delete_rest).with("nodes/monkey").and_return("foo") + @node.name("monkey") + @node.destroy + end + end + + describe "save" do + it "should update a node if it already exists" do + @node.name("monkey") + @rest.should_receive(:put_rest).with("nodes/monkey", @node).and_return("foo") + @node.save + end + + it "should not try and create if it can update" do + @node.name("monkey") + @rest.should_receive(:put_rest).with("nodes/monkey", @node).and_return("foo") + @rest.should_not_receive(:post_rest) + @node.save + end + + it "should create if it cannot update" do + @node.name("monkey") + @rest.should_receive(:put_rest).and_raise(Net::HTTPServerException.new(1, 3)) + @rest.should_receive(:post_rest).with("nodes", @node) + @node.save + end + end end describe "couchdb model" do @@ -327,7 +364,7 @@ describe Chef::Node do it "should load a node from couchdb by name" do @mock_couch.should_receive(:load).with("node", "coffee").and_return(true) Chef::CouchDB.stub!(:new).and_return(@mock_couch) - Chef::Node.load("coffee") + Chef::Node.cdb_load("coffee") end end @@ -338,7 +375,7 @@ describe Chef::Node do node = Chef::Node.new node.name "bob" node.couchdb_rev = 1 - node.destroy + node.cdb_destroy end end @@ -353,11 +390,11 @@ describe Chef::Node do it "should save the node to couchdb" do @mock_couch.should_receive(:store).with("node", "bob", @node).and_return({ "rev" => 33 }) - @node.save + @node.cdb_save end it "should store the new couchdb_rev" do - @node.save + @node.cdb_save @node.couchdb_rev.should eql(33) end end diff --git a/cucumber.yml b/cucumber.yml index 18c0e97328..13f05ec452 100644 --- a/cucumber.yml +++ b/cucumber.yml @@ -14,6 +14,7 @@ api_roles_list: --tags roles_list --format pretty -r features/steps -r features/ api_roles_show: --tags roles_show --format pretty -r features/steps -r features/support features api_roles_update: --tags roles_update --format pretty -r features/steps -r features/support features api_nodes: --tags @api_nodes --format pretty -r features/steps -r features/support features +api_nodes_sync: --tags @cookbook_sync --format pretty -r features/steps -r features/support features api_nodes_create: --tags nodes_create --format pretty -r features/steps -r features/support features api_nodes_delete: --tags nodes_delete --format pretty -r features/steps -r features/support features api_nodes_list: --tags nodes_list --format pretty -r features/steps -r features/support features diff --git a/features/api/nodes/list_nodes_api.feature b/features/api/nodes/list_nodes_api.feature index 0c6681ae13..030bc18092 100644 --- a/features/api/nodes/list_nodes_api.feature +++ b/features/api/nodes/list_nodes_api.feature @@ -14,7 +14,7 @@ Feature: List nodes via the REST API Given a 'registration' named 'bobo' exists Given a 'node' named 'webserver' exists When I 'GET' the path '/nodes' - Then the inflated responses key 'webserver' should include '^http://.+/nodes/webserver$' + Then the inflated responses key 'webserver' should match '^http://.+/nodes/webserver$' Scenario: List nodes when two have been created Given a 'registration' named 'bobo' exists @@ -22,8 +22,8 @@ Feature: List nodes via the REST API And a 'node' named 'dbserver' exists When I 'GET' the path '/nodes' Then the inflated response should be '2' items long - Then the inflated responses key 'webserver' should include '^http://.+/nodes/webserver$' - Then the inflated responses key 'dbserver' should include '^http://.+/nodes/dbserver$' + Then the inflated responses key 'webserver' should match '^http://.+/nodes/webserver$' + Then the inflated responses key 'dbserver' should match '^http://.+/nodes/dbserver$' Scenario: List nodes none have been created with a wrong private key Given a 'registration' named 'bobo' exists diff --git a/features/api/roles/list_roles_api.feature b/features/api/roles/list_roles_api.feature index 72251d2251..9eb9f177aa 100644 --- a/features/api/roles/list_roles_api.feature +++ b/features/api/roles/list_roles_api.feature @@ -14,7 +14,7 @@ Feature: List roles via the REST API Given a 'registration' named 'bobo' exists Given a 'role' named 'webserver' exists When I 'GET' the path '/roles' - Then the inflated response should include '^http://.+/roles/webserver$' + Then the inflated responses key 'webserver' should match '^http://.+/roles/webserver$' Scenario: List roles when two have been created Given a 'registration' named 'bobo' exists @@ -22,9 +22,9 @@ Feature: List roles via the REST API And a 'role' named 'db' exists When I 'GET' the path '/roles' Then the inflated response should be '3' items long - And the inflated response should include '^http://.+/roles/role_test$' - And the inflated response should include '^http://.+/roles/webserver$' - And the inflated response should include '^http://.+/roles/db$' + And the inflated responses key 'role_test' should match '^http://.+/roles/role_test$' + And the inflated responses key 'webserver' should match '^http://.+/roles/webserver$' + And the inflated responses key 'db' should match '^http://.+/roles/db$' Scenario: List roles when none have been created with a wrong private key Given a 'registration' named 'bobo' exists diff --git a/features/steps/fixture_steps.rb b/features/steps/fixture_steps.rb index f5f2ee5632..cf54d08ec2 100644 --- a/features/steps/fixture_steps.rb +++ b/features/steps/fixture_steps.rb @@ -169,7 +169,9 @@ Given /^an? '(.+)' named '(.+)' exists$/ do |stash_name, stash_key| @rest = Chef::REST.new(Chef::Config[:registration_url], 'not_admin', "#{tmpdir}/not_admin.pem") end else - if @stash[stash_name].respond_to?(:save)#stash_name == "registration" + if @stash[stash_name].respond_to?(:cdb_save) + @stash[stash_name].cdb_save + elsif @stash[stash_name].respond_to?(:save)#stash_name == "registration" @stash[stash_name].save else request("#{stash_name.pluralize}", { diff --git a/features/steps/response_steps.rb b/features/steps/response_steps.rb index 1cc150184e..aa347c7cb9 100644 --- a/features/steps/response_steps.rb +++ b/features/steps/response_steps.rb @@ -71,7 +71,11 @@ Then /^the inflated response should include '(.+)'$/ do |entry| end Then /^the inflated response should be '(.+)' items long$/ do |length| - self.inflated_response.length.should == length.to_i + if length.respond_to?(:keys) + self.inflated_response.keys.length.should == length.to_i + else + self.inflated_response.length.should == length.to_i + end end Then /^the '(.+)' header should match '(.+)'$/ do |header, regex| |