chef-server-api 0.8.16 → 0.9.0.a3

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.
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