knife-essentials 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
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