chef-server-api 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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