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,111 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Nuo Yan (<nuo@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/api_client'
21
+
22
+ class ChefServerApi::Clients < ChefServerApi::Application
23
+ provides :json
24
+
25
+ before :authenticate_every
26
+ before :is_admin, :only => :index
27
+ before :is_correct_node, :only => [ :show, :create, :update, :destroy ]
28
+
29
+ # GET /clients
30
+ def index
31
+ @list = Chef::ApiClient.cdb_list(true)
32
+ display(@list.inject({}) { |result, element| result[element.name] = absolute_slice_url(:client, :id => element.name); result })
33
+ end
34
+
35
+ # GET /clients/:id
36
+ def show
37
+ begin
38
+ @client = Chef::ApiClient.cdb_load(params[:id])
39
+ rescue Chef::Exceptions::CouchDBNotFound => e
40
+ raise NotFound, "Cannot load client #{params[:id]}"
41
+ end
42
+ #display({ :name => @client.name, :admin => @client.admin, :public_key => @client.public_key })
43
+ display @client
44
+ end
45
+
46
+ # POST /clients
47
+ def create
48
+ exists = true
49
+ if params.has_key?(:inflated_object)
50
+ params[:name] ||= params[:inflated_object].name
51
+ params[:admin] ||= params[:inflated_object].admin
52
+ end
53
+
54
+ begin
55
+ Chef::ApiClient.cdb_load(params[:name])
56
+ rescue Chef::Exceptions::CouchDBNotFound
57
+ exists = false
58
+ end
59
+ raise Conflict, "Client already exists" if exists
60
+
61
+ @client = Chef::ApiClient.new
62
+ @client.name(params[:name])
63
+ @client.admin(params[:admin]) if params[:admin]
64
+ @client.create_keys
65
+ @client.cdb_save
66
+
67
+ self.status = 201
68
+ headers['Location'] = absolute_slice_url(:client, @client.name)
69
+ display({ :uri => absolute_slice_url(:client, @client.name), :private_key => @client.private_key })
70
+ end
71
+
72
+ # PUT /clients/:id
73
+ def update
74
+ if params.has_key?(:inflated_object)
75
+ params[:private_key] ||= params[:inflated_object].private_key
76
+ params[:admin] ||= params[:inflated_object].admin
77
+ end
78
+
79
+ begin
80
+ @client = Chef::ApiClient.cdb_load(params[:id])
81
+ rescue Chef::Exceptions::CouchDBNotFound => e
82
+ raise NotFound, "Cannot load client #{params[:id]}"
83
+ end
84
+
85
+ @client.admin(params[:admin]) unless params[:admin].nil?
86
+
87
+ results = { :name => @client.name, :admin => @client.admin }
88
+
89
+ if params[:private_key] == true
90
+ @client.create_keys
91
+ results[:private_key] = @client.private_key
92
+ end
93
+
94
+ @client.cdb_save
95
+
96
+ display(results)
97
+ end
98
+
99
+ # DELETE /clients/:id
100
+ def destroy
101
+ begin
102
+ @client = Chef::ApiClient.cdb_load(params[:id])
103
+ rescue Chef::Exceptions::CouchDBNotFound => e
104
+ raise NotFound, "Cannot load client #{params[:id]}"
105
+ end
106
+ @client.cdb_destroy
107
+ display({ :name => @client.name })
108
+ end
109
+
110
+ end
111
+
@@ -0,0 +1,213 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Author:: Christopher Walters (<cw@opscode.com>)
5
+ # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'chef' / 'cookbook_loader'
22
+ require 'chef' / 'cookbook' / 'metadata'
23
+
24
+ class ChefServerApi::Cookbooks < ChefServerApi::Application
25
+
26
+ provides :json
27
+
28
+ before :authenticate_every
29
+
30
+ include Chef::Mixin::Checksum
31
+ include Merb::ChefServerApi::TarballHelper
32
+
33
+ def index
34
+ cl = Chef::CookbookLoader.new
35
+ cookbook_list = Hash.new
36
+ cl.each do |cookbook|
37
+ cookbook_list[cookbook.name] = absolute_slice_url(:cookbook, :id => cookbook.name.to_s)
38
+ end
39
+ display cookbook_list
40
+ end
41
+
42
+ def show
43
+ cl = Chef::CookbookLoader.new
44
+ begin
45
+ cookbook = cl[params[:id]]
46
+ rescue ArgumentError => e
47
+ raise NotFound, "Cannot find a cookbook named #{params[:id]}"
48
+ end
49
+ results = load_cookbook_files(cookbook)
50
+ results[:name] = cookbook.name.to_s
51
+ results[:metadata] = cl.metadata[cookbook.name.to_sym]
52
+ display results
53
+ end
54
+
55
+ def show_segment
56
+ cl = Chef::CookbookLoader.new
57
+ begin
58
+ cookbook = cl[params[:cookbook_id]]
59
+ rescue ArgumentError => e
60
+ raise NotFound, "Cannot find a cookbook named #{params[:cookbook_id]}"
61
+ end
62
+ cookbook_files = load_cookbook_files(cookbook)
63
+ raise NotFound unless cookbook_files.has_key?(params[:segment].to_sym)
64
+
65
+ if params[:id]
66
+ case params[:segment]
67
+ when "templates","files"
68
+ if params[:recursive]
69
+ serve_directory_preferred(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
70
+ else
71
+ serve_segment_preferred(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
72
+ end
73
+ else
74
+ serve_segment_file(cookbook, params[:segment], cookbook_files[params[:segment].to_sym])
75
+ end
76
+ else
77
+ display cookbook_files[params[:segment].to_sym]
78
+ end
79
+ end
80
+
81
+ def serve_segment_preferred(cookbook, segment, files)
82
+
83
+ to_send = nil
84
+
85
+ preferences.each do |pref|
86
+ unless to_send
87
+ Chef::Log.debug("Looking for a file with name `#{params[:id]}' and specificity #{pref}")
88
+ to_send = files.detect do |file|
89
+ Chef::Log.debug("#{pref.inspect} #{file.inspect}")
90
+ file[:name] == params[:id] && file[:specificity] == pref
91
+
92
+ end
93
+ end
94
+ end
95
+
96
+ raise NotFound, "Cannot find a suitable #{segment} file for #{params[:id]}!" unless to_send
97
+ current_checksum = to_send[:checksum]
98
+ Chef::Log.debug("#{to_send[:name]} Client Checksum: #{params[:checksum]}, Server Checksum: #{current_checksum}")
99
+ if current_checksum == params[:checksum]
100
+ raise NotModified, "File #{to_send[:name]} has not changed"
101
+ else
102
+ file_name = nil
103
+ segment_files(segment.to_sym, cookbook).each do |f|
104
+ if f =~ /#{to_send[:specificity]}\/#{to_send[:name]}$/
105
+ file_name = File.expand_path(f)
106
+ break
107
+ end
108
+ end
109
+ raise NotFound, "Cannot find the real file for #{to_send[:specificity]} #{to_send[:name]} - this is a 42 error (shouldn't ever happen)" unless file_name
110
+ send_file(file_name)
111
+ end
112
+ end
113
+
114
+ def serve_directory_preferred(cookbook, segment, files)
115
+ preferred_dir_contents = []
116
+ preferences.each do |preference|
117
+ preferred_dir_contents = files.select { |file| file[:name] =~ /^#{params[:id]}/ && file[:specificity] == preference }
118
+ break unless preferred_dir_contents.empty?
119
+ end
120
+
121
+ raise NotFound, "Cannot find a suitable directory for #{params[:id]}" if preferred_dir_contents.empty?
122
+
123
+ display preferred_dir_contents.map { |file| file[:name].sub(/^#{params[:id]}/, '') }
124
+ end
125
+
126
+ def preferences
127
+ ["host-#{params[:fqdn]}",
128
+ "#{params[:platform]}-#{params[:version]}",
129
+ "#{params[:platform]}",
130
+ "default"]
131
+ end
132
+
133
+ def serve_segment_file(cookbook, segment, files)
134
+ to_send = files.detect { |f| f[:name] == params[:id] }
135
+ raise NotFound, "Cannot find a suitable #{segment} file!" unless to_send
136
+ current_checksum = to_send[:checksum]
137
+ Chef::Log.debug("#{to_send[:name]} Client Checksum: #{params[:checksum]}, Server Checksum: #{current_checksum}")
138
+ if current_checksum == params[:checksum]
139
+ raise NotModified, "File #{to_send[:name]} has not changed"
140
+ else
141
+ file_name = nil
142
+ segment_files(segment.to_sym, cookbook).each do |f|
143
+ next unless File.basename(f) == to_send[:name]
144
+ file_name = File.expand_path(f)
145
+ end
146
+ raise NotFound, "Cannot find the real file for #{to_send[:name]} - this is a 42 error (shouldn't ever happen)" unless file_name
147
+ send_file(file_name)
148
+ end
149
+ end
150
+
151
+ def create
152
+ # validate name and file parameters and throw an error if a cookbook with the same name already exists
153
+ raise BadRequest, "missing required parameter: name" unless params[:name]
154
+ desired_name = params[:name]
155
+ raise BadRequest, "invalid parameter: name must be at least one character long and contain only letters, numbers, periods (.), underscores (_), and hyphens (-)" unless desired_name =~ /\A[\w.-]+\Z/
156
+ begin
157
+ validate_file_parameter(desired_name, params[:file])
158
+ rescue FileParameterException => te
159
+ raise BadRequest, te.message
160
+ end
161
+
162
+ begin
163
+ Chef::CookbookLoader.new[desired_name]
164
+ raise BadRequest, "Cookbook with the name #{desired_name} already exists"
165
+ rescue ArgumentError
166
+ end
167
+
168
+ expand_tarball_and_put_in_repository(desired_name, params[:file][:tempfile])
169
+
170
+ # construct successful response
171
+ self.status = 201
172
+ location = absolute_slice_url(:cookbook, :id => desired_name)
173
+ headers['Location'] = location
174
+ result = { 'uri' => location }
175
+ display result
176
+ end
177
+
178
+ def get_tarball
179
+ cookbook_name = params[:cookbook_id]
180
+ expected_location = cookbook_location(cookbook_name)
181
+ raise NotFound, "Cannot find cookbook named #{cookbook_name} at #{expected_location}. Note: Tarball generation only applies to cookbooks under the first directory in the server's Chef::Config.cookbook_path variable and does to apply overrides." unless File.directory? expected_location
182
+
183
+ send_file(get_or_create_cookbook_tarball_location(cookbook_name))
184
+ end
185
+
186
+ def update
187
+ cookbook_name = params[:cookbook_id]
188
+ cookbook_path = cookbook_location(cookbook_name)
189
+ raise NotFound, "Cannot find cookbook named #{cookbook_name}" unless File.directory? cookbook_path
190
+ begin
191
+ validate_file_parameter(cookbook_name, params[:file])
192
+ rescue FileParameterException => te
193
+ raise BadRequest, te.message
194
+ end
195
+
196
+ expand_tarball_and_put_in_repository(cookbook_name, params[:file][:tempfile])
197
+
198
+ display Hash.new
199
+ end
200
+
201
+ def destroy
202
+ cookbook_name = params[:id]
203
+ cookbook_path = cookbook_location(cookbook_name)
204
+ raise NotFound, "Cannot find cookbook named #{cookbook_name}" unless File.directory? cookbook_path
205
+
206
+ FileUtils.rm_rf(cookbook_path)
207
+ FileUtils.rm_f(cookbook_tarball_location(cookbook_name))
208
+
209
+ display Hash.new
210
+ end
211
+
212
+ end
213
+
@@ -0,0 +1,75 @@
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/data_bag'
21
+
22
+ class ChefServerApi::Data < ChefServerApi::Application
23
+
24
+ provides :json
25
+
26
+ before :authenticate_every
27
+ before :is_admin, :only => [ :create, :destroy ]
28
+
29
+ def index
30
+ @bag_list = Chef::DataBag.cdb_list(false)
31
+ display(@bag_list.inject({}) { |r,b| r[b] = absolute_slice_url(:datum, :id => b); r })
32
+
33
+ end
34
+
35
+ def show
36
+ begin
37
+ @data_bag = Chef::DataBag.cdb_load(params[:id])
38
+ rescue Chef::Exceptions::CouchDBNotFound => e
39
+ raise NotFound, "Cannot load data bag #{params[:id]}"
40
+ end
41
+ display(@data_bag.list.inject({}) { |res, i| res[i] = absolute_slice_url(:data_bag_item, :data_bag_id => @data_bag.name, :id => i); res })
42
+ end
43
+
44
+ def create
45
+ @data_bag = nil
46
+ if params.has_key?("inflated_object")
47
+ @data_bag = params["inflated_object"]
48
+ else
49
+ @data_bag = Chef::DataBag.new
50
+ @data_bag.name(params["name"])
51
+ end
52
+ exists = true
53
+ begin
54
+ Chef::DataBag.cdb_load(@data_bag.name)
55
+ rescue Chef::Exceptions::CouchDBNotFound
56
+ exists = false
57
+ end
58
+ raise Conflict, "Data bag already exists" if exists
59
+ self.status = 201
60
+ @data_bag.cdb_save
61
+ display({ :uri => absolute_slice_url(:datum, :id => @data_bag.name) })
62
+ end
63
+
64
+ def destroy
65
+ begin
66
+ @data_bag = Chef::DataBag.cdb_load(params[:id])
67
+ rescue Chef::Exceptions::CouchDBNotFound => e
68
+ raise NotFound, "Cannot load data bag #{params[:id]}"
69
+ end
70
+ @data_bag.cdb_destroy
71
+ @data_bag.couchdb_rev = nil
72
+ display @data_bag
73
+ end
74
+
75
+ end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Author:: Nuo Yan (<nuo@opscode.com>)
5
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'chef/data_bag'
22
+ require 'chef/data_bag_item'
23
+
24
+ class ChefServerApi::DataItem < ChefServerApi::Application
25
+
26
+ provides :json
27
+
28
+ before :populate_data_bag
29
+ before :authenticate_every
30
+ before :is_admin, :only => [ :create, :update, :destroy ]
31
+
32
+ def populate_data_bag
33
+ begin
34
+ @data_bag = Chef::DataBag.cdb_load(params[:data_bag_id])
35
+ rescue Chef::Exceptions::CouchDBNotFound => e
36
+ raise NotFound, "Cannot load data bag #{params[:data_bag_id]}"
37
+ end
38
+ end
39
+
40
+ def show
41
+ begin
42
+ @data_bag_item = Chef::DataBagItem.cdb_load(params[:data_bag_id], params[:id])
43
+ rescue Chef::Exceptions::CouchDBNotFound => e
44
+ raise NotFound, "Cannot load data bag #{params[:data_bag_id]} item #{params[:id]}"
45
+ end
46
+ display @data_bag_item.raw_data
47
+ end
48
+
49
+ def create
50
+ raw_data = nil
51
+ if params.has_key?("inflated_object")
52
+ raw_data = params["inflated_object"].raw_data
53
+ else
54
+ raw_data = params
55
+ raw_data.delete(:action)
56
+ raw_data.delete(:controller)
57
+ raw_data.delete(:data_bag_id)
58
+ end
59
+ @data_bag_item = nil
60
+ begin
61
+ @data_bag_item = Chef::DataBagItem.cdb_load(@data_bag.name, params[:id])
62
+ rescue Chef::Exceptions::CouchDBNotFound
63
+ @data_bag_item = Chef::DataBagItem.new
64
+ @data_bag_item.data_bag(@data_bag.name)
65
+ else
66
+ raise Conflict, "Databag Item #{params[:id]} already exists" if @data_bag_item
67
+ end
68
+ @data_bag_item.raw_data = raw_data
69
+ @data_bag_item.cdb_save
70
+ display @data_bag_item.raw_data
71
+ end
72
+
73
+ def update
74
+ raw_data = nil
75
+ if params.has_key?("inflated_object")
76
+ raw_data = params["inflated_object"].raw_data
77
+ else
78
+ raw_data = params
79
+ raw_data.delete(:action)
80
+ raw_data.delete(:controller)
81
+ raw_data.delete(:data_bag_id)
82
+ end
83
+
84
+ begin
85
+ @data_bag_item = Chef::DataBagItem.cdb_load(@data_bag.name, params[:id])
86
+ rescue Chef::Exceptions::CouchDBNotFound => e
87
+ raise NotFound, "Cannot load Databag Item #{params[:id]}"
88
+ end
89
+
90
+ @data_bag_item.raw_data = raw_data
91
+ @data_bag_item.cdb_save
92
+ display @data_bag_item.raw_data
93
+
94
+ end
95
+
96
+
97
+ def destroy
98
+ begin
99
+ @data_bag_item = Chef::DataBagItem.cdb_load(params[:data_bag_id], params[:id])
100
+ rescue Chef::Exceptions::CouchDBNotFound => e
101
+ raise NotFound, "Cannot load data bag #{params[:data_bag_id]} item #{params[:id]}"
102
+ end
103
+ @data_bag_item.cdb_destroy
104
+ @data_bag_item.couchdb_rev = nil
105
+ display @data_bag_item
106
+ end
107
+
108
+ end