forked from puppetlabs/opv
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(puppetlabs#3) check_powershell resource
- Loading branch information
1 parent
81bff37
commit 69580f0
Showing
5 changed files
with
216 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'puppet/resource_api' | ||
require 'puppet/resource_api/simple_provider' | ||
require 'ruby-pwsh' | ||
require 'retriable' | ||
|
||
# Implementation for the check_powershell type using the Resource API. | ||
class Puppet::Provider::CheckPowershell::CheckPowershell | ||
def get(_context) | ||
[] | ||
end | ||
|
||
def set(context, changes); end | ||
|
||
# Update the check_powershell provider to use the above attributes to execute up to retries number of times | ||
# with success being defined as having one of the expected_statuses | ||
# and the body of the response matches body_matcher while taking into account request_timeout. | ||
|
||
def insync?(context, _name, attribute_name, _is_hash, should_hash) | ||
context.debug("Checking whether #{attribute_name} is up-to-date") | ||
|
||
posh = Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args) | ||
# This callback provides the exception that was raised in the current try, the try_number, the elapsed_time for all tries so far, and the time in seconds of the next_interval. | ||
do_this_on_each_retry = proc do |exception, try, elapsed_time, next_interval| | ||
context.info("#{exception.class}: '#{exception.message}' - #{try} tries in #{elapsed_time} seconds and #{next_interval} seconds until the next try.") unless exception.nil? | ||
end | ||
|
||
Retriable.retriable(tries: should_hash[:retries], max_elapsed_time: should_hash[:request_timeout], max_interval: should_hash[:max_backoff], | ||
multiplier: should_hash[:exponential_backoff_base], on_retry: do_this_on_each_retry) do | ||
response = posh.execute(should_hash[:command]) | ||
unless should_hash[:expected_exitcode].include? response[:exitcode].to_i | ||
raise Puppet::Error, "check_powershell exitcode check failed. The return exitcode '#{response[:exitcode]}' is not matching with the expected_exitcode '#{should_hash[:expected_exitcode]}'" | ||
end | ||
context.debug("The return exitcode '#{response[:exitcode]}' is matching with the expected_exitcode '#{should_hash[:expected_exitcode]}'") | ||
unless response[:stdout].match(should_hash[:output_matcher]) | ||
raise Puppet::Error, "check_powershell output check failed. The return output '#{response[:stdout]}' is not matching output_matcher '#{should_hash[:output_matcher]}'" | ||
end | ||
context.debug("The return output '#{response[:stdout]}' is matching with output_matcher '#{should_hash[:output_matcher]}'") | ||
context.debug("Successfully executed the command '#{should_hash[:command]}'") | ||
return true | ||
end | ||
false | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'puppet/resource_api' | ||
|
||
Puppet::ResourceApi.register_type( | ||
name: 'check_powershell', | ||
docs: <<-EOS, | ||
@summary a check_powershell type | ||
@example | ||
check_powershell { 'https://www.example.com': } | ||
Use this to check whether a web server is responding correctly. This can be used both as a prerequisite (don't manage something if a dependency is unhealthy) or to check whether everything went right after managing something. | ||
EOS | ||
features: ['custom_insync'], | ||
attributes: { | ||
command: { | ||
type: 'String', | ||
desc: 'The powershell command to run.', | ||
behaviour: :namevar, | ||
}, | ||
expected_exitcode: { | ||
type: 'Array[Integer]', | ||
desc: 'An array of acceptable exit codes.', | ||
behaviour: :parameter, | ||
default: [0], | ||
}, | ||
output_matcher: { | ||
type: 'Regexp', | ||
desc: 'A call is considered a success if its output matches this regular expression', | ||
behaviour: :parameter, | ||
default: //, | ||
}, | ||
execution_timeout: { | ||
type: 'Numeric', | ||
desc: 'Number of seconds for a single execution to wait for a response to return a success before aborting.', | ||
behaviour: :parameter, | ||
default: 60, | ||
}, | ||
retries: { | ||
type: 'Integer', | ||
desc: 'Number of requests to make before giving up.', | ||
behaviour: :parameter, | ||
default: 1, | ||
}, | ||
backoff: { | ||
type: 'Numeric', | ||
desc: 'Initial number of seconds to wait between requests.', | ||
behaviour: :parameter, | ||
default: 10, | ||
}, | ||
exponential_backoff_base: { | ||
type: 'Numeric', | ||
desc: 'Exponential base for the exponential backoff calculations.', | ||
behaviour: :parameter, | ||
default: 2, | ||
}, | ||
max_backoff: { | ||
type: 'Numeric', | ||
desc: 'An upper limit to the backoff duration.', | ||
behaviour: :parameter, | ||
default: 120, | ||
}, | ||
timeout: { | ||
type: 'Numeric', | ||
desc: 'Number of seconds allocated overall for the check to return a success before giving up.', | ||
behaviour: :parameter, | ||
default: 600, | ||
}, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
spec/unit/puppet/provider/check_powershell/check_powershell_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
ensure_module_defined('Puppet::Provider::CheckPowershell') | ||
require 'puppet/provider/check_powershell/check_powershell' | ||
|
||
RSpec.describe Puppet::Provider::CheckPowershell::CheckPowershell do | ||
subject(:provider) { described_class.new } | ||
|
||
let(:context) { instance_double('Puppet::ResourceApi::BaseContext') } | ||
let(:posh) { instance_double('Pwsh::Manager') } | ||
let(:valid_command) { '$PSVersionTable.PSVersion' } | ||
let(:invalid_command) { 'invalid$PSVersion' } | ||
let(:valid_hash) do | ||
{ name: 'foo', command: valid_command, expected_exitcode: [0], output_matcher: %r{Major}, request_timeout: 30, retries: 1, backoff: 1, exponential_backoff_base: 2, max_backoff: 40, timeout: 60 } | ||
end | ||
let(:invalid_hash) do | ||
{ name: 'foos', command: invalid_command, expected_exitcode: [2], output_matcher: %r{test}, request_timeout: 30, retries: 3, backoff: 1, exponential_backoff_base: 2, max_backoff: 40, timeout: 60 } | ||
end | ||
|
||
describe 'get(context)' do | ||
it 'processes resources' do | ||
expect(provider.get(context)).to eq [] | ||
end | ||
end | ||
|
||
describe 'insync?(context, name, attribute_name, is_hash, should_hash) without Retry' do | ||
it 'processes resources' do | ||
allow(Pwsh::Manager).to receive(:powershell_path).and_return('C:\\Windows') | ||
allow(Pwsh::Manager).to receive(:powershell_args).and_return(['-NoProfile']) | ||
allow(Pwsh::Manager).to receive(:instance).with(any_args).and_return(posh) | ||
allow(posh).to receive(:execute).with(valid_command).and_return({ stdout: 'Major', exitcode: 0 }) | ||
expect(context).to receive(:debug).with('Checking whether foo is up-to-date') | ||
expect(context).to receive(:debug).with("The return exitcode '0' is matching with the expected_exitcode '[0]'") | ||
expect(context).to receive(:debug).with("The return output 'Major' is matching with output_matcher '(?-mix:Major)'") | ||
expect(context).to receive(:debug).with("Successfully executed the command '$PSVersionTable.PSVersion'") | ||
expect(provider.insync?(context, 'foo', 'foo', valid_hash, valid_hash)).to be(true) | ||
end | ||
end | ||
|
||
describe 'insync?(context, name, attribute_name, is_hash, should_hash) expected_exitcode not matching' do | ||
it 'processes resources' do | ||
allow(Pwsh::Manager).to receive(:powershell_path).and_return('C:\\Windows') | ||
allow(Pwsh::Manager).to receive(:powershell_args).and_return(['-NoProfile']) | ||
allow(Pwsh::Manager).to receive(:instance).with(any_args).and_return(posh) | ||
allow(posh).to receive(:execute).with(invalid_command).and_return({ stdout: 'Major', exitcode: 3 }) | ||
allow(context).to receive(:debug) | ||
allow(context).to receive(:debug) | ||
expect(context).to receive(:debug).with('Checking whether foo is up-to-date') | ||
expect(context).to receive(:info).with(%r{1 tries}) | ||
expect(context).to receive(:info).with(%r{2 tries}) | ||
expect(context).to receive(:info).with(%r{3 tries}) | ||
expect { provider.insync?(context, 'foo', 'foo', invalid_hash, invalid_hash) }.to raise_error(%r{check_powershell exitcode check failed.}) | ||
end | ||
end | ||
|
||
describe 'insync?(context, name, attribute_name, is_hash, should_hash) output_matcher not matching' do | ||
it 'processes resources' do | ||
allow(Pwsh::Manager).to receive(:powershell_path).and_return('C:\\Windows') | ||
allow(Pwsh::Manager).to receive(:powershell_args).and_return(['-NoProfile']) | ||
allow(Pwsh::Manager).to receive(:instance).with(any_args).and_return(posh) | ||
allow(posh).to receive(:execute).with(invalid_command).and_return({ stdout: 'invalid', exitcode: 2 }) | ||
allow(context).to receive(:debug) | ||
allow(context).to receive(:debug) | ||
expect(context).to receive(:debug).with('Checking whether foo is up-to-date') | ||
expect(context).to receive(:debug).with("The return exitcode '2' is matching with the expected_exitcode '[2]'") | ||
expect(context).to receive(:info).with(%r{1 tries}) | ||
expect(context).to receive(:info).with(%r{2 tries}) | ||
expect(context).to receive(:info).with(%r{3 tries}) | ||
expect { provider.insync?(context, 'foo', 'foo', invalid_hash, invalid_hash) }.to raise_error(%r{check_powershell output check failed.}) | ||
end | ||
end | ||
|
||
describe 'insync?(context, name, attribute_name, is_hash, should_hash) with Retry' do | ||
it 'processes resources' do | ||
allow(Pwsh::Manager).to receive(:powershell_path).and_return('C:\\Windows') | ||
allow(Pwsh::Manager).to receive(:powershell_args).and_return(['-NoProfile']) | ||
allow(Pwsh::Manager).to receive(:instance).with(any_args).and_return(posh) | ||
allow(context).to receive(:debug) | ||
allow(context).to receive(:debug) | ||
expect(context).to receive(:debug).with('Checking whether foo is up-to-date') | ||
allow(posh).to receive(:execute).with(invalid_command).and_raise(StandardError) | ||
expect(context).to receive(:info).with(%r{1 tries}) | ||
expect(context).to receive(:info).with(%r{2 tries}) | ||
expect(context).to receive(:info).with(%r{3 tries}) | ||
expect { provider.insync?(context, 'foo', 'foo', invalid_hash, invalid_hash) }.to raise_error(StandardError) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
require 'puppet/type/check_powershell' | ||
|
||
RSpec.describe 'the check_powershell type' do | ||
it 'loads' do | ||
expect(Puppet::Type.type(:check_powershell)).not_to be_nil | ||
end | ||
end |