knife-essentials 0.2.1 → 0.3
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/README.rdoc +9 -0
- data/lib/chef/knife/download.rb +32 -0
- data/lib/chef_fs/diff.rb +17 -8
- data/lib/chef_fs/file_system.rb +108 -0
- data/lib/chef_fs/file_system/file_system_entry.rb +32 -3
- data/lib/chef_fs/version.rb +1 -1
- metadata +2 -1
data/README.rdoc
CHANGED
@@ -7,6 +7,7 @@ to manipulate Chef using a common set of verbs that work on *everything*
|
|
7
7
|
that is stored in the Chef server.
|
8
8
|
|
9
9
|
knife diff cookbooks/*apache*
|
10
|
+
knife download roles data_bags cookbooks/emacs
|
10
11
|
knife list data_bags/users
|
11
12
|
knife show roles/*base*
|
12
13
|
|
@@ -53,6 +54,7 @@ This plugin builds a Ruby Gem (but has not yet been released to RubyGems). To i
|
|
53
54
|
chef_fs installs a number of useful knife verbs, including:
|
54
55
|
|
55
56
|
knife diff [pattern1 pattern2 ...]
|
57
|
+
knife download [pattern1 pattern2 ...]
|
56
58
|
knife list [pattern1 pattern2 ...]
|
57
59
|
knife show [pattern2 pattern2 ...]
|
58
60
|
|
@@ -81,6 +83,13 @@ To run the knife plugin functionality, install a version of Chef > 0.10.10:
|
|
81
83
|
Diffs objects on the server against files in the local repository. Output is similar to
|
82
84
|
+git diff+.
|
83
85
|
|
86
|
+
== knife download
|
87
|
+
|
88
|
+
knife download [pattern1 pattern2 ...]
|
89
|
+
|
90
|
+
Downloads objects from the server to your local repository. Pass --purge to delete local
|
91
|
+
files and directories which do not exist on the server.
|
92
|
+
|
84
93
|
== knife list
|
85
94
|
|
86
95
|
knife list [pattern1 pattern2 ...]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'chef_fs/knife'
|
2
|
+
require 'chef_fs/command_line'
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
class Knife
|
6
|
+
class Download < ChefFS::Knife
|
7
|
+
banner "download PATTERNS"
|
8
|
+
|
9
|
+
option :recurse,
|
10
|
+
:long => '--[no-]recurse',
|
11
|
+
:boolean => true,
|
12
|
+
:default => true,
|
13
|
+
:description => "List directories recursively."
|
14
|
+
|
15
|
+
option :purge,
|
16
|
+
:long => '--[no-]purge',
|
17
|
+
:boolean => true,
|
18
|
+
:default => false,
|
19
|
+
:description => "Delete matching local files and directories that do not exist remotely."
|
20
|
+
|
21
|
+
def run
|
22
|
+
patterns = pattern_args_from(name_args.length > 0 ? name_args : [ "" ])
|
23
|
+
|
24
|
+
# Get the matches (recursively)
|
25
|
+
patterns.each do |pattern|
|
26
|
+
ChefFS::FileSystem.copy_to(pattern, chef_fs, local_fs, config[:recurse] ? nil : 1, config[:purge])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/lib/chef_fs/diff.rb
CHANGED
@@ -12,7 +12,7 @@ module ChefFS
|
|
12
12
|
Digest::MD5.hexdigest(value)
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.
|
15
|
+
def self.diff_files_quick(old_file, new_file)
|
16
16
|
#
|
17
17
|
# Short-circuit expensive comparison (could be an extra network
|
18
18
|
# request) if a pre-calculated checksum is there
|
@@ -42,7 +42,16 @@ module ChefFS
|
|
42
42
|
end
|
43
43
|
|
44
44
|
# If the checksums are the same, they are the same. Return.
|
45
|
-
return false if old_checksum == new_checksum
|
45
|
+
return [ false, old_value, new_value ] if old_checksum == new_checksum
|
46
|
+
end
|
47
|
+
|
48
|
+
return [ nil, old_value, new_value ]
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.diff_files(old_file, new_file)
|
52
|
+
different, old_value, new_value = diff_files_quick(old_file, new_file)
|
53
|
+
if different != nil
|
54
|
+
return different
|
46
55
|
end
|
47
56
|
|
48
57
|
#
|
@@ -87,8 +96,8 @@ module ChefFS
|
|
87
96
|
ChefFS::FileSystem.list(a_root, pattern) do |a|
|
88
97
|
found_paths << a.path
|
89
98
|
b = ChefFS::FileSystem.resolve_path(b_root, a.path)
|
90
|
-
diffable_leaves(a, b, recurse_depth) do |a_leaf, b_leaf|
|
91
|
-
yield [ a_leaf, b_leaf ]
|
99
|
+
diffable_leaves(a, b, recurse_depth) do |a_leaf, b_leaf, leaf_recurse_depth|
|
100
|
+
yield [ a_leaf, b_leaf, leaf_recurse_depth ]
|
92
101
|
end
|
93
102
|
end
|
94
103
|
|
@@ -97,7 +106,7 @@ module ChefFS
|
|
97
106
|
ChefFS::FileSystem.list(b_root, pattern) do |b|
|
98
107
|
if !found_paths.include?(b.path)
|
99
108
|
a = ChefFS::FileSystem.resolve_path(a_root, b.path)
|
100
|
-
yield [ a, b ]
|
109
|
+
yield [ a, b, recurse_depth ]
|
101
110
|
end
|
102
111
|
end
|
103
112
|
end
|
@@ -126,15 +135,15 @@ module ChefFS
|
|
126
135
|
a_children_names = Set.new
|
127
136
|
a.children.each do |a_child|
|
128
137
|
a_children_names << a_child.name
|
129
|
-
diffable_leaves(a_child, b.child(a_child.name), recurse_depth ? recurse_depth - 1 : nil) do |a_leaf, b_leaf|
|
130
|
-
yield [ a_leaf, b_leaf ]
|
138
|
+
diffable_leaves(a_child, b.child(a_child.name), recurse_depth ? recurse_depth - 1 : nil) do |a_leaf, b_leaf, leaf_recurse_depth|
|
139
|
+
yield [ a_leaf, b_leaf, leaf_recurse_depth ]
|
131
140
|
end
|
132
141
|
end
|
133
142
|
|
134
143
|
# Check b for children that aren't in a
|
135
144
|
b.children.each do |b_child|
|
136
145
|
if !a_children_names.include?(b_child.name)
|
137
|
-
yield [ a.child(b_child.name), b_child ]
|
146
|
+
yield [ a.child(b_child.name), b_child, recurse_depth ]
|
138
147
|
end
|
139
148
|
end
|
140
149
|
return
|
data/lib/chef_fs/file_system.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'chef_fs/path_utils'
|
2
|
+
require 'chef_fs/diff'
|
2
3
|
|
3
4
|
module ChefFS
|
4
5
|
module FileSystem
|
6
|
+
def self.copy_to(pattern, src_root, dest_root)
|
7
|
+
|
8
|
+
end
|
9
|
+
|
5
10
|
# Yields a list of all things under (and including) this entry that match the
|
6
11
|
# given pattern.
|
7
12
|
#
|
@@ -64,6 +69,109 @@ module ChefFS
|
|
64
69
|
end
|
65
70
|
result
|
66
71
|
end
|
72
|
+
|
73
|
+
# Copy everything matching the given pattern from src to dest.
|
74
|
+
#
|
75
|
+
# After this method completes, everything in dest matching the
|
76
|
+
# given pattern will look identical to src.
|
77
|
+
#
|
78
|
+
# ==== Attributes
|
79
|
+
#
|
80
|
+
# * +pattern+ - ChefFS::FilePattern to match children under
|
81
|
+
# * +src_root+ - the root from which things will be copied
|
82
|
+
# * +dest_root+ - the root to which things will be copied
|
83
|
+
# * +recurse_depth+ - the maximum depth to copy things. +nil+
|
84
|
+
# means infinite depth. 0 means no recursion.
|
85
|
+
# * +purge+ - if +true+, items in +dest+ that are not in +src+
|
86
|
+
# will be deleted from +dest+. If +false+, these items will
|
87
|
+
# be left alone.
|
88
|
+
#
|
89
|
+
# ==== Examples
|
90
|
+
#
|
91
|
+
# ChefFS::FileSystem.copy_to(FilePattern.new('/cookbooks', chef_fs, local_fs, nil, true)
|
92
|
+
#
|
93
|
+
def self.copy_to(pattern, src_root, dest_root, recurse_depth, purge)
|
94
|
+
found_result = false
|
95
|
+
# Find things we might want to copy
|
96
|
+
ChefFS::Diff::diffable_leaves_from_pattern(pattern, src_root, dest_root, recurse_depth) do |src_leaf, dest_leaf, child_recurse_depth|
|
97
|
+
found_result = true
|
98
|
+
copy_leaves(src_leaf, dest_leaf, child_recurse_depth, purge)
|
99
|
+
end
|
100
|
+
if !found_result && pattern.exact_path
|
101
|
+
yield "#{pattern}: No such file or directory on remote or local"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Copy two known leaves (could be files or dirs)
|
108
|
+
def self.copy_leaves(src_entry, dest_entry, recurse_depth, purge)
|
109
|
+
# A NOTE about this algorithm:
|
110
|
+
# There are cases where this algorithm does too many network requests.
|
111
|
+
# knife upload with a specific filename will first check if the file
|
112
|
+
# exists (a "dir" in the parent) before deciding whether to POST or
|
113
|
+
# PUT it. If we just tried PUT (or POST) and then tried the other if
|
114
|
+
# the conflict failed, we wouldn't need to check existence.
|
115
|
+
# On the other hand, we may already have DONE the request, in which
|
116
|
+
# case we shouldn't waste time trying PUT if we know the file doesn't
|
117
|
+
# exist.
|
118
|
+
# Will need to decide how that works with checksums, though.
|
119
|
+
|
120
|
+
if !src_entry.exists?
|
121
|
+
if purge
|
122
|
+
# If we would not have uploaded it, we will not purge it.
|
123
|
+
if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?)
|
124
|
+
Chef::Log.info("Deleting extra entry #{dest_entry.path_for_printing} (purge is on)")
|
125
|
+
dest_entry.delete
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
elsif !dest_entry.exists?
|
130
|
+
if dest_entry.parent.can_have_child?(src_entry.name, src_entry.dir?)
|
131
|
+
if src_entry.dir?
|
132
|
+
Chef::Log.info("Creating #{dest_entry.path_for_printing}/")
|
133
|
+
new_dest_dir = dest_entry.parent.create_child(src_entry.name, nil)
|
134
|
+
# Directory creation is recursive.
|
135
|
+
if recurse_depth != 0
|
136
|
+
src_entry.children.each do |src_child|
|
137
|
+
new_dest_child = new_dest_dir.child(src_child.name)
|
138
|
+
copy_leaves(src_child, new_dest_child, recurse_depth ? recurse_depth - 1 : recurse_depth, purge)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
Chef::Log.info("Creating #{dest_entry.path_for_printing}")
|
143
|
+
dest_entry.parent.create_child(src_entry.name, src_entry.read)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
else
|
148
|
+
# Both exist.
|
149
|
+
# If they are different types, log an error.
|
150
|
+
if src_entry.dir?
|
151
|
+
if dest_entry.dir?
|
152
|
+
# If they are both directories, we'll end up recursing later.
|
153
|
+
else
|
154
|
+
# If they are different types.
|
155
|
+
Chef::Log.error("File #{dest_entry.path_for_printing} is a directory while file #{dest_entry.path_for_printing} is a regular file\n")
|
156
|
+
return
|
157
|
+
end
|
158
|
+
else
|
159
|
+
if dest_entry.dir?
|
160
|
+
Chef::Log.error("File #{dest_entry.path_for_printing} is a directory while file #{dest_entry.path_for_printing} is a regular file\n")
|
161
|
+
return
|
162
|
+
else
|
163
|
+
# Both are files! Copy them unless we're sure they are the same.
|
164
|
+
different, src_value, dest_value = ChefFS::Diff.diff_files_quick(src_entry, dest_entry)
|
165
|
+
if different || different == nil
|
166
|
+
Chef::Log.info("Copying #{src_entry.path_for_printing} to #{dest_entry.path_for_printing}")
|
167
|
+
src_value = src_entry.read if src_value == :not_retrieved
|
168
|
+
dest_entry.write(src_value)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
67
175
|
end
|
68
176
|
end
|
69
177
|
|
@@ -2,6 +2,7 @@ require 'chef_fs/file_system/base_fs_dir'
|
|
2
2
|
require 'chef_fs/file_system/rest_list_dir'
|
3
3
|
require 'chef_fs/file_system/not_found_error'
|
4
4
|
require 'chef_fs/path_utils'
|
5
|
+
require 'fileutils'
|
5
6
|
|
6
7
|
module ChefFS
|
7
8
|
module FileSystem
|
@@ -18,18 +19,46 @@ module ChefFS
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def children
|
21
|
-
|
22
|
+
begin
|
23
|
+
@children ||= Dir.entries(file_path).select { |entry| entry != '.' && entry != '..' }.map { |entry| FileSystemEntry.new(entry, self) }
|
24
|
+
rescue Errno::ENOENT
|
25
|
+
raise ChefFS::FileSystem::NotFoundError.new($!), "#{file_path} not found"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def create_child(child_name, file_contents=nil)
|
30
|
+
result = FileSystemEntry.new(child_name, self)
|
31
|
+
if file_contents
|
32
|
+
result.write(file_contents)
|
33
|
+
else
|
34
|
+
Dir.mkdir(result.file_path)
|
35
|
+
end
|
36
|
+
result
|
22
37
|
end
|
23
38
|
|
24
39
|
def dir?
|
25
40
|
File.directory?(file_path)
|
26
41
|
end
|
27
42
|
|
43
|
+
def delete
|
44
|
+
if dir?
|
45
|
+
FileUtils.rm_rf(file_path)
|
46
|
+
else
|
47
|
+
File.delete(file_path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
28
51
|
def read
|
29
52
|
begin
|
30
53
|
IO.read(file_path)
|
31
|
-
rescue
|
32
|
-
raise ChefFS::FileSystem::NotFoundError.new($!), "#{
|
54
|
+
rescue Errno::ENOENT
|
55
|
+
raise ChefFS::FileSystem::NotFoundError.new($!), "#{file_path} not found"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def write(content)
|
60
|
+
File.open(file_path, 'w') do |file|
|
61
|
+
file.write(content)
|
33
62
|
end
|
34
63
|
end
|
35
64
|
end
|
data/lib/chef_fs/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-essentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.3'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -23,6 +23,7 @@ files:
|
|
23
23
|
- README.rdoc
|
24
24
|
- Rakefile
|
25
25
|
- lib/chef/knife/diff.rb
|
26
|
+
- lib/chef/knife/download.rb
|
26
27
|
- lib/chef/knife/list.rb
|
27
28
|
- lib/chef/knife/show.rb
|
28
29
|
- lib/chef_fs/command_line.rb
|