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