chef-server-api 0.9.18 → 0.10.0.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +0 -1
- data/app/controllers/application.rb +1 -1
- data/app/controllers/cookbooks.rb +51 -36
- data/app/controllers/environments.rb +207 -0
- data/app/controllers/main.rb +17 -10
- data/app/controllers/nodes.rb +34 -88
- data/app/controllers/roles.rb +23 -5
- data/app/controllers/search.rb +6 -16
- data/app/helpers/cookbook_version_helper.rb +72 -0
- data/app/views/layout/application.html.erb +49 -0
- data/app/views/main/index.html.erb +0 -0
- data/bin/chef-server +1 -0
- data/config/init.rb +6 -0
- data/config/rack.rb +3 -0
- data/config/router.rb +20 -7
- data/lib/chef-server-api/version.rb +1 -1
- data/spec/spec_helper.rb +82 -5
- data/spec/spec_model_helper.rb +67 -0
- data/spec/unit/environments_controller_spec.rb +139 -0
- data/spec/unit/nodes_controller_environments_spec.rb +132 -0
- data/spec/unit/nodes_controller_spec.rb +81 -0
- metadata +41 -13
- data/app/views/layout/chef_server_api.html.haml +0 -23
- data/app/views/main/index.html.haml +0 -5
data/app/controllers/roles.rb
CHANGED
@@ -48,11 +48,7 @@ class Roles < Application
|
|
48
48
|
raise NotFound, "Cannot load role #{params[:id]}"
|
49
49
|
end
|
50
50
|
|
51
|
-
@role.
|
52
|
-
@role.recipes(params["inflated_object"].recipes) if defined?(params["inflated_object"].recipes)
|
53
|
-
@role.run_list(params["inflated_object"].run_list)
|
54
|
-
@role.default_attributes(params["inflated_object"].default_attributes)
|
55
|
-
@role.override_attributes(params["inflated_object"].override_attributes)
|
51
|
+
@role.update_from!(params["inflated_object"])
|
56
52
|
@role.cdb_save
|
57
53
|
self.status = 200
|
58
54
|
@role.couchdb_rev = nil
|
@@ -70,4 +66,26 @@ class Roles < Application
|
|
70
66
|
display @role
|
71
67
|
end
|
72
68
|
|
69
|
+
# GET /roles/:id/environments/:env_id
|
70
|
+
def environment
|
71
|
+
begin
|
72
|
+
@role = Chef::Role.cdb_load(params[:role_id])
|
73
|
+
rescue Chef::Exceptions::CouchDBNotFound => e
|
74
|
+
raise NotFound, "Cannot load role #{params[:role_id]}"
|
75
|
+
end
|
76
|
+
display("run_list" => @role.env_run_lists[params[:env_id]])
|
77
|
+
end
|
78
|
+
|
79
|
+
# GET /roles/:id/environments
|
80
|
+
def environments
|
81
|
+
begin
|
82
|
+
@role = Chef::Role.cdb_load(params[:role_id])
|
83
|
+
rescue Chef::Exceptions::CouchDBNotFound => e
|
84
|
+
raise NotFound, "Cannot load role #{params[:role_id]}"
|
85
|
+
end
|
86
|
+
|
87
|
+
display(@role.env_run_lists.keys.sort)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
73
91
|
end
|
data/app/controllers/search.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
|
20
|
-
require 'chef/
|
20
|
+
require 'chef/solr_query'
|
21
21
|
|
22
22
|
class Search < Application
|
23
23
|
provides :json
|
@@ -27,34 +27,24 @@ class Search < Application
|
|
27
27
|
|
28
28
|
def index
|
29
29
|
indexes = valid_indexes
|
30
|
-
display(indexes.inject({}) { |r,i| r[i] = absolute_url(:
|
30
|
+
display(indexes.inject({}) { |r,i| r[i] = absolute_url(:search_show, i); r })
|
31
31
|
end
|
32
32
|
|
33
33
|
def valid_indexes
|
34
34
|
indexes = Chef::DataBag.cdb_list(false)
|
35
|
-
indexes += %w{ role node client }
|
35
|
+
indexes += %w{ role node client environment}
|
36
36
|
end
|
37
37
|
|
38
38
|
def show
|
39
39
|
unless valid_indexes.include?(params[:id])
|
40
40
|
raise NotFound, "I don't know how to search for #{params[:id]} data objects."
|
41
41
|
end
|
42
|
-
|
43
|
-
|
44
|
-
params[:q] ||= "*:*"
|
45
|
-
params[:sort] ||= nil
|
46
|
-
params[:start] ||= 0
|
47
|
-
params[:rows] ||= 20
|
48
|
-
objects, start, total = query.search(params[:id], params[:q], params[:sort], params[:start], params[:rows])
|
49
|
-
display({
|
50
|
-
"rows" => objects,
|
51
|
-
"start" => start,
|
52
|
-
"total" => total
|
53
|
-
})
|
42
|
+
params[:type] = params.delete(:id)
|
43
|
+
display(Chef::SolrQuery.from_params(params).search)
|
54
44
|
end
|
55
45
|
|
56
46
|
def reindex
|
57
|
-
display(Chef::
|
47
|
+
display(Chef::SolrQuery.new.rebuild_index)
|
58
48
|
end
|
59
49
|
|
60
50
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Stephen Delano (<stephen@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
module Merb
|
20
|
+
module CookbookVersionHelper
|
21
|
+
|
22
|
+
include Merb::ControllerExceptions
|
23
|
+
|
24
|
+
# takes a cookbook_name and an array of versions and returns a hash
|
25
|
+
# params:
|
26
|
+
# - cookbook_name: the name of the cookbook
|
27
|
+
# - versions: a sorted list of version strings
|
28
|
+
#
|
29
|
+
# returns:
|
30
|
+
# {
|
31
|
+
# :url => http://url,
|
32
|
+
# :versions => [
|
33
|
+
# { :version => version, :url => http://url/version },
|
34
|
+
# { :version => version, :url => http://url/version }
|
35
|
+
# ]
|
36
|
+
# }
|
37
|
+
def expand_cookbook_urls(cookbook_name, versions, num_versions)
|
38
|
+
versions = versions[0...num_versions.to_i] unless num_versions == "all"
|
39
|
+
version_list = versions.inject([]) do |res, version|
|
40
|
+
res.push({
|
41
|
+
:url => absolute_url(:cookbook_version, :cookbook_name => cookbook_name, :cookbook_version => version),
|
42
|
+
:version => version
|
43
|
+
})
|
44
|
+
res
|
45
|
+
end
|
46
|
+
url = absolute_url(:cookbook, :cookbook_name => cookbook_name)
|
47
|
+
{:url => url, :versions => version_list}
|
48
|
+
end
|
49
|
+
|
50
|
+
# validate and return the number of versions requested
|
51
|
+
# by the user
|
52
|
+
#
|
53
|
+
# raises an exception if an invalid number is requested
|
54
|
+
#
|
55
|
+
# params:
|
56
|
+
# - default: the number of versions to default to
|
57
|
+
#
|
58
|
+
# valid num_versions query parameter:
|
59
|
+
# - an integer >= 0
|
60
|
+
# - the string "all"
|
61
|
+
def num_versions!(default="1")
|
62
|
+
input = params[:num_versions]
|
63
|
+
result = if input
|
64
|
+
valid_input = (input == "all" || Integer(input) >= 0) rescue false
|
65
|
+
raise BadRequest, "You have requested an invalid number of versions (x >= 0 || 'all')" unless valid_input
|
66
|
+
input
|
67
|
+
else
|
68
|
+
default
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<?xml version='1.0' encoding='utf-8' ?>
|
2
|
+
<!DOCTYPE html>
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<meta content='text/html; charset=utf-8' http-equiv='content-type' />
|
6
|
+
<title>Chef Server</title>
|
7
|
+
<link href="/stylesheets/base.css" type="text/css" rel="Stylesheet" charset="utf-8" media="all" />
|
8
|
+
<link href="/stylesheets/themes/djime-cerulean/style.css" type="text/css" rel="Stylesheet" charset="utf-8" media="all" />
|
9
|
+
<link href="/stylesheets/chef.css" type="text/css" rel="Stylesheet" charset="utf-8" media="all" />
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<div id='container'>
|
13
|
+
<div id='header'>
|
14
|
+
<h1><a href="<%= absolute_url(:top) -%>">Chef REST API</a></h1>
|
15
|
+
</div>
|
16
|
+
<div id='main-navigation'>
|
17
|
+
<div class='clear'></div>
|
18
|
+
</div>
|
19
|
+
<div id='wrapper'>
|
20
|
+
<div id='main'>
|
21
|
+
<div class='block' id='block-text'>
|
22
|
+
<div class='content'>
|
23
|
+
<h2 class='title'>The REST API</h2>
|
24
|
+
<div class='inner'>
|
25
|
+
This is the Chef API Server.
|
26
|
+
<% if @webui_url %>
|
27
|
+
<a href="<%= @webui_url -%>">Are you looking for the Web UI?</a>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
<div class='inner'>
|
31
|
+
For more information about Chef, head on over to the <a href="http://wiki.opscode.com/">wiki.</a>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
<div id='footer'>
|
36
|
+
<div class='block'>
|
37
|
+
<p>Copyright © 2009-2011 Opscode, Inc.</p>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<div id='sidebar'>
|
42
|
+
<div class='block notice' id='sidebar_block_notice'></div>
|
43
|
+
<div class='block' id='sidebar_block'></div>
|
44
|
+
</div>
|
45
|
+
<div class='clear'></div>
|
46
|
+
</div>
|
47
|
+
</div>
|
48
|
+
</body>
|
49
|
+
</html>
|
File without changes
|
data/bin/chef-server
CHANGED
@@ -25,6 +25,7 @@ require "merb-core"
|
|
25
25
|
|
26
26
|
# Load chef and chef-server-api from source rather than gem, if present
|
27
27
|
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../chef/lib/'))
|
28
|
+
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../../chef-solr/lib/'))
|
28
29
|
$:.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
|
29
30
|
|
30
31
|
# Print the version if we have -v or --version
|
data/config/init.rb
CHANGED
@@ -38,8 +38,10 @@ require 'chef/data_bag_item'
|
|
38
38
|
require 'chef/cookbook_version'
|
39
39
|
require 'chef/sandbox'
|
40
40
|
require 'chef/checksum'
|
41
|
+
require 'chef/environment'
|
41
42
|
require 'chef/monkey_patches/regexp'
|
42
43
|
|
44
|
+
|
43
45
|
require 'mixlib/authentication'
|
44
46
|
|
45
47
|
Mixlib::Authentication::Log.logger = Ohai::Log.logger = Chef::Log.logger
|
@@ -72,6 +74,7 @@ unless Merb::Config.environment == "test"
|
|
72
74
|
Chef::CookbookVersion.create_design_document
|
73
75
|
Chef::Sandbox.create_design_document
|
74
76
|
Chef::Checksum.create_design_document
|
77
|
+
Chef::Environment.create_design_document
|
75
78
|
|
76
79
|
# Create the signing key and certificate
|
77
80
|
Chef::Certificate.generate_signing_ca
|
@@ -82,6 +85,9 @@ unless Merb::Config.environment == "test"
|
|
82
85
|
# Generate the Web UI Key
|
83
86
|
Chef::Certificate.gen_validation_key(Chef::Config[:web_ui_client_name], Chef::Config[:web_ui_key], true)
|
84
87
|
|
88
|
+
# Create the '_default' Environment
|
89
|
+
Chef::Environment.create_default_environment
|
90
|
+
|
85
91
|
Chef::Log.info('Loading roles')
|
86
92
|
Chef::Role.sync_from_disk_to_couchdb
|
87
93
|
end
|
data/config/rack.rb
CHANGED
data/config/router.rb
CHANGED
@@ -27,7 +27,20 @@ Merb::Router.prepare do
|
|
27
27
|
:method => 'get').
|
28
28
|
to(:controller => "nodes", :action => "cookbooks")
|
29
29
|
# Roles
|
30
|
-
resources :roles
|
30
|
+
resources :roles do |r|
|
31
|
+
r.match('/environments', :method => 'get').to(:controller => "roles", :action => "environments")
|
32
|
+
r.match('/environments/:env_id', :method => 'get').to(:controller=>"roles", :action=>"environment")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Environments
|
36
|
+
resources :environments do |e|
|
37
|
+
e.match("/cookbooks", :method => "get").to(:controller=>"environments", :action=>"list_cookbooks")
|
38
|
+
e.match("/cookbooks/:cookbook_id", :method => "get").to(:controller=>"environments", :action=>"cookbook")
|
39
|
+
e.match("/recipes", :method => "get").to(:controller=>"environments", :action=>"list_recipes")
|
40
|
+
e.match("/nodes", :method => "get").to(:controller=>"environments", :action=>"list_nodes")
|
41
|
+
e.match("/roles/:role_id", :method => "get").to(:controller=>"environments", :action => "role")
|
42
|
+
e.match("/cookbook_versions", :method => "post").to(:controller=>"environments", :action=>"cookbook_versions_for_run_list")
|
43
|
+
end
|
31
44
|
|
32
45
|
# Status
|
33
46
|
match("/status").to(:controller => "status", :action => "index").name(:status)
|
@@ -40,7 +53,9 @@ Merb::Router.prepare do
|
|
40
53
|
match("/clients/:id", :id => /[\w\.-]+/, :method=>"delete").to(:controller=>'clients', :action=>'destroy')
|
41
54
|
|
42
55
|
# Search
|
43
|
-
resources :search
|
56
|
+
#resources :search
|
57
|
+
match('/search', :method => 'get').to(:controller => 'search', :action => 'index').name(:search)
|
58
|
+
match('/search/:id', :method => 'get').to(:controller => 'search', :action => 'show').name(:search_show)
|
44
59
|
match('/search/reindex', :method => 'post').to(:controller => "search", :action => "reindex")
|
45
60
|
|
46
61
|
# Cookbooks
|
@@ -48,9 +63,7 @@ Merb::Router.prepare do
|
|
48
63
|
|
49
64
|
match("/cookbooks",
|
50
65
|
:method => 'get'
|
51
|
-
).to(:controller => "cookbooks", :action => "index")
|
52
|
-
|
53
|
-
match("/cookbooks/_latest", :method=>'get').to(:controller=>'cookbooks',:action=>'index_latest')
|
66
|
+
).to(:controller => "cookbooks", :action => "index").name(:cookbooks)
|
54
67
|
|
55
68
|
match("/cookbooks/_recipes", :method=>'get').to(:controller=>'cookbooks',:action=>'index_recipes')
|
56
69
|
|
@@ -120,10 +133,10 @@ Merb::Router.prepare do
|
|
120
133
|
@json_params ||= begin
|
121
134
|
if Merb::Const::JSON_MIME_TYPE_REGEXP.match(content_type)
|
122
135
|
begin
|
123
|
-
# Call Chef's JSON utility instead of the default in Merb,
|
136
|
+
# Call Chef's JSON utility instead of the default in Merb,
|
124
137
|
# JSON.parse.
|
125
138
|
jobj = Chef::JSONCompat.from_json(raw_post)
|
126
|
-
jobj = jobj
|
139
|
+
jobj = Mash.from_hash(jobj) if jobj.is_a?(Hash)
|
127
140
|
rescue JSON::ParserError
|
128
141
|
jobj = Mash.new
|
129
142
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,83 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
#
|
2
|
+
# Author:: Tim Hinderliter (<tim@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "rubygems"
|
20
|
+
require "merb-core"
|
21
|
+
require "rspec"
|
22
|
+
|
23
|
+
Merb.push_path(:spec_helpers, "spec" / "spec_helpers", "**/*.rb")
|
24
|
+
Merb.push_path(:spec_fixtures, "spec" / "fixtures", "**/*.rb")
|
25
|
+
|
26
|
+
Merb.start_environment(:testing => true, :adapter => 'runner', :environment => ENV['MERB_ENV'] || 'test')
|
27
|
+
|
28
|
+
RSpec.configure do |config|
|
29
|
+
config.include(Merb::Test::RouteHelper)
|
30
|
+
config.include(Merb::Test::ControllerHelper)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_json(path, params = {}, env = {}, &block)
|
34
|
+
request_json("GET", path, params, env, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def post_json(path, post_body, env = {}, &block)
|
38
|
+
request_json("POST", path, {}, env) do |controller|
|
39
|
+
# Merb FakeRequest allows me no way to pass JSON across the
|
40
|
+
# FakeRequest/StringIO boundary, so we hack it here.
|
41
|
+
if post_body.is_a?(Hash)
|
42
|
+
controller.params.merge!(post_body)
|
43
|
+
else
|
44
|
+
controller.params['inflated_object'] = post_body
|
45
|
+
end
|
46
|
+
block.call if block
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Make an HTTP call of <method>, assign the accept header to
|
51
|
+
# application/json, and return the JSON-parsed output.
|
52
|
+
#
|
53
|
+
# Side effects:
|
54
|
+
# Raw textual output available in @response_raw
|
55
|
+
# Controller used available in @controller
|
56
|
+
def request_json(method, path, params, env, &block)
|
57
|
+
@controller = mock_request(path, params, env.merge({'HTTP_ACCEPT' => "application/json", :request_method => method})) do |controller|
|
58
|
+
stub_authentication(controller)
|
59
|
+
block.call(controller) if block
|
60
|
+
end
|
61
|
+
|
62
|
+
@response_raw = @controller.body
|
63
|
+
@response_json = Chef::JSONCompat.from_json(@response_raw)
|
64
|
+
end
|
65
|
+
|
66
|
+
def stub_authentication(controller)
|
67
|
+
username = "tester"
|
68
|
+
|
69
|
+
user = Chef::ApiClient.new
|
70
|
+
user.name(username)
|
71
|
+
user.admin(true)
|
72
|
+
|
73
|
+
# authenticate_every has a side-effect of setting @auth_user
|
74
|
+
controller.stub!(:authenticate_every).and_return(true)
|
75
|
+
controller.instance_variable_set(:@auth_user, user)
|
76
|
+
end
|
77
|
+
|
78
|
+
def root_url
|
79
|
+
# include organization name to run these tests in the platform
|
80
|
+
"http://localhost"
|
81
|
+
end
|
82
|
+
|
5
83
|
|
6
|
-
require 'chef'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Tim Hinderliter (<tim@opscode.com>)
|
3
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
# Set of methods to create model objects for spec tests.
|
20
|
+
# None of these methods save or otherwise commit the objects they
|
21
|
+
# create; they simply initialize the respective model object and
|
22
|
+
# set its name (and other important attributes, where appropriate).
|
23
|
+
|
24
|
+
def make_node(name)
|
25
|
+
res = Chef::Node.new
|
26
|
+
res.name(name)
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
def make_role(name)
|
31
|
+
res = Chef::Role.new
|
32
|
+
res.name(name)
|
33
|
+
res
|
34
|
+
end
|
35
|
+
|
36
|
+
def make_environment(name)
|
37
|
+
res = Chef::Environment.new
|
38
|
+
res.name(name)
|
39
|
+
res
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_cookbook(name, version)
|
43
|
+
res = Chef::CookbookVersion.new(name)
|
44
|
+
res.version = version
|
45
|
+
res
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_runlist(*items)
|
49
|
+
res = Chef::RunList.new
|
50
|
+
items.each do |item|
|
51
|
+
res << item
|
52
|
+
end
|
53
|
+
res
|
54
|
+
end
|
55
|
+
|
56
|
+
# Take an Array of cookbook_versions,
|
57
|
+
# And return a hash like:
|
58
|
+
# {
|
59
|
+
# "cookbook_name" => [CookbookVersion, CookbookVersion],
|
60
|
+
# }
|
61
|
+
def make_filtered_cookbook_hash(*array_cookbook_versions)
|
62
|
+
array_cookbook_versions.inject({}) do |res, cookbook_version|
|
63
|
+
res[cookbook_version.name] ||= Array.new
|
64
|
+
res[cookbook_version.name] << cookbook_version
|
65
|
+
res
|
66
|
+
end
|
67
|
+
end
|