Skip to content

Commit beafb0f

Browse files
committed
add onlyif && unless features
lint remove debug fixe check_all_attributes lint
1 parent 1abd3c1 commit beafb0f

File tree

11 files changed

+988
-34
lines changed

11 files changed

+988
-34
lines changed

.rubocop_todo.yml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This configuration was generated by
22
# `rubocop --auto-gen-config`
3-
# on 2023-08-17 21:26:09 UTC using RuboCop version 1.50.2.
3+
# on 2024-08-06 17:00:26 UTC using RuboCop version 1.50.2.
44
# The point is for the user to remove these configuration records
55
# one by one as the offenses are removed from the code base.
66
# Note that changes in the inspected code, or installation of new
@@ -12,6 +12,44 @@ Lint/NonAtomicFileOperation:
1212
Exclude:
1313
- 'lib/puppet/provider/archive/ruby.rb'
1414

15+
# Offense count: 1
16+
Naming/AccessorMethodName:
17+
Exclude:
18+
- 'lib/puppet/type/archive.rb'
19+
20+
# Offense count: 1
21+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
22+
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
23+
Naming/MethodParameterName:
24+
Exclude:
25+
- 'spec/lib/puppet_spec/files.rb'
26+
27+
# Offense count: 9
28+
# Configuration parameters: AssignmentOnly.
29+
RSpec/InstanceVariable:
30+
Exclude:
31+
- 'spec/unit/puppet/provider/archive/ruby_spec.rb'
32+
- 'spec/unit/puppet/type/archive_spec.rb'
33+
34+
# Offense count: 1
35+
# Configuration parameters: EnforcedStyle.
36+
# SupportedStyles: have_received, receive
37+
RSpec/MessageSpies:
38+
Exclude:
39+
- 'spec/lib/puppet_spec/compiler.rb'
40+
41+
# Offense count: 4
42+
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
43+
RSpec/VerifiedDoubles:
44+
Exclude:
45+
- 'spec/unit/puppet/type/archive_spec.rb'
46+
47+
# Offense count: 4
48+
# Configuration parameters: AllowedVariables.
49+
Style/GlobalVars:
50+
Exclude:
51+
- 'spec/lib/puppet_spec/files.rb'
52+
1553
# Offense count: 1
1654
# This cop supports unsafe autocorrection (--autocorrect-all).
1755
# Configuration parameters: AllowComments.

REFERENCE.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,13 +632,19 @@ Parameters
632632
Examples
633633
--------
634634

635+
#### Examples
636+
637+
#####
638+
639+
```puppet
635640
archive::nexus { '/tmp/jtstand-ui-0.98.jar':
636641
url => 'https://oss.sonatype.org',
637642
gav => 'org.codehaus.jtstand:jtstand-ui:0.98',
638643
repository => 'codehaus-releases',
639644
packaging => 'jar',
640645
extract => false,
641646
}
647+
```
642648

643649
#### Parameters
644650

@@ -895,6 +901,72 @@ whether archive file should be present/absent (default: present)
895901

896902
Default value: `present`
897903

904+
##### `onlyif`
905+
906+
A test command that checks the state of the target system and restricts
907+
when the `archive` can run. If present, Puppet runs this test command
908+
first, and only runs the main command if the test has an exit code of 0
909+
(success). For example:
910+
911+
```
912+
archive { '/tmp/jta-1.1.jar':
913+
ensure => present,
914+
extract => true,
915+
extract_path => '/tmp',
916+
source => 'http://central.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar',
917+
onlyif => 'test `java -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}'` -gt 15',
918+
cleanup => true,
919+
env_path => ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
920+
}
921+
```
922+
923+
Since this command is used in the process of determining whether the
924+
`archive` is already in sync, it must be run during a noop Puppet run.
925+
926+
This parameter can also take an array of commands. For example:
927+
928+
onlyif => ['test -f /tmp/file1', 'test -f /tmp/file2'],
929+
930+
or an array of arrays. For example:
931+
932+
onlyif => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']
933+
934+
This `archive` would only run if every command in the array has an
935+
exit code of 0 (success).
936+
937+
##### `unless`
938+
939+
A test command that checks the state of the target system and restricts
940+
when the `archive` can run. If present, Puppet runs this test command
941+
first, then runs the main command unless the test has an exit code of 0
942+
(success). For example:
943+
944+
```
945+
archive { '/tmp/jta-1.1.jar':
946+
ensure => present,
947+
extract => true,
948+
extract_path => '/tmp',
949+
source => 'http://central.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar',
950+
unless => 'test `java -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}'` -gt 15',
951+
cleanup => true,
952+
env_path => ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
953+
}
954+
```
955+
956+
Since this command is used in the process of determining whether the
957+
`archive` is already in sync, it must be run during a noop Puppet run.
958+
959+
This parameter can also take an array of commands. For example:
960+
961+
unless => ['test -f /tmp/file1', 'test -f /tmp/file2'],
962+
963+
or an array of arrays. For example:
964+
965+
unless => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']
966+
967+
This `archive` would only run if every command in the array has a
968+
non-zero exit code.
969+
898970
#### Parameters
899971

900972
The following parameters are available in the `archive` type.
@@ -910,6 +982,8 @@ The following parameters are available in the `archive` type.
910982
* [`digest_type`](#-archive--digest_type)
911983
* [`digest_url`](#-archive--digest_url)
912984
* [`download_options`](#-archive--download_options)
985+
* [`env_path`](#-archive--env_path)
986+
* [`environment`](#-archive--environment)
913987
* [`extract`](#-archive--extract)
914988
* [`extract_command`](#-archive--extract_command)
915989
* [`extract_flags`](#-archive--extract_flags)
@@ -998,6 +1072,20 @@ archive file checksum source (instead of specifying checksum)
9981072

9991073
provider download options (affects curl, wget, gs, and only s3 downloads for ruby provider)
10001074

1075+
##### <a name="-archive--env_path"></a>`env_path`
1076+
1077+
The search path used for check execution.
1078+
Commands must be fully qualified if no path is specified. Paths
1079+
can be specified as an array or as a '
1080+
1081+
##### <a name="-archive--environment"></a>`environment`
1082+
1083+
An array of any additional environment variables you want to set for a
1084+
command, such as `[ 'HOME=/root', '[email protected]']`.
1085+
Note that if you use this to set PATH, it will override the `path`
1086+
attribute. Multiple environment variables should be specified as an
1087+
array.
1088+
10011089
##### <a name="-archive--extract"></a>`extract`
10021090

10031091
Valid values: `true`, `false`

lib/puppet/provider/archive/ruby.rb

Lines changed: 122 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
require 'securerandom'
77
require '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
@@ -59,6 +60,7 @@
5960
#
6061

6162
Puppet::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
261369
end

0 commit comments

Comments
 (0)