Skip to content

Commit

Permalink
[curl] updates provider to fix some issues
Browse files Browse the repository at this point in the history
  • Loading branch information
ashrithr committed Mar 18, 2014
1 parent 72e782b commit 182d766
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 24 deletions.
90 changes: 74 additions & 16 deletions utils/lib/puppet/provider/curl/ruby.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,9 @@
require 'rubygems' if RUBY_VERSION < '1.9' require 'rubygems' if RUBY_VERSION < '1.9'
require 'fileutils' require 'fileutils'
require 'net/http' require 'net/http'
require 'json' # we need json gem installed on all nodes # we need these gems installed on all nodes
require 'json'
require 'jsonpath'


Puppet::Type.type(:curl).provide(:ruby) do Puppet::Type.type(:curl).provide(:ruby) do
# limit the use of this resource to redhat and debian based systems # limit the use of this resource to redhat and debian based systems
Expand Down Expand Up @@ -85,12 +87,15 @@ def process_params(domain, request_method, params, uri)
:returns => params[:returns], :returns => params[:returns],
:does_not_return => params[:does_not_return], :does_not_return => params[:does_not_return],
:contains => params[:contains], :contains => params[:contains],
:does_not_contain => params[:does_not_contain] :does_not_contain => params[:does_not_contain],
:contains_key => params[:contains_key],
:contains_value => params[:contains_value]
) )
rescue Exception => e rescue Exception => e
error = e error = e
Puppet.debug e.backtrace.join('\n') Puppet.debug e.backtrace.join('\n')
raise Puppet::Error, "An exception was raised when invoking web request: #{e} (#{e.class})" raise Puppet::Error, "An exception was raised when invoking web" \
" request: #{e} (#{e.class})"
ensure ensure
unless result.nil? unless result.nil?
log_response( log_response(
Expand All @@ -117,18 +122,20 @@ def skip_request?(curl, params, uri)


result = curl.request( result = curl.request(
method.to_sym, method.to_sym,
uri, condition[method], # make the request passed by the method in the condition
params[:request_type] || 'json', params[:request_type] || 'json',
condition[:parameters] && eval(condition[:parameters]) || {} condition[:parameters] && eval(condition[:parameters]) || {} # parse the params if any
) )
result_succeeded = true result_succeeded = true


begin begin
verify_result(curl, result, condition) verify_result(curl, result, condition)
rescue Puppet::Error rescue Puppet::Error => ex
Puppet.debug ex.message
result_succeeded = false result_succeeded = false
end end
if (c == :only_if && !result_succeeded) || (c == :unless && result_succeeded) if (c == :only_if && !result_succeeded) ||
(c == :unless && result_succeeded)
return true return true
end end
end end
Expand All @@ -150,6 +157,11 @@ def verify_result(curl, result, verify = {})
if verify[:does_not_contain].nil? && !verify['does_not_contain'].nil? if verify[:does_not_contain].nil? && !verify['does_not_contain'].nil?
verify[:does_not_contain] = verify['does_not_contain'] verify[:does_not_contain] = verify['does_not_contain']
end end
if verify[:contains_key].nil? && !verify['contains_key'].nil?
verify[:contains_key] = verify['contains_key']
verify[:contains_value] = verify['contains_value']
end
Puppet.debug "Parsed verify hash: " + verify.inspect


# #
# validates the status codes user is expecting # validates the status codes user is expecting
Expand All @@ -164,16 +176,17 @@ def verify_result(curl, result, verify = {})
"was not expecting one of #{verify[:does_not_return]}" "was not expecting one of #{verify[:does_not_return]}"
end end


# if !verify[:contains].nil? && !curl.valid_json_path?(result, verify[:contains])
# TODO update the method #valid_json_key? to validate the user input for a request
#
if !verify[:contains].nil? && !curl.valid_json_key?(verify[:contains])
raise Puppet::Error, "Expecting #{verify[:contains]} in the result" raise Puppet::Error, "Expecting #{verify[:contains]} in the result"
end end


if !verify[:does_not_contain].nil? && curl.valid_json_key?(verify[:does_not_contain]) if !verify[:does_not_contain].nil? && curl.valid_json_path?(result, verify[:does_not_contain])
raise Puppet::Error, "Not expecting #{verify[:does_not_contain]} in the result" raise Puppet::Error, "Not expecting #{verify[:does_not_contain]} in the result"
end end

if !verify[:contains_key].nil? && !curl.valid_key_value?(result, verify[:contains_key], verify[:contains_value])
raise Puppet::Error, "Key '#{verify[:contains_key]}' does not match value '#{verify[:contains_value]}' in the result"
end
end end


# Logs the reponse to a specific file, creates the directory if does not exists # Logs the reponse to a specific file, creates the directory if does not exists
Expand All @@ -186,7 +199,6 @@ def log_response(params)
puppet_params = params[:puppet_params] puppet_params = params[:puppet_params]


if puppet_params[:log_to] if puppet_params[:log_to]
return if puppet_params[:only_log_errors] == :true && error.nil?
logfile = puppet_params[:log_to].strip logfile = puppet_params[:log_to].strip
exists = File.exists?(logfile) exists = File.exists?(logfile)
isfile = File.file?(logfile) || (!exists && (logfile[-1].chr != '/')) isfile = File.file?(logfile) || (!exists && (logfile[-1].chr != '/'))
Expand Down Expand Up @@ -294,6 +306,30 @@ def self.normalize_param(key, value)
end end
end end


# Monkey patch Hash to find nested key/value pairs
class Hash
# finds and returns a key in a nested hash
def deep_return(key, object=self, found=nil)
if object.respond_to?(:key?) && object.key?(key)
return object[key]
elsif object.is_a? Enumerable
object.find { |*a| found = deep_return(key, a.last) }
return found
end
end

# retunrs if a key is present in the nested hash
def deep_find(key)
if key?(key)
true
else
self.values.inject(nil) do |memo, v|
memo ||= v.deep_find(key) if v.respond_to?(:deep_find)
end
end
end
end

# Class to handle web requests using 'net/http' and 'uri', specifically modelled # Class to handle web requests using 'net/http' and 'uri', specifically modelled
# to handle json based web requests # to handle json based web requests
# #
Expand Down Expand Up @@ -364,11 +400,33 @@ def valid_status_code?(response, valid_values = [])
valid_values.include?(response.code) valid_values.include?(response.code)
end end


# Verifies if a key contains specified message # Verifies if a value is returned with the input jpath
def valid_json_key?(result, key_value) # @param [String] valid json string
return # @param [String] valid jpath like following
# Usage:
# 1. To find if the price of any element is less than 20
# valid_json_path? '{"books":[{"title":"A Tale of Two Somethings","price":18.88}]}', '$..price[?(@ < 20)]'
# 2. To find if a name key has value 'dfs_replication'
# valid_json_path? '{"items":[{"name":"dfs_replication","value":"1"}}', "$..name[?(@ == 'dfs_replication')]"
def valid_json_path?(response, jpath)
Puppet.debug "JPath: " + jpath.inspect
Puppet.debug "Valid JsonPath: " + JsonPath.on(response.body, jpath).inspect
!JsonPath.on(response.body, jpath).empty?
end

def valid_key_value?(response, key, value)
Puppet.debug "Validating if key(#{key}) has value(#{value})"
parsed_response = JSON.parse(response.body)
valid = false
if parsed_response.deep_find(key)
# key is present, now validate if value is same as user
valid = true if parsed_response.deep_return(key) == value
end
return valid
end end


# Parses the json string and wraps it using openstruct object so that it's
# parsable
def json_response(response) def json_response(response)
body = JSON.parse(response.body) body = JSON.parse(response.body)
OpenStruct.new(:code => response.code, :body => body) OpenStruct.new(:code => response.code, :body => body)
Expand Down
19 changes: 11 additions & 8 deletions utils/lib/puppet/type/curl.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
# Custom puppet type for making a web-request
require 'uri' require 'uri'


Puppet::Type.newtype(:curl) do Puppet::Type.newtype(:curl) do

desc 'Custom puppet resource to make web requests' desc 'Custom puppet resource to make web requests'


newparam :name newparam :name
Expand Down Expand Up @@ -76,14 +76,22 @@
end end


newparam(:contains) do newparam(:contains) do
desc "XPath to verify as part of the result" desc "JPath to verify as part of the result"
munge do |value| munge do |value|
Puppet::Type::Curl.munge_array_params(value) Puppet::Type::Curl.munge_array_params(value)
end end
end end


newparam(:contains_key) do
desc "check the json output for a specific key"
end

newparam(:contains_value) do
desc "check the json output for a specific value matching the key"
end

newparam(:does_not_contain) do newparam(:does_not_contain) do
desc "XPath to verify as not being part of the result" desc "JPath to verify as not being part of the result"
munge do |value| munge do |value|
Puppet::Type::Curl.munge_array_params(value) Puppet::Type::Curl.munge_array_params(value)
end end
Expand All @@ -93,11 +101,6 @@
desc "Log requests/responses to the specified file or directory" desc "Log requests/responses to the specified file or directory"
end end


newparam(:only_log_errors) do
desc "Boolean indicating if we should only log responses which did not pass validations"
newvalues(:true, :false)
end

newparam(:only_if) do newparam(:only_if) do
desc "Invoke request only if the specified request returns true" desc "Invoke request only if the specified request returns true"
end end
Expand Down

0 comments on commit 182d766

Please sign in to comment.