knife-essentials 0.9.4 → 0.9.5

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.
Files changed (29) hide show
  1. data/lib/chef/knife/diff_essentials.rb +2 -5
  2. data/lib/chef/knife/raw_essentials.rb +2 -2
  3. data/lib/chef_fs/command_line.rb +98 -79
  4. data/lib/chef_fs/data_handler/acl_data_handler.rb +22 -0
  5. data/lib/chef_fs/data_handler/container_data_handler.rb +20 -0
  6. data/lib/chef_fs/data_handler/group_data_handler.rb +33 -0
  7. data/lib/chef_fs/data_handler/user_data_handler.rb +6 -1
  8. data/lib/chef_fs/file_system/acl_dir.rb +62 -0
  9. data/lib/chef_fs/file_system/acl_entry.rb +54 -0
  10. data/lib/chef_fs/file_system/acls_dir.rb +66 -0
  11. data/lib/chef_fs/file_system/base_fs_object.rb +0 -74
  12. data/lib/chef_fs/file_system/chef_repository_file_system_root_dir.rb +9 -0
  13. data/lib/chef_fs/file_system/chef_server_root_dir.rb +21 -2
  14. data/lib/chef_fs/file_system/cookbook_dir.rb +1 -1
  15. data/lib/chef_fs/file_system/cookbooks_acl_dir.rb +39 -0
  16. data/lib/chef_fs/file_system/cookbooks_dir.rb +3 -2
  17. data/lib/chef_fs/file_system/data_bags_dir.rb +1 -5
  18. data/lib/chef_fs/file_system/nodes_dir.rb +13 -3
  19. data/lib/chef_fs/file_system/rest_list_dir.rb +5 -7
  20. data/lib/chef_fs/file_system/rest_list_entry.rb +18 -18
  21. data/lib/chef_fs/knife.rb +7 -4
  22. data/lib/chef_fs/raw_request.rb +73 -0
  23. data/lib/chef_fs/version.rb +1 -1
  24. data/spec/support/integration_helper.rb +12 -1
  25. metadata +10 -6
  26. data/spec/chef_fs/file_system/chef_server_root_dir_spec.rb +0 -252
  27. data/spec/chef_fs/file_system/cookbook_dir_spec.rb +0 -582
  28. data/spec/chef_fs/file_system/cookbooks_dir_spec.rb +0 -165
  29. data/spec/chef_fs/file_system/data_bags_dir_spec.rb +0 -235
@@ -0,0 +1,66 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 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_fs/file_system/base_fs_dir'
20
+ require 'chef_fs/file_system/acl_dir'
21
+ require 'chef_fs/file_system/cookbooks_acl_dir'
22
+ require 'chef_fs/file_system/acl_entry'
23
+ require 'chef_fs/data_handler/acl_data_handler'
24
+
25
+ module ChefFS
26
+ module FileSystem
27
+ class AclsDir < BaseFSDir
28
+ ENTITY_TYPES = %w(clients containers cookbooks data_bags environments groups nodes roles) # we don't read sandboxes, so we don't read their acls
29
+
30
+ def initialize(parent)
31
+ super('acls', parent)
32
+ end
33
+
34
+ def data_handler
35
+ @data_handler ||= ChefFS::DataHandler::AclDataHandler.new
36
+ end
37
+
38
+ def api_path
39
+ parent.api_path
40
+ end
41
+
42
+ def can_have_child?(name, is_dir)
43
+ is_dir ? ENTITY_TYPES.include(name) : name == 'organization.json'
44
+ end
45
+
46
+ def children
47
+ if @children.nil?
48
+ @children = ENTITY_TYPES.map do |entity_type|
49
+ case entity_type
50
+ when 'cookbooks'
51
+ CookbooksAclDir.new(entity_type, self)
52
+ else
53
+ AclDir.new(entity_type, self)
54
+ end
55
+ end
56
+ @children << AclEntry.new('organization.json', self, true) # the org acl is retrieved as GET /organizations/ORGNAME/ANYTHINGATALL/_acl
57
+ end
58
+ @children
59
+ end
60
+
61
+ def rest
62
+ parent.rest
63
+ end
64
+ end
65
+ end
66
+ end
@@ -103,11 +103,6 @@ module ChefFS
103
103
  []
104
104
  end
105
105
 
106
- def chef_hash
107
- raise NotFoundError.new(self) if !exists?
108
- nil
109
- end
110
-
111
106
  # Expand this entry into a chef object (Chef::Role, ::Node, etc.)
112
107
  def chef_object
113
108
  raise NotFoundError.new(self) if !exists?
@@ -176,75 +171,6 @@ module ChefFS
176
171
  # Important directory attributes: name, parent, path, root
177
172
  # Overridable attributes: dir?, child(name), path_for_printing
178
173
  # Abstract: read, write, delete, children, can_have_child?, create_child, compare_to
179
-
180
- # Consider putting this into a concern module and including it instead
181
- def raw_request(_api_path)
182
- self.class.api_request(rest, :GET, rest.create_url(_api_path), {}, false)
183
- end
184
-
185
-
186
- class << self
187
- # Copied so that it does not automatically inflate an object
188
- # This is also used by knife raw_essentials
189
-
190
- ACCEPT_ENCODING = "Accept-Encoding".freeze
191
- ENCODING_GZIP_DEFLATE = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3".freeze
192
-
193
- def redirected_to(response)
194
- return nil unless response.kind_of?(Net::HTTPRedirection)
195
- # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this
196
- return nil if response.kind_of?(Net::HTTPNotModified)
197
- response['location']
198
- end
199
-
200
-
201
- def build_headers(chef_rest, method, url, headers={}, json_body=false, raw=false)
202
- # headers = @default_headers.merge(headers)
203
- #headers['Accept'] = "application/json" unless raw
204
- headers['Accept'] = "application/json" unless raw
205
- headers["Content-Type"] = 'application/json' if json_body
206
- headers['Content-Length'] = json_body.bytesize.to_s if json_body
207
- headers[Chef::REST::RESTRequest::ACCEPT_ENCODING] = Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE
208
- headers.merge!(chef_rest.authentication_headers(method, url, json_body)) if chef_rest.sign_requests?
209
- headers.merge!(Chef::Config[:custom_http_headers]) if Chef::Config[:custom_http_headers]
210
- headers
211
- end
212
-
213
- def api_request(chef_rest, method, url, headers={}, data=false)
214
- json_body = data
215
- # json_body = data ? Chef::JSONCompat.to_json(data) : nil
216
- # Force encoding to binary to fix SSL related EOFErrors
217
- # cf. http://tickets.opscode.com/browse/CHEF-2363
218
- # http://redmine.ruby-lang.org/issues/5233
219
- # json_body.force_encoding(Encoding::BINARY) if json_body.respond_to?(:force_encoding)
220
- headers = build_headers(chef_rest, method, url, headers, json_body)
221
-
222
- chef_rest.retriable_rest_request(method, url, json_body, headers) do |rest_request|
223
- response = rest_request.call {|r| r.read_body}
224
-
225
- response_body = chef_rest.decompress_body(response)
226
-
227
- if response.kind_of?(Net::HTTPSuccess)
228
- response_body
229
- elsif redirect_location = redirected_to(response)
230
- raise "Redirected to #{create_url(redirect_location)}"
231
- follow_redirect {api_request(:GET, create_url(redirect_location))}
232
- else
233
- # have to decompress the body before making an exception for it. But the body could be nil.
234
- response.body.replace(chef_rest.decompress_body(response)) if response.body.respond_to?(:replace)
235
-
236
- if response['content-type'] =~ /json/
237
- exception = response_body
238
- msg = "HTTP Request Returned #{response.code} #{response.message}: "
239
- msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s)
240
- Chef::Log.info(msg)
241
- end
242
- response.error!
243
- end
244
- end
245
- end
246
- end
247
-
248
174
  end # class BaseFsObject
249
175
  end
250
176
  end
@@ -26,6 +26,9 @@ require 'chef_fs/data_handler/environment_data_handler'
26
26
  require 'chef_fs/data_handler/node_data_handler'
27
27
  require 'chef_fs/data_handler/role_data_handler'
28
28
  require 'chef_fs/data_handler/user_data_handler'
29
+ require 'chef_fs/data_handler/group_data_handler'
30
+ require 'chef_fs/data_handler/container_data_handler'
31
+ require 'chef_fs/data_handler/acl_data_handler'
29
32
 
30
33
  module ChefFS
31
34
  module FileSystem
@@ -89,6 +92,12 @@ module ChefFS
89
92
  ChefFS::DataHandler::RoleDataHandler.new
90
93
  when 'users'
91
94
  ChefFS::DataHandler::UserDataHandler.new
95
+ when 'groups'
96
+ ChefFS::DataHandler::GroupDataHandler.new
97
+ when 'containers'
98
+ ChefFS::DataHandler::ContainerDataHandler.new
99
+ when 'acls'
100
+ ChefFS::DataHandler::AclDataHandler.new
92
101
  else
93
102
  raise "Unknown top level path #{name}"
94
103
  end
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
+ require 'chef_fs/file_system/acls_dir'
19
20
  require 'chef_fs/file_system/base_fs_dir'
20
21
  require 'chef_fs/file_system/rest_list_dir'
21
22
  require 'chef_fs/file_system/cookbooks_dir'
@@ -26,17 +27,19 @@ require 'chef/rest'
26
27
  require 'chef_fs/data_handler/client_data_handler'
27
28
  require 'chef_fs/data_handler/role_data_handler'
28
29
  require 'chef_fs/data_handler/user_data_handler'
30
+ require 'chef_fs/data_handler/group_data_handler'
31
+ require 'chef_fs/data_handler/container_data_handler'
29
32
 
30
33
  module ChefFS
31
34
  module FileSystem
32
35
  class ChefServerRootDir < BaseFSDir
33
- def initialize(root_name, chef_config, repo_mode)
36
+ def initialize(root_name, chef_config)
34
37
  super("", nil)
35
38
  @chef_server_url = chef_config[:chef_server_url]
36
39
  @chef_username = chef_config[:node_name]
37
40
  @chef_private_key = chef_config[:client_key]
38
41
  @environment = chef_config[:environment]
39
- @repo_mode = repo_mode
42
+ @repo_mode = chef_config[:repo_mode]
40
43
  @root_name = root_name
41
44
  end
42
45
 
@@ -62,6 +65,14 @@ module ChefFS
62
65
  is_dir && children.any? { |child| child.name == name }
63
66
  end
64
67
 
68
+ def org
69
+ @org ||= if URI.parse(chef_server_url).path =~ /^\/+organizations\/+([^\/]+)$/
70
+ $1
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
65
76
  def children
66
77
  @children ||= begin
67
78
  result = [
@@ -76,6 +87,14 @@ module ChefFS
76
87
  NodesDir.new(self),
77
88
  RestListDir.new("users", self, nil, ChefFS::DataHandler::UserDataHandler.new)
78
89
  ]
90
+ elsif repo_mode == 'hosted_everything'
91
+ result += [
92
+ AclsDir.new(self),
93
+ RestListDir.new("clients", self, nil, ChefFS::DataHandler::ClientDataHandler.new),
94
+ RestListDir.new("containers", self, nil, ChefFS::DataHandler::ContainerDataHandler.new),
95
+ RestListDir.new("groups", self, nil, ChefFS::DataHandler::GroupDataHandler.new),
96
+ NodesDir.new(self)
97
+ ]
79
98
  end
80
99
  result.sort_by { |child| child.name }
81
100
  end
@@ -185,7 +185,7 @@ module ChefFS
185
185
  old_retry_count = Chef::Config[:http_retry_count]
186
186
  begin
187
187
  Chef::Config[:http_retry_count] = 0
188
- @chef_object ||= rest.get_rest(api_path)
188
+ @chef_object ||= Chef::CookbookVersion.json_create(ChefFS::RawRequest.raw_json(rest, api_path))
189
189
  ensure
190
190
  Chef::Config[:http_retry_count] = old_retry_count
191
191
  end
@@ -0,0 +1,39 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2013 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_fs/file_system/acl_dir'
20
+ require 'chef_fs/file_system/acl_entry'
21
+
22
+ module ChefFS
23
+ module FileSystem
24
+ class CookbooksAclDir < AclDir
25
+ # If versioned_cookbooks is on, the list of cookbooks will have versions
26
+ # in them. But all versions of a cookbook have the same acl, so even if
27
+ # we have cookbooks/apache2-1.0.0 and cookbooks/apache2-1.1.2, we will
28
+ # only have one acl: acls/cookbooks/apache2.json. Thus, the list of
29
+ # children of acls/cookbooks is a unique list of cookbook *names*.
30
+ def children
31
+ if @children.nil?
32
+ names = parent.parent.child(name).children.map { |child| "#{child.cookbook_name}.json" }
33
+ @children = names.uniq.map { |name| AclEntry.new(name, self, true) }
34
+ end
35
+ @children
36
+ end
37
+ end
38
+ end
39
+ end
@@ -18,6 +18,7 @@
18
18
 
19
19
  require 'chef_fs/file_system/rest_list_dir'
20
20
  require 'chef_fs/file_system/cookbook_dir'
21
+ require 'chef_fs/raw_request'
21
22
 
22
23
  require 'tmpdir'
23
24
 
@@ -45,13 +46,13 @@ module ChefFS
45
46
  @children ||= begin
46
47
  if Chef::Config[:versioned_cookbooks]
47
48
  result = []
48
- rest.get_rest("#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks|
49
+ ChefFS::RawRequest.raw_json(rest, "#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks|
49
50
  cookbooks['versions'].each do |cookbook_version|
50
51
  result << CookbookDir.new("#{cookbook_name}-#{cookbook_version['version']}", self, :exists => true)
51
52
  end
52
53
  end
53
54
  else
54
- result = rest.get_rest(api_path).keys.map { |cookbook_name| CookbookDir.new(cookbook_name, self, :exists => true) }
55
+ result = ChefFS::RawRequest.raw_json(rest, api_path).keys.map { |cookbook_name| CookbookDir.new(cookbook_name, self, :exists => true) }
55
56
  end
56
57
  result.sort_by(&:name)
57
58
  end
@@ -33,7 +33,7 @@ module ChefFS
33
33
 
34
34
  def children
35
35
  begin
36
- @children ||= chef_collection.keys.sort.map do |entry|
36
+ @children ||= ChefFS::RawRequest.raw_json(rest, api_path).keys.sort.map do |entry|
37
37
  DataBagDir.new(entry, self, true)
38
38
  end
39
39
  rescue Net::HTTPServerException
@@ -45,10 +45,6 @@ module ChefFS
45
45
  end
46
46
  end
47
47
 
48
- def chef_collection
49
- rest.get_rest(api_path)
50
- end
51
-
52
48
  def can_have_child?(name, is_dir)
53
49
  is_dir
54
50
  end
@@ -28,9 +28,19 @@ module ChefFS
28
28
  super("nodes", parent, nil, ChefFS::DataHandler::NodeDataHandler.new)
29
29
  end
30
30
 
31
- # Override to respond to environment
32
- def chef_collection
33
- rest.get_rest(env_api_path)
31
+ # Identical to RestListDir.children, except supports environments
32
+ def children
33
+ begin
34
+ @children ||= ChefFS::RawRequest.raw_json(rest, env_api_path).keys.sort.map do |key|
35
+ _make_child_entry("#{key}.json", true)
36
+ end
37
+ rescue Net::HTTPServerException => e
38
+ if $!.response.code == "404"
39
+ raise ChefFS::FileSystem::NotFoundError.new(self, $!)
40
+ else
41
+ raise ChefFS::FileSystem::OperationFailedError.new(:children, self, e), "HTTP error retrieving children: #{e}"
42
+ end
43
+ end
34
44
  end
35
45
 
36
46
  def env_api_path
@@ -44,7 +44,7 @@ module ChefFS
44
44
 
45
45
  def children
46
46
  begin
47
- @children ||= chef_collection.keys.sort.map do |key|
47
+ @children ||= ChefFS::RawRequest.raw_json(rest, api_path).keys.sort.map do |key|
48
48
  _make_child_entry("#{key}.json", true)
49
49
  end
50
50
  rescue Net::HTTPServerException => e
@@ -56,16 +56,10 @@ module ChefFS
56
56
  end
57
57
  end
58
58
 
59
- def chef_collection
60
- rest.get_rest(api_path)
61
- end
62
-
63
59
  def identity_key
64
60
  'name'
65
61
  end
66
62
 
67
- # NOTE if you change this significantly, you will likely need to change
68
- # DataBagDir.create_child as well.
69
63
  def create_child(name, file_contents)
70
64
  begin
71
65
  object = JSON.parse(file_contents, :create_additions => false)
@@ -97,6 +91,10 @@ module ChefFS
97
91
  result
98
92
  end
99
93
 
94
+ def org
95
+ parent.org
96
+ end
97
+
100
98
  def environment
101
99
  parent.environment
102
100
  end
@@ -19,6 +19,7 @@
19
19
  require 'chef_fs/file_system/base_fs_object'
20
20
  require 'chef_fs/file_system/not_found_error'
21
21
  require 'chef_fs/file_system/operation_failed_error'
22
+ require 'chef_fs/raw_request'
22
23
  require 'chef/role'
23
24
  require 'chef/node'
24
25
 
@@ -45,6 +46,10 @@ module ChefFS
45
46
  "#{parent.api_path}/#{api_child_name}"
46
47
  end
47
48
 
49
+ def org
50
+ parent.org
51
+ end
52
+
48
53
  def environment
49
54
  parent.environment
50
55
  end
@@ -73,31 +78,26 @@ module ChefFS
73
78
  end
74
79
 
75
80
  def read
76
- # Minimize the value so the results don't look terrible
77
- Chef::JSONCompat.to_json_pretty(minimize_value(chef_hash))
78
- end
79
-
80
- def chef_hash
81
- JSON.parse(raw_request(api_path), :create_additions => false)
82
- rescue Net::HTTPServerException => e
83
- if $!.response.code == "404"
84
- raise ChefFS::FileSystem::NotFoundError.new(self, $!)
85
- else
86
- raise ChefFS::FileSystem::OperationFailedError.new(:read, self, e)
87
- end
81
+ Chef::JSONCompat.to_json_pretty(_read_hash)
88
82
  end
89
83
 
90
- def chef_object
84
+ def _read_hash
91
85
  begin
92
- # REST will inflate the Chef object using json_class
93
- rest.get_rest(api_path)
86
+ json = ChefFS::RawRequest.raw_request(rest, api_path)
94
87
  rescue Net::HTTPServerException => e
95
88
  if $!.response.code == "404"
96
89
  raise ChefFS::FileSystem::NotFoundError.new(self, $!)
97
90
  else
98
- raise ChefFS::FileSystem::OperationFailedError.new(:read, self, e)
91
+ raise ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "HTTP error reading: #{e}"
99
92
  end
100
93
  end
94
+ # Minimize the value (get rid of defaults) so the results don't look terrible
95
+ minimize_value(JSON.parse(json, :create_additions => false))
96
+ end
97
+
98
+ def chef_object
99
+ # REST will inflate the Chef object using json_class
100
+ data_handler.json_class.json_create(read)
101
101
  end
102
102
 
103
103
  def minimize_value(value)
@@ -114,7 +114,7 @@ module ChefFS
114
114
 
115
115
  # Grab this value
116
116
  begin
117
- value = chef_object.to_hash
117
+ value = _read_hash
118
118
  rescue ChefFS::FileSystem::NotFoundError
119
119
  return [ false, :none, other_value_json ]
120
120
  end
@@ -162,7 +162,7 @@ module ChefFS
162
162
  if e.response.code == "404"
163
163
  raise ChefFS::FileSystem::NotFoundError.new(self, e)
164
164
  else
165
- raise ChefFS::FileSystem::OperationFailedError.new(:write, self, e)
165
+ raise ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "HTTP error writing: #{e}"
166
166
  end
167
167
  end
168
168
  end