chef-server-api 0.8.16 → 0.9.0.a3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ require File.dirname(__FILE__) + '/lib/chef-server-api/version'
2
+
1
3
  require 'rubygems'
2
4
  require 'rake/gempackagetask'
3
5
 
@@ -5,7 +7,6 @@ require 'merb-core'
5
7
  require 'merb-core/tasks/merb'
6
8
 
7
9
  GEM_NAME = "chef-server-api"
8
- CHEF_SERVER_VERSION="0.8.16"
9
10
  AUTHOR = "Opscode"
10
11
  EMAIL = "chef@opscode.com"
11
12
  HOMEPAGE = "http://wiki.opscode.com/display/chef"
@@ -13,7 +14,7 @@ SUMMARY = "A systems integration framework, built to bring the benefits of confi
13
14
 
14
15
  spec = Gem::Specification.new do |s|
15
16
  s.name = GEM_NAME
16
- s.version = CHEF_SERVER_VERSION
17
+ s.version = ChefServerApi::VERSION
17
18
  s.platform = Gem::Platform::RUBY
18
19
  s.has_rdoc = true
19
20
  s.extra_rdoc_files = ["README.rdoc", "LICENSE" ]
@@ -23,16 +24,20 @@ spec = Gem::Specification.new do |s|
23
24
  s.email = EMAIL
24
25
  s.homepage = HOMEPAGE
25
26
 
26
- s.add_dependency "merb-core", "~> 1.0.0"
27
- s.add_dependency "merb-slices", "~> 1.0.0"
28
- s.add_dependency "merb-assets", "~> 1.0.0"
29
- s.add_dependency "merb-helpers", "~> 1.0.0"
27
+ s.add_dependency "merb-core", "~> 1.1.0"
28
+ s.add_dependency "merb-slices", "~> 1.1.0"
29
+ s.add_dependency "merb-assets", "~> 1.1.0"
30
+ s.add_dependency "merb-helpers", "~> 1.1.0"
31
+ s.add_dependency "merb-param-protection", "~> 1.1.0"
30
32
 
31
33
  s.add_dependency "json", "<= 1.4.2"
32
34
 
33
35
  s.add_dependency "uuidtools", "~> 2.1.1"
34
36
 
35
37
  s.add_dependency "thin"
38
+
39
+ s.bindir = "bin"
40
+ s.executables = %w( chef-server )
36
41
 
37
42
  s.require_path = 'lib'
38
43
  s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{config,lib,spec,app,public,stubs}/**/*")
@@ -44,12 +49,12 @@ end
44
49
 
45
50
  desc "Install the gem"
46
51
  task :install => :package do
47
- sh %{gem install pkg/#{GEM_NAME}-#{CHEF_SERVER_VERSION} --no-rdoc --no-ri}
52
+ sh %{gem install pkg/#{GEM_NAME}-#{ChefServerApi::VERSION} --no-rdoc --no-ri}
48
53
  end
49
54
 
50
55
  desc "Uninstall the gem"
51
56
  task :uninstall do
52
- sh %{gem uninstall #{GEM_NAME} -x -v #{CHEF_SERVER_VERSION} }
57
+ sh %{gem uninstall #{GEM_NAME} -x -v #{ChefServerApi::VERSION} }
53
58
  end
54
59
 
55
60
  desc "Create a gemspec file"
@@ -58,4 +63,3 @@ task :gemspec do
58
63
  file.puts spec.to_ruby
59
64
  end
60
65
  end
61
-
@@ -1,16 +1,17 @@
1
1
  #
2
- #
3
2
  # Author:: Adam Jacob (<adam@opscode.com>)
4
3
  # Author:: Christopher Brown (<cb@opscode.com>)
5
- # Copyright:: Copyright (c) 2008 Opscode, Inc.
4
+ # Author:: Christopher Walters (<cw@opscode.com>)
5
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
6
+ # Copyright:: Copyright (c) 2008-2010 Opscode, Inc.
6
7
  # License:: Apache License, Version 2.0
7
8
  #
8
9
  # Licensed under the Apache License, Version 2.0 (the "License");
9
10
  # you may not use this file except in compliance with the License.
10
11
  # You may obtain a copy of the License at
11
- #
12
+ #
12
13
  # http://www.apache.org/licenses/LICENSE-2.0
13
- #
14
+ #
14
15
  # Unless required by applicable law or agreed to in writing, software
15
16
  # distributed under the License is distributed on an "AS IS" BASIS,
16
17
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,43 +19,14 @@
18
19
  # limitations under the License.
19
20
  #
20
21
 
21
- require "chef" / "mixin" / "checksum"
22
- require "chef" / "cookbook_loader"
22
+ require "chef/mixin/checksum"
23
+ require "chef/cookbook_loader"
23
24
  require "mixlib/authentication/signatureverification"
24
25
 
25
- class ChefServerApi::Application < Merb::Controller
26
+ class Application < Merb::Controller
26
27
 
27
28
  include Chef::Mixin::Checksum
28
29
 
29
- controller_for_slice
30
-
31
- # Generate the absolute url for a slice - takes the slice's :path_prefix into account.
32
- #
33
- # @param slice_name<Symbol>
34
- # The name of the slice - in identifier_sym format (underscored).
35
- # @param *args<Array[Symbol,Hash]>
36
- # There are several possibilities regarding arguments:
37
- # - when passing a Hash only, the :default route of the current
38
- # slice will be used
39
- # - when a Symbol is passed, it's used as the route name
40
- # - a Hash with additional params can optionally be passed
41
- #
42
- # @return <String> A uri based on the requested slice.
43
- #
44
- # @example absolute_slice_url(:awesome, :format => 'html')
45
- # @example absolute_slice_url(:forum, :posts, :format => 'xml')
46
- def absolute_slice_url(slice_name, *args)
47
- options = {}
48
- if args.length == 1 && args[0].respond_to?(:keys)
49
- options = args[0]
50
- else
51
- options = extract_options_from_args!(args) || {}
52
- end
53
- protocol = options.delete(:protocol) || request.protocol
54
- host = options.delete(:host) || request.host
55
- protocol + "://" + host + slice_url(slice_name, *args)
56
- end
57
-
58
30
  def authenticate_every
59
31
  authenticator = Mixlib::Authentication::SignatureVerification.new
60
32
 
@@ -79,12 +51,12 @@ class ChefServerApi::Application < Merb::Controller
79
51
 
80
52
  auth
81
53
  end
82
-
83
- def is_admin
54
+
55
+ def is_admin
84
56
  if @auth_user.admin
85
57
  true
86
58
  else
87
- raise Unauthorized, "You are not allowed to take this action."
59
+ raise Forbidden, "You are not allowed to take this action."
88
60
  end
89
61
  end
90
62
 
@@ -92,18 +64,18 @@ class ChefServerApi::Application < Merb::Controller
92
64
  if @auth_user.admin || @auth_user.name == Chef::Config[:validation_client_name]
93
65
  true
94
66
  else
95
- raise Unauthorized, "You are not allowed to take this action."
67
+ raise Forbidden, "You are not allowed to take this action."
96
68
  end
97
69
  end
98
70
 
99
- def is_correct_node
71
+ def admin_or_requesting_node
100
72
  if @auth_user.admin || @auth_user.name == params[:id]
101
73
  true
102
74
  else
103
- raise Unauthorized, "You are not the correct node (auth_user name: #{@auth_user.name}, params[:id]: #{params[:id]}), or are not an API administrator (admin: #{@auth_user.admin})."
75
+ raise Forbidden, "You are not the correct node (auth_user name: #{@auth_user.name}, params[:id]: #{params[:id]}), or are not an API administrator (admin: #{@auth_user.admin})."
104
76
  end
105
77
  end
106
-
78
+
107
79
  # Store the URI of the current request in the session.
108
80
  #
109
81
  # We can return to this location by calling #redirect_back_or_default.
@@ -118,169 +90,14 @@ class ChefServerApi::Application < Merb::Controller
118
90
  session[:return_to] = nil
119
91
  redirect loc
120
92
  end
121
-
122
- def access_denied
123
- case content_type
124
- when :html
125
- store_location
126
- redirect slice_url(:openid_consumer), :message => { :error => "You don't have access to that, please login."}
127
- else
128
- raise Unauthorized, "You must authenticate first!"
129
- end
130
- end
131
-
132
- # Load a cookbook and return a hash with a list of all the files of a
133
- # given segment (attributes, recipes, definitions, libraries)
134
- #
135
- # === Parameters
136
- # cookbook_id<String>:: The cookbook to load
137
- # segment<Symbol>:: :attributes, :recipes, :definitions, :libraries
138
- #
139
- # === Returns
140
- # <Hash>:: A hash consisting of the short name of the file in :name, and the full path
141
- # to the file in :file.
142
- def load_cookbook_segment(cookbook, segment)
143
- files_list = segment_files(segment, cookbook)
144
-
145
- files = Hash.new
146
- files_list.each do |f|
147
- full = File.expand_path(f)
148
- name = File.basename(full)
149
- files[name] = {
150
- :name => name,
151
- :file => full,
152
- }
153
- end
154
- files
155
- end
156
-
157
- def segment_files(segment, cookbook)
158
- files_list = nil
159
- case segment
160
- when :attributes
161
- files_list = cookbook.attribute_files
162
- when :recipes
163
- files_list = cookbook.recipe_files
164
- when :definitions
165
- files_list = cookbook.definition_files
166
- when :libraries
167
- files_list = cookbook.lib_files
168
- when :providers
169
- files_list = cookbook.provider_files
170
- when :resources
171
- files_list = cookbook.resource_files
172
- when :files
173
- files_list = cookbook.remote_files
174
- when :templates
175
- files_list = cookbook.template_files
176
- else
177
- raise ArgumentError, "segment must be one of :attributes, :recipes, :definitions, :remote_files, :template_files, :resources, :providers or :libraries"
178
- end
179
- files_list
180
- end
181
-
182
- def specific_cookbooks(node_name, cl)
183
- valid_cookbooks = Hash.new
184
- begin
185
- node = Chef::Node.cdb_load(node_name)
186
- recipes, default_attrs, override_attrs = node.run_list.expand('couchdb')
187
- rescue Net::HTTPServerException
188
- recipes = []
189
- end
190
- recipes.each do |recipe|
191
- valid_cookbooks = expand_cookbook_deps(valid_cookbooks, cl, recipe)
192
- end
193
- valid_cookbooks
194
- end
195
93
 
196
- def expand_cookbook_deps(valid_cookbooks, cl, recipe)
197
- cookbook = recipe
198
- if recipe =~ /^(.+)::/
199
- cookbook = $1
200
- end
201
- Chef::Log.debug("Node requires #{cookbook}")
202
- valid_cookbooks[cookbook] = true
203
- cl.metadata[cookbook.to_sym].dependencies.each do |dep, versions|
204
- expand_cookbook_deps(valid_cookbooks, cl, dep) unless valid_cookbooks[dep]
205
- end
206
- valid_cookbooks
207
- end
208
-
209
- def load_cookbook_files(cookbook)
210
- response = {
211
- :recipes => Array.new,
212
- :definitions => Array.new,
213
- :libraries => Array.new,
214
- :attributes => Array.new,
215
- :files => Array.new,
216
- :templates => Array.new,
217
- :resources => Array.new,
218
- :providers => Array.new
219
- }
220
- [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates ].each do |segment|
221
- segment_files(segment, cookbook).each do |sf|
222
- next if File.directory?(sf)
223
- file_name = nil
224
- file_url = nil
225
- file_specificity = nil
226
-
227
- if segment == :templates || segment == :files
228
- mo = sf.match("cookbooks/#{cookbook.name}/#{segment}/(.+?)/(.+)")
229
- unless mo
230
- Chef::Log.debug("Skipping file #{sf}, as it doesn't have a proper segment.")
231
- next
232
- end
233
- specificity = mo[1]
234
- file_name = mo[2]
235
- url_options = { :cookbook_id => cookbook.name.to_s, :segment => segment, :id => file_name }
236
-
237
- case specificity
238
- when "default"
239
- when /^host-(.+)$/
240
- url_options[:fqdn] = $1
241
- when /^(.+)-(.+)$/
242
- url_options[:platform] = $1
243
- url_options[:version] = $2
244
- when /^(.+)$/
245
- url_options[:platform] = $1
246
- end
247
-
248
- file_specificity = specificity
249
- file_url = absolute_slice_url(:cookbook_segment, url_options)
250
- else
251
- mo = sf.match("cookbooks/#{cookbook.name}/#{segment}/(.+)")
252
- file_name = mo[1]
253
- url_options = { :cookbook_id => cookbook.name.to_s, :segment => segment, :id => file_name }
254
- file_url = absolute_slice_url(:cookbook_segment, url_options)
255
- end
256
- rs = {
257
- :name => file_name,
258
- :uri => file_url,
259
- :checksum => checksum(sf)
260
- }
261
- rs[:specificity] = file_specificity if file_specificity
262
- response[segment] << rs
263
- end
264
- end
265
- response
94
+ def access_denied
95
+ raise Unauthorized, "You must authenticate first!"
266
96
  end
267
97
 
268
- def load_all_files(node_name=nil)
269
- cl = Chef::CookbookLoader.new
270
- valid_cookbooks = node_name ? specific_cookbooks(node_name, cl) : {}
271
- cookbook_list = Hash.new
272
- cl.each do |cookbook|
273
- if node_name
274
- next unless valid_cookbooks[cookbook.name.to_s]
275
- end
276
- cookbook_list[cookbook.name.to_s] = load_cookbook_files(cookbook)
277
- end
278
- cookbook_list
279
- end
280
-
281
98
  def get_available_recipes
282
- cl = Chef::CookbookLoader.new
283
- available_recipes = cl.sort{ |a,b| a.name.to_s <=> b.name.to_s }.inject([]) do |result, element|
99
+ all_cookbooks_list = Chef::CookbookVersion.cdb_list(true)
100
+ available_recipes = all_cookbooks_list.sort{ |a,b| a.name.to_s <=> b.name.to_s }.inject([]) do |result, element|
284
101
  element.recipes.sort.each do |r|
285
102
  if r =~ /^(.+)::default$/
286
103
  result << $1
@@ -19,18 +19,18 @@
19
19
 
20
20
  require 'chef/api_client'
21
21
 
22
- class ChefServerApi::Clients < ChefServerApi::Application
22
+ class Clients < Application
23
23
  provides :json
24
24
 
25
25
  before :authenticate_every
26
26
  before :is_admin, :only => [ :index, :update, :destroy ]
27
27
  before :is_admin_or_validator, :only => [ :create ]
28
- before :is_correct_node, :only => [ :show ]
28
+ before :admin_or_requesting_node, :only => [ :show ]
29
29
 
30
30
  # GET /clients
31
31
  def index
32
32
  @list = Chef::ApiClient.cdb_list(true)
33
- display(@list.inject({}) { |result, element| result[element.name] = absolute_slice_url(:client, :id => element.name); result })
33
+ display(@list.inject({}) { |result, element| result[element.name] = absolute_url(:client, :id => element.name); result })
34
34
  end
35
35
 
36
36
  # GET /clients/:id
@@ -72,8 +72,8 @@ class ChefServerApi::Clients < ChefServerApi::Application
72
72
  @client.cdb_save
73
73
 
74
74
  self.status = 201
75
- headers['Location'] = absolute_slice_url(:client, @client.name)
76
- display({ :uri => absolute_slice_url(:client, @client.name), :private_key => @client.private_key })
75
+ headers['Location'] = absolute_url(:client, @client.name)
76
+ display({ :uri => absolute_url(:client, @client.name), :private_key => @client.private_key })
77
77
  end
78
78
 
79
79
  # PUT /clients/:id
@@ -2,7 +2,8 @@
2
2
  # Author:: Adam Jacob (<adam@opscode.com>)
3
3
  # Author:: Christopher Brown (<cb@opscode.com>)
4
4
  # Author:: Christopher Walters (<cw@opscode.com>)
5
- # Copyright:: Copyright (c) 2008, 2009 Opscode, Inc.
5
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
6
+ # Copyright:: Copyright (c) 2008, 2009, 2010 Opscode, Inc.
6
7
  # License:: Apache License, Version 2.0
7
8
  #
8
9
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,192 +22,115 @@
21
22
  require 'chef' / 'cookbook_loader'
22
23
  require 'chef' / 'cookbook' / 'metadata'
23
24
 
24
- class ChefServerApi::Cookbooks < ChefServerApi::Application
25
+ class Cookbooks < Application
25
26
 
26
27
  provides :json
27
28
 
28
29
  before :authenticate_every
30
+ before :params_helper
31
+
32
+ attr_accessor :cookbook_name, :cookbook_version
33
+
34
+ def params_helper
35
+ self.cookbook_name = params[:cookbook_name]
36
+ self.cookbook_version = params[:cookbook_version]
37
+ end
29
38
 
30
39
  include Chef::Mixin::Checksum
31
- include Merb::ChefServerApi::TarballHelper
40
+ include Merb::TarballHelper
32
41
 
33
42
  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)
43
+ cookbook_list = Chef::CookbookVersion.cdb_list
44
+ response = Hash.new
45
+ cookbook_list.each do |cookbook_name|
46
+ cookbook_name =~ /^(.+)-(\d+\.\d+\.\d+)$/
47
+ response[$1] = absolute_url(:cookbook, :cookbook_name => $1)
38
48
  end
39
- display cookbook_list
49
+ display response
40
50
  end
41
51
 
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
52
+ def show_versions
53
+ versions = Chef::CookbookVersion.cdb_by_name(cookbook_name)
54
+ raise NotFound, "Cannot find a cookbook named #{cookbook_name}" unless versions && versions.size > 0
55
+ display versions
79
56
  end
80
57
 
81
- def serve_segment_preferred(cookbook, segment, files)
58
+ def show
59
+ cookbook = get_cookbook_version(cookbook_name, cookbook_version)
60
+ display cookbook.generate_manifest_with_urls { |opts| absolute_url(:cookbook_file, opts) }
61
+ end
82
62
 
83
- to_send = nil
63
+ def show_file
64
+ cookbook = get_cookbook_version(cookbook_name, cookbook_version)
84
65
 
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
66
+ checksum = params[:checksum]
67
+ raise NotFound, "Cookbook #{cookbook_name} version #{cookbook_version} does not contain a file with checksum #{checksum}" unless cookbook.checksums.keys.include?(checksum)
95
68
 
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"]
69
+ filename = checksum_location(checksum)
70
+ raise InternalServerError, "File with checksum #{checksum} not found in the repository (this should not happen)" unless File.exists?(filename)
71
+
72
+ send_file(filename)
131
73
  end
132
74
 
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)
75
+ def update
76
+ raise(BadRequest, "You didn't pass me a valid object!") unless params.has_key?('inflated_object')
77
+ raise(BadRequest, "You didn't pass me a Chef::CookbookVersion object!") unless params['inflated_object'].kind_of?(Chef::CookbookVersion)
78
+ unless params["inflated_object"].name == cookbook_name
79
+ raise(BadRequest, "You said the cookbook was named #{params['inflated_object'].name}, but the URL says it should be #{cookbook_name}.")
148
80
  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
81
+
82
+ unless params["inflated_object"].version == cookbook_version
83
+ raise(BadRequest, "You said the cookbook was version #{params['inflated_object'].version}, but the URL says it should be #{cookbook_version}.")
160
84
  end
161
85
 
162
86
  begin
163
- Chef::CookbookLoader.new[desired_name]
164
- raise BadRequest, "Cookbook with the name #{desired_name} already exists"
165
- rescue ArgumentError
87
+ cookbook = Chef::CookbookVersion.cdb_load(cookbook_name, cookbook_version)
88
+ cookbook.manifest = params['inflated_object'].manifest
89
+ rescue Chef::Exceptions::CouchDBNotFound => e
90
+ Chef::Log.debug("Cookbook #{cookbook_name} version #{cookbook_version} does not exist")
91
+ cookbook = params['inflated_object']
166
92
  end
167
93
 
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
94
+ # ensure that all checksums referred to by the manifest have been uploaded.
95
+ Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
96
+ next unless cookbook.manifest[segment]
97
+ cookbook.manifest[segment].each do |manifest_record|
98
+ checksum = manifest_record[:checksum]
99
+ path = manifest_record[:path]
100
+
101
+ begin
102
+ checksum_obj = Chef::Checksum.cdb_load(checksum)
103
+ rescue Chef::Exceptions::CouchDBNotFound => cdbx
104
+ checksum_obj = nil
105
+ end
106
+
107
+ raise BadRequest, "Manifest has checksum #{checksum} (path #{path}) but it hasn't yet been uploaded" unless checksum_obj
108
+ end
109
+ end
182
110
 
183
- send_file(get_or_create_cookbook_tarball_location(cookbook_name))
111
+ raise InternalServerError, "Error saving cookbook" unless cookbook.cdb_save
112
+
113
+ display cookbook
184
114
  end
185
115
 
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
116
+ def destroy
190
117
  begin
191
- validate_file_parameter(cookbook_name, params[:file])
192
- rescue FileParameterException => te
193
- raise BadRequest, te.message
118
+ cookbook = get_cookbook_version(cookbook_name, cookbook_version)
119
+ rescue ArgumentError => e
120
+ raise NotFound, "Cannot find a cookbook named #{cookbook_name} with version #{cookbook_version}"
194
121
  end
195
-
196
- expand_tarball_and_put_in_repository(cookbook_name, params[:file][:tempfile])
197
-
198
- display Hash.new
122
+
123
+ display cookbook.cdb_destroy
199
124
  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
125
 
206
- FileUtils.rm_rf(cookbook_path)
207
- FileUtils.rm_f(cookbook_tarball_location(cookbook_name))
126
+ private
208
127
 
209
- display Hash.new
128
+ def get_cookbook_version(name, version)
129
+ begin
130
+ Chef::CookbookVersion.cdb_load(name, version)
131
+ rescue Chef::Exceptions::CouchDBNotFound => e
132
+ raise NotFound, "Cannot find a cookbook named #{name} with version #{version}"
133
+ end
210
134
  end
211
135
 
212
136
  end