Adds a new resource type to puppet for web requests
      and implements a provider of that type using the
      ruby curl interface provided by the 'curb' rubygem

      This is the first revision of the patch I had previous sent out,
      containing improved validations, methods in the global namespace
      being encapsulated in the type/provider, the re-renaming of 'web
      request' back to 'web' so as to better represent the 'web resource'

Signed-off-by: Mo Morsi <[email protected]>
---
Local-branch: feature/master/7474
 lib/puppet/external/curl.rb     |   47 ++++++++++++++
 lib/puppet/provider/web/curl.rb |  131 +++++++++++++++++++++++++++++++++++++++
 lib/puppet/type/web.rb          |   96 ++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+), 0 deletions(-)
 create mode 100644 lib/puppet/external/curl.rb
 create mode 100644 lib/puppet/provider/web/curl.rb
 create mode 100644 lib/puppet/type/web.rb

diff --git a/lib/puppet/external/curl.rb b/lib/puppet/external/curl.rb
new file mode 100644
index 0000000..ab189c8
--- /dev/null
+++ b/lib/puppet/external/curl.rb
@@ -0,0 +1,47 @@
+# Provides an interface to curl using the curb gem for puppet
+require 'curb'
+
+class Curl::Easy
+
+  def self.web_request(method, uri, request_params, params = {})
+    raise Puppet::Error, "Must specify http method (#{method}) and uri 
(#{uri})" if method.nil? || uri.nil?
+
+    curl = self.new
+
+    if params.has_key?(:cookie)
+      curl.enable_cookies = true
+      curl.cookiefile = params[:cookie]
+      curl.cookiejar  = params[:cookie]
+    end
+
+    curl.follow_location = (params.has_key?(:follow) && params[:follow])
+
+    case(method)
+    when 'get'
+      url = uri
+      url += ";" + request_params.collect { |k,v| "#{k}=#{v}" }.join("&") 
unless request_params.nil?
+      curl.url = url
+      curl.http_get
+      return curl
+
+    when 'post'
+      cparams = []
+      request_params.each_pair { |k,v| cparams << Curl::PostField.content(k,v) 
} unless request_params.nil?
+      curl.url = uri
+      curl.http_post(cparams)
+      return curl
+
+    #when 'put'
+    #when 'delete'
+    end
+  end
+
+  def valid_status_code?(valid_values=[])
+    valid_values.include?(response_code.to_s)
+  end
+
+  def valid_response_body?(test=/.*/)
+    body_str =~ Regexp.new(test)
+  end
+
+end
diff --git a/lib/puppet/provider/web/curl.rb b/lib/puppet/provider/web/curl.rb
new file mode 100644
index 0000000..e84fe02
--- /dev/null
+++ b/lib/puppet/provider/web/curl.rb
@@ -0,0 +1,131 @@
+require 'uuid'
+require 'fileutils'
+require 'puppet/external/curl'
+
+# Puppet provider definition
+Puppet::Type.type(:web).provide :curl do
+  desc "Use curl to access web resources"
+
+  def get
+    @uri
+  end
+
+  def post
+    @uri
+  end
+
+  def delete
+    @uri
+  end
+
+  def put
+    @uri
+  end
+
+  def get=(uri)
+    @uri = uri
+    process_params('get', @resource, uri)
+  end
+
+  def post=(uri)
+    @uri = uri
+    process_params('post', @resource, uri)
+  end
+
+  def delete=(uri)
+    @uri = uri
+    process_params('delete', @resource, uri)
+  end
+
+  def put=(uri)
+    @uri = uri
+    process_params('put', @resource, uri)
+  end
+
+  private
+
+  # Helper to process/parse web parameters
+  def process_params(request_method, params, uri)
+    begin
+      # Set request method and generate a unique session key
+      session = "/tmp/#{UUID.new.generate}"
+  
+      # Invoke a login request if necessary
+      session_request(params[:login]['http_method'], params[:login]['uri'], 
session,
+                      params[:login].reject{ |k,v| k == 'http_method' || k == 
'uri' },
+                      params ) if params[:login]
+
+
+      # Check to see if we should actually run the request
+      return if params[:if]     && 
+                skip_request?(params[:if]['http_method'], params[:if]['uri'], 
session, 
+                              params[:if]['parameters'],  params[:if])
+
+      return if params[:unless] && 
+                !skip_request?(params[:unless]['http_method'], 
params[:unless]['uri'], session, 
+                               params[:unless]['parameters'],  params[:unless])
+
+      # Actually run the request and verify the result
+      result = Curl::Easy::web_request(request_method, uri, 
params[:parameters],
+                                       :cookie => session, :follow => 
params[:follow])
+      verify_result(result,
+                    :returns => params[:returns],
+                    :body    => params[:verify])
+      result.close
+  
+      # Invoke a logout request if necessary
+      session_request(params[:logout]['http_method'], params[:logout]['uri'], 
session,
+                      params[:logout].reject{ |k,v| k == 'http_method' || k == 
'uri' },
+                      params ) if params[:logout]
+  
+    rescue Exception => e
+      raise Puppet::Error, "An exception was raised when invoking web request: 
#{e}"
+  
+    ensure
+      FileUtils.rm_f(session) if params[:logout]
+    end
+  end
+
+  # Helper to issue login/logout requests
+  def session_request(http_method, uri, session, request_params, params = {})
+    Curl::Easy::web_request(http_method, uri, request_params,
+                            :cookie => session, :follow => 
params[:follow]).close
+                      
+  end
+
+  # Helper to issue request to query if we should skip main request or not
+  def skip_request?(http_method, uri, session, request_params, params = {})
+    result = Curl::Easy::web_request(http_method, uri, request_params,
+                                     :cookie => session, :follow => 
params[:follow])
+
+    begin
+      verify_result(result,
+                    :returns => params['returns'],
+                    :body    => params['verify'])
+
+    rescue Puppet::Error => e
+      return true
+
+    ensure
+      result.close
+    end
+
+    return false
+  end
+
+  # Helper to verify the response
+  def verify_result(result, verify = {})
+    if verify.has_key?(:returns) && !verify[:returns].nil? &&
+       !result.valid_status_code?(verify[:returns]) 
+         raise Puppet::Error, "Invalid HTTP Return Code: 
#{result.response_code},
+                               was expecting one of #{verify[:returns].join(", 
")}"
+    end
+
+    if verify.has_key?(:body) && !verify[:body].nil? &&
+       !result.valid_response_body?(verify[:body])
+         raise Puppet::Error, "Expecting #{verify[:body]} in the result"
+    end
+  end
+  
+  
+end
diff --git a/lib/puppet/type/web.rb b/lib/puppet/type/web.rb
new file mode 100644
index 0000000..a9e912c
--- /dev/null
+++ b/lib/puppet/type/web.rb
@@ -0,0 +1,96 @@
+require 'uri'
+
+Puppet::Type.newtype(:web) do
+    @doc = "Issue a request to a resource on the world wide web"
+
+    private
+
+    def self.validate_uri(url)
+      begin
+        uri = URI.parse(url)
+        raise ArgumentError, "Specified uri #{url} is not valid" if 
![URI::HTTP, URI::HTTPS].include?(uri.class)
+      rescue URI::InvalidURIError
+        raise ArgumentError, "Specified uri #{url} is not valid"
+      end
+    end
+
+    def self.validate_http_status(status)
+      status = [status] unless status.is_a?(Array)
+      status.each { |stat|
+        stat = stat.to_s
+        unless ['100', '101', '102', '122',
+                '200', '201', '202', '203', '204', '205', '206', '207', '226',
+                '300', '301', '302', '303', '304', '305', '306', '307',
+                '400', '401', '402', '403', '404', '405', '406', '407', '408', 
'409',
+                '410', '411', '412', '413', '414', '415', '416', '417', '418',
+                '422', '423', '424', '425', '426', '444', '449', '450', '499',
+                '500', '501', '502', '503', '504', '505', '506', '507', '508', 
' 509', '510'
+                ].include?(stat)
+          raise ArgumentError, "Invalid http status code #{stat} specified"
+        end
+      }
+    end
+
+    newparam :name
+
+    newproperty(:get) do
+      desc "Issue get request to the specified uri"
+      validate do |value| Puppet::Type::Web.validate_uri(value) end
+    end
+
+    newproperty(:post) do
+      desc "Issue post request to the specified uri"
+      validate do |value| Puppet::Type::Web.validate_uri(value) end
+    end
+
+    newproperty(:delete) do
+      desc "Issue delete request to the specified uri"
+      validate do |value| Puppet::Type::Web.validate_uri(value) end
+    end
+
+    newproperty(:put) do
+      desc "Issue put request to the specified uri"
+      validate do |value| Puppet::Type::Web.validate_uri(value) end
+    end
+
+    newparam(:parameters) do
+      desc "Hash of parameters to include in the web request"
+      validate do |value| Puppet::Type::Web.validate_uri(value) end
+    end
+
+    newparam(:returns) do
+      desc "Expected http return codes of the request"
+      defaultto ["200"]
+      validate do |value| Puppet::Type::Web.validate_http_status(value) end
+      munge    do |value|
+        value = [value] unless value.is_a?(Array)
+        value = value.collect { |val| val.to_s }
+        value
+      end
+    end
+
+    newparam(:follow) do
+      desc "Boolean indicating if redirects should be followed"
+      newvalues(:true, :false)
+    end
+
+    newparam(:verify) do
+      desc "String to verify as being part of the result"
+    end
+
+    newparam(:login) do
+      desc "Login parameters to be used if a login is required before making 
the request"
+    end
+
+    newparam(:logout) do
+      desc "Logout parameters to be used if a logout is requred after making 
the request"
+    end
+
+    newparam(:unless) do
+      desc "Do not run request if the request specified here succeeds"
+    end
+
+    newparam(:if) do
+      desc "Only run request if the request specified here succeeds"
+    end
+end
-- 
1.7.2.3

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to