knife-essentials 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/lib/chef/knife/diff_essentials.rb +6 -1
  2. data/lib/chef_fs/command_line.rb +68 -75
  3. data/lib/chef_fs/data_handler/client_data_handler.rb +28 -0
  4. data/lib/chef_fs/data_handler/cookbook_data_handler.rb +36 -0
  5. data/lib/chef_fs/data_handler/data_bag_item_data_handler.rb +32 -0
  6. data/lib/chef_fs/data_handler/data_handler_base.rb +110 -0
  7. data/lib/chef_fs/data_handler/environment_data_handler.rb +38 -0
  8. data/lib/chef_fs/data_handler/node_data_handler.rb +34 -0
  9. data/lib/chef_fs/data_handler/role_data_handler.rb +38 -0
  10. data/lib/chef_fs/data_handler/user_data_handler.rb +20 -0
  11. data/lib/chef_fs/file_system.rb +4 -0
  12. data/lib/chef_fs/file_system/chef_repository_file_system_entry.rb +5 -7
  13. data/lib/chef_fs/file_system/chef_repository_file_system_root_dir.rb +14 -13
  14. data/lib/chef_fs/file_system/chef_server_root_dir.rb +6 -3
  15. data/lib/chef_fs/file_system/cookbook_file.rb +1 -5
  16. data/lib/chef_fs/file_system/data_bag_dir.rb +4 -9
  17. data/lib/chef_fs/file_system/data_bag_item.rb +6 -0
  18. data/lib/chef_fs/file_system/environments_dir.rb +2 -1
  19. data/lib/chef_fs/file_system/file_system_error.rb +3 -1
  20. data/lib/chef_fs/file_system/must_delete_recursively_error.rb +1 -4
  21. data/lib/chef_fs/file_system/nodes_dir.rb +2 -1
  22. data/lib/chef_fs/file_system/not_found_error.rb +1 -4
  23. data/lib/chef_fs/file_system/operation_failed_error.rb +32 -0
  24. data/lib/chef_fs/file_system/operation_not_allowed_error.rb +1 -2
  25. data/lib/chef_fs/file_system/rest_list_dir.rb +33 -6
  26. data/lib/chef_fs/file_system/rest_list_entry.rb +53 -15
  27. data/lib/chef_fs/version.rb +1 -1
  28. data/spec/chef_fs/file_system/chef_server_root_dir_spec.rb +1 -0
  29. data/spec/chef_fs/file_system/data_bags_dir_spec.rb +1 -0
  30. data/spec/integration/diff_spec.rb +75 -0
  31. data/spec/integration/download_spec.rb +27 -0
  32. data/spec/integration/raw_spec.rb +2 -24
  33. data/spec/integration/show_spec.rb +40 -23
  34. data/spec/integration/upload_spec.rb +61 -0
  35. metadata +11 -2
@@ -26,6 +26,11 @@ class Chef
26
26
  :boolean => true,
27
27
  :description => "Only show names and statuses of modified files: Added, Deleted, Modified, and Type Changed."
28
28
 
29
+ option :diff_filter,
30
+ :long => '--diff-filter=[(A|D|M|T)...[*]]',
31
+ :description => "Select only files that are Added (A), Deleted (D), Modified (M), or have their type (i.e. regular file, directory) changed (T). Any combination of the filter characters (including none) can be used. When * (All-or-none) is added to the combination, all paths are selected if
32
+ there is any file that matches other criteria in the comparison; if there is no file that matches other criteria, nothing is selected."
33
+
29
34
  def run
30
35
  if config[:name_only]
31
36
  output_mode = :name_only
@@ -38,7 +43,7 @@ class Chef
38
43
  # Get the matches (recursively)
39
44
  error = false
40
45
  patterns.each do |pattern|
41
- found_match = ChefFS::CommandLine.diff_print(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, output_mode, proc { |entry| format_path(entry) } ) do |diff|
46
+ found_match = ChefFS::CommandLine.diff_print(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, output_mode, proc { |entry| format_path(entry) }, config[:diff_filter] ) do |diff|
42
47
  stdout.print diff
43
48
  end
44
49
  if !found_match
@@ -21,7 +21,7 @@ require 'chef_fs/file_system'
21
21
  module ChefFS
22
22
  module CommandLine
23
23
 
24
- def self.diff_print(pattern, a_root, b_root, recurse_depth, output_mode, format_path = nil)
24
+ def self.diff_print(pattern, a_root, b_root, recurse_depth, output_mode, format_path = nil, diff_filter = nil)
25
25
  if format_path.nil?
26
26
  format_path = proc { |entry| entry.path_for_printing }
27
27
  end
@@ -33,71 +33,77 @@ module ChefFS
33
33
  old_path = format_path.call(old_entry)
34
34
  new_path = format_path.call(new_entry)
35
35
 
36
- if get_content && old_value && new_value
37
- result = ''
38
- result << "diff --knife #{old_path} #{new_path}\n"
39
- if old_value == :none
40
- result << "new file\n"
41
- old_path = "/dev/null"
42
- old_value = ''
36
+ case type
37
+ when :common_subdirectories
38
+ if output_mode != :name_only && output_mode != :name_status
39
+ yield "Common subdirectories: #{new_path}\n"
43
40
  end
44
- if new_value == :none
41
+
42
+ when :directory_to_file
43
+ next if diff_filter && diff_filter !~ /T/
44
+ if output_mode == :name_only
45
+ yield "#{new_path}\n"
46
+ elsif output_mode == :name_status
47
+ yield "T\t#{new_path}\n"
48
+ else
49
+ yield "File #{old_path} is a directory while file #{new_path} is a regular file\n"
50
+ end
51
+
52
+ when :file_to_directory
53
+ next if diff_filter && diff_filter !~ /T/
54
+ if output_mode == :name_only
55
+ yield "#{new_path}\n"
56
+ elsif output_mode == :name_status
57
+ yield "T\t#{new_path}\n"
58
+ else
59
+ yield "File #{old_path} is a regular file while file #{new_path} is a directory\n"
60
+ end
61
+
62
+ when :deleted
63
+ next if diff_filter && diff_filter !~ /D/
64
+ if output_mode == :name_only
65
+ yield "#{new_path}\n"
66
+ elsif output_mode == :name_status
67
+ yield "D\t#{new_path}\n"
68
+ elsif old_value
69
+ result = "diff --knife #{old_path} #{new_path}\n"
45
70
  result << "deleted file\n"
46
- new_path = "/dev/null"
47
- new_value = ''
71
+ result << diff_text(old_path, '/dev/null', old_value, '')
72
+ yield result
73
+ else
74
+ yield "Only in #{format_path.call(old_entry.parent)}: #{old_entry.name}\n"
48
75
  end
49
- result << diff_text(old_path, new_path, old_value, new_value)
50
- yield result
51
- else
52
- case type
53
- when :common_subdirectories
54
- if output_mode != :name_only && output_mode != :name_status
55
- yield "Common subdirectories: #{new_path}\n"
56
- end
57
- when :directory_to_file
58
- if output_mode == :name_only
59
- yield "#{new_path}\n"
60
- elsif output_mode == :name_status
61
- yield "T\t#{new_path}\n"
62
- else
63
- yield "File #{old_path} is a directory while file #{new_path} is a regular file\n"
64
- end
65
- when :file_to_directory
66
- if output_mode == :name_only
67
- yield "#{new_path}\n"
68
- elsif output_mode == :name_status
69
- yield "T\t#{new_path}\n"
70
- else
71
- yield "File #{old_path} is a regular file while file #{new_path} is a directory\n"
72
- end
73
- when :deleted
74
- if output_mode == :name_only
75
- yield "#{new_path}\n"
76
- elsif output_mode == :name_status
77
- yield "D\t#{new_path}\n"
78
- else
79
- yield "Only in #{format_path.call(old_entry.parent)}: #{old_entry.name}\n"
80
- end
81
- when :added
82
- if output_mode == :name_only
83
- yield "#{new_path}\n"
84
- elsif output_mode == :name_status
85
- yield "A\t#{new_path}\n"
86
- else
87
- yield "Only in #{format_path.call(new_entry.parent)}: #{new_entry.name}\n"
88
- end
89
- when :modified
90
- if output_mode == :name_only
91
- yield "#{new_path}\n"
92
- elsif output_mode == :name_status
93
- yield "M\t#{new_path}\n"
94
- end
95
- when :both_nonexistent
96
- when :added_cannot_upload
97
- when :deleted_cannot_download
98
- when :same
99
- # Skip these silently
76
+
77
+ when :added
78
+ next if diff_filter && diff_filter !~ /A/
79
+ if output_mode == :name_only
80
+ yield "#{new_path}\n"
81
+ elsif output_mode == :name_status
82
+ yield "A\t#{new_path}\n"
83
+ elsif new_value
84
+ result = "diff --knife #{old_path} #{new_path}\n"
85
+ result << "new file\n"
86
+ result << diff_text('/dev/null', new_path, '', new_value)
87
+ yield result
88
+ else
89
+ yield "Only in #{format_path.call(new_entry.parent)}: #{new_entry.name}\n"
100
90
  end
91
+ when :modified
92
+ next if diff_filter && diff_filter !~ /M/
93
+ if output_mode == :name_only
94
+ yield "#{new_path}\n"
95
+ elsif output_mode == :name_status
96
+ yield "M\t#{new_path}\n"
97
+ else
98
+ result = "diff --knife #{old_path} #{new_path}\n"
99
+ result << diff_text(old_path, new_path, old_value, new_value)
100
+ yield result
101
+ end
102
+ when :both_nonexistent
103
+ when :added_cannot_upload
104
+ when :deleted_cannot_download
105
+ when :same
106
+ # Skip these silently
101
107
  end
102
108
  end
103
109
  found_match
@@ -229,19 +235,6 @@ module ChefFS
229
235
  end
230
236
 
231
237
  def self.diff_text(old_path, new_path, old_value, new_value)
232
- # Reformat JSON for a nicer diff.
233
- if old_path =~ /\.json$/
234
- begin
235
- reformatted_old_value = canonicalize_json(old_value)
236
- reformatted_new_value = canonicalize_json(new_value)
237
- old_value = reformatted_old_value
238
- new_value = reformatted_new_value
239
- rescue
240
- # If JSON parsing fails, we just won't change any values and fall back
241
- # to normal diff.
242
- end
243
- end
244
-
245
238
  # Copy to tempfiles before diffing
246
239
  # TODO don't copy things that are already in files! Or find an in-memory diff algorithm
247
240
  begin
@@ -0,0 +1,28 @@
1
+ require 'chef_fs/data_handler/data_handler_base'
2
+ require 'chef/api_client'
3
+
4
+ module ChefFS
5
+ module DataHandler
6
+ class ClientDataHandler < DataHandlerBase
7
+ def normalize(client, entry)
8
+ super(client, {
9
+ 'name' => remove_dot_json(entry.name),
10
+ 'admin' => false,
11
+ 'validator' => false,
12
+ 'json_class' => 'Chef::ApiClient',
13
+ 'chef_type' => 'client'
14
+ })
15
+ end
16
+
17
+ def preserve_key(key)
18
+ return key == 'name'
19
+ end
20
+
21
+ def chef_class
22
+ Chef::ApiClient
23
+ end
24
+
25
+ # There is no Ruby API for Chef::ApiClient
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require 'chef_fs/data_handler/data_handler_base'
2
+ require 'chef/cookbook/metadata'
3
+
4
+ module ChefFS
5
+ module DataHandler
6
+ class CookbookDataHandler < DataHandlerBase
7
+ def normalize(cookbook, entry)
8
+ version = entry.name
9
+ name = entry.parent.name
10
+ result = super(cookbook, {
11
+ 'name' => "#{name}-#{version}",
12
+ 'version' => version,
13
+ 'cookbook_name' => name,
14
+ 'json_class' => 'Chef::CookbookVersion',
15
+ 'chef_type' => 'cookbook_version',
16
+ 'frozen?' => false,
17
+ 'metadata' => {}
18
+ })
19
+ result['metadata'] = super(result['metadata'], {
20
+ 'version' => version,
21
+ 'name' => name
22
+ })
23
+ end
24
+
25
+ def preserve_key(key)
26
+ return key == 'cookbook_name' || key == 'version'
27
+ end
28
+
29
+ def chef_class
30
+ Chef::Cookbook::Metadata
31
+ end
32
+
33
+ # Not using this yet, so not sure if to_ruby will be useful.
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ require 'chef_fs/data_handler/data_handler_base'
2
+ require 'chef/data_bag_item'
3
+
4
+ module ChefFS
5
+ module DataHandler
6
+ class DataBagItemDataHandler < DataHandlerBase
7
+ def normalize(data_bag_item, entry)
8
+ # If it's wrapped with raw_data, unwrap it.
9
+ if data_bag_item['json_class'] == 'Chef::DataBagItem' && data_bag_item['raw_data']
10
+ data_bag_item = data_bag_item['raw_data']
11
+ end
12
+ # chef_type and data_bag only come back from PUT and POST, but we'll
13
+ # normalize them in in case someone is comparing with those results.
14
+ super(data_bag_item, {
15
+ 'chef_type' => 'data_bag_item',
16
+ 'data_bag' => entry.parent.name,
17
+ 'id' => remove_dot_json(entry.name)
18
+ })
19
+ end
20
+
21
+ def preserve_key(key)
22
+ return key == 'id'
23
+ end
24
+
25
+ def chef_class
26
+ Chef::DataBagItem
27
+ end
28
+
29
+ # Data bags do not support .rb files (or if they do, it's undocumented)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,110 @@
1
+ module ChefFS
2
+ module DataHandler
3
+ class DataHandlerBase
4
+ def minimize(object, entry)
5
+ default_object = default(entry)
6
+ object.each_pair do |key, value|
7
+ if default_object[key] == value && !preserve_key(key)
8
+ object.delete(key)
9
+ end
10
+ end
11
+ object
12
+ end
13
+
14
+ def remove_dot_json(name)
15
+ if name.length < 5 || name[-5,5] != ".json"
16
+ raise "Invalid name #{path}: must end in .json"
17
+ end
18
+ name[0,name.length-5]
19
+ end
20
+
21
+ def preserve_key(key)
22
+ false
23
+ end
24
+
25
+ def default(entry)
26
+ normalize({}, entry)
27
+ end
28
+
29
+ def normalize(object, defaults)
30
+ # Make a normalized result in the specified order for diffing
31
+ result = {}
32
+ defaults.each_pair do |key, default|
33
+ result[key] = object.has_key?(key) ? object[key] : default
34
+ end
35
+ object.each_pair do |key, value|
36
+ result[key] = value if !result.has_key?(key)
37
+ end
38
+ result
39
+ end
40
+
41
+ def normalize_run_list(run_list)
42
+ run_list.map{|item|
43
+ case item.to_s
44
+ when /^recipe\[.*\]$/
45
+ item # explicit recipe
46
+ when /^role\[.*\]$/
47
+ item # explicit role
48
+ else
49
+ "recipe[#{item}]"
50
+ end
51
+ }.uniq
52
+ end
53
+
54
+ def from_ruby(ruby)
55
+ chef_class.from_file(ruby).to_hash
56
+ end
57
+
58
+ def chef_object(object)
59
+ chef_class.json_create(object)
60
+ end
61
+
62
+ def to_ruby(object)
63
+ raise NotImplementedError
64
+ end
65
+
66
+ def chef_class
67
+ raise NotImplementedError
68
+ end
69
+
70
+ def to_ruby_keys(object, keys)
71
+ result = ''
72
+ keys.each do |key|
73
+ if object[key]
74
+ if object[key].is_a?(Hash)
75
+ if object[key].size > 0
76
+ result << key
77
+ first = true
78
+ object[key].each_pair do |k,v|
79
+ if first
80
+ first = false
81
+ else
82
+ result << ' '*key.length
83
+ end
84
+ result << " #{k.inspect} => #{v.inspect}\n"
85
+ end
86
+ end
87
+ elsif object[key].is_a?(Array)
88
+ if object[key].size > 0
89
+ result << key
90
+ first = true
91
+ object[key].each do |value|
92
+ if first
93
+ first = false
94
+ else
95
+ result << ", "
96
+ end
97
+ result << value.inspect
98
+ end
99
+ result << "\n"
100
+ end
101
+ elsif !object[key].nil?
102
+ result << "#{key} #{object[key].inspect}\n"
103
+ end
104
+ end
105
+ end
106
+ result
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,38 @@
1
+ require 'chef_fs/data_handler/data_handler_base'
2
+ require 'chef/environment'
3
+
4
+ module ChefFS
5
+ module DataHandler
6
+ class EnvironmentDataHandler < DataHandlerBase
7
+ def normalize(environment, entry)
8
+ super(environment, {
9
+ 'name' => remove_dot_json(entry.name),
10
+ 'description' => '',
11
+ 'cookbook_versions' => {},
12
+ 'default_attributes' => {},
13
+ 'override_attributes' => {},
14
+ 'json_class' => 'Chef::Environment',
15
+ 'chef_type' => 'environment'
16
+ })
17
+ end
18
+
19
+ def preserve_key(key)
20
+ return key == 'name'
21
+ end
22
+
23
+ def chef_class
24
+ Chef::Environment
25
+ end
26
+
27
+ def to_ruby(object)
28
+ result = to_ruby_keys(object, %w(name description default_attributes override_attributes))
29
+ if object['cookbook_versions']
30
+ object['cookbook_versions'].each_pair do |name, version|
31
+ result << "cookbook #{name.inspect}, #{version.inspect}"
32
+ end
33
+ end
34
+ result
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require 'chef_fs/data_handler/data_handler_base'
2
+ require 'chef/node'
3
+
4
+ module ChefFS
5
+ module DataHandler
6
+ class NodeDataHandler < DataHandlerBase
7
+ def normalize(node, entry)
8
+ result = super(node, {
9
+ 'name' => remove_dot_json(entry.name),
10
+ 'json_class' => 'Chef::Node',
11
+ 'chef_type' => 'node',
12
+ 'chef_environment' => '_default',
13
+ 'override' => {},
14
+ 'normal' => {},
15
+ 'default' => {},
16
+ 'automatic' => {},
17
+ 'run_list' => []
18
+ })
19
+ result['run_list'] = normalize_run_list(result['run_list'])
20
+ result
21
+ end
22
+
23
+ def preserve_key(key)
24
+ return key == 'name'
25
+ end
26
+
27
+ def chef_class
28
+ Chef::Node
29
+ end
30
+
31
+ # Nodes do not support .rb files
32
+ end
33
+ end
34
+ end