Resat

DESCRIPTION

Synopsis

Resat is a script engine which allows grouping web requests into scenarios.

A scenario consists of serie of HTTP requests called steps.

Each step may be associated with guards and/or filters and/or handlers.

The syntax used to defined scenarios is simple and can be used by programmers and non-programmers alike. See the WRITING SCENARIOS section below for examples.

  • Guards keep making the same request until the response header and/or body satisfy(ies) certain conditions.

  • Filters validate the response and may save some of its elements in variables. Variables can be used to define requests, guards and filters.

  • Handlers allow writing custom code to handle a request and its response.

Scenarios are defined as YAML documents that must adhere to the Kwalify schemas defined in schemas/scenarios.yaml. See the comments in this file for additional information.

Resat is configured through a YAML configuration file which defines default values that applies to all requests including the host name, base url, whether to use SSL, common headers and body parameters and optionally a username and password to be used with basic authentication. This configuration file is located in config/resat.yaml by default.

Why resat?

There are two main use cases for resat:

  1. Scripting: Resat can be used to chaing together a serie of REST API calls that can be used to perform repetitive tasks.

  2. API testing: For REST API implementors, resat is the ideal automated regression tool. This is the tool we use at RightScale to test our APIs.

How to use

resat can be used as a ruby library or as an application. Using it as library involves instantiating the engine and calling the ‘run’ method:

require 'resat'

options             = OpenStruct.new
options.verbose     = false
options.quiet       = false
options.norecursion = false
options.loglevel    = 'info'
options.logfile     = 'resat.log'
options.configfile  = 'config/resat.yaml'
options.schemasdir  = 'schemas'

Resat::Log.init(options)
engine = Resat::Engine.new(options)
engine.run('my_scenario.yaml')

if engine.succeeded?
  puts engine.summary.dark_blue
else
  puts engine.summary.dark_red
end
puts "#{engine.requests_count} request(s)."
puts "#{engine.ignored_count} scenario(s) ignored."
puts "#{engine.skipped_count} YAML file(s) skipped."

See the examples and usage sections below for using resat as an application.

Examples

Run the scenario defined in scenario.yaml:

$ resat scenario.yaml

Execute scenarios defined in the ‘scenarios’ directory and its sub-directories:

$ resat scenarios

Only execute the scenarios defined in the current directory, do not execute scenarios found in sub-directories:

$ resat -n .

Usage

resat [options] target

For help use: resat -h

Options

-h, --help Display help message
-v, --version Display version, then exit
-q, --quiet Output as little as possible, override verbose
-V, --verbose Verbose output
-n, --norecursion Don't run scenarios defined in sub-directories
-d, --define NAME:VAL Define global variable (can appear multiple times,
                      escape ':' with '::')
-f, --failonerror Stop resat from continuing to run if an error occurs
-c, --config PATH Config file path (config/resat.yaml by default)
-s, --schemasdir DIR Path to schemas directory (schemas/ by default)
-l, --loglevel LVL Log level: debug, info, warn, error (info by default)
-F, --logfile PATH Log file path (resat.log by default)

INSTALLATION

  • From source: run the following command from the root folder to be able to run resat from anywhere:

$ sudo ln -s `pwd`/bin/resat /usr/local/bin/resat
  • Using the gem:

$ sudo gem install resat

DEVELOPMENT

Source

The source code of Resat is available via Git: github.com/raphael/resat.git Fork the project and send pull requests to contribute!

Dependencies

resat relies on Kwalify for validating YAML files:

$ sudo gem install kwalify

WRITING SCENARIOS

At the heart of your resat scripts are the scenarios. A scenario consists of one or more steps. A scenario may include other scenarios. A single execution of Resat can apply to multiple scenarios (all scenarios in a given folder).

A simple scenario containing a single step is defined below:

name: List all servers
steps:
  - request:
      operation: index
      resource:  servers

The first element of the scenario is its name. The name is used by the command line tool for update and error outputs.

The second element is the list of steps. A step must contain a request. A request can correspond to one of the REST CRUD operations and apply to a resource. CRUD operations are create, show, index, update, and destroy.

Operations that apply to a single resource rather than to all resources require the id element:

name: Show server 42
steps:
  - request:
      operation: show
      resource:  servers
      id:        42

Resat also allows defining custom REST operations for making web requests that don’t map to a standard CRUD operation. A custom operation is defined by a type corresponding to the HTTP verb that the request should use (i.e. get, post, put or delete) and its name.

name: Twitter Timelines
steps:
  - request:
      resource:  statuses
      custom:                         # Use a custom operation
        name:    public_timeline.xml  # Operation name
        type:    get                  # GET request

Alternatively, the path of a request can be defined manually:

name: Twitter Timeline
steps:
  - request:
      path: statuses/public_timeline.xml
      type: get

Requests can then be followed by filters which can validate the response and/or extract elements from it.

name: Get Mephisto ServerTemplate
steps:
  - request:
      operation:      index
      resource:       server_templates
    filters:
      - name:         get server template href
        target:       body
        validators:
          - field:    server-templates/ec2-server-template[nickname='Mephisto all-in-one v8']/href
            is_empty: false
        extractors:
          - field:    server-templates/ec2-server-template[nickname='Mephisto all-in-one v8']/href
            variable: server_template_href

Variables that are extracted from a request response can then be used for other requests, filters or guards. A variable is used using the $ sign followed by the variable name. A variable may be written to an output file if it has the save element and the configuration file defines an output file. A variable can also be exported to other scenarios that will get run in the same Resat execution (so a scenario can create resources and save their ids and a following scenario can reuse the ids to delete or update the resources).

The element to extract can be a response header or a response body field. If it is a response body field then an XPATH query is used to identity which part of the response body should be extracted.

The value to be extracted can be further defined using a regular expression with a capture block. The regular expression is applied to the field matching the XPATH associated with the extractor.

Note: Because XPATH is used to define fields in extractors and validators, only requests that return XML can be followed by filters.

name: Create Mephisto Server
steps:
  - request:
      operation:      create
      resource:       servers
      valid_codes:
        - 201
      params:
        - name:       server[nickname]
          value:      'resat created server'
        - name:       server[server_template_href]
          value:      $server_template_href
        - name:       server[deployment_href]
          value:      $deployment_href
    filters:
      - name:         validate server response
        target:       body
        is_empty:     true
      - name:         extract server id
        target:       header
        extractors:
          - field:    location
            pattern:  '.*\/(\d+)$'
            variable: server_id

A scenario request can also use guards. A guard identifies a response element similarly to an extractor (response header or body field identified by an XPATH and optionally a regular expression). A guard specifies a value that the element must match together with a period and a timeout that should be used to retry the request until the value matches the guard or the timeout is reached.

name: Wait until server 42 is operational
steps:
  - request:
      resource:  servers
      id:        42
      operation: show
    guards:
      - target:  body
        field:   server/state
        pattern: 'operational'
        period:  10
        timeout: 300
        name:    server operational

Finally a scenario request can include handlers. Handlers can only be included when resat is used as a library. The handler definition lists a unique name followed the corresponding ruby module name.

name: Store servers definitions
steps:
  - request:
      resource:  servers
      operation: index
    handlers:
      - name:    save results
        module:  ServersPersister

The ruby module must define a process method which accepts two arguments:

def process(request, response)
  • request: an instance of Net::HTTPRequest corresponding to the request associated with this handler.

  • response: an instance of Net::HTTPResponse which contains the associated response.

It should also define a failures method which can return a list of errors. The errors will get logged and optionally stop the execution of resat if the failonerror option is set to true.

ADDITIONAL RESOURCES

LICENSE

Resat - Web scripting for the masses

Author

Raphael Simon (<[email protected]>)

Copyright

Copyright © 2009 RightScale, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.