55
66require 'securerandom'
77require 'tempfile'
8+ require 'puppet/util/execution'
89
910# This provider implements a simple state-machine. The following attempts to #
1011# document it. In general, `def adjective?` implements a [state], while `def
5960#
6061
6162Puppet ::Type . type ( :archive ) . provide ( :ruby ) do
63+ include Puppet ::Util ::Execution
6264 optional_commands aws : 'aws'
6365 optional_commands gsutil : 'gsutil'
6466 defaultfor feature : :microsoft_windows
@@ -95,18 +97,6 @@ def tempfile_name
9597 end
9698 end
9799
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-
110100 def checksum
111101 resource [ :checksum ] || ( resource [ :checksum ] = remote_checksum if resource [ :checksum_url ] )
112102 end
@@ -127,7 +117,7 @@ def remote_checksum
127117 # returns boolean
128118 def checksum? ( store_checksum = true )
129119 return false unless File . exist? archive_filepath
130- return true if resource [ :checksum_type ] == :none
120+ return true if resource [ :checksum_type ] == :none
131121
132122 archive = PuppetX ::Bodeco ::Archive . new ( archive_filepath )
133123 archive_checksum = archive . checksum ( resource [ :checksum_type ] )
@@ -156,7 +146,7 @@ def extract
156146 end
157147
158148 def extracted?
159- resource [ :creates ] && File . exist? ( resource [ :creates ] )
149+ resource . check_all_attributes
160150 end
161151
162152 def transfer_download ( archive_filepath )
@@ -258,4 +248,122 @@ def optional_switch(value, option)
258248 [ ]
259249 end
260250 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
261369end
0 commit comments