knife-essentials 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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