5
5
6
6
require 'securerandom'
7
7
require 'tempfile'
8
+ require 'puppet/util/execution'
8
9
9
10
# This provider implements a simple state-machine. The following attempts to #
10
11
# document it. In general, `def adjective?` implements a [state], while `def
59
60
#
60
61
61
62
Puppet ::Type . type ( :archive ) . provide ( :ruby ) do
63
+ include Puppet ::Util ::Execution
62
64
optional_commands aws : 'aws'
63
65
optional_commands gsutil : 'gsutil'
64
66
defaultfor feature : :microsoft_windows
@@ -95,18 +97,6 @@ def tempfile_name
95
97
end
96
98
end
97
99
98
- def creates
99
- if resource [ :extract ] == :true
100
- extracted? ? resource [ :creates ] : 'archive not extracted'
101
- else
102
- resource [ :creates ]
103
- end
104
- end
105
-
106
- def creates = ( _value )
107
- extract
108
- end
109
-
110
100
def checksum
111
101
resource [ :checksum ] || ( resource [ :checksum ] = remote_checksum if resource [ :checksum_url ] )
112
102
end
@@ -127,7 +117,7 @@ def remote_checksum
127
117
# returns boolean
128
118
def checksum? ( store_checksum = true )
129
119
return false unless File . exist? archive_filepath
130
- return true if resource [ :checksum_type ] == :none
120
+ return true if resource [ :checksum_type ] == :none
131
121
132
122
archive = PuppetX ::Bodeco ::Archive . new ( archive_filepath )
133
123
archive_checksum = archive . checksum ( resource [ :checksum_type ] )
@@ -156,7 +146,7 @@ def extract
156
146
end
157
147
158
148
def extracted?
159
- resource [ :creates ] && File . exist? ( resource [ :creates ] )
149
+ resource . check_all_attributes
160
150
end
161
151
162
152
def transfer_download ( archive_filepath )
@@ -258,4 +248,122 @@ def optional_switch(value, option)
258
248
[ ]
259
249
end
260
250
end
251
+
252
+ # Verify that we have the executable
253
+ def checkexe ( command )
254
+ exe = extractexe ( command )
255
+ if Facter . value ( :osfamily ) == 'windows'
256
+ if absolute_path? ( exe )
257
+ if !Puppet ::FileSystem . exist? ( exe )
258
+ raise ArgumentError , format ( _ ( "Could not find command '%{exe}'" ) , exe : exe )
259
+ elsif !File . file? ( exe )
260
+ raise ArgumentError , format ( _ ( "'%{exe}' is a %{klass}, not a file" ) , exe : exe , klass : File . ftype ( exe ) )
261
+ end
262
+ end
263
+ elsif File . expand_path ( exe ) == exe
264
+ if !Puppet ::FileSystem . exist? ( exe )
265
+ raise ArgumentError , format ( _ ( "Could not find command '%{exe}'" ) , exe : exe )
266
+ elsif !File . file? ( exe )
267
+ raise ArgumentError , format ( _ ( "'%{exe}' is a %{klass}, not a file" ) , exe : exe , klass : File . ftype ( exe ) )
268
+ elsif !File . executable? ( exe )
269
+ raise ArgumentError , format ( _ ( "'%{exe}' is not executable" ) , exe : exe )
270
+ end
271
+ end
272
+
273
+ if resource [ :env_path ]
274
+ Puppet ::Util . withenv PATH : resource [ :env_path ] . join ( File ::PATH_SEPARATOR ) do
275
+ return if which ( exe )
276
+ end
277
+ end
278
+
279
+ # 'which' will only return the command if it's executable, so we can't
280
+ # distinguish not found from not executable
281
+ raise ArgumentError , format ( _ ( "Could not find command '%{exe}'" ) , exe : exe )
282
+ end
283
+
284
+ def environment
285
+ env = { }
286
+
287
+ if ( path = resource [ :env_path ] )
288
+ env [ :PATH ] = path . join ( File ::PATH_SEPARATOR )
289
+ end
290
+
291
+ return env unless ( envlist = resource [ :environment ] )
292
+
293
+ envlist = [ envlist ] unless envlist . is_a? Array
294
+ envlist . each do |setting |
295
+ unless ( match = %r{^(\w +)=((.|\n )*)$} . match ( setting ) )
296
+ warning "Cannot understand environment setting #{ setting . inspect } "
297
+ next
298
+ end
299
+ var = match [ 1 ]
300
+ value = match [ 2 ]
301
+
302
+ warning "Overriding environment setting '#{ var } ' with '#{ value } '" if env . include? ( var ) || env . include? ( var . to_sym )
303
+
304
+ if value . nil? || value . empty?
305
+ msg = "Empty environment setting '#{ var } '"
306
+ Puppet . warn_once ( 'undefined_variables' , "empty_env_var_#{ var } " , msg , resource . file , resource . line )
307
+ end
308
+
309
+ env [ var ] = value
310
+ end
311
+
312
+ env
313
+ end
314
+
315
+ def run ( command , check = false )
316
+ checkexe ( command )
317
+
318
+ debug "Executing#{ check ? ' check' : '' } #{ command } "
319
+
320
+ cwd = resource [ :extract ] ? resource [ :extract_path ] : File . dirname ( resource [ :path ] )
321
+ # It's ok if cwd is nil. In that case Puppet::Util::Execution.execute() simply will not attempt to
322
+ # change the working directory, which is exactly the right behavior when no cwd parameter is
323
+ # expressed on the resource. Moreover, attempting to change to the directory that is already
324
+ # the working directory can fail under some circumstances, so avoiding the directory change attempt
325
+ # is preferable to defaulting cwd to that directory.
326
+
327
+ # NOTE: that we are passing "false" for the "override_locale" parameter, which ensures that the user's
328
+ # default/system locale will be respected. Callers may override this behavior by setting locale-related
329
+ # environment variables (LANG, LC_ALL, etc.) in their 'environment' configuration.
330
+ output = Puppet ::Util ::Execution . execute (
331
+ command ,
332
+ failonfail : false ,
333
+ combine : true ,
334
+ cwd : cwd ,
335
+ uid : resource [ :user ] ,
336
+ gid : resource [ :group ] ,
337
+ override_locale : false ,
338
+ custom_environment : environment ,
339
+ sensitive : false
340
+ )
341
+ # The shell returns 127 if the command is missing.
342
+ raise ArgumentError , output if output . exitstatus == 127
343
+
344
+ # Return output twice as processstatus was returned before, but only exitstatus was ever called.
345
+ # Output has the exitstatus on it so it is returned instead. This is here twice as changing this
346
+ # would result in a change to the underlying API.
347
+ [ output , output ]
348
+ end
349
+
350
+ def extractexe ( command )
351
+ if command . is_a? Array
352
+ command . first
353
+ else
354
+ match = %r{^"([^"]+)"|^'([^']+)'} . match ( command )
355
+ if match
356
+ # extract whichever of the two sides matched the content.
357
+ match [ 1 ] or match [ 2 ]
358
+ else
359
+ command . split ( %r{ } ) [ 0 ]
360
+ end
361
+ end
362
+ end
363
+
364
+ def validatecmd ( command )
365
+ exe = extractexe ( command )
366
+ # if we're not fully qualified, require a path
367
+ self . fail "'#{ exe } ' is not qualified and no path was specified. Please qualify the command or specify a path." if !absolute_path? ( exe ) && resource [ :path ] . nil?
368
+ end
261
369
end
0 commit comments