Class: Puppet::ResourceApi::Command
- Inherits:
-
Object
- Object
- Puppet::ResourceApi::Command
- Defined in:
- lib/puppet/resource_api/command.rb
Overview
A useful interface to safely run system commands
See github.com/DavidS/puppet-specifications/blob/reasourceapi/language/resource-api/README.md#commands for a complete specification
Defined Under Namespace
Classes: Result
Instance Attribute Summary collapse
-
#command ⇒ Object
readonly
Returns the value of attribute command.
-
#cwd ⇒ Object
Returns the value of attribute cwd.
-
#environment ⇒ Object
Returns the value of attribute environment.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(command) ⇒ Command
constructor
A new instance of Command.
- #run(context, *args, stdin_source: :none, stdin_value: nil, stdin_io: nil, stdin_encoding: nil, stdout_destination: :log, stdout_loglevel: :debug, stdout_encoding: nil, stderr_destination: :log, stderr_loglevel: :warning, stderr_encoding: nil, noop: false) ⇒ Object
Constructor Details
#initialize(command) ⇒ Command
Returns a new instance of Command.
21 22 23 24 25 |
# File 'lib/puppet/resource_api/command.rb', line 21 def initialize(command) @command = command @cwd = '/' @environment = {} end |
Instance Attribute Details
#command ⇒ Object (readonly)
Returns the value of attribute command.
19 20 21 |
# File 'lib/puppet/resource_api/command.rb', line 19 def command @command end |
#cwd ⇒ Object
Returns the value of attribute cwd.
17 18 19 |
# File 'lib/puppet/resource_api/command.rb', line 17 def cwd @cwd end |
#environment ⇒ Object
Returns the value of attribute environment.
17 18 19 |
# File 'lib/puppet/resource_api/command.rb', line 17 def environment @environment end |
Class Method Details
.prepare_process(_context, command, *args, environment:, cwd:) ⇒ Object
112 113 114 115 116 117 118 119 120 |
# File 'lib/puppet/resource_api/command.rb', line 112 def self.prepare_process(_context, command, *args, environment:, cwd:) process = ChildProcess.build(command, *args) environment.each do |k, v| process.environment[k] = v end process.cwd = cwd process end |
Instance Method Details
#run(context, *args, stdin_source: :none, stdin_value: nil, stdin_io: nil, stdin_encoding: nil, stdout_destination: :log, stdout_loglevel: :debug, stdout_encoding: nil, stderr_destination: :log, stderr_loglevel: :warning, stderr_encoding: nil, noop: false) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/puppet/resource_api/command.rb', line 27 def run(context, *args, stdin_source: :none, stdin_value: nil, stdin_io: nil, stdin_encoding: nil, stdout_destination: :log, stdout_loglevel: :debug, stdout_encoding: nil, stderr_destination: :log, stderr_loglevel: :warning, stderr_encoding: nil, noop: false) raise ArgumentError, "context is a '#{context.class}', expected a 'Puppet::ResourceApi::BaseContext'" unless context.is_a? Puppet::ResourceApi::BaseContext return if noop process = self.class.prepare_process(context, command, *args, environment: environment, cwd: cwd) process.duplex = true stdout_r, stdout_w = IO.pipe process.io.stdout = stdout_w process.io.stdout.set_encoding(process.io.stdout.external_encoding, stdout_encoding) if stdout_encoding stderr_r, stderr_w = IO.pipe process.io.stderr = stderr_w process.io.stderr.set_encoding(process.io.stderr.external_encoding, stderr_encoding) if stderr_encoding process.io.stdin.set_encoding(process.io.stdin.external_encoding, stdin_encoding) if stdin_encoding process.start stdout_w.close stderr_w.close case stdin_source when :none # rubocop:disable Lint/EmptyWhen # nothing to do here when :io while (v = stdin_io.read) && !v.empty? # `empty?` signals EOF, can't use the `length` variant due to encoding issues process.io.stdin.write v end when :value process.io.stdin.write stdin_value end process.io.stdin.close result = Result.new # TODO: https://tickets.puppetlabs.com/browse/PDK-542 - capture/buffer full lines while process.alive? || !stdout_r.eof? || !stderr_r.eof? rs, _ws, _errs = IO.select([stdout_r, stderr_r]) rs.each do |pipe| loglevel = if pipe == stdout_r stdout_loglevel else stderr_loglevel end destination = if pipe == stdout_r stdout_destination else stderr_destination end begin chunk = pipe.read_nonblock(1024) case destination when :log chunk.split("\n").each { |l| context.send(loglevel, l.strip) } when :store if pipe == stdout_r result.stdout += chunk else result.stderr += chunk end end rescue Errno::EBADF # rubocop:disable Lint/HandleExceptions # This can be thrown on Windows after the process has gone away # ignore, retry WaitReadable through outer loop rescue IO::WaitReadable, EOFError # rubocop:disable Lint/HandleExceptions # ignore, retry WaitReadable through outer loop end end end result.exit_code = process.wait raise Puppet::ResourceApi::CommandExecutionError, 'Command %{command} failed with exit code %{exit_code}' % { command: command, exit_code: result.exit_code } unless result.exit_code.zero? result rescue ChildProcess::LaunchError => e raise Puppet::ResourceApi::CommandNotFoundError, 'Error when executing %{command}: %{error}' % { command: command, error: e.to_s } end |