knife-essentials 0.9.6 → 0.9.7
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.
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'chef_fs/knife'
|
2
|
+
require 'chef_fs/file_system'
|
3
|
+
require 'chef_fs/file_system/not_found_error'
|
4
|
+
|
5
|
+
class Chef
|
6
|
+
class Knife
|
7
|
+
remove_const(:Edit) if const_defined?(:Edit) && Edit.name == 'Chef::Knife::Edit' # override Chef's version
|
8
|
+
class Edit < ::ChefFS::Knife
|
9
|
+
ChefFS = ::ChefFS
|
10
|
+
banner "knife edit [PATTERN1 ... PATTERNn]"
|
11
|
+
|
12
|
+
common_options
|
13
|
+
|
14
|
+
option :local,
|
15
|
+
:long => '--local',
|
16
|
+
:boolean => true,
|
17
|
+
:description => "Show local files instead of remote"
|
18
|
+
|
19
|
+
def run
|
20
|
+
# Get the matches (recursively)
|
21
|
+
error = false
|
22
|
+
pattern_args.each do |pattern|
|
23
|
+
ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
|
24
|
+
if result.dir?
|
25
|
+
ui.error "#{format_path(result)}: is a directory" if pattern.exact_path
|
26
|
+
error = true
|
27
|
+
else
|
28
|
+
begin
|
29
|
+
new_value = edit_text(result.read, File.extname(result.name))
|
30
|
+
if new_value
|
31
|
+
result.write(new_value)
|
32
|
+
output "Updated #{format_path(result)}"
|
33
|
+
else
|
34
|
+
output "#{format_path(result)} unchanged!"
|
35
|
+
end
|
36
|
+
rescue ChefFS::FileSystem::OperationNotAllowedError => e
|
37
|
+
ui.error "#{format_path(e.entry)}: #{e.reason}."
|
38
|
+
error = true
|
39
|
+
rescue ChefFS::FileSystem::NotFoundError => e
|
40
|
+
ui.error "#{format_path(e.entry)}: No such file or directory"
|
41
|
+
error = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if error
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def edit_text(text, extension)
|
52
|
+
if (!config[:disable_editing])
|
53
|
+
file = Tempfile.new([ 'knife-edit-', extension ])
|
54
|
+
begin
|
55
|
+
# Write the text to a temporary file
|
56
|
+
file.open
|
57
|
+
file.write(text)
|
58
|
+
file.close
|
59
|
+
|
60
|
+
# Let the user edit the temporary file
|
61
|
+
if !system("#{config[:editor]} #{file.path}")
|
62
|
+
raise "Please set EDITOR environment variable"
|
63
|
+
end
|
64
|
+
|
65
|
+
file.open
|
66
|
+
result_text = file.read
|
67
|
+
return result_text if result_text != text
|
68
|
+
|
69
|
+
ensure
|
70
|
+
file.close!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'chef_fs/knife'
|
2
|
+
require 'chef_fs/file_system'
|
3
|
+
require 'chef_fs/file_system/not_found_error'
|
4
|
+
|
5
|
+
class Chef
|
6
|
+
class Knife
|
7
|
+
remove_const(:Xargs) if const_defined?(:Xargs) && Xargs.name == 'Chef::Knife::Xargs' # override Chef's version
|
8
|
+
class Xargs < ::ChefFS::Knife
|
9
|
+
ChefFS = ::ChefFS
|
10
|
+
banner "knife xargs [COMMAND]"
|
11
|
+
|
12
|
+
common_options
|
13
|
+
|
14
|
+
# TODO modify to remote-only / local-only pattern (more like delete)
|
15
|
+
option :local,
|
16
|
+
:long => '--local',
|
17
|
+
:boolean => true,
|
18
|
+
:description => "Xargs local files instead of remote"
|
19
|
+
|
20
|
+
option :patterns,
|
21
|
+
:long => '--pattern [PATTERN]',
|
22
|
+
:short => '-p [PATTERN]',
|
23
|
+
:description => "Pattern on command line (if these are not specified, a list of patterns is expected on standard input). Multiple patterns may be passed in this way.",
|
24
|
+
:arg_arity => [1,-1]
|
25
|
+
|
26
|
+
option :diff,
|
27
|
+
:long => '--[no-]diff',
|
28
|
+
:default => true,
|
29
|
+
:boolean => true,
|
30
|
+
:description => "Whether to show a diff when files change (default: true)"
|
31
|
+
|
32
|
+
option :dry_run,
|
33
|
+
:long => '--dry-run',
|
34
|
+
:boolean => true,
|
35
|
+
:description => "Prevents changes from actually being uploaded to the server."
|
36
|
+
|
37
|
+
option :force,
|
38
|
+
:long => '--[no-]force',
|
39
|
+
:boolean => true,
|
40
|
+
:default => false,
|
41
|
+
:description => "Force upload of files even if they are not changed (quicker and harmless, but doesn't print out what it changed)"
|
42
|
+
|
43
|
+
option :replace_first,
|
44
|
+
:long => '--replace-first REPLACESTR',
|
45
|
+
:short => '-J REPLACESTR',
|
46
|
+
:description => "String to replace with filenames. -J will only replace the FIRST occurrence of the replacement string."
|
47
|
+
|
48
|
+
option :replace_all,
|
49
|
+
:long => '--replace REPLACESTR',
|
50
|
+
:short => '-I REPLACESTR',
|
51
|
+
:description => "String to replace with filenames. -I will replace ALL occurrence of the replacement string."
|
52
|
+
|
53
|
+
option :max_arguments_per_command,
|
54
|
+
:long => '--max-args MAXARGS',
|
55
|
+
:short => '-n MAXARGS',
|
56
|
+
:description => "Maximum number of arguments per command line."
|
57
|
+
|
58
|
+
option :max_command_line,
|
59
|
+
:long => '--max-chars LENGTH',
|
60
|
+
:short => '-s LENGTH',
|
61
|
+
:description => "Maximum size of command line, in characters"
|
62
|
+
|
63
|
+
option :verbose_commands,
|
64
|
+
:short => '-t',
|
65
|
+
:description => "Print command to be run on the command line"
|
66
|
+
|
67
|
+
option :null_separator,
|
68
|
+
:short => '-0',
|
69
|
+
:boolean => true,
|
70
|
+
:description => "Use the NULL character (\0) as a separator, instead of whitespace"
|
71
|
+
|
72
|
+
def run
|
73
|
+
error = false
|
74
|
+
# Get the matches (recursively)
|
75
|
+
files = []
|
76
|
+
pattern_args_from(get_patterns).each do |pattern|
|
77
|
+
ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
|
78
|
+
if result.dir?
|
79
|
+
# TODO option to include directories
|
80
|
+
ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it."
|
81
|
+
else
|
82
|
+
files << result
|
83
|
+
ran = false
|
84
|
+
|
85
|
+
# If the command would be bigger than max command line, back it off a bit
|
86
|
+
# and run a slightly smaller command (with one less arg)
|
87
|
+
if config[:max_command_line]
|
88
|
+
command, tempfiles = create_command(files)
|
89
|
+
begin
|
90
|
+
if command.length > config[:max_command_line].to_i
|
91
|
+
if files.length > 1
|
92
|
+
command, tempfiles_minus_one = create_command(files[0..-2])
|
93
|
+
begin
|
94
|
+
error = true if xargs_files(command, tempfiles_minus_one)
|
95
|
+
files = [ files[-1] ]
|
96
|
+
ran = true
|
97
|
+
ensure
|
98
|
+
destroy_tempfiles(tempfiles)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
error = true if xargs_files(command, tempfiles)
|
102
|
+
files = [ ]
|
103
|
+
ran = true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
ensure
|
107
|
+
destroy_tempfiles(tempfiles)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# If the command has hit the limit for the # of arguments, run it
|
112
|
+
if !ran && config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i
|
113
|
+
command, tempfiles = create_command(files)
|
114
|
+
begin
|
115
|
+
error = true if xargs_files(command, tempfiles)
|
116
|
+
files = []
|
117
|
+
ran = true
|
118
|
+
ensure
|
119
|
+
destroy_tempfiles(tempfiles)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Any leftovers commands shall be run
|
127
|
+
if files.size > 0
|
128
|
+
command, tempfiles = create_command(files)
|
129
|
+
begin
|
130
|
+
error = true if xargs_files(command, tempfiles)
|
131
|
+
ensure
|
132
|
+
destroy_tempfiles(tempfiles)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
if error
|
137
|
+
exit 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_patterns
|
142
|
+
if config[:patterns]
|
143
|
+
[ config[:patterns] ].flatten
|
144
|
+
elsif config[:null_separator]
|
145
|
+
stdin.binmode
|
146
|
+
stdin.read.split("\000")
|
147
|
+
else
|
148
|
+
stdin.read.split(/\s+/)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def create_command(files)
|
153
|
+
command = name_args.join(' ')
|
154
|
+
|
155
|
+
# Create the (empty) tempfiles
|
156
|
+
tempfiles = {}
|
157
|
+
begin
|
158
|
+
# Create the temporary files
|
159
|
+
files.each do |file|
|
160
|
+
tempfile = Tempfile.new(file.name)
|
161
|
+
tempfiles[tempfile] = { :file => file }
|
162
|
+
end
|
163
|
+
rescue
|
164
|
+
destroy_tempfiles(files)
|
165
|
+
raise
|
166
|
+
end
|
167
|
+
|
168
|
+
# Create the command
|
169
|
+
paths = tempfiles.keys.map { |tempfile| tempfile.path }.join(' ')
|
170
|
+
if config[:replace_all]
|
171
|
+
final_command = command.gsub(config[:replace_all], paths)
|
172
|
+
elsif config[:replace_first]
|
173
|
+
final_command = command.sub(config[:replace_first], paths)
|
174
|
+
else
|
175
|
+
final_command = "#{command} #{paths}"
|
176
|
+
end
|
177
|
+
|
178
|
+
[final_command, tempfiles]
|
179
|
+
end
|
180
|
+
|
181
|
+
def destroy_tempfiles(tempfiles)
|
182
|
+
# Unlink the files now that we're done with them
|
183
|
+
tempfiles.keys.each { |tempfile| tempfile.close! }
|
184
|
+
end
|
185
|
+
|
186
|
+
def xargs_files(command, tempfiles)
|
187
|
+
error = false
|
188
|
+
# Create the temporary files
|
189
|
+
tempfiles.each_pair do |tempfile, file|
|
190
|
+
begin
|
191
|
+
value = file[:file].read
|
192
|
+
file[:value] = value
|
193
|
+
tempfile.open
|
194
|
+
tempfile.write(value)
|
195
|
+
tempfile.close
|
196
|
+
rescue ChefFS::FileSystem::OperationNotAllowedError => e
|
197
|
+
ui.error "#{format_path(e.entry)}: #{e.reason}."
|
198
|
+
error = true
|
199
|
+
tempfile.close!
|
200
|
+
tempfiles.delete(tempfile)
|
201
|
+
next
|
202
|
+
rescue ChefFS::FileSystem::NotFoundError => e
|
203
|
+
ui.error "#{format_path(e.entry)}: No such file or directory"
|
204
|
+
error = true
|
205
|
+
tempfile.close!
|
206
|
+
tempfiles.delete(tempfile)
|
207
|
+
next
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
return error if error && tempfiles.size == 0
|
212
|
+
|
213
|
+
# Run the command
|
214
|
+
if config[:verbose_commands] || Chef::Config[:verbosity] && Chef::Config[:verbosity] >= 1
|
215
|
+
output sub_filenames(command, tempfiles)
|
216
|
+
end
|
217
|
+
command_output = `#{command}`
|
218
|
+
command_output = sub_filenames(command_output, tempfiles)
|
219
|
+
output command_output
|
220
|
+
|
221
|
+
# Check if the output is different
|
222
|
+
tempfiles.each_pair do |tempfile, file|
|
223
|
+
# Read the new output
|
224
|
+
new_value = IO.binread(tempfile.path)
|
225
|
+
|
226
|
+
# Upload the output if different
|
227
|
+
if config[:force] || new_value != file[:value]
|
228
|
+
if config[:dry_run]
|
229
|
+
output "Would update #{format_path(file[:file])}"
|
230
|
+
else
|
231
|
+
file[:file].write(new_value)
|
232
|
+
output "Updated #{format_path(file[:file])}"
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Print a diff of what was uploaded
|
237
|
+
if config[:diff] && new_value != file[:value]
|
238
|
+
old_file = Tempfile.open(file[:file].name)
|
239
|
+
begin
|
240
|
+
old_file.write(file[:value])
|
241
|
+
old_file.close
|
242
|
+
|
243
|
+
diff = `diff -u #{old_file.path} #{tempfile.path}`
|
244
|
+
diff.gsub!(old_file.path, "#{format_path(file[:file])} (old)")
|
245
|
+
diff.gsub!(tempfile.path, "#{format_path(file[:file])} (new)")
|
246
|
+
output diff
|
247
|
+
ensure
|
248
|
+
old_file.close!
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
error
|
254
|
+
end
|
255
|
+
|
256
|
+
def sub_filenames(str, tempfiles)
|
257
|
+
tempfiles.each_pair do |tempfile, file|
|
258
|
+
str.gsub!(tempfile.path, format_path(file[:file]))
|
259
|
+
end
|
260
|
+
str
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
@@ -47,7 +47,7 @@ module ChefFS
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def create_child(name, file_contents)
|
50
|
-
raise OperationNotAllowedError.new(:create_child, self)
|
50
|
+
raise OperationNotAllowedError.new(:create_child, self), "ACLs can only be updated, and can only be created when the corresponding object is created."
|
51
51
|
end
|
52
52
|
|
53
53
|
def data_handler
|
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.9.
|
4
|
+
version: 0.9.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef-zero
|
@@ -58,10 +58,12 @@ files:
|
|
58
58
|
- lib/chef/knife/deps_essentials.rb
|
59
59
|
- lib/chef/knife/diff_essentials.rb
|
60
60
|
- lib/chef/knife/download_essentials.rb
|
61
|
+
- lib/chef/knife/edit_essentials.rb
|
61
62
|
- lib/chef/knife/list_essentials.rb
|
62
63
|
- lib/chef/knife/raw_essentials.rb
|
63
64
|
- lib/chef/knife/show_essentials.rb
|
64
65
|
- lib/chef/knife/upload_essentials.rb
|
66
|
+
- lib/chef/knife/xargs_essentials.rb
|
65
67
|
- lib/chef_fs/command_line.rb
|
66
68
|
- lib/chef_fs/data_handler/acl_data_handler.rb
|
67
69
|
- lib/chef_fs/data_handler/client_data_handler.rb
|
@@ -158,3 +160,4 @@ signing_key:
|
|
158
160
|
specification_version: 3
|
159
161
|
summary: Universal knife verbs that work with your Chef repository
|
160
162
|
test_files: []
|
163
|
+
has_rdoc: true
|