mirror of
https://github.com/inspec/inspec
synced 2024-11-10 07:04:15 +00:00
Merge pull request #5443 from inspec/cw/timeouts
Add timeout option to command resource
This commit is contained in:
commit
8286ec8072
9 changed files with 108 additions and 1 deletions
|
@ -258,6 +258,8 @@ This subcommand has additional options:
|
|||
Specifies the bastion port if applicable
|
||||
* ``--bastion-user=BASTION_USER``
|
||||
Specifies the bastion user if applicable
|
||||
* ``--command-timeout=SECONDS``
|
||||
Maximum seconds to allow a command to run. Default 3600.
|
||||
* ``--config=CONFIG``
|
||||
Read configuration from JSON file (`-` reads from stdin).
|
||||
* ``--controls=one two three``
|
||||
|
@ -422,6 +424,8 @@ This subcommand has additional options:
|
|||
Specifies the bastion user if applicable
|
||||
* ``-c``, ``--command=COMMAND``
|
||||
A single command string to run instead of launching the shell
|
||||
* ``--command-timeout=SECONDS``
|
||||
Maximum seconds to allow a command to run. Default 3600.
|
||||
* ``--config=CONFIG``
|
||||
Read configuration from JSON file (`-` reads from stdin).
|
||||
* ``--depends=one two three``
|
||||
|
|
|
@ -136,6 +136,30 @@ Wix includes several tools -- such as `candle` (preprocesses and compiles source
|
|||
end
|
||||
end
|
||||
|
||||
### Timing Out Long-Running Commands
|
||||
|
||||
On target platforms that support the feature, the command resource takes an optional `timeout:` parameter which specifies how long the command may run in seconds before erroring out and failing the control.
|
||||
|
||||
```ruby
|
||||
describe command("find / -owner badguy", timeout: 300) do
|
||||
its("stdout") { should be_empty }
|
||||
end
|
||||
```
|
||||
|
||||
This example would run the `find` command for up to 300 seconds, then give up and fail the control if it exceeded that time.
|
||||
On supported target platforms, the default timeout is 3600 seconds (one hour).
|
||||
|
||||
Aside from setting the value on a per-resource basis, you may also use the `--command-timeout` CLI option to globally set a command timeout. The CLI option takes precedence over any per-resource `timeout:` options.
|
||||
|
||||
Currently supported target platforms include:
|
||||
* Local Unix-like OSes, including macOS
|
||||
* SSH targets
|
||||
* Windows targets via WinRM
|
||||
|
||||
Any target platforms not listed are not supported at this time.
|
||||
|
||||
On unsupported platforms, the timeout value is ignored and the command will run indefinitely.
|
||||
|
||||
### Redacting Sensitive Commands
|
||||
|
||||
By default the command that is ran is shown in the Chef InSpec output. This can be problematic if the command contains sensitive arguments such as a password. These sensitive parts can be redacted by passing in `redact_regex` and a regular expression to redact. Optionally, you can use 2 capture groups to fine tune what is redacted.
|
||||
|
|
|
@ -166,6 +166,9 @@ module Inspec
|
|||
desc: "After normal execution order, results are sorted by control ID, or by file (default), or randomly. None uses legacy unsorted mode."
|
||||
option :filter_empty_profiles, type: :boolean, default: false,
|
||||
desc: "Filter empty profiles (profiles without controls) from the report."
|
||||
option :command_timeout, type: :numeric, default: 3600,
|
||||
desc: "Maximum seconds to allow commands to run during execution. Default 3600.",
|
||||
long_desc: "Maximum seconds to allow commands to run during execution. Default 3600. A timed out command is considered an error."
|
||||
end
|
||||
|
||||
def self.help(*args)
|
||||
|
|
|
@ -321,6 +321,9 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|||
desc: "A space-delimited list of local folders containing profiles whose libraries and resources will be loaded into the new shell"
|
||||
option :distinct_exit, type: :boolean, default: true,
|
||||
desc: "Exit with code 100 if any tests fail, and 101 if any are skipped but none failed (default). If disabled, exit 0 on skips and 1 for failures."
|
||||
option :command_timeout, type: :numeric, default: 3600,
|
||||
desc: "Maximum seconds to allow a command to run. Default 3600.",
|
||||
long_desc: "Maximum seconds to allow commands to run. Default 3600. A timed out command is considered an error."
|
||||
option :inspect, type: :boolean, default: false, desc: "Use verbose/debugging output for resources."
|
||||
def shell_func
|
||||
o = config
|
||||
|
|
|
@ -32,6 +32,16 @@ module Inspec::Resources
|
|||
|
||||
@command = cmd
|
||||
|
||||
cli_timeout = Inspec::Config.cached["command_timeout"].to_i
|
||||
# Can access this via Inspec::InspecCLI.commands["exec"].options[:command_timeout].default,
|
||||
# but that may not be loaded for kitchen-inspec and other pure gem consumers
|
||||
default_cli_timeout = 3600
|
||||
if cli_timeout != default_cli_timeout
|
||||
@timeout = cli_timeout
|
||||
else
|
||||
@timeout = options[:timeout]&.to_i || default_cli_timeout
|
||||
end
|
||||
|
||||
if options[:redact_regex]
|
||||
unless options[:redact_regex].is_a?(Regexp)
|
||||
# Make sure command is replaced so sensitive output isn't shown
|
||||
|
@ -44,7 +54,15 @@ module Inspec::Resources
|
|||
end
|
||||
|
||||
def result
|
||||
@result ||= inspec.backend.run_command(@command)
|
||||
@result ||= begin
|
||||
inspec.backend.run_command(@command, timeout: @timeout)
|
||||
rescue Train::CommandTimeoutReached
|
||||
# Without a small sleep, the train connection gets broken
|
||||
# We've already timed out, so a small sleep is not likely to be painful here.
|
||||
sleep 0.1
|
||||
raise Inspec::Exceptions::ResourceFailed,
|
||||
"Command `#{@command}` timed out after #{@timeout} seconds"
|
||||
end
|
||||
end
|
||||
|
||||
def stdout
|
||||
|
|
11
test/fixtures/profiles/timeouts/controls/inline-timeout.rb
vendored
Normal file
11
test/fixtures/profiles/timeouts/controls/inline-timeout.rb
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
control "timeout-test-01" do
|
||||
# Do something that will timeout, with inline timeout option
|
||||
describe command("sleep 10; echo oops", timeout: 2) do
|
||||
its("stdout") { should be_empty }
|
||||
end
|
||||
|
||||
# Validate that the connection still works after a timeout
|
||||
describe command("echo hello") do
|
||||
its("exit_status") { should cmp 0 }
|
||||
end
|
||||
end
|
10
test/fixtures/profiles/timeouts/inspec.yml
vendored
Normal file
10
test/fixtures/profiles/timeouts/inspec.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
name: sleepy
|
||||
title: InSpec Profile
|
||||
maintainer: The Authors
|
||||
copyright: The Authors
|
||||
copyright_email: you@example.com
|
||||
license: Apache-2.0
|
||||
summary: A profile that contains tests that take a long time to execute, to test the command timeout feature
|
||||
version: 0.1.0
|
||||
supports:
|
||||
platform: os
|
|
@ -1051,4 +1051,37 @@ Test Summary: 2 successful, 0 failures, 0 skipped\n"
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "when running a profile using timeouts on a command resource" do
|
||||
let(:profile) { "#{profile_path}/timeouts" }
|
||||
|
||||
describe "when using the DSL command resource option" do
|
||||
let(:run_result) { run_inspec_process("exec #{profile}") }
|
||||
|
||||
it "properly timesout an inlined command resource" do
|
||||
# Command timeout not available on local windows pipe train transports
|
||||
skip if windows?
|
||||
_(run_result.stderr).must_be_empty
|
||||
|
||||
# Control with inline timeout should be interrupted correctly
|
||||
_(run_result.stdout).must_include "Command `sleep 10; echo oops` timed out after 2 seconds"
|
||||
# Subsequent control must still run correctly
|
||||
_(run_result.stdout).must_include "Command: `echo hello` exit_status is expected to cmp == 0"
|
||||
end
|
||||
end
|
||||
|
||||
describe "when using the CLI option to override the command timeout" do
|
||||
let(:run_result) { run_inspec_process("exec #{profile} --command-timeout 1") }
|
||||
it "properly overrides the DSL setting with the CLI timeout option" do
|
||||
# Command timeout not available on local windows pipe train transports
|
||||
skip if windows?
|
||||
_(run_result.stderr).must_be_empty
|
||||
|
||||
# Command timeout should be interrupted correctly, with CLI timeout applied
|
||||
_(run_result.stdout).must_include "Command `sleep 10; echo oops` timed out after 1 seconds"
|
||||
# Subsequent control must still run correctly
|
||||
_(run_result.stdout).must_include "Command: `echo hello` exit_status is expected to cmp == 0"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -95,6 +95,7 @@ describe "Inspec::Resources::JSON" do
|
|||
# stdout:empty, stderr:empty
|
||||
|
||||
def run_json_cmd(cmd)
|
||||
Inspec::Config.cached["command_timeout"] = 3600 # Reset to default
|
||||
quick_resource("json", :linux, command: cmd)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue