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
+
@@ -14,6 +14,8 @@ module ChefFS
14
14
  })
15
15
  result.keys.each do |key|
16
16
  result[key] = super(result[key], { 'actors' => [], 'groups' => [] })
17
+ result[key]['actors'] = result[key]['actors'].sort
18
+ result[key]['groups'] = result[key]['groups'].sort
17
19
  end
18
20
  result
19
21
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module ChefFS
2
- VERSION = "0.9.6"
2
+ VERSION = "0.9.7"
3
3
  end
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.6
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-03-15 00:00:00.000000000 Z
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