knife-essentials 0.3.1 → 0.4
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.
- data/lib/chef/knife/diff.rb +19 -1
- data/lib/chef_fs/command_line.rb +60 -22
- data/lib/chef_fs/diff.rb +8 -8
- data/lib/chef_fs/file_system/chef_server_root_dir.rb +22 -14
- data/lib/chef_fs/file_system/cookbook_subdir.rb +0 -2
- data/lib/chef_fs/knife.rb +8 -1
- data/lib/chef_fs/version.rb +1 -1
- data/spec/chef_fs/diff_spec.rb +56 -4
- data/spec/chef_fs/file_system/chef_server_root_dir_spec.rb +4 -5
- data/spec/chef_fs/file_system/cookbooks_dir_spec.rb +3 -3
- data/spec/chef_fs/file_system/data_bags_dir_spec.rb +4 -5
- metadata +1 -1
data/lib/chef/knife/diff.rb
CHANGED
@@ -12,12 +12,30 @@ class Chef
|
|
12
12
|
:default => true,
|
13
13
|
:description => "List directories recursively."
|
14
14
|
|
15
|
+
option :name_only,
|
16
|
+
:long => '--name-only',
|
17
|
+
:boolean => true,
|
18
|
+
:description => "Only show names of modified files."
|
19
|
+
|
20
|
+
option :name_status,
|
21
|
+
:long => '--name-status',
|
22
|
+
:boolean => true,
|
23
|
+
:description => "Only show names and statuses of modified files: Added, Deleted, Modified, and Type Changed."
|
24
|
+
|
25
|
+
common_options
|
26
|
+
|
15
27
|
def run
|
28
|
+
if config[:name_only]
|
29
|
+
output_mode = :name_only
|
30
|
+
end
|
31
|
+
if config[:name_status]
|
32
|
+
output_mode = :name_status
|
33
|
+
end
|
16
34
|
patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ])
|
17
35
|
|
18
36
|
# Get the matches (recursively)
|
19
37
|
patterns.each do |pattern|
|
20
|
-
ChefFS::CommandLine.diff(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1) do |diff|
|
38
|
+
ChefFS::CommandLine.diff(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, output_mode) do |diff|
|
21
39
|
puts diff
|
22
40
|
end
|
23
41
|
end
|
data/lib/chef_fs/command_line.rb
CHANGED
@@ -2,11 +2,11 @@ require 'chef_fs/diff'
|
|
2
2
|
|
3
3
|
module ChefFS
|
4
4
|
module CommandLine
|
5
|
-
def self.diff(pattern, a_root, b_root, recurse_depth)
|
5
|
+
def self.diff(pattern, a_root, b_root, recurse_depth, output_mode)
|
6
6
|
found_result = false
|
7
7
|
ChefFS::Diff::diffable_leaves_from_pattern(pattern, a_root, b_root, recurse_depth) do |a_leaf, b_leaf|
|
8
8
|
found_result = true
|
9
|
-
diff = diff_leaves(a_leaf, b_leaf)
|
9
|
+
diff = diff_leaves(a_leaf, b_leaf, output_mode)
|
10
10
|
yield diff if diff != ''
|
11
11
|
end
|
12
12
|
if !found_result && pattern.exact_path
|
@@ -17,27 +17,53 @@ module ChefFS
|
|
17
17
|
private
|
18
18
|
|
19
19
|
# Diff two known leaves (could be files or dirs)
|
20
|
-
def self.diff_leaves(old_file, new_file)
|
20
|
+
def self.diff_leaves(old_file, new_file, output_mode)
|
21
21
|
result = ''
|
22
22
|
# If both are directories
|
23
23
|
# If old is a directory and new is a file
|
24
24
|
# If old is a directory and new does not exist
|
25
25
|
if old_file.dir?
|
26
26
|
if new_file.dir?
|
27
|
-
|
27
|
+
if output_mode != :name_only && output_mode != :name_status
|
28
|
+
result << "Common subdirectories: #{old_file.path}\n"
|
29
|
+
end
|
28
30
|
elsif new_file.exists?
|
29
|
-
|
31
|
+
if output_mode == :name_only
|
32
|
+
result << "#{new_file.path_for_printing}\n"
|
33
|
+
elsif output_mode == :name_status
|
34
|
+
result << "T\t#{new_file.path_for_printing}\n"
|
35
|
+
else
|
36
|
+
result << "File #{new_file.path_for_printing} is a directory while file #{new_file.path_for_printing} is a regular file\n"
|
37
|
+
end
|
30
38
|
elsif new_file.parent.can_have_child?(old_file.name, old_file.dir?)
|
31
|
-
|
39
|
+
if output_mode == :name_only
|
40
|
+
result << "#{new_file.path_for_printing}\n"
|
41
|
+
elsif output_mode == :name_status
|
42
|
+
result << "D\t#{new_file.path_for_printing}\n"
|
43
|
+
else
|
44
|
+
result << "Only in #{old_file.parent.path_for_printing}: #{old_file.name}\n"
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
48
|
# If new is a directory and old does not exist
|
35
49
|
# If new is a directory and old is a file
|
36
50
|
elsif new_file.dir?
|
37
51
|
if old_file.exists?
|
38
|
-
|
52
|
+
if output_mode == :name_only
|
53
|
+
result << "#{new_file.path_for_printing}\n"
|
54
|
+
elsif output_mode == :name_status
|
55
|
+
result << "T\t#{new_file.path_for_printing}\n"
|
56
|
+
else
|
57
|
+
result << "File #{old_file.path_for_printing} is a regular file while file #{old_file.path_for_printing} is a directory\n"
|
58
|
+
end
|
39
59
|
elsif old_file.parent.can_have_child?(new_file.name, new_file.dir?)
|
40
|
-
|
60
|
+
if output_mode == :name_only
|
61
|
+
result << "#{new_file.path_for_printing}\n"
|
62
|
+
elsif output_mode == :name_status
|
63
|
+
result << "A\t#{new_file.path_for_printing}\n"
|
64
|
+
else
|
65
|
+
result << "Only in #{new_file.parent.path_for_printing}: #{new_file.name}\n"
|
66
|
+
end
|
41
67
|
end
|
42
68
|
|
43
69
|
else
|
@@ -45,7 +71,7 @@ module ChefFS
|
|
45
71
|
different, old_value, new_value = ChefFS::Diff::diff_files(old_file, new_file)
|
46
72
|
if different
|
47
73
|
# If one of the files doesn't exist, we only want to print the diff if the
|
48
|
-
# other file *could be
|
74
|
+
# other file *could be uploaded/downloaded*.
|
49
75
|
if !old_value && !old_file.parent.can_have_child?(new_file.name, new_file.dir?)
|
50
76
|
return result
|
51
77
|
end
|
@@ -53,20 +79,32 @@ module ChefFS
|
|
53
79
|
return result
|
54
80
|
end
|
55
81
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
82
|
+
if output_mode == :name_only
|
83
|
+
result << "#{new_file.path_for_printing}\n"
|
84
|
+
elsif output_mode == :name_status
|
85
|
+
if !old_value
|
86
|
+
result << "A\t#{new_file.path_for_printing}\n"
|
87
|
+
elsif !new_value
|
88
|
+
result << "D\t#{new_file.path_for_printing}\n"
|
89
|
+
else
|
90
|
+
result << "M\t#{new_file.path_for_printing}\n"
|
91
|
+
end
|
92
|
+
else
|
93
|
+
old_path = old_file.path_for_printing
|
94
|
+
new_path = new_file.path_for_printing
|
95
|
+
result << "diff --knife #{old_path} #{new_path}\n"
|
96
|
+
if !old_value
|
97
|
+
result << "new file\n"
|
98
|
+
old_path = "/dev/null"
|
99
|
+
old_value = ''
|
100
|
+
end
|
101
|
+
if !new_value
|
102
|
+
result << "deleted file\n"
|
103
|
+
new_path = "/dev/null"
|
104
|
+
new_value = ''
|
105
|
+
end
|
106
|
+
result << diff_text(old_path, new_path, old_value, new_value)
|
68
107
|
end
|
69
|
-
result << diff_text(old_path, new_path, old_value, new_value)
|
70
108
|
end
|
71
109
|
end
|
72
110
|
return result
|
data/lib/chef_fs/diff.rb
CHANGED
@@ -61,20 +61,20 @@ module ChefFS
|
|
61
61
|
new_value = read_file_value(new_file) if new_value == :not_retrieved
|
62
62
|
|
63
63
|
return false if old_value == new_value
|
64
|
-
return false if old_value && new_value &&
|
64
|
+
return false if old_value && new_value && context_aware_diff(old_file, new_file, old_value, new_value) == false
|
65
65
|
return [ true, old_value, new_value ]
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.context_aware_diff(old_file, new_file, old_value, new_value)
|
69
|
-
# TODO handle errors in reading JSON
|
70
69
|
if old_file.content_type == :json || new_file.content_type == :json
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
70
|
+
begin
|
71
|
+
new_value = Chef::JSONCompat.from_json(new_value).to_hash
|
72
|
+
old_value = Chef::JSONCompat.from_json(old_value).to_hash
|
73
|
+
return old_value != new_value
|
74
|
+
rescue JSON::ParserError
|
75
|
+
end
|
77
76
|
end
|
77
|
+
return nil
|
78
78
|
end
|
79
79
|
|
80
80
|
# Gets all common leaves, recursively, starting from the results of
|
@@ -7,12 +7,13 @@ require 'chef_fs/file_system/nodes_dir'
|
|
7
7
|
module ChefFS
|
8
8
|
module FileSystem
|
9
9
|
class ChefServerRootDir < BaseFSDir
|
10
|
-
def initialize(root_name,
|
10
|
+
def initialize(root_name, chef_config, repo_mode)
|
11
11
|
super("", nil)
|
12
|
-
@chef_server_url =
|
13
|
-
@chef_username =
|
14
|
-
@chef_private_key =
|
15
|
-
@environment =
|
12
|
+
@chef_server_url = chef_config[:chef_server_url]
|
13
|
+
@chef_username = chef_config[:node_name]
|
14
|
+
@chef_private_key = chef_config[:client_key]
|
15
|
+
@environment = chef_config[:environment]
|
16
|
+
@repo_mode = repo_mode
|
16
17
|
@root_name = root_name
|
17
18
|
end
|
18
19
|
|
@@ -20,6 +21,7 @@ module ChefFS
|
|
20
21
|
attr_reader :chef_username
|
21
22
|
attr_reader :chef_private_key
|
22
23
|
attr_reader :environment
|
24
|
+
attr_reader :repo_mode
|
23
25
|
|
24
26
|
def rest
|
25
27
|
Chef::REST.new(chef_server_url, chef_username, chef_private_key)
|
@@ -38,15 +40,21 @@ module ChefFS
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def children
|
41
|
-
@children ||=
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
@children ||= begin
|
44
|
+
result = [
|
45
|
+
CookbooksDir.new(self),
|
46
|
+
DataBagsDir.new(self),
|
47
|
+
RestListDir.new("environments", self),
|
48
|
+
RestListDir.new("roles", self)
|
49
|
+
]
|
50
|
+
if repo_mode == 'everything'
|
51
|
+
result += [
|
52
|
+
RestListDir.new("clients", self),
|
53
|
+
NodesDir.new(self)
|
54
|
+
]
|
55
|
+
end
|
56
|
+
result.sort_by { |child| child.name }
|
57
|
+
end
|
50
58
|
end
|
51
59
|
end
|
52
60
|
end
|
data/lib/chef_fs/knife.rb
CHANGED
@@ -6,12 +6,19 @@ require 'chef/config'
|
|
6
6
|
|
7
7
|
module ChefFS
|
8
8
|
class Knife < Chef::Knife
|
9
|
+
def self.common_options
|
10
|
+
option :repo_mode,
|
11
|
+
:long => '--repo-mode MODE',
|
12
|
+
:default => "default",
|
13
|
+
:description => "Specifies the local repository layout. Values: default or full"
|
14
|
+
end
|
15
|
+
|
9
16
|
def base_path
|
10
17
|
@base_path ||= "/" + ChefFS::PathUtils::relative_to(File.absolute_path(Dir.pwd), chef_repo)
|
11
18
|
end
|
12
19
|
|
13
20
|
def chef_fs
|
14
|
-
@chef_fs ||= ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config)
|
21
|
+
@chef_fs ||= ChefFS::FileSystem::ChefServerRootDir.new("remote", Chef::Config, config[:repo_mode])
|
15
22
|
end
|
16
23
|
|
17
24
|
def chef_repo
|
data/lib/chef_fs/version.rb
CHANGED
data/spec/chef_fs/diff_spec.rb
CHANGED
@@ -136,7 +136,7 @@ describe ChefFS::Diff do
|
|
136
136
|
end
|
137
137
|
it 'ChefFS::CommandLine.diff(/)' do
|
138
138
|
results = []
|
139
|
-
ChefFS::CommandLine.diff(pattern('/'), a, b, nil) do |diff|
|
139
|
+
ChefFS::CommandLine.diff(pattern('/'), a, b, nil, nil) do |diff|
|
140
140
|
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
141
141
|
end
|
142
142
|
results.should =~ [
|
@@ -212,7 +212,7 @@ new file
|
|
212
212
|
end
|
213
213
|
it 'ChefFS::CommandLine.diff(/both_dirs)' do
|
214
214
|
results = []
|
215
|
-
ChefFS::CommandLine.diff(pattern('/both_dirs'), a, b, nil) do |diff|
|
215
|
+
ChefFS::CommandLine.diff(pattern('/both_dirs'), a, b, nil, nil) do |diff|
|
216
216
|
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
217
217
|
end
|
218
218
|
results.should =~ [
|
@@ -254,7 +254,7 @@ new file
|
|
254
254
|
end
|
255
255
|
it 'ChefFS::CommandLine.diff(/) with depth 1' do
|
256
256
|
results = []
|
257
|
-
ChefFS::CommandLine.diff(pattern('/'), a, b, 1) do |diff|
|
257
|
+
ChefFS::CommandLine.diff(pattern('/'), a, b, 1, nil) do |diff|
|
258
258
|
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
259
259
|
end
|
260
260
|
results.should =~ [
|
@@ -288,7 +288,7 @@ new file
|
|
288
288
|
end
|
289
289
|
it 'ChefFS::CommandLine.diff(/*_*) with depth 0' do
|
290
290
|
results = []
|
291
|
-
ChefFS::CommandLine.diff(pattern('/*_*'), a, b, 0) do |diff|
|
291
|
+
ChefFS::CommandLine.diff(pattern('/*_*'), a, b, 0, nil) do |diff|
|
292
292
|
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
293
293
|
end
|
294
294
|
results.should =~ [
|
@@ -320,5 +320,57 @@ new file
|
|
320
320
|
+b_only_file
|
321
321
|
' ]
|
322
322
|
end
|
323
|
+
it 'ChefFS::CommandLine.diff(/) in name-only mode' do
|
324
|
+
results = []
|
325
|
+
ChefFS::CommandLine.diff(pattern('/'), a, b, nil, :name_only) do |diff|
|
326
|
+
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
327
|
+
end
|
328
|
+
results.should =~ [
|
329
|
+
"b/both_dirs/sub_both_files_different\n",
|
330
|
+
"b/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub\n",
|
331
|
+
"b/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub\n",
|
332
|
+
"b/both_dirs/sub_a_only_dir\n",
|
333
|
+
"b/both_dirs/sub_a_only_file\n",
|
334
|
+
"b/both_dirs/sub_b_only_dir\n",
|
335
|
+
"b/both_dirs/sub_b_only_file\n",
|
336
|
+
"b/both_dirs/sub_dir_in_a_file_in_b\n",
|
337
|
+
"b/both_dirs/sub_file_in_a_dir_in_b\n",
|
338
|
+
"b/both_files_different\n",
|
339
|
+
"b/dirs_empty_in_b_filled_in_a/subsub\n",
|
340
|
+
"b/dirs_empty_in_a_filled_in_b/subsub\n",
|
341
|
+
"b/a_only_dir\n",
|
342
|
+
"b/a_only_file\n",
|
343
|
+
"b/b_only_dir\n",
|
344
|
+
"b/b_only_file\n",
|
345
|
+
"b/dir_in_a_file_in_b\n",
|
346
|
+
"b/file_in_a_dir_in_b\n"
|
347
|
+
]
|
348
|
+
end
|
349
|
+
it 'ChefFS::CommandLine.diff(/) in name-status mode' do
|
350
|
+
results = []
|
351
|
+
ChefFS::CommandLine.diff(pattern('/'), a, b, nil, :name_status) do |diff|
|
352
|
+
results << diff.gsub(/\s+\d\d\d\d-\d\d-\d\d\s\d?\d:\d\d:\d\d\.\d{9} -\d\d\d\d/, ' DATE')
|
353
|
+
end
|
354
|
+
results.should =~ [
|
355
|
+
"M\tb/both_dirs/sub_both_files_different\n",
|
356
|
+
"D\tb/both_dirs/sub_dirs_empty_in_b_filled_in_a/subsub\n",
|
357
|
+
"A\tb/both_dirs/sub_dirs_empty_in_a_filled_in_b/subsub\n",
|
358
|
+
"D\tb/both_dirs/sub_a_only_dir\n",
|
359
|
+
"D\tb/both_dirs/sub_a_only_file\n",
|
360
|
+
"A\tb/both_dirs/sub_b_only_dir\n",
|
361
|
+
"A\tb/both_dirs/sub_b_only_file\n",
|
362
|
+
"T\tb/both_dirs/sub_dir_in_a_file_in_b\n",
|
363
|
+
"T\tb/both_dirs/sub_file_in_a_dir_in_b\n",
|
364
|
+
"M\tb/both_files_different\n",
|
365
|
+
"D\tb/dirs_empty_in_b_filled_in_a/subsub\n",
|
366
|
+
"A\tb/dirs_empty_in_a_filled_in_b/subsub\n",
|
367
|
+
"D\tb/a_only_dir\n",
|
368
|
+
"D\tb/a_only_file\n",
|
369
|
+
"A\tb/b_only_dir\n",
|
370
|
+
"A\tb/b_only_file\n",
|
371
|
+
"T\tb/dir_in_a_file_in_b\n",
|
372
|
+
"T\tb/file_in_a_dir_in_b\n"
|
373
|
+
]
|
374
|
+
end
|
323
375
|
end
|
324
376
|
end
|
@@ -22,7 +22,7 @@ describe ChefFS::FileSystem::ChefServerRootDir do
|
|
22
22
|
endpoint_leaf.exists?.should be_true
|
23
23
|
end
|
24
24
|
it 'read returns content' do
|
25
|
-
@rest.should_receive(:get_rest).with("#{endpoint_name}/#{endpoint_leaf_name}
|
25
|
+
@rest.should_receive(:get_rest).with("#{endpoint_name}/#{endpoint_leaf_name}").once.and_return(
|
26
26
|
{
|
27
27
|
'a' => 'b'
|
28
28
|
})
|
@@ -101,7 +101,7 @@ describe ChefFS::FileSystem::ChefServerRootDir do
|
|
101
101
|
nonexistent_child.dir?.should be_false
|
102
102
|
end
|
103
103
|
it 'read returns NotFoundError' do
|
104
|
-
@rest.should_receive(:get_rest).with("#{endpoint_name}/blah
|
104
|
+
@rest.should_receive(:get_rest).with("#{endpoint_name}/blah").once.and_raise(Net::HTTPServerException.new(nil,Net::HTTPResponse.new(nil,'404',nil)))
|
105
105
|
expect { nonexistent_child.read }.to raise_error(ChefFS::FileSystem::NotFoundError)
|
106
106
|
end
|
107
107
|
end
|
@@ -112,9 +112,8 @@ describe ChefFS::FileSystem::ChefServerRootDir do
|
|
112
112
|
{
|
113
113
|
:chef_server_url => 'url',
|
114
114
|
:node_name => 'username',
|
115
|
-
:client_key => 'key'
|
116
|
-
|
117
|
-
})
|
115
|
+
:client_key => 'key'
|
116
|
+
}, 'everything')
|
118
117
|
}
|
119
118
|
before(:each) do
|
120
119
|
@rest = double("rest")
|
@@ -7,9 +7,9 @@ describe ChefFS::FileSystem::CookbooksDir do
|
|
7
7
|
{
|
8
8
|
:chef_server_url => 'url',
|
9
9
|
:node_name => 'username',
|
10
|
-
:client_key => 'key'
|
11
|
-
|
12
|
-
|
10
|
+
:client_key => 'key'
|
11
|
+
},
|
12
|
+
'everything')
|
13
13
|
}
|
14
14
|
let(:cookbooks_dir) { root_dir.child('cookbooks') }
|
15
15
|
let(:should_list_cookbooks) do
|
@@ -6,9 +6,8 @@ describe ChefFS::FileSystem::DataBagsDir do
|
|
6
6
|
{
|
7
7
|
:chef_server_url => 'url',
|
8
8
|
:node_name => 'username',
|
9
|
-
:client_key => 'key'
|
10
|
-
|
11
|
-
})
|
9
|
+
:client_key => 'key'
|
10
|
+
}, 'everything')
|
12
11
|
}
|
13
12
|
let(:data_bags_dir) { root_dir.child('data_bags') }
|
14
13
|
let(:should_list_data_bags) do
|
@@ -73,7 +72,7 @@ describe ChefFS::FileSystem::DataBagsDir do
|
|
73
72
|
data_bag_item.path_for_printing.should == "remote/data_bags/#{data_bag_dir_name}/#{data_bag_item_name}"
|
74
73
|
end
|
75
74
|
it 'reads correctly' do
|
76
|
-
@rest.should_receive(:get_rest).with("data/#{data_bag_dir_name}/#{data_bag_item_short_name}
|
75
|
+
@rest.should_receive(:get_rest).with("data/#{data_bag_dir_name}/#{data_bag_item_short_name}").once.and_return({
|
77
76
|
'a' => 'b'
|
78
77
|
})
|
79
78
|
data_bag_item.read.should == '{
|
@@ -155,7 +154,7 @@ describe ChefFS::FileSystem::DataBagsDir do
|
|
155
154
|
nonexistent_child.dir?.should be_false
|
156
155
|
end
|
157
156
|
it 'read returns NotFoundError' do
|
158
|
-
@rest.should_receive(:get_rest).with("data/#{data_bag_dir_name}/blah
|
157
|
+
@rest.should_receive(:get_rest).with("data/#{data_bag_dir_name}/blah").once.and_raise(Net::HTTPServerException.new(nil,Net::HTTPResponse.new(nil,'404',nil)))
|
159
158
|
expect { nonexistent_child.read }.to raise_error(ChefFS::FileSystem::NotFoundError)
|
160
159
|
end
|
161
160
|
end
|