Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add onlyif && unless features #529

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2023-08-17 21:26:09 UTC using RuboCop version 1.50.2.
# on 2024-08-06 17:00:26 UTC using RuboCop version 1.50.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand All @@ -12,6 +12,44 @@ Lint/NonAtomicFileOperation:
Exclude:
- 'lib/puppet/provider/archive/ruby.rb'

# Offense count: 1
Naming/AccessorMethodName:
Exclude:
- 'lib/puppet/type/archive.rb'

# Offense count: 1
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
Naming/MethodParameterName:
Exclude:
- 'spec/lib/puppet_spec/files.rb'

# Offense count: 9
# Configuration parameters: AssignmentOnly.
RSpec/InstanceVariable:
Exclude:
- 'spec/unit/puppet/provider/archive/ruby_spec.rb'
- 'spec/unit/puppet/type/archive_spec.rb'

# Offense count: 1
# Configuration parameters: EnforcedStyle.
# SupportedStyles: have_received, receive
RSpec/MessageSpies:
Exclude:
- 'spec/lib/puppet_spec/compiler.rb'

# Offense count: 4
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
RSpec/VerifiedDoubles:
Exclude:
- 'spec/unit/puppet/type/archive_spec.rb'

# Offense count: 4
# Configuration parameters: AllowedVariables.
Style/GlobalVars:
Exclude:
- 'spec/lib/puppet_spec/files.rb'

# Offense count: 1
# This cop supports unsafe autocorrection (--autocorrect-all).
# Configuration parameters: AllowComments.
Expand Down
88 changes: 88 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,13 +632,19 @@ Parameters
Examples
--------

#### Examples

#####

```puppet
archive::nexus { '/tmp/jtstand-ui-0.98.jar':
url => 'https://oss.sonatype.org',
gav => 'org.codehaus.jtstand:jtstand-ui:0.98',
repository => 'codehaus-releases',
packaging => 'jar',
extract => false,
}
```

#### Parameters

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

Default value: `present`

##### `onlyif`

A test command that checks the state of the target system and restricts
when the `archive` can run. If present, Puppet runs this test command
first, and only runs the main command if the test has an exit code of 0
(success). For example:

```
archive { '/tmp/jta-1.1.jar':
ensure => present,
extract => true,
extract_path => '/tmp',
source => 'http://central.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar',
onlyif => 'test `java -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}'` -gt 15',
cleanup => true,
env_path => ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
}
```

Since this command is used in the process of determining whether the
`archive` is already in sync, it must be run during a noop Puppet run.

This parameter can also take an array of commands. For example:

onlyif => ['test -f /tmp/file1', 'test -f /tmp/file2'],

or an array of arrays. For example:

onlyif => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']

This `archive` would only run if every command in the array has an
exit code of 0 (success).

##### `unless`

A test command that checks the state of the target system and restricts
when the `archive` can run. If present, Puppet runs this test command
first, then runs the main command unless the test has an exit code of 0
(success). For example:

```
archive { '/tmp/jta-1.1.jar':
ensure => present,
extract => true,
extract_path => '/tmp',
source => 'http://central.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar',
unless => 'test `java -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}'` -gt 15',
cleanup => true,
env_path => ["/bin", "/usr/bin", "/sbin", "/usr/sbin"],
}
```

Since this command is used in the process of determining whether the
`archive` is already in sync, it must be run during a noop Puppet run.

This parameter can also take an array of commands. For example:

unless => ['test -f /tmp/file1', 'test -f /tmp/file2'],

or an array of arrays. For example:

unless => [['test', '-f', '/tmp/file1'], 'test -f /tmp/file2']

This `archive` would only run if every command in the array has a
non-zero exit code.

#### Parameters

The following parameters are available in the `archive` type.
Expand All @@ -910,6 +982,8 @@ The following parameters are available in the `archive` type.
* [`digest_type`](#-archive--digest_type)
* [`digest_url`](#-archive--digest_url)
* [`download_options`](#-archive--download_options)
* [`env_path`](#-archive--env_path)
* [`environment`](#-archive--environment)
* [`extract`](#-archive--extract)
* [`extract_command`](#-archive--extract_command)
* [`extract_flags`](#-archive--extract_flags)
Expand Down Expand Up @@ -998,6 +1072,20 @@ archive file checksum source (instead of specifying checksum)

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

##### <a name="-archive--env_path"></a>`env_path`

The search path used for check execution.
Commands must be fully qualified if no path is specified. Paths
can be specified as an array or as a '

##### <a name="-archive--environment"></a>`environment`

An array of any additional environment variables you want to set for a
command, such as `[ 'HOME=/root', '[email protected]']`.
Note that if you use this to set PATH, it will override the `path`
attribute. Multiple environment variables should be specified as an
array.

##### <a name="-archive--extract"></a>`extract`

Valid values: `true`, `false`
Expand Down
136 changes: 122 additions & 14 deletions lib/puppet/provider/archive/ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

require 'securerandom'
require 'tempfile'
require 'puppet/util/execution'

# This provider implements a simple state-machine. The following attempts to #
# document it. In general, `def adjective?` implements a [state], while `def
Expand Down Expand Up @@ -59,6 +60,7 @@
#

Puppet::Type.type(:archive).provide(:ruby) do
include Puppet::Util::Execution
optional_commands aws: 'aws'
optional_commands gsutil: 'gsutil'
defaultfor feature: :microsoft_windows
Expand Down Expand Up @@ -95,18 +97,6 @@ def tempfile_name
end
end

def creates
if resource[:extract] == :true
extracted? ? resource[:creates] : 'archive not extracted'
else
resource[:creates]
end
end

def creates=(_value)
extract
end

def checksum
resource[:checksum] || (resource[:checksum] = remote_checksum if resource[:checksum_url])
end
Expand All @@ -127,7 +117,7 @@ def remote_checksum
# returns boolean
def checksum?(store_checksum = true)
return false unless File.exist? archive_filepath
return true if resource[:checksum_type] == :none
return true if resource[:checksum_type] == :none

archive = PuppetX::Bodeco::Archive.new(archive_filepath)
archive_checksum = archive.checksum(resource[:checksum_type])
Expand Down Expand Up @@ -156,7 +146,7 @@ def extract
end

def extracted?
resource[:creates] && File.exist?(resource[:creates])
resource.check_all_attributes
end

def transfer_download(archive_filepath)
Expand Down Expand Up @@ -258,4 +248,122 @@ def optional_switch(value, option)
[]
end
end

# Verify that we have the executable
def checkexe(command)
exe = extractexe(command)
if Facter.value(:osfamily) == 'windows'
if absolute_path?(exe)
if !Puppet::FileSystem.exist?(exe)
raise ArgumentError, format(_("Could not find command '%{exe}'"), exe: exe)
elsif !File.file?(exe)
raise ArgumentError, format(_("'%{exe}' is a %{klass}, not a file"), exe: exe, klass: File.ftype(exe))
end
end
elsif File.expand_path(exe) == exe
if !Puppet::FileSystem.exist?(exe)
raise ArgumentError, format(_("Could not find command '%{exe}'"), exe: exe)
elsif !File.file?(exe)
raise ArgumentError, format(_("'%{exe}' is a %{klass}, not a file"), exe: exe, klass: File.ftype(exe))
elsif !File.executable?(exe)
raise ArgumentError, format(_("'%{exe}' is not executable"), exe: exe)
end
end

if resource[:env_path]
Puppet::Util.withenv PATH: resource[:env_path].join(File::PATH_SEPARATOR) do
return if which(exe)
end
end

# 'which' will only return the command if it's executable, so we can't
# distinguish not found from not executable
raise ArgumentError, format(_("Could not find command '%{exe}'"), exe: exe)
end

def environment
env = {}

if (path = resource[:env_path])
env[:PATH] = path.join(File::PATH_SEPARATOR)
end

return env unless (envlist = resource[:environment])

envlist = [envlist] unless envlist.is_a? Array
envlist.each do |setting|
unless (match = %r{^(\w+)=((.|\n)*)$}.match(setting))
warning "Cannot understand environment setting #{setting.inspect}"
next
end
var = match[1]
value = match[2]

warning "Overriding environment setting '#{var}' with '#{value}'" if env.include?(var) || env.include?(var.to_sym)

if value.nil? || value.empty?
msg = "Empty environment setting '#{var}'"
Puppet.warn_once('undefined_variables', "empty_env_var_#{var}", msg, resource.file, resource.line)
end

env[var] = value
end

env
end

def run(command, check = false)
checkexe(command)

debug "Executing#{check ? ' check' : ''} #{command}"

cwd = resource[:extract] ? resource[:extract_path] : File.dirname(resource[:path])
# It's ok if cwd is nil. In that case Puppet::Util::Execution.execute() simply will not attempt to
# change the working directory, which is exactly the right behavior when no cwd parameter is
# expressed on the resource. Moreover, attempting to change to the directory that is already
# the working directory can fail under some circumstances, so avoiding the directory change attempt
# is preferable to defaulting cwd to that directory.

# NOTE: that we are passing "false" for the "override_locale" parameter, which ensures that the user's
# default/system locale will be respected. Callers may override this behavior by setting locale-related
# environment variables (LANG, LC_ALL, etc.) in their 'environment' configuration.
output = Puppet::Util::Execution.execute(
command,
failonfail: false,
combine: true,
cwd: cwd,
uid: resource[:user],
gid: resource[:group],
override_locale: false,
custom_environment: environment,
sensitive: false
)
# The shell returns 127 if the command is missing.
raise ArgumentError, output if output.exitstatus == 127

# Return output twice as processstatus was returned before, but only exitstatus was ever called.
# Output has the exitstatus on it so it is returned instead. This is here twice as changing this
# would result in a change to the underlying API.
[output, output]
end

def extractexe(command)
if command.is_a? Array
command.first
else
match = %r{^"([^"]+)"|^'([^']+)'}.match(command)
if match
# extract whichever of the two sides matched the content.
match[1] or match[2]
else
command.split(%r{ })[0]
end
end
end

def validatecmd(command)
exe = extractexe(command)
# if we're not fully qualified, require a path
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?
end
end
Loading
Loading