chef-server-api 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +92 -0
  3. data/Rakefile +59 -0
  4. data/app/controllers/application.rb +289 -0
  5. data/app/controllers/clients.rb +111 -0
  6. data/app/controllers/cookbooks.rb +213 -0
  7. data/app/controllers/data.rb +75 -0
  8. data/app/controllers/data_item.rb +108 -0
  9. data/app/controllers/exceptions.rb +40 -0
  10. data/app/controllers/main.rb +18 -0
  11. data/app/controllers/nodes.rb +102 -0
  12. data/app/controllers/roles.rb +73 -0
  13. data/app/controllers/search.rb +60 -0
  14. data/app/controllers/users.rb +77 -0
  15. data/app/helpers/application_helper.rb +163 -0
  16. data/app/helpers/exceptions_helper.rb +6 -0
  17. data/app/helpers/global_helpers.rb +25 -0
  18. data/app/helpers/nodes_helper.rb +26 -0
  19. data/app/helpers/roles_helper.rb +5 -0
  20. data/app/helpers/tarball_helper.rb +82 -0
  21. data/app/views/exceptions/bad_request.json.erb +1 -0
  22. data/app/views/exceptions/internal_server_error.html.erb +216 -0
  23. data/app/views/exceptions/not_acceptable.html.haml +5 -0
  24. data/app/views/exceptions/not_found.html.erb +47 -0
  25. data/app/views/exceptions/standard_error.html.erb +217 -0
  26. data/app/views/layout/chef_server_api.html.haml +23 -0
  27. data/app/views/main/index.html.haml +5 -0
  28. data/config/init.rb +45 -0
  29. data/config/router.rb +6 -0
  30. data/lib/chef-server-api.rb +158 -0
  31. data/lib/chef-server-api/merbtasks.rb +103 -0
  32. data/lib/chef-server-api/slicetasks.rb +20 -0
  33. data/lib/chef-server-api/spectasks.rb +53 -0
  34. data/public/images/avatar.png +0 -0
  35. data/public/images/indicator.gif +0 -0
  36. data/public/images/merb.jpg +0 -0
  37. data/public/stylesheets/base.css +336 -0
  38. data/public/stylesheets/chef.css +157 -0
  39. data/public/stylesheets/themes/bec-green/style.css +290 -0
  40. data/public/stylesheets/themes/bec/style.css +301 -0
  41. data/public/stylesheets/themes/blue/style.css +280 -0
  42. data/public/stylesheets/themes/default/style.css +267 -0
  43. data/public/stylesheets/themes/djime-cerulean/style.css +298 -0
  44. data/public/stylesheets/themes/kathleene/style.css +272 -0
  45. data/public/stylesheets/themes/orange/style.css +263 -0
  46. data/public/stylesheets/themes/reidb-greenish/style.css +301 -0
  47. data/stubs/app/controllers/application.rb +2 -0
  48. data/stubs/app/controllers/main.rb +2 -0
  49. metadata +193 -0
@@ -0,0 +1,40 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ class Exceptions < ChefServerApi::Application
21
+
22
+ provides :json
23
+
24
+ def not_acceptable
25
+ if request.accept =~ /application\/json/
26
+ display({ "error" => request.exceptions })
27
+ else
28
+ render
29
+ end
30
+ end
31
+
32
+ def standard_error
33
+ if request.accept =~ /application\/json/
34
+ display({ "error" => request.exceptions })
35
+ else
36
+ raise request.exceptions.first
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,18 @@
1
+ class ChefServerApi::Main < ChefServerApi::Application
2
+
3
+ before :authenticate_every
4
+ provides :html, :json
5
+
6
+ def index
7
+ display(
8
+ {
9
+ absolute_slice_url(:nodes) => "Manage Nodes",
10
+ absolute_slice_url(:roles) => "Manage Roles",
11
+ absolute_slice_url(:cookbooks) => "Manage Cookbooks",
12
+ absolute_slice_url(:data) => "Manage Data Bags",
13
+ absolute_slice_url(:search) => "Search"
14
+ }
15
+ )
16
+ end
17
+
18
+ end
@@ -0,0 +1,102 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef' / 'node'
21
+
22
+ class ChefServerApi::Nodes < ChefServerApi::Application
23
+
24
+ provides :json
25
+
26
+ before :authenticate_every
27
+ before :is_correct_node, :only => [ :update, :destroy, :cookbooks ]
28
+
29
+ def index
30
+ @node_list = Chef::Node.cdb_list
31
+ display(@node_list.inject({}) do |r,n|
32
+ r[n] = absolute_slice_url(:node, n); r
33
+ end)
34
+ end
35
+
36
+ def show
37
+ begin
38
+ @node = Chef::Node.cdb_load(params[:id])
39
+ rescue Chef::Exceptions::CouchDBNotFound => e
40
+ raise NotFound, "Cannot load node #{params[:id]}"
41
+ end
42
+ @node.couchdb_rev = nil
43
+ recipes, default, override = @node.run_list.expand("couchdb")
44
+ @node.default_attrs = default
45
+ @node.override_attrs = override
46
+ display @node
47
+ end
48
+
49
+ def create
50
+ @node = params["inflated_object"]
51
+ exists = true
52
+ begin
53
+ Chef::Node.cdb_load(@node.name)
54
+ rescue Chef::Exceptions::CouchDBNotFound
55
+ exists = false
56
+ end
57
+ raise Conflict, "Node already exists" if exists
58
+ self.status = 201
59
+ @node.cdb_save
60
+ display({ :uri => absolute_slice_url(:node, @node.name) })
61
+ end
62
+
63
+ def update
64
+ begin
65
+ @node = Chef::Node.cdb_load(params[:id])
66
+ rescue Chef::Exceptions::CouchDBNotFound => e
67
+ raise NotFound, "Cannot load node #{params[:id]}"
68
+ end
69
+
70
+ updated = params['inflated_object']
71
+ @node.run_list.reset!(updated.run_list)
72
+ @node.attribute = updated.attribute
73
+ @node.override_attrs = updated.override_attrs
74
+ @node.default_attrs = updated.default_attrs
75
+ @node.cdb_save
76
+ @node.couchdb_rev = nil
77
+ display(@node)
78
+ end
79
+
80
+ def destroy
81
+ begin
82
+ @node = Chef::Node.cdb_load(params[:id])
83
+ rescue Chef::Exceptions::CouchDBNotFound => e
84
+ raise NotFound, "Cannot load node #{params[:id]}"
85
+ end
86
+ @node.cdb_destroy
87
+ @node.couchdb_rev = nil
88
+ display @node
89
+ end
90
+
91
+ def cookbooks
92
+ begin
93
+ @node = Chef::Node.cdb_load(params[:id])
94
+ rescue Chef::Exceptions::CouchDBNotFound => e
95
+ raise NotFound, "Cannot load node #{params[:id]}"
96
+ end
97
+
98
+ display(load_all_files(params[:id]))
99
+ end
100
+
101
+ end
102
+
@@ -0,0 +1,73 @@
1
+ require 'chef/role'
2
+
3
+ class ChefServerApi::Roles < ChefServerApi::Application
4
+ provides :json
5
+
6
+ before :authenticate_every
7
+ before :is_admin, :only => [ :create, :update, :destroy ]
8
+
9
+ # GET /roles
10
+ def index
11
+ @role_list = Chef::Role.cdb_list(true)
12
+ display(@role_list.inject({}) { |r,role| r[role.name] = absolute_slice_url(:role, role.name); r })
13
+ end
14
+
15
+ # GET /roles/:id
16
+ def show
17
+ begin
18
+ @role = Chef::Role.cdb_load(params[:id])
19
+ rescue Chef::Exceptions::CouchDBNotFound => e
20
+ raise NotFound, "Cannot load role #{params[:id]}"
21
+ end
22
+ @role.couchdb_rev = nil
23
+ display @role
24
+ end
25
+
26
+ # POST /roles
27
+ def create
28
+ @role = params["inflated_object"]
29
+ exists = true
30
+ begin
31
+ Chef::Role.cdb_load(@role.name)
32
+ rescue Chef::Exceptions::CouchDBNotFound
33
+ exists = false
34
+ end
35
+ raise Conflict, "Role already exists" if exists
36
+
37
+ @role.cdb_save
38
+
39
+ self.status = 201
40
+ display({ :uri => absolute_slice_url(:role, :id => @role.name) })
41
+ end
42
+
43
+ # PUT /roles/:id
44
+ def update
45
+ begin
46
+ @role = Chef::Role.cdb_load(params[:id])
47
+ rescue Chef::Exceptions::CouchDBNotFound => e
48
+ raise NotFound, "Cannot load role #{params[:id]}"
49
+ end
50
+
51
+ @role.description(params["inflated_object"].description)
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)
56
+ @role.cdb_save
57
+ self.status = 200
58
+ @role.couchdb_rev = nil
59
+ display(@role)
60
+ end
61
+
62
+ # DELETE /roles/:id
63
+ def destroy
64
+ begin
65
+ @role = Chef::Role.cdb_load(params[:id])
66
+ rescue Chef::Exceptions::CouchDBNotFound => e
67
+ raise NotFound, "Cannot load role #{params[:id]}"
68
+ end
69
+ @role.cdb_destroy
70
+ display @role
71
+ end
72
+
73
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+
20
+ require 'chef/solr/query'
21
+
22
+ class ChefServerApi::Search < ChefServerApi::Application
23
+ provides :json
24
+
25
+ before :authenticate_every
26
+ before :is_admin, :only => [:reindex]
27
+
28
+ def index
29
+ indexes = valid_indexes
30
+ display(indexes.inject({}) { |r,i| r[i] = absolute_slice_url(:search, i); r })
31
+ end
32
+
33
+ def valid_indexes
34
+ indexes = Chef::DataBag.cdb_list(false)
35
+ indexes += %w{ role node client }
36
+ end
37
+
38
+ def show
39
+ unless valid_indexes.include?(params[:id])
40
+ raise NotFound, "I don't know how to search for #{params[:id]} data objects."
41
+ end
42
+
43
+ query = Chef::Solr::Query.new(Chef::Config[:solr_url], Chef::Config[:couchdb_database])
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
+ })
54
+ end
55
+
56
+ def reindex
57
+ display(Chef::Solr.new.rebuild_index)
58
+ end
59
+
60
+ end
@@ -0,0 +1,77 @@
1
+ #
2
+ # Author:: Nuo Yan (<nuo@opscode.com>)
3
+ # Copyright:: Copyright (c) 2009 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 File.join("chef", "webui_user")
20
+
21
+ class ChefServerApi::Users < ChefServerApi::Application
22
+ provides :json
23
+
24
+ before :authenticate_every
25
+
26
+ # GET to /users
27
+ def index
28
+ @user_list = Chef::WebUIUser.cdb_list
29
+ display(@user_list.inject({}) { |r,n| r[n] = absolute_slice_url(:user, n); r })
30
+ end
31
+
32
+ # GET to /users/:id
33
+ def show
34
+ begin
35
+ @user = Chef::WebUIUser.cdb_load(params[:id])
36
+ rescue Chef::Exceptions::CouchDBNotFound => e
37
+ raise NotFound, "Cannot load user #{params[:id]}"
38
+ end
39
+ display @user
40
+ end
41
+
42
+ # PUT to /users/:id
43
+ def update
44
+ begin
45
+ Chef::WebUIUser.cdb_load(params[:id])
46
+ rescue Chef::Exceptions::CouchDBNotFound => e
47
+ raise NotFound, "Cannot load user #{params[:id]}"
48
+ end
49
+ @user = params['inflated_object']
50
+ @user.cdb_save
51
+ display(@user)
52
+ end
53
+
54
+ # POST to /users
55
+ def create
56
+ @user = params["inflated_object"]
57
+ begin
58
+ Chef::WebUIUser.cdb_load(@user.name)
59
+ rescue Chef::Exceptions::CouchDBNotFound
60
+ @user.cdb_save
61
+ self.status = 201
62
+ else
63
+ raise Conflict, "User already exists"
64
+ end
65
+ display({ :uri => absolute_slice_url(:user, @user.name) })
66
+ end
67
+
68
+ def destroy
69
+ begin
70
+ @user = Chef::WebUIUser.cdb_load(params[:id])
71
+ rescue Chef::Exceptions::CouchDBNotFound => e
72
+ raise NotFound, "Cannot load user #{params[:id]}"
73
+ end
74
+ @user.cdb_destroy
75
+ display @user
76
+ end
77
+ end
@@ -0,0 +1,163 @@
1
+ module Merb
2
+ module ChefServerApi
3
+ module ApplicationHelper
4
+
5
+ # Generate the absolute url for a slice - takes the slice's :path_prefix into account.
6
+ #
7
+ # @param slice_name<Symbol>
8
+ # The name of the slice - in identifier_sym format (underscored).
9
+ # @param *args<Array[Symbol,Hash]>
10
+ # There are several possibilities regarding arguments:
11
+ # - when passing a Hash only, the :default route of the current
12
+ # slice will be used
13
+ # - when a Symbol is passed, it's used as the route name
14
+ # - a Hash with additional params can optionally be passed
15
+ #
16
+ # @return <String> A uri based on the requested slice.
17
+ #
18
+ # @example absolute_slice_url(:awesome, :format => 'html')
19
+ # @example absolute_slice_url(:forum, :posts, :format => 'xml')
20
+ def absolute_slice_url(slice_name, *args)
21
+ options = extract_options_from_args!(args) || {}
22
+ protocol = options.delete(:protocol) || request.protocol
23
+ host = options.delete(:host) || request.host
24
+
25
+ protocol + "://" + host + slice_url(slice_name,*args)
26
+ end
27
+
28
+ # @param *segments<Array[#to_s]> Path segments to append.
29
+ #
30
+ # @return <String>
31
+ # A path relative to the public directory, with added segments.
32
+ def image_path(*segments)
33
+ public_path_for(:image, *segments)
34
+ end
35
+
36
+ # @param *segments<Array[#to_s]> Path segments to append.
37
+ #
38
+ # @return <String>
39
+ # A path relative to the public directory, with added segments.
40
+ def javascript_path(*segments)
41
+ public_path_for(:javascript, *segments)
42
+ end
43
+
44
+ # @param *segments<Array[#to_s]> Path segments to append.
45
+ #
46
+ # @return <String>
47
+ # A path relative to the public directory, with added segments.
48
+ def stylesheet_path(*segments)
49
+ public_path_for(:stylesheet, *segments)
50
+ end
51
+
52
+ # Construct a path relative to the public directory
53
+ #
54
+ # @param <Symbol> The type of component.
55
+ # @param *segments<Array[#to_s]> Path segments to append.
56
+ #
57
+ # @return <String>
58
+ # A path relative to the public directory, with added segments.
59
+ def public_path_for(type, *segments)
60
+ ::ChefServerApi.public_path_for(type, *segments)
61
+ end
62
+
63
+ # Construct an app-level path.
64
+ #
65
+ # @param <Symbol> The type of component.
66
+ # @param *segments<Array[#to_s]> Path segments to append.
67
+ #
68
+ # @return <String>
69
+ # A path within the host application, with added segments.
70
+ def app_path_for(type, *segments)
71
+ ::ChefServerApi.app_path_for(type, *segments)
72
+ end
73
+
74
+ # Construct a slice-level path.
75
+ #
76
+ # @param <Symbol> The type of component.
77
+ # @param *segments<Array[#to_s]> Path segments to append.
78
+ #
79
+ # @return <String>
80
+ # A path within the slice source (Gem), with added segments.
81
+ def slice_path_for(type, *segments)
82
+ ::ChefServerApi.slice_path_for(type, *segments)
83
+ end
84
+
85
+ def build_tree(name, node, default={}, override={})
86
+ node = Chef::Mixin::DeepMerge.merge(default, node)
87
+ node = Chef::Mixin::DeepMerge.merge(node, override)
88
+ html = "<table id='#{name}' class='tree table'>"
89
+ html << "<tr><th class='first'>Attribute</th><th class='last'>Value</th></tr>"
90
+ count = 0
91
+ parent = 0
92
+ append_tree(name, html, node, count, parent, override)
93
+ html << "</table>"
94
+ html
95
+ end
96
+
97
+ def append_tree(name, html, node, count, parent, override)
98
+ node.sort{ |a,b| a[0] <=> b[0] }.each do |key, value|
99
+ to_send = Array.new
100
+ count += 1
101
+ is_parent = false
102
+ local_html = ""
103
+ local_html << "<tr id='#{name}-#{count}' class='collapsed #{name}"
104
+ if parent != 0
105
+ local_html << " child-of-#{name}-#{parent}' style='display: none;'>"
106
+ else
107
+ local_html << "'>"
108
+ end
109
+ local_html << "<td class='table-key'><span toggle='#{name}-#{count}'/>#{key}</td>"
110
+ case value
111
+ when Hash
112
+ is_parent = true
113
+ local_html << "<td></td>"
114
+ p = count
115
+ to_send << Proc.new { append_tree(name, html, value, count, p, override) }
116
+ when Array
117
+ is_parent = true
118
+ local_html << "<td></td>"
119
+ as_hash = {}
120
+ value.each_index { |i| as_hash[i] = value[i] }
121
+ p = count
122
+ to_send << Proc.new { append_tree(name, html, as_hash, count, p, override) }
123
+ else
124
+ local_html << "<td><div class='json-attr'>#{value}</div></td>"
125
+ end
126
+ local_html << "</tr>"
127
+ local_html.sub!(/class='collapsed/, 'class=\'collapsed parent') if is_parent
128
+ local_html.sub!(/<span/, "<span class='expander'") if is_parent
129
+ html << local_html
130
+ to_send.each { |s| count = s.call }
131
+ count += to_send.length
132
+ end
133
+ count
134
+ end
135
+
136
+ # Recursively build a tree of lists.
137
+ #def build_tree(node)
138
+ # list = "<dl>"
139
+ # list << "\n<!-- Beginning of Tree -->"
140
+ # walk = lambda do |key,value|
141
+ # case value
142
+ # when Hash, Array
143
+ # list << "\n<!-- Beginning of Enumerable obj -->"
144
+ # list << "\n<dt>#{key}</dt>"
145
+ # list << "<dd>"
146
+ # list << "\t<dl>\n"
147
+ # value.each(&walk)
148
+ # list << "\t</dl>\n"
149
+ # list << "</dd>"
150
+ # list << "\n<!-- End of Enumerable obj -->"
151
+ #
152
+ # else
153
+ # list << "\n<dt>#{key}</dt>"
154
+ # list << "<dd>#{value}</dd>"
155
+ # end
156
+ # end
157
+ # node.sort{ |a,b| a[0] <=> b[0] }.each(&walk)
158
+ # list << "</dl>"
159
+ #end
160
+
161
+ end
162
+ end
163
+ end