summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jacob <adam@opscode.com>2009-09-26 17:03:13 -0700
committerAdam Jacob <adam@opscode.com>2009-09-26 17:03:13 -0700
commite0db7b261d78a112cfc2710ee08fba2cf9679c46 (patch)
tree2c16d131dec0b0845a51a1e354dfca59c23a607b
parent3014147e50475bb7d432439654a20cfb4110b18e (diff)
downloadchef-e0db7b261d78a112cfc2710ee08fba2cf9679c46.tar.gz
Working Node Web UI vs API.
Updated the API to use the cdb_* methods for Chef::Node and Chef::Role.list. Updated the Web UI to exclusively use the non-cdb backing store methods. Non-cdb backing store methods hit the API.
-rw-r--r--Rakefile6
-rw-r--r--chef-server-api/app/controllers/application.rb2
-rw-r--r--chef-server-api/app/controllers/nodes.rb16
-rw-r--r--chef-server-api/app/controllers/roles.rb4
-rw-r--r--chef-server-webui/app/controllers/application.rb56
-rw-r--r--chef-server-webui/app/controllers/nodes.rb9
-rw-r--r--chef-server-webui/app/views/nodes/show.html.haml2
-rw-r--r--chef/lib/chef/node.rb40
-rw-r--r--chef/lib/chef/role.rb16
-rw-r--r--chef/spec/unit/node_spec.rb45
-rw-r--r--cucumber.yml1
-rw-r--r--features/api/nodes/list_nodes_api.feature6
-rw-r--r--features/api/roles/list_roles_api.feature8
-rw-r--r--features/steps/fixture_steps.rb4
-rw-r--r--features/steps/response_steps.rb6
15 files changed, 162 insertions, 59 deletions
diff --git a/Rakefile b/Rakefile
index 301c4d08c2..31d80fbfbd 100644
--- a/Rakefile
+++ b/Rakefile
@@ -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|