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 +13 -9
- data/app/controllers/application.rb +19 -202
- data/app/controllers/clients.rb +5 -5
- data/app/controllers/cookbooks.rb +79 -155
- data/app/controllers/{data.rb → data_bags.rb} +4 -4
- data/app/controllers/data_item.rb +1 -1
- data/app/controllers/exceptions.rb +1 -1
- data/app/controllers/main.rb +6 -6
- data/app/controllers/nodes.rb +72 -10
- data/app/controllers/roles.rb +3 -3
- data/app/controllers/sandboxes.rb +179 -0
- data/app/controllers/search.rb +8 -8
- data/app/controllers/users.rb +17 -17
- data/app/helpers/tarball_helper.rb +84 -54
- data/app/views/layout/chef_server_api.html.haml +1 -1
- data/bin/chef-server +75 -0
- data/config/environments/development.rb +6 -0
- data/config/init.rb +55 -12
- data/config/rack.rb +5 -0
- data/config/router.rb +81 -5
- data/lib/chef-server-api.rb +2 -158
- data/lib/chef-server-api/version.rb +3 -0
- metadata +56 -43
- data/app/helpers/application_helper.rb +0 -163
- data/app/helpers/exceptions_helper.rb +0 -6
- data/app/helpers/global_helpers.rb +0 -25
- data/app/helpers/nodes_helper.rb +0 -26
- data/app/helpers/roles_helper.rb +0 -5
- data/lib/chef-server-api/merbtasks.rb +0 -103
- data/lib/chef-server-api/slicetasks.rb +0 -20
- data/lib/chef-server-api/spectasks.rb +0 -53
- data/stubs/app/controllers/application.rb +0 -2
- data/stubs/app/controllers/main.rb +0 -2
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 =
|
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.
|
27
|
-
s.add_dependency "merb-slices", "~> 1.
|
28
|
-
s.add_dependency "merb-assets", "~> 1.
|
29
|
-
s.add_dependency "merb-helpers", "~> 1.
|
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}-#{
|
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 #{
|
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
|
-
#
|
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
|
22
|
-
require "chef
|
22
|
+
require "chef/mixin/checksum"
|
23
|
+
require "chef/cookbook_loader"
|
23
24
|
require "mixlib/authentication/signatureverification"
|
24
25
|
|
25
|
-
class
|
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
|
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
|
67
|
+
raise Forbidden, "You are not allowed to take this action."
|
96
68
|
end
|
97
69
|
end
|
98
70
|
|
99
|
-
def
|
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
|
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
|
197
|
-
|
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
|
-
|
283
|
-
available_recipes =
|
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
|
data/app/controllers/clients.rb
CHANGED
@@ -19,18 +19,18 @@
|
|
19
19
|
|
20
20
|
require 'chef/api_client'
|
21
21
|
|
22
|
-
class
|
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 :
|
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] =
|
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'] =
|
76
|
-
display({ :uri =>
|
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
|
-
#
|
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
|
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::
|
40
|
+
include Merb::TarballHelper
|
32
41
|
|
33
42
|
def index
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
49
|
+
display response
|
40
50
|
end
|
41
51
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
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
|
-
|
63
|
+
def show_file
|
64
|
+
cookbook = get_cookbook_version(cookbook_name, cookbook_version)
|
84
65
|
|
85
|
-
|
86
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
134
|
-
|
135
|
-
raise
|
136
|
-
|
137
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
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::
|
164
|
-
|
165
|
-
rescue
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
-
|
111
|
+
raise InternalServerError, "Error saving cookbook" unless cookbook.cdb_save
|
112
|
+
|
113
|
+
display cookbook
|
184
114
|
end
|
185
115
|
|
186
|
-
def
|
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
|
-
|
192
|
-
rescue
|
193
|
-
raise
|
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
|
-
|
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
|
-
|
207
|
-
FileUtils.rm_f(cookbook_tarball_location(cookbook_name))
|
126
|
+
private
|
208
127
|
|
209
|
-
|
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
|