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.
@@ -19,7 +19,7 @@
19
19
 
20
20
  require 'chef/data_bag'
21
21
 
22
- class ChefServerApi::Data < ChefServerApi::Application
22
+ class DataBags < Application
23
23
 
24
24
  provides :json
25
25
 
@@ -28,7 +28,7 @@ class ChefServerApi::Data < ChefServerApi::Application
28
28
 
29
29
  def index
30
30
  @bag_list = Chef::DataBag.cdb_list(false)
31
- display(@bag_list.inject({}) { |r,b| r[b] = absolute_slice_url(:datum, :id => b); r })
31
+ display(@bag_list.inject({}) { |r,b| r[b] = absolute_url(:datum, :id => b); r })
32
32
 
33
33
  end
34
34
 
@@ -38,7 +38,7 @@ class ChefServerApi::Data < ChefServerApi::Application
38
38
  rescue Chef::Exceptions::CouchDBNotFound => e
39
39
  raise NotFound, "Cannot load data bag #{params[:id]}"
40
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 })
41
+ display(@data_bag.list.inject({}) { |res, i| res[i] = absolute_url(:data_bag_item, :data_bag_id => @data_bag.name, :id => i); res })
42
42
  end
43
43
 
44
44
  def create
@@ -58,7 +58,7 @@ class ChefServerApi::Data < ChefServerApi::Application
58
58
  raise Conflict, "Data bag already exists" if exists
59
59
  self.status = 201
60
60
  @data_bag.cdb_save
61
- display({ :uri => absolute_slice_url(:datum, :id => @data_bag.name) })
61
+ display({ :uri => absolute_url(:datum, :id => @data_bag.name) })
62
62
  end
63
63
 
64
64
  def destroy
@@ -21,7 +21,7 @@
21
21
  require 'chef/data_bag'
22
22
  require 'chef/data_bag_item'
23
23
 
24
- class ChefServerApi::DataItem < ChefServerApi::Application
24
+ class DataItem < Application
25
25
 
26
26
  provides :json
27
27
 
@@ -17,7 +17,7 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- class Exceptions < ChefServerApi::Application
20
+ class Exceptions < Application
21
21
 
22
22
  provides :json
23
23
 
@@ -1,4 +1,4 @@
1
- class ChefServerApi::Main < ChefServerApi::Application
1
+ class Main < Application
2
2
 
3
3
  before :authenticate_every
4
4
  provides :html, :json
@@ -6,11 +6,11 @@ class ChefServerApi::Main < ChefServerApi::Application
6
6
  def index
7
7
  display(
8
8
  {
9
- absolute_slice_url(:nodes) => "Manage Nodes",
10
- absolute_slice_url(:roles) => "Manage Roles",
11
- absolute_slice_url(:cookbooks) => "Manage Cookbooks",
12
- absolute_slice_url(:data) => "Manage Data Bags",
13
- absolute_slice_url(:search) => "Search"
9
+ absolute_url(:nodes) => "Manage Nodes",
10
+ absolute_url(:roles) => "Manage Roles",
11
+ absolute_url(:cookbooks) => "Manage Cookbooks",
12
+ absolute_url(:data) => "Manage Data Bags",
13
+ absolute_url(:search) => "Search"
14
14
  }
15
15
  )
16
16
  end
@@ -19,17 +19,17 @@
19
19
 
20
20
  require 'chef' / 'node'
21
21
 
22
- class ChefServerApi::Nodes < ChefServerApi::Application
22
+ class Nodes < Application
23
23
 
24
24
  provides :json
25
25
 
26
26
  before :authenticate_every
27
- before :is_correct_node, :only => [ :update, :destroy, :cookbooks ]
27
+ before :admin_or_requesting_node, :only => [ :update, :destroy, :cookbooks ]
28
28
 
29
29
  def index
30
30
  @node_list = Chef::Node.cdb_list
31
31
  display(@node_list.inject({}) do |r,n|
32
- r[n] = absolute_slice_url(:node, n); r
32
+ r[n] = absolute_url(:node, n); r
33
33
  end)
34
34
  end
35
35
 
@@ -40,9 +40,6 @@ class ChefServerApi::Nodes < ChefServerApi::Application
40
40
  raise NotFound, "Cannot load node #{params[:id]}"
41
41
  end
42
42
  @node.couchdb_rev = nil
43
- recipes, default, override = @node.run_list.expand("couchdb")
44
- @node.default_attrs = default
45
- @node.override_attrs = override
46
43
  display @node
47
44
  end
48
45
 
@@ -55,7 +52,7 @@ class ChefServerApi::Nodes < ChefServerApi::Application
55
52
  end
56
53
  self.status = 201
57
54
  @node.cdb_save
58
- display({ :uri => absolute_slice_url(:node, @node.name) })
55
+ display({ :uri => absolute_url(:node, @node.name) })
59
56
  end
60
57
 
61
58
  def update
@@ -67,7 +64,8 @@ class ChefServerApi::Nodes < ChefServerApi::Application
67
64
 
68
65
  updated = params['inflated_object']
69
66
  @node.run_list.reset!(updated.run_list)
70
- @node.attribute = updated.attribute
67
+ @node.automatic_attrs = updated.automatic_attrs
68
+ @node.normal_attrs = updated.normal_attrs
71
69
  @node.override_attrs = updated.override_attrs
72
70
  @node.default_attrs = updated.default_attrs
73
71
  @node.cdb_save
@@ -92,9 +90,73 @@ class ChefServerApi::Nodes < ChefServerApi::Application
92
90
  rescue Chef::Exceptions::CouchDBNotFound => e
93
91
  raise NotFound, "Cannot load node #{params[:id]}"
94
92
  end
95
-
93
+
96
94
  display(load_all_files(params[:id]))
97
95
  end
98
-
96
+
97
+ private
98
+
99
+ def load_all_files(node_name)
100
+ all_cookbooks = Chef::CookbookVersion.cdb_list(true).inject({}) {|hsh,record| hsh[record.name] = record ; hsh}
101
+
102
+ included_cookbooks = cookbooks_for_node(node_name, all_cookbooks)
103
+ nodes_cookbooks = Hash.new
104
+ included_cookbooks.each do |cookbook_name, cookbook|
105
+ next unless cookbook
106
+
107
+ nodes_cookbooks[cookbook_name.to_s] = cookbook.generate_manifest_with_urls{|opts| absolute_url(:cookbook_file, opts) }
108
+ end
109
+
110
+ nodes_cookbooks
111
+ end
112
+
113
+ # returns name -> CookbookVersion for all cookbooks included on the given node.
114
+ def cookbooks_for_node(node_name, all_cookbooks)
115
+ # get node's explicit dependencies
116
+ node = Chef::Node.cdb_load(node_name)
117
+ run_list_items, default_attrs, override_attrs = node.run_list.expand('couchdb')
118
+
119
+ # walk run list and accumulate included dependencies
120
+ run_list_items.inject({}) do |included_cookbooks, run_list_item|
121
+ expand_cookbook_deps(included_cookbooks, all_cookbooks, run_list_item)
122
+ included_cookbooks
123
+ end
124
+ end
125
+
126
+ # Accumulates transitive cookbook dependencies no more than once in included_cookbooks
127
+ # included_cookbooks == hash of name -> CookbookVersion, which is used for returning
128
+ # result as well as for tracking which cookbooks we've already
129
+ # recursed into
130
+ # all_cookbooks == hash of name -> CookbookVersion, all cookbooks available
131
+ # run_list_items == name of cookbook to include
132
+ def expand_cookbook_deps(included_cookbooks, all_cookbooks, run_list_item)
133
+ # determine the run list item's parent cookbook, which might be run_list_item in the default case
134
+ cookbook_name = (run_list_item[/^(.+)::/, 1] || run_list_item.to_s)
135
+ Chef::Log.debug("Node requires #{cookbook_name}")
136
+
137
+ # include its dependencies
138
+ included_cookbooks[cookbook_name] = all_cookbooks[cookbook_name]
139
+ if !all_cookbooks[cookbook_name]
140
+ return false
141
+ # NOTE [dan/cw] We don't think changing this to an exception breaks stuff.
142
+ # Chef::Log.warn "#{__FILE__}:#{__LINE__}: in expand_cookbook_deps, cookbook/role #{cookbook_name} could not be found, ignoring it in cookbook expansion"
143
+ # return included_cookbooks
144
+ end
145
+
146
+ # TODO: 5/27/2010 cw: implement dep_version_constraints according to
147
+ # http://wiki.opscode.com/display/chef/Metadata#Metadata-depends,
148
+ all_cookbooks[cookbook_name].metadata.dependencies.each do |depname, dep_version_constraints|
149
+ # recursively expand dependencies into included_cookbooks unless
150
+ # we've already done it
151
+ unless included_cookbooks[depname]
152
+ unless expand_cookbook_deps(included_cookbooks, all_cookbooks, depname)
153
+ raise PreconditionFailed, "cookbook #{cookbook_name} depends on cookbook #{depname}, but #{depname} does not exist"
154
+
155
+ end
156
+ end
157
+ end
158
+ true
159
+ end
160
+
99
161
  end
100
162
 
@@ -1,6 +1,6 @@
1
1
  require 'chef/role'
2
2
 
3
- class ChefServerApi::Roles < ChefServerApi::Application
3
+ class Roles < Application
4
4
  provides :json
5
5
 
6
6
  before :authenticate_every
@@ -9,7 +9,7 @@ class ChefServerApi::Roles < ChefServerApi::Application
9
9
  # GET /roles
10
10
  def index
11
11
  @role_list = Chef::Role.cdb_list(true)
12
- display(@role_list.inject({}) { |r,role| r[role.name] = absolute_slice_url(:role, role.name); r })
12
+ display(@role_list.inject({}) { |r,role| r[role.name] = absolute_url(:role, role.name); r })
13
13
  end
14
14
 
15
15
  # GET /roles/:id
@@ -37,7 +37,7 @@ class ChefServerApi::Roles < ChefServerApi::Application
37
37
  @role.cdb_save
38
38
 
39
39
  self.status = 201
40
- display({ :uri => absolute_slice_url(:role, :id => @role.name) })
40
+ display({ :uri => absolute_url(:role, :id => @role.name) })
41
41
  end
42
42
 
43
43
  # PUT /roles/:id
@@ -0,0 +1,179 @@
1
+ #
2
+ # Author:: Tim Hinderliter (<tim@opscode.com>)
3
+ # Copyright:: Copyright (c) 2010 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/sandbox'
20
+ require 'chef/checksum'
21
+
22
+ # Sandboxes are used to upload files to the server (e.g., cookbook upload).
23
+ class Sandboxes < Application
24
+
25
+ provides :json
26
+
27
+ before :authenticate_every
28
+
29
+ include Chef::Mixin::Checksum
30
+ include Merb::TarballHelper
31
+
32
+ def index
33
+ couch_sandbox_list = Chef::Sandbox::cdb_list(true)
34
+
35
+ sandbox_list = Hash.new
36
+ couch_sandbox_list.each do |sandbox|
37
+ sandbox_list[sandbox.guid] = absolute_url(:sandbox, :sandbox_id => sandbox.guid)
38
+ end
39
+ display sandbox_list
40
+ end
41
+
42
+ def show
43
+ begin
44
+ sandbox = Chef::Sandbox.cdb_load(params[:sandbox_id])
45
+ rescue Chef::Exceptions::CouchDBNotFound => e
46
+ raise NotFound, "Cannot find a sandbox named #{params[:sandbox_id]}"
47
+ end
48
+
49
+ display sandbox
50
+ end
51
+
52
+ def create
53
+ checksums = params[:checksums]
54
+
55
+ raise BadRequest, "missing required parameter: checksums" unless checksums
56
+ raise BadRequest, "required parameter checksums is not a hash: #{checksums.class.name}" unless checksums.is_a?(Hash)
57
+
58
+ new_sandbox = Chef::Sandbox.new
59
+ result_checksums = Hash.new
60
+
61
+ all_existing_checksums = Chef::Checksum.cdb_all_checksums
62
+ checksums.keys.each do |checksum|
63
+ if all_existing_checksums[checksum]
64
+ result_checksums[checksum] = {
65
+ :needs_upload => false
66
+ }
67
+ else
68
+ result_checksums[checksum] = {
69
+ :url => absolute_url(:sandbox_checksum, :sandbox_id => new_sandbox.guid, :checksum => checksum),
70
+ :needs_upload => true
71
+ }
72
+ new_sandbox.checksums << checksum
73
+ end
74
+ end
75
+
76
+ FileUtils.mkdir_p(sandbox_location(new_sandbox.guid))
77
+
78
+ new_sandbox.cdb_save
79
+
80
+ # construct successful response
81
+ self.status = 201
82
+ location = absolute_url(:sandbox, :sandbox_id => new_sandbox.guid)
83
+ headers['Location'] = location
84
+ result = { 'uri' => location, 'checksums' => result_checksums, 'sandbox_id' => new_sandbox.guid }
85
+ #result = { 'uri' => location }
86
+
87
+ display result
88
+ end
89
+
90
+ def upload_checksum
91
+ sandbox_guid = params[:sandbox_id]
92
+ checksum = params[:checksum]
93
+
94
+ existing_sandbox = Chef::Sandbox.cdb_load(sandbox_guid)
95
+ raise NotFound, "cannot find sandbox with guid #{sandbox_guid}" unless existing_sandbox
96
+ raise NotFound, "checksum #{checksum} isn't a part of sandbox #{sandbox_guid}" unless existing_sandbox.checksums.member?(checksum)
97
+
98
+ Tempfile.open("sandbox") do |src|
99
+ src.write(self.request.env["rack.input"].string)
100
+ src.close
101
+
102
+ observed_checksum = Chef::Cache::Checksum.generate_md5_checksum_for_file(src.path)
103
+
104
+ raise BadRequest, "Checksum did not match: expected #{checksum}, observed #{observed_checksum}" unless observed_checksum == checksum
105
+
106
+ dest = sandbox_checksum_location(sandbox_guid, checksum)
107
+ Chef::Log.info("upload_checksum: move #{src} to #{dest}")
108
+ FileUtils.mv(src.path, dest)
109
+ end
110
+
111
+ url = absolute_url(:sandbox_checksum, :sandbox_id => sandbox_guid, :checksum => checksum)
112
+ result = { 'uri' => url }
113
+ display result
114
+ end
115
+
116
+ def update
117
+ # look up the sandbox by its guid
118
+ existing_sandbox = Chef::Sandbox.cdb_load(params[:sandbox_id])
119
+ raise NotFound, "cannot find sandbox with guid #{sandbox_id}" unless existing_sandbox
120
+
121
+ raise BadRequest, "cannot update sandbox #{sandbox_id}: already complete" if existing_sandbox.is_completed
122
+
123
+ if params[:is_completed]
124
+ existing_sandbox.is_completed = (params[:is_completed] == true)
125
+
126
+ if existing_sandbox.is_completed
127
+ # Check if files were uploaded to sandbox directory before we
128
+ # commit the sandbox. Fail if any weren't.
129
+ existing_sandbox.checksums.each do |checksum|
130
+ checksum_filename = sandbox_checksum_location(existing_sandbox.guid, checksum)
131
+ if !File.exists?(checksum_filename)
132
+ raise BadRequest, "cannot update sandbox #{params[:sandbox_id]}: checksum #{checksum} was not uploaded"
133
+ end
134
+ end
135
+
136
+ # If we've gotten here all the files have been uploaded.
137
+ # Track the steps to undo everything we've done. If any steps fail,
138
+ # we will undo the successful steps that came before it
139
+ begin
140
+ undo_steps = Array.new
141
+ existing_sandbox.checksums.each do |checksum|
142
+ checksum_filename_in_sandbox = sandbox_checksum_location(existing_sandbox.guid, checksum)
143
+ checksum_filename_final = checksum_location(checksum)
144
+ FileUtils.mkdir_p File.dirname(checksum_filename_final)
145
+
146
+ Chef::Log.info("sandbox finalization: move #{checksum_filename_in_sandbox} to #{checksum_filename_final}")
147
+ File.rename(checksum_filename_in_sandbox, checksum_filename_final)
148
+
149
+ # mark the checksum as successfully updated
150
+ Chef::Checksum.new(checksum).cdb_save
151
+
152
+ undo_steps << proc {
153
+ Chef::Log.warn("sandbox finalization undo: moving #{checksum_filename_final} back to #{checksum_filename_in_sandbox}")
154
+ File.rename(checksum_filename_final, checksum_filename_in_sandbox)
155
+ }
156
+ end
157
+ rescue
158
+ # undo the successful moves we did before
159
+ Chef::Log.error("sandbox finalization: got exception moving files, undoing previous changes: #{$!} -- #{$!.backtrace.join("\n")}")
160
+ undo_steps.each do |undo_step|
161
+ undo_step.call
162
+ end
163
+ raise
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ existing_sandbox.cdb_save
170
+
171
+ display existing_sandbox
172
+ end
173
+
174
+ def destroy
175
+ raise NotFound, "Destroy not implemented"
176
+ end
177
+
178
+ end
179
+
@@ -7,9 +7,9 @@
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
8
8
  # you may not use this file except in compliance with the License.
9
9
  # You may obtain a copy of the License at
10
- #
10
+ #
11
11
  # http://www.apache.org/licenses/LICENSE-2.0
12
- #
12
+ #
13
13
  # Unless required by applicable law or agreed to in writing, software
14
14
  # distributed under the License is distributed on an "AS IS" BASIS,
15
15
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,15 +19,15 @@
19
19
 
20
20
  require 'chef/solr/query'
21
21
 
22
- class ChefServerApi::Search < ChefServerApi::Application
22
+ class Search < Application
23
23
  provides :json
24
-
24
+
25
25
  before :authenticate_every
26
26
  before :is_admin, :only => [:reindex]
27
-
27
+
28
28
  def index
29
29
  indexes = valid_indexes
30
- display(indexes.inject({}) { |r,i| r[i] = absolute_slice_url(:search, i); r })
30
+ display(indexes.inject({}) { |r,i| r[i] = absolute_url(:search, i); r })
31
31
  end
32
32
 
33
33
  def valid_indexes
@@ -40,7 +40,7 @@ class ChefServerApi::Search < ChefServerApi::Application
40
40
  raise NotFound, "I don't know how to search for #{params[:id]} data objects."
41
41
  end
42
42
 
43
- query = Chef::Solr::Query.new(Chef::Config[:solr_url], Chef::Config[:couchdb_database])
43
+ query = Chef::Solr::Query.new(Chef::Config[:solr_url])
44
44
  params[:q] ||= "*:*"
45
45
  params[:sort] ||= nil
46
46
  params[:start] ||= 0
@@ -52,7 +52,7 @@ class ChefServerApi::Search < ChefServerApi::Application
52
52
  "total" => total
53
53
  })
54
54
  end
55
-
55
+
56
56
  def reindex
57
57
  display(Chef::Solr.new.rebuild_index)
58
58
  end