[
  {
    "path": ".gitignore",
    "content": "pkg/\n.rvmrc\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ruby:2.1.10\nRUN apt-get update -qq && apt-get install -y \\\n    build-essential \\\n    libxml2 \\\n    libxml2-dev \\\n    && rm -rf /var/lib/apt/lists/*\nWORKDIR /right_aws\nCOPY Gemfile Gemfile.lock /right_aws/\nRUN bundle install\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"http://rubygems.org\"\n\ngem \"right_http_connection\", \">= 1.5.0\", git: \"https://github.com/flexera-public/right_http_connection\"\ngem 'libxml-ruby', \"2.8.0\"\n\ngroup :development do\n  gem 'rake', '10.4.2'\nend\n"
  },
  {
    "path": "History.txt",
    "content": "== 1.1.0 2007-08-10\nInitial release.\n\n== 1.2.0 2007-09-12\n\n* r1718, todd, 2007-09-12 15:34:37\n  * # 458, Extensive documentation review, rework, and expansion.  Also added\n    coverage analysis to the test suite using RCov.\n\n* r1690, todd, 2007-09-07 15:23:11\n  * # 447, Add support.rb to manifest\n\n* r1688, todd, 2007-09-07 13:57:39\n  * # 447, Use Active Support if available, but don't require it.  Load our own\n    extensions if it's not present.  This keeps us from overloading ActiveSupport if\n    a user's already using it.\n\n* r1687, todd, 2007-09-07 11:36:43\n  * # 447, Removed dependency on activesupport\n\n* r1676, konstantin, 2007-09-06 01:27:09\n  * paid AMIs, small fix\n\n* r1667, konstantin, 2007-09-05 12:58:10\n  * # 427, paid AMI support for ec2_instances\n\n* r1658, konstantin, 2007-09-05 01:02:25\n  * params improvements for ec2.describe_xxx, now ones can use a String as well as an Array.\n\n* r1653, konstantin, 2007-09-04 12:31:19\n  * libxml and paid AMI support added\n\n* r1581, tve, 2007-08-24 16:21:45\n  * Improved RightAws documentation\n\n== 1.3.0 2007-09-26\n\n* r1754, todd, 2007-09-19 13:48:34\n  * # 487, # 488, Consolidate a lot of code that was repeated in three places.\n    Fix error handling path when using streaming GET interfaces with S3.\n    Also add a stub for RightHttpConnection which allows more control over the\n    unit tests.  Expand the unit tests and coverage tests.\n\n* r1755, todd, 2007-09-19 14:29:19\n  * # 487, RDoc fixes after code consolidation\n\n* r1866, konstantin, 2007-10-05 06:17:36\n  * # 220, close connection on HTTP 5xx/4xx errors\n\n== 1.4.0 2007-10-10\n\n* r1868, konstantin, 2007-10-05 06:38:37\n  * # 220, inst_type branch merge into 1.3.0 release\n\n* r1869, konstantin, 2007-10-05 07:32:34\n  * right_http_connection 1.1.1 requirements fixed\n\n* r1879, konstantin, 2007-10-07 02:11:24\n  * # 524, blocks added to ec2_describe_xxx to support aws_cache\n\n* r1924, konstantin, 2007-10-12 11:35:06\n  * # 536, user_data bug fix\n\n* r1929, tve,  2007-10-15 00:00:11\n  * Fix libxml/rexml selection bug\n\n* r1938, konstantin, 2007-10-16 10:53:56\n  * instance type support is set to default\n\n== 1.4.3 2007-10-25\n\n* r1983, konstantin, 2007-10-25 22:33:00 +0400\n  * Fixed ActiveSupport requirement bug (thanks to Toby)\n  * Fixed HttpConnection logging to stdout bug (thanks to Toby)\n\n== 1.4.4\n\n* r1999, tve, 2007-11-01 00:07:00 -0700\n  * Fixed escaping issue affecting key names in S3 gem\n  * Fixed duplicate marker in S3 incremental bucket listing\n\n* r2001, konstantin, 2007-11-01 12:03:13  +0300\n  * Fixed multiple permissions assignment on Grantee#grant/revoke\n  * Fixed new grantee permissions set ingnore (Grantee#apply)\n  * S3::Grantee#exists? method added\n\n* r2109, konstantin, 2007-11-12 21:49:36  +0300\n  * RightAwsBaseInterface: caching implemented.\n    (The Ec2 functions are being cached: describe_images, describe_instances,\n    describe_security_groups and describe_key_pairs)\n\n== 1.4.5 - 1.4.6\n* r 2619, konstantin, 01-17-08 16:18:36  +0300\n  * S3 Location constraints support added.\n  * Fixed bug with trailing '/' in the bucket name for 'EU' located buckets\n  * Added: S3Interface#bucket_location, S3::Bucket#location\n\n== 1.4.7\n* r 2622, konstantin, 01-18-08 13:52:20  +0300\n  * Virtual domains doc added\n  * S3 Query API fixed to support virtual domains.\n\n== 1.4.8\n* r 2650, konstantin, 01-24-08 11:12:00  +0300\n  * net_fix.rb moved to right_http_connection\n\n== 1.5.0\n* r 2688, konstantin, 02-30-08 15:42:00  +0300\n  * SDB support added.\n  * RightAws::S3::bucket and RightAws::S3::Bucket.create methods behaviour\n    changed: param +create+ is set to +false+ by default.\n\n== 1.6.0\n\n* r2780, todd, 2008-02-11 11:41:07 -0800 (Mon, 11 Feb 2008), 4 lines\n  * Some doc updates & tweaks: we now support ultra-large PUTs to S3, small SDB\n    cleanups, add SDB to the list of interfaces in RightAws, update some copyrights,\n    warn about loading attachment_fu AFTER right_aws (big no-no).\n\n* r2784, todd, 2008-02-11 13:46:47 -0800 (Mon, 11 Feb 2008), 2 lines\n  * One final clarification: you may get a Net::HTTP bad monkey patch exception\n\n* r2880, todd, 2008-02-25 18:06:22 -0800 (Mon, 25 Feb 2008), 2 lines\n  * Add SQS 'Gen 2' interface implementation\n\n* r2913, todd, 2008-02-29 16:57:07 -0800 (Fri, 29 Feb 2008), 3 lines\n  * SqsGen2 (object interface), unit tests for both interfaces, documentation updates.\n\n* r2922, todd, 2008-03-03 15:26:42 -0800 (Mon, 03 Mar 2008), 2 lines\n  * couple of documentation tweaks in prep for 1.6.0\n\n== 1.6.1\n\n* r2963, todd, 2008-03-06 19:10:23 -0800 (Thu, 06 Mar 2008), 3 lines\n  * (#950) Many minor fixes in incrementally_list_bucket to prevent a death loop\n    in certain rare conditions\n\n== 1.7.0\n\n* r3051, konstantin, 2008-03-14 21:26:12 +0300 (Fri, 14 Mar 2008), 1 line\n  * #897, ActiveSdb alpha release\n\n== 1.7.1\n\n  Do not autoload right_sdb with the rest of the modules; it requires uuidtools.\n    We want the user to explicly request ActiveSdb, at least as long as it is\n    alpha/beta.\n\n  Fix escaping problem in SqsGen2Interface: POST bodies did not properly escape the '&' character\n\n== 1.7.2\n\n  Release Notes:\n\n  RightAws includes some new features, including:\n  - Support in RightAws::S3 and RightAws::S3Interface for S3 key copy, move, and rename\n  - Support for signature version 0 request authentication to EC2, SQS, and SDB\n  - Enhanced S3 object meta-header read and update\n  - Interoperability with clouds running Eucalyptus (http://eucalyptus.cs.ucsb.edu)\n    [ Contributed by the Eucalyptus group ]\n  - Support for c1.medium and c1.xlarge instance types\n\n  Bug fixes include:\n  - Corrected the failure, under certain conditions, of retries of streaming PUTs to S3.\n    We now reset the seek pointer of the streaming IO object to its initial position.\n  - Removal of an accidental dependency on ActiveSupport in RightAws::S3Interface.get_link().\n  - Monkey-patch of the Ruby File class on Windows platforms to correct a problem in lstat.\n    The lstat bug was causing failure of very large file uploads on Windows [ Contributed by Benjamin Allfree ]\n  - Fixed parsing of the ETag field for S3 objects\n\n== 1.7.3\n\n  Release Notes:\n\n  - Removed the 1.7.2 monkey-patch of the Ruby File class on Windows.  This patch broke Rails 2.0.\n    The patch is now included in the README for anyone to use at their own risk.\n\n== 1.8.0\n\n  Release Notes:\n\n  This release adds major new features to RightAws to support Amazon's new\n  Elastic Block Store (EBS).  Via the RightAws::Ec2 module, users can create\n  and delete EBS volumes, attach and detach them from instances, snapshot\n  volumes, and list the available volumes and snapshots.\n\n  Bug fixes include correction of RightAws::S3 copy's failure to url-encode\nthe source key.\n\n== 1.8.1\n\n  Release Notes:\n\n  RightScale::SdbInterface & ::ActiveSdb have several enhancements, including:\n  - RightAws::SdbInterface#last_query_expression added for debug puposes\n  - RightAws::ActiveSdb::Base#query :order and :auto_load options added to support query\n    result sorting and attributes auto loading\n  - RightAws::ActiveSdb::Base#find_all_by_ and find_by_ helpers improved to support\n    :order, :auto_load, :limit and :next_token options\n  - RightAws::SdbInterface#delete_attributes bug fixed\n  - SdbInterface allows specification of a string value to use for\n    representing Ruby nil in SDB.\n  - Sdb tests fixed and improved\n\n  The ::S3 interface now has support for S3's server access logging.\n  Amazon considers server access logging to be a beta or provisional feature.\n\n=== 1.9.0\n\n  Release Notes:\n  - RightAws::Ec2 now supports Windows instances. Added:\n   - Ec2::get_initial_password\n   - Ec2::bundle_instance\n   - Ec2::describe_bundle_tasks\n   - Ec::cancel_bundle_task\n\n  - Full Amazon CloudFront support added with RightAws::AcfInterface\n  - Bug fixes to S3Interface::store_object_and_verify and\n    S3Interface::retrieve_object_and_verify (thanks to numerous user reports)\n  - Updates to caching for Ec2::describe_images_by methods\n  - Ec2 now has Ec2::last_request_id\n\n=== 1.10.0\n\n  Release Notes:\n  - AwsBase: signature v2 support added\n  - Ec2: describe_availability_zones improved to support regions\n  - CloudFront: docs fixes\n  - SDB: added: SQL-like query, select and query_with_attributes support\n  - SDB: fixed no method error when searching for id that doesn't exist\n\n=== 1.11.0\n\n  Release Notes:\n  - Full Amazon RDS instances support added with RightAws::RdsInterface\n  - Boot from EBS support added\n  - VPC support added\n  - Latest EC2 API 2009-10-31 support added\n  - Some of bugs fixed\n\n=== 2.0.0\n\n  Release Notes:\n  - Added:\n    - Ruby 1.9 support\n    - Ec2:\n      - SpotInstances support\n      - m2.xlarge instances\n      - GetPasswordData API call (see get_password_data_v2)\n      - SecurityGroups support for Eucalyptus clouds\n    - EBS:\n      - :delete_on_termination field for volumes\n    - SimpleDB:\n      - BatchPutAttributes support\n    - ActiveSDB:\n      - Dynamic attribute accessors\n      - \"Columns\" support\n      - Simple Type Casting support\n    - ELB:\n      - API '2009-11-25' support (stickiness policies)\n    - ACF:\n      - API '2010-03-01' support (origin access policy and streaming distributions)\n  - Bunch of small issues were fixed\n  - Time objects were replaced by Strings (as Amazon returns them) to make the gem more consistent:\n    - :last_modified_time in:\n        RightAws::AcfInterface#incrementally_list_distributions,\n        RightAws::AcfInterface#create_distribution_by_config,\n        RightAws::AcfInterface#get_distribution,\n        RightAws::AcfInterface#get_distribution_config\n    - :timestamp in:\n        RightAws::AcwInterface#get_metric_statistics\n    - :aws_created_at in:\n        RightAws::Ec2#create_volume,\n        RightAws::Ec2#describe_volumes\n    - :aws_attached_at in:\n        RightAws::Ec2#attach_volume,\n        RightAws::Ec2#detach_volume,\n        RightAws::Ec2#describe_volumes\n    - :aws_started_at in:\n        RightAws::Ec2#describe_snapshots,\n        RightAws::Ec2#create_snapshot,\n        RightAws::Ec2#try_create_snapshot\n    - :created_time in:\n        RightAws::ElbInterface#describe_load_balancers\n\n=== 2.1.0\n  Release Notes:\n  - Added:\n    - Route 53: API '2010-10-01'\n    - ACF: API '2010-11-01'\n    - EC2:\n      - API '2010-08-31'\n      - Port based group permissions support\n      - HPC Support\n      - Tags Suport\n      - ClientToken support added on instance launch\n    - RDS: API \"2010-07-28\"\n    - ELB: API '2010-07-01' (SSL support)\n    - IAM: API '2010-05-08' (AWS Identity and Access Management interface)\n    - 301 Redirect support added\n  - Removed:\n    - ActiveSupport dependency\n    - SDB: uuid gem dependency\n  - this gem requires right_http_connection 0bc3343232133bdb38c237d8285525d74495d3f5 or later\n  - \"Raise On Timeout On Action\" feature added to avoid duplicate resources creation if a timeout error occures and a retry is performed\n\n=== 3.0.0\n  Release Notes:\n  - Fixed/Added:\n    - ClientToken (launch_instances, run_instances) is not used for Eucalyptus clouds\n    - VPC2, stage1. Next methods were updated:\n      - associate_address, modify_security_group, create_security_group, create_vpc, delete_security_group,\n        describe_addresses, describe_images, describe_instance_attribute, describe_regions, describe_reserved_instances_offerings,\n        describe_security_groups, describe_snapshots, describe_spot_instance_requests, describe_spot_price_history,\n        describe_vpcs, disassociate_address, modify_image_attribute, modify_snapshot_attribute, release_address,\n        request_spot_instances, stop_instances\n    - EC2: ClientToken (launch_instances, run_instances) is not used for Eucalyptus clouds\n    - RDS:\n      - RDS: API 2011-04-01\n      - Make :instance_class param more consistent: :db_instance_class --> :instance_class\n      - Issue 53: regression in latest master version of right_rds_interface\n      - Issue 73: Can't get list of instances with RdsInterface\n    - EBS: Issue 54: regression in right_ec2_ebs.rb\n    - Add the port number with server name in the v2 signature string if it is not the RFC standard number (author: unakatsuo).\n    - EMR (Elastic Map Reduce) support\n    - SNS (Simple Notification Service) support\n    - SDB: ConsistentRead support\n    - bunch of micro bugs\n\n=== 3.0.1\n  Release Notes:\n  - Fixed: \n    - SignatureDoesNotMatch on file download via get_link()\n    - S3#bucket should not fail for non admin creds\n    - couple doc typos\n\n=== 3.0.2\n  Release Notes:\n  - Fixed:\n    - S3 Content-Type not set in Ruby 1.9.2\n    - error in rds_interface describe_db_snapshots in Ruby 1.9.2\n\n=== 3.0.4\n  Release Notes:\n  - Fixed:\n    - #125 - fixes redirect bug in file PUT requests (Cary)\n    - some other minor fixes\n\n=== 3.0.5\n  Release Notes:\n  - Added: API '2012-06-15' support for CreateVolume and DescribeVolumes API calls (to support IOPS)\n  - Fixed:\n    - Single-threaded multipart upload support (https://github.com/rightscale/right_aws/pull/116)\n    - S3 multi object delete (https://github.com/rightscale/right_aws/pull/106)\n    - Support for \"ami_version\" added in emr interface (https://github.com/rightscale/right_aws/pull/129)\n    - S3: Added block references to several methods (https://github.com/rightscale/right_aws/pull/130)\n    - Some other minor changes\n\n=== 3.1.0\n  Release Notes:\n  - Added:\n    - EC2:\n      - hs1.8xlarge, cr1.8xlarge instance types\n      - API version '2012-10-01' support for ReservedInstances and ReservedInstancesOfferings\n      - API version '2012-10-15' for some of VPC calls (including new DescribeAccountAttributes)\n  - Removed:  UUID dependency\n  - Fixed:\n    - EC2:\n      - describe_reserved_instances_offerings was fixed to support pagination\n      - typo in create_vpc\n    - RDS: instances types list\n    - Some other minor bugs\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2009-2012 RightScale, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Manifest.txt",
    "content": "History.txt\nManifest.txt\nREADME.txt\nRakefile\nlib/awsbase/support.rb\nlib/awsbase/benchmark_fix.rb\nlib/awsbase/right_awsbase.rb\nlib/ec2/right_ec2.rb\nlib/ec2/right_ec2_images.rb\nlib/ec2/right_ec2_instances.rb\nlib/ec2/right_ec2_security_groups.rb\nlib/ec2/right_ec2_spot_instances.rb\nlib/ec2/right_ec2_ebs.rb\nlib/ec2/right_ec2_reserved_instances.rb\nlib/ec2/right_ec2_vpc.rb\nlib/ec2/right_ec2_vpc2.rb\nlib/ec2/right_ec2_monitoring.rb\nlib/ec2/right_ec2_placement_groups.rb\nlib/ec2/right_ec2_windows_mobility.rb\nlib/ec2/right_ec2_tags.rb\nlib/right_aws.rb\nlib/s3/right_s3.rb\nlib/acw/right_acw_interface.rb\nlib/elb/right_elb_interface.rb\nlib/as/right_as_interface.rb\nlib/s3/right_s3_interface.rb\nlib/sdb/active_sdb.rb\nlib/sdb/right_sdb_interface.rb\nlib/sqs/right_sqs.rb\nlib/sqs/right_sqs_gen2.rb\nlib/sqs/right_sqs_gen2_interface.rb\nlib/sqs/right_sqs_interface.rb\nlib/acf/right_acf_interface.rb\nlib/acf/right_acf_streaming_interface.rb\nlib/acf/right_acf_origin_access_identities.rb\nlib/acf/right_acf_invalidations.rb\nlib/rds/right_rds_interface.rb\nlib/iam/right_iam_interface.rb\nlib/iam/right_iam_groups.rb\nlib/iam/right_iam_users.rb\nlib/iam/right_iam_access_keys.rb\nlib/iam/right_iam_mfa_devices.rb\nlib/route_53/right_route_53_interface.rb\ntest/ec2/test_helper.rb\ntest/ec2/test_right_ec2.rb\ntest/http_connection.rb\ntest/s3/test_helper.rb\ntest/s3/test_right_s3.rb\ntest/s3/test_right_s3_stubbed.rb\ntest/sdb/test_active_sdb.rb\ntest/sdb/test_helper.rb\ntest/sdb/test_right_sdb.rb\ntest/sqs/test_helper.rb\ntest/sqs/test_right_sqs.rb\ntest/sqs/test_right_sqs_gen2.rb\ntest/test_credentials.rb\ntest/ts_right_aws.rb\ntest/acf/test_helper.rb\ntest/acf/test_right_acf.rb\ntest/rds/test_helper.rb\ntest/rds/test_right_rds.rb\n"
  },
  {
    "path": "README.txt",
    "content": "= RightScale Amazon Web Services Ruby Gems\n\nPublished by RightScale, Inc. under the MIT License.\nFor information about RightScale, see http://www.rightscale.com\n\n== THE GEM IS NO LONGER MAINTAINED\n\nWe recommend you use https://github.com/rightscale/right_aws_api instead.\n\n== DESCRIPTION:\n\nThe RightScale AWS gems have been designed to provide a robust, fast, and secure interface to Amazon EC2, EBS, S3, SQS, SDB, and CloudFront. \nThese gems have been used in production by RightScale since late 2006 and are being maintained to track enhancements made by Amazon. \nThe RightScale AWS gems comprise:\n\n- RightAws::Ec2 -- interface to Amazon EC2 (Elastic Compute Cloud), VPC (Virtual Private Cloud) and the associated EBS (Elastic Block Store)\n- RightAws::S3 and RightAws::S3Interface -- interface to Amazon S3 (Simple Storage Service)\n- RightAws::Sqs and RightAws::SqsInterface -- interface to first-generation Amazon SQS (Simple Queue Service)\n- RightAws::SqsGen2 and RightAws::SqsGen2Interface -- interface to second-generation Amazon SQS (Simple Queue Service)\n- RightAws::SdbInterface and RightAws::ActiveSdb -- interface to Amazon SDB (SimpleDB)\n- RightAws::AcfInterface -- interface to Amazon CloudFront, a content distribution service\n- RightAws::AsInterface  -- interface to Amazon Auto Scaling\n- RightAws::AcwInterface -- interface to Amazon Cloud Watch\n- RightAws::ElbInterface -- interface to Amazon Elastic Load Balancer\n- RightAws::RdsInterface -- interface to Amazon RDS instances\n\n== FEATURES:\n\n- Full programmmatic access to EC2, EBS, S3, SQS, SDB, CloudFront, AS, ACW, ELB and RDS.\n- Complete error handling: all operations check for errors and report complete\n  error information by raising an AwsError.\n- Persistent HTTP connections with robust network-level retry layer using\n  RightHttpConnection).  This includes socket timeouts and retries.\n- Robust HTTP-level retry layer.  Certain (user-adjustable) HTTP errors returned\n  by Amazon's services are classified as temporary errors.\n  These errors are automaticallly retried using exponentially increasing intervals.\n  The number of retries is user-configurable.\n- Fast REXML-based parsing of responses (as fast as a pure Ruby solution allows).\n- Uses libxml (if available) for faster response parsing. \n- Support for large S3 list operations.  Buckets and key subfolders containing\n  many (> 1000) keys are listed in entirety.  Operations based on list (like\n  bucket clear) work on arbitrary numbers of keys.\n- Support for streaming GETs from S3, and streaming PUTs to S3 if the data source is a file.\n- Support for single-threaded usage, multithreaded usage, as well as usage with multiple\n  AWS accounts.\n- Support for both first- and second-generation SQS (API versions 2007-05-01\n  and 2008-01-01).  These versions of SQS are not compatible.\n- Support for signature versions 0 and 1 on SQS, SDB, and EC2.\n- Interoperability with any cloud running Eucalyptus (http://eucalyptus.cs.ucsb.edu)\n- Test suite (requires AWS account to do \"live\" testing).\n\n== THREADING:\n\nAll RightScale AWS interfaces offer two threading options:\n1. Use a single persistent HTTP connection per process.\n2. Use a persistent HTTP connection per Ruby thread.\n \nEither way, it doesn't matter how many (for example) RightAws::S3 objects you create,\nthey all use the same per-program or per-thread\nconnection. The purpose of sharing the connection is to keep a single\npersistent HTTP connection open to avoid paying connection\noverhead on every request. However, if you have multiple concurrent\nthreads, you may want or need an HTTP connection per thread to enable\nconcurrent requests to AWS. The way this plays out in practice is:\n1. If you have a non-multithreaded Ruby program, use the non-multithreaded setting.\n2. If you have a multi-threaded Ruby program, use the multithreaded setting to enable\n   concurrent requests to S3 (or SQS, or SDB, or EC2).\n3. For running under Mongrel/Rails, use the non-multithreaded setting even though\n   mongrel is multithreaded.  This is because only one Rails handler is invoked at\n   time (i.e. it acts like a single-threaded program)\n\nNote that due to limitations in the I/O of the Ruby interpreter you\nmay not get the degree of parallelism you may expect with the multi-threaded setting.\n\n== GETTING STARTED:\n\n* For EC2 read RightAws::Ec2 and consult the Amazon EC2 API documentation at\n  http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=87\n* For S3 read RightAws::S3 and consult the Amazon S3 API documentation at\n  http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=48\n* For first generation SQS read RightAws::Sqs and consult the Amazon SQS API documentation at\n  http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=31\n* For second generation SQS read RightAws::SqsGen2, RightAws::SqsGen2Interface, and consult the Amazon SQS API documentation at\n  http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1214&categoryID=31 \n\n  Amazon's Migration Guide for moving from first to second generation SQS is\n  avalable at:\n  http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1148\n* For SDB read RightAws::SdbInterface, RightAws::ActiveSdb, and consult the Amazon SDB API documentation at\n  http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=141\n* For CloudFront (ACF) read RightAws::AcfInterface and consult the Amazon CloudFront API documentation at \n  http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=213\n\n== KNOWN ISSUES:\n\n- 7/08: A user has reported that uploads of large files on Windows may be broken on some\n  Win platforms due to a buggy File.lstat.size.  Use the following monkey-patch at your own risk, \n  as it has been proven to break Rails 2.0 on Windows:\n\n    require 'win32/file'\n    class File\n      def lstat\n        self.stat\n      end\n    end\n\n\n- Attempting to use the Gibberish plugin (used by the Beast forum app) \n  will break right_aws as well as lots of other code.  Gibberish\n  changes the semantics of core Ruby (specifically, the String class) and thus presents a reliability\n  problem for most Ruby programs.\n\n- 2/11/08: If you use RightAws in conjunction with attachment_fu, the\n  right_aws gem must be included (using the require statement) AFTER\n  attachment_fu.  If right_aws is loaded before attachment_fu, you'll\n  encounter errors similar to:\n\n  s3.amazonaws.com temporarily unavailable: (wrong number of arguments (5 for 4))\n\n  or\n\n  'incompatible Net::HTTP monkey-patch'\n\n  This is due to a conflict between the right_http_connection gem and another\n  gem required by attachment_fu.\n\n- 8/07: Amazon has changed the semantics of the SQS service.  A\n  new queue may not be created within 60 seconds of the destruction of any\n  older queue with the same name.  Certain methods of RightAws::Sqs and\n  RightAws::SqsInterface will fail with the message:\n  \"AWS.SimpleQueueService.QueueDeletedRecently: You must wait 60 seconds after deleting a queue before you can create another with the same name.\"\n  \n== REQUIREMENTS:\n\nRightAws requires REXML and the right_http_connection gem.\nIf libxml and its Ruby bindings (distributed in the libxml-ruby gem) are\npresent, RightAws can be configured to use them:\n  RightAws::RightAWSParser.xml_lib = 'libxml'\nAny error with the libxml installation will result in RightAws failing-safe to\nREXML parsing.\n\n== INSTALL:\n\nsudo gem install right_aws\n\n== LICENSE:\n\nCopyright (c) 2007-2013 RightScale, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Rakefile",
    "content": "# -*- ruby -*-\n\nrequire 'rubygems'\nrequire \"rake/testtask\"\nrequire 'rubygems/package_task'\nrequire 'rake/clean'\n$: << File.dirname(__FILE__)\ntestglobs =     [\"test/ts_right_aws.rb\"]\n\nbegin\n  require 'bundler'\nrescue LoadError => e\n  STDERR.puts(\"Bundler is not available, some rake tasks will not be defined: #{e.message}\")\nelse\n  Bundler::GemHelper.install_tasks :name => 'right_aws'\nend\n\n# == Gem == #\n\ngemtask = Gem::PackageTask.new(Gem::Specification.load(\"right_aws.gemspec\")) do |package|\n  package.package_dir = ENV['PACKAGE_DIR'] || 'pkg'\n  package.need_zip = true\n  package.need_tar = true\nend\n\ndirectory gemtask.package_dir\n\nCLEAN.include(gemtask.package_dir)\n\ndesc \"Test just the SQS interface\"\ntask :testsqs do\n  require 'test/test_credentials'\n  require 'test/http_connection'\n  TestCredentials.get_credentials\n  require 'test/sqs/test_right_sqs.rb'\nend\n\ndesc \"Test just the second generation SQS interface\"\ntask :testsqs2 do\n  require 'test/test_credentials'\n  require 'test/http_connection'\n  TestCredentials.get_credentials\n  require 'test/sqs/test_right_sqs_gen2.rb'\nend\n\ndesc \"Test just the S3 interface\"\ntask :tests3 do\n  require 'test/test_credentials'\n  require 'test/http_connection'\n  TestCredentials.get_credentials\n  require 'test/s3/test_right_s3.rb'\nend\n\ndesc \"Test just the S3 interface using local stubs\"\ntask :tests3local do\n  require 'test/test_credentials'\n  require 'test/http_connection'\n  TestCredentials.get_credentials\n  require 'test/s3/test_right_s3_stubbed.rb'\nend\n\ndesc \"Test just the EC2 interface\"\ntask :testec2 do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/ec2/test_right_ec2.rb'\nend\n\ndesc \"Test just the SDB interface\"\ntask :testsdb do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/sdb/test_right_sdb.rb'\nend\n\ndesc \"Test active SDB interface\"\ntask :testactivesdb do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/sdb/test_active_sdb.rb'\nend\n\ndesc \"Test CloudFront interface\"\ntask :testacf do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/acf/test_right_acf.rb'\nend\n\ndesc \"Test RDS interface\"\ntask :testrds do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/rds/test_right_rds.rb'\nend\n\ndesc \"Test just the SNS interface\"\ntask :testsns do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/sns/test_right_sns.rb'\nend\n\ndesc \"Test Route 53 interface\"\ntask :testroute53 do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/route_53/test_right_route_53'\nend\n\ndesc \"Test ELB interface\"\ntask :testelb do\n  require 'test/test_credentials'\n  TestCredentials.get_credentials\n  require 'test/elb/test_right_elb'\nend\n\n# vim: syntax=Ruby\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3.8\"\nservices:\n  app:\n    build: .\n    command: bash -c \"bundle exec rake -T\"\n    volumes:\n      - .:/right_aws\n"
  },
  {
    "path": "lib/acf/right_acf_interface.rb",
    "content": "#\n# Copyright (c) 2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\nmodule RightAws\n\n  # = RightAws::AcfInterface -- RightScale Amazon's CloudFront interface\n  # The AcfInterface class provides a complete interface to Amazon's\n  # CloudFront service.\n  #\n  # For explanations of the semantics of each call, please refer to\n  # Amazon's documentation at\n  # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=211\n  #\n  # Example:\n  #\n  #  acf = RightAws::AcfInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX')\n  #\n  #  list = acf.list_distributions #=>\n  #    [{:status             => \"Deployed\",\n  #      :domain_name        => \"d74zzrxmpmygb.6hops.net\",\n  #      :aws_id             => \"E4U91HCJHGXVC\",\n  #      :s3_origin          => {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n  #      :cnames             => [\"x1.my-awesome-site.net\", \"x1.my-awesome-site.net\"]\n  #      :comment            => \"My comments\",\n  #      :last_modified_time => \"2008-09-10T17:00:04.000Z\" }, ..., {...} ]\n  #\n  #  distibution = list.first\n  #\n  #  info = acf.get_distribution(distibution[:aws_id]) #=>\n  #    {:last_modified_time=>\"2010-05-19T18:54:38.242Z\",\n  #     :status=>\"Deployed\",\n  #     :domain_name=>\"dpzl38cuix402.cloudfront.net\",\n  #     :caller_reference=>\"201005181943052207677116\",\n  #     :e_tag=>\"EJSXFGM5JL8ER\",\n  #     :s3_origin=>\n  #      {:dns_name=>\"bucket-for-konstantin-eu.s3.amazonaws.com\",\n  #       :origin_access_identity=>\n  #        \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n  #     :aws_id=>\"E5P8HQ3ZAZIXD\",\n  #     :enabled=>false}\n  #\n  #  config = acf.get_distribution_config(distibution[:aws_id]) #=>\n  #    {:enabled          => true,\n  #     :caller_reference => \"200809102100536497863003\",\n  #     :e_tag            => \"E39OHHU1ON65SI\",\n  #     :cnames           => [\"web1.my-awesome-site.net\", \"web2.my-awesome-site.net\"]\n  #     :comment          => \"Woo-Hoo!\",\n  #     :s3_origin        => {:dns_name => \"my-bucket.s3.amazonaws.com\"}}\n  #\n  #  config[:comment] = 'Olah-lah!'\n  #  config[:enabled] = false\n  #  config[:cnames] << \"web3.my-awesome-site.net\"\n  #\n  #  acf.set_distribution_config(distibution[:aws_id], config) #=> true\n  #\n  class AcfInterface < RightAwsBase\n    \n    include RightAwsBaseInterface\n\n    API_VERSION      = \"2010-11-01\"\n    DEFAULT_HOST     = 'cloudfront.amazonaws.com'\n    DEFAULT_PORT     = 443\n    DEFAULT_PROTOCOL = 'https'\n    DEFAULT_PATH     = '/'\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to a CloudFront account. All handles share the same per process or per thread\n    # HTTP connection to CloudFront. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://cloudfront.amazonaws.com'\n    # * <tt>:server</tt>: CloudFront service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: CloudFront service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    #\n    #  acf = RightAws::AcfInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX',\n    #    {:logger => Logger.new('/tmp/x.log')}) #=>  #<RightAws::AcfInterface::0xb7b3c30c>\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'ACF',\n             :default_host        => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['ACF_URL'] ? URI.parse(ENV['ACF_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['ACF_API_VERSION'] || API_VERSION },\n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n\n    #-----------------------------------------------------------------\n    #      Requests\n    #-----------------------------------------------------------------\n\n    # Generates request hash for REST API.\n    def generate_request(method, path, params={}, body=nil, headers={})  # :nodoc:\n      # Params\n      params.delete_if{ |key, val| val.right_blank? }\n      unless params.right_blank?\n        path += \"?\" + params.to_a.collect{ |key,val| \"#{AwsUtils::amz_escape(key)}=#{AwsUtils::amz_escape(val.to_s)}\" }.join(\"&\")\n      end\n      # Headers\n      headers = AwsUtils::fix_headers(headers)\n      headers['content-type'] ||= 'text/xml' if body\n      headers['date'] = Time.now.httpdate\n      # Auth\n      signature = AwsUtils::sign(@aws_secret_access_key, headers['date'])\n      headers['Authorization'] = \"AWS #{@aws_access_key_id}:#{signature}\"\n      # Request\n      path    = \"#{@params[:service]}#{@params[:api_version]}/#{path}\"\n      request = \"Net::HTTP::#{method.capitalize}\".right_constantize.new(path)\n      request.body = body if body\n      # Set request headers\n      headers.each { |key, value| request[key.to_s] = value }\n      # prepare output hash\n      { :request  => request, \n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n      end\n      \n      # Sends request to Amazon and parses the response.\n      # Raises AwsError if any banana happened.\n    def request_info(request, parser, &block) # :nodoc:\n      request_info_impl(:acf_connection, @@bench, request, parser, &block)\n    end\n\n    #-----------------------------------------------------------------\n    #      Helpers:\n    #-----------------------------------------------------------------\n\n    def generate_call_reference # :nodoc:\n      result = Time.now.strftime('%Y%m%d%H%M%S')\n      10.times{ result << rand(10).to_s }\n      result\n    end\n\n    def merge_headers(hash) # :nodoc:\n      hash[:location] = @last_response['Location'] if @last_response['Location']\n      hash[:e_tag]    = @last_response['ETag']     if @last_response['ETag']\n      hash\n    end\n\n    def distribution_config_to_xml(config, xml_wrapper='DistributionConfig') # :nodoc:\n      cnames = logging = trusted_signers = s3_origin = custom_origin = default_root_object = ''\n      # CNAMES\n      unless config[:cnames].right_blank?\n        Array(config[:cnames]).each { |cname| cnames += \"  <CNAME>#{cname}</CNAME>\\n\" }\n      end\n      # Logging\n      unless config[:logging].right_blank?\n        logging = \"  <Logging>\\n\" +\n                  \"    <Bucket>#{config[:logging][:bucket]}</Bucket>\\n\" +\n                  \"    <Prefix>#{config[:logging][:prefix]}</Prefix>\\n\" +\n                  \"  </Logging>\\n\"\n      end\n      unless config[:required_protocols].right_blank?\n        required_protocols = \"  <RequiredProtocols>\\n\" +\n                             \"    <Protocol>#{config[:required_protocols]}</Protocol>\\n\" +\n                             \"  </RequiredProtocols>\\n\"\n      else required_protocols = \"\"\n      end\n      # Default Root Object\n      unless config[:default_root_object].right_blank?\n        default_root_object = \"  <DefaultRootObject>#{config[:default_root_object]}</DefaultRootObject>\\n\" unless config[:default_root_object].right_blank?\n      end\n      # Trusted Signers\n      unless config[:trusted_signers].right_blank?\n        trusted_signers = \"  <TrustedSigners>\\n\"\n        Array(config[:trusted_signers]).each do |trusted_signer|\n          trusted_signers += if trusted_signer.to_s[/self/i]\n                               \"    <Self/>\\n\"\n                             else\n                               \"    <AwsAccountNumber>#{trusted_signer}</AwsAccountNumber>\\n\"\n                             end\n        end\n        trusted_signers += \"  </TrustedSigners>\\n\"\n      end\n      # S3Origin\n      unless config[:s3_origin].right_blank?\n        origin_access_identity = ''\n        # Origin Access Identity\n        unless config[:s3_origin][:origin_access_identity].right_blank?\n          origin_access_identity = config[:s3_origin][:origin_access_identity]\n          unless origin_access_identity[%r{^origin-access-identity}]\n            origin_access_identity = \"origin-access-identity/cloudfront/#{origin_access_identity}\"\n          end\n          origin_access_identity = \"    <OriginAccessIdentity>#{origin_access_identity}</OriginAccessIdentity>\\n\"\n        end\n        s3_origin = \"  <S3Origin>\\n\" +\n                    \"    <DNSName>#{config[:s3_origin][:dns_name]}</DNSName>\\n\" +\n                    \"#{origin_access_identity}\" +\n                    \"  </S3Origin>\\n\"\n      end\n      # Custom Origin\n      unless config[:custom_origin].right_blank?\n        http_port = https_port = origin_protocol_policy = ''\n        http_port              = \"    <HTTPPort>#{config[:custom_origin][:http_port]}</HTTPPort>\\n\"                                      unless config[:custom_origin][:http_port].right_blank?\n        https_port             = \"    <HTTPSPort>#{config[:custom_origin][:https_port]}</HTTPSPort>\"                                     unless config[:custom_origin][:https_port].right_blank?\n        origin_protocol_policy = \"    <OriginProtocolPolicy>#{config[:custom_origin][:origin_protocol_policy]}</OriginProtocolPolicy>\\n\" unless config[:custom_origin][:origin_protocol_policy].right_blank?\n        custom_origin = \"  <CustomOrigin>\\n\" +\n                        \"    <DNSName>#{config[:custom_origin][:dns_name]}</DNSName>\\n\" +\n                        \"#{http_port}\" +\n                        \"#{https_port}\" +\n                        \"#{origin_protocol_policy}\" +\n                        \"  </CustomOrigin>\\n\"\n      end\n      # XML\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\"                                +\n      \"<#{xml_wrapper} xmlns=\\\"http://#{@params[:server]}/doc/#{API_VERSION}/\\\">\\n\" +\n      \"  <CallerReference>#{config[:caller_reference]}</CallerReference>\\n\"         +\n      \"  <Comment>#{AwsUtils::xml_escape(config[:comment].to_s)}</Comment>\\n\"       +\n      \"  <Enabled>#{config[:enabled]}</Enabled>\\n\" +\n      s3_origin           +\n      custom_origin       +\n      default_root_object +\n      cnames              +\n      logging             +\n      required_protocols  +\n      trusted_signers     +\n      \"</#{xml_wrapper}>\"\n    end\n\n    #-----------------------------------------------------------------\n    #      API Calls:\n    #-----------------------------------------------------------------\n\n    # List all distributions.\n    # Returns an array of distributions or RightAws::AwsError exception.\n    #\n    #  acf.list_distributions #=>\n    #    [{:status=>\"Deployed\",\n    #      :domain_name=>\"dgmde.6os.net\",\n    #      :comment=>\"ONE LINE OF COMMENT\",\n    #      :last_modified_time=>\"2009-06-16T16:10:02.210Z\",\n    #      :s3_origin=>{:dns_name=>\"example.s3.amazonaws.com\"},\n    #      :aws_id=>\"12Q05OOMFN7SYL\",\n    #      :enabled=>true}, ... ]\n    #\n    def list_distributions\n      result = []\n      incrementally_list_distributions do |response|\n        result += response[:distributions]\n        true\n      end\n      result\n    end\n\n    # Incrementally list distributions.\n    # \n    # Optional params: +:marker+ and +:max_items+.\n    #\n    #   # get first distribution\n    #   incrementally_list_distributions(:max_items => 1) #=>\n    #      {:distributions=>\n    #        [{:status=>\"Deployed\",\n    #          :aws_id=>\"E2Q0AOOMFNPSYL\",\n    #          :s3_origin=>{:dns_name=>\"example.s3.amazonaws.com\"},\n    #          :domain_name=>\"d1s5gmdtmafnre.6hops.net\",\n    #          :comment=>\"ONE LINE OF COMMENT\",\n    #          :last_modified_time=>\"2008-10-22T19:31:23.000Z\",\n    #          :enabled=>true,\n    #          :cnames=>[]}],\n    #       :is_truncated=>true,\n    #       :max_items=>1,\n    #       :marker=>\"\",\n    #       :next_marker=>\"E2Q0AOOMFNPSYL\"}\n    #\n    #   # get max 100 distributions (the list will be restricted by a default MaxItems value ==100 )\n    #   incrementally_list_distributions\n    #\n    #   # list distributions by 10\n    #   incrementally_list_distributions(:max_items => 10) do |response|\n    #     puts response.inspect # a list of 10 distributions\n    #     true # return false if the listing should be broken otherwise use true\n    #   end\n    #\n    def incrementally_list_distributions(params={}, &block)\n      opts = {}\n      opts['MaxItems'] = params[:max_items] if params[:max_items]\n      opts['Marker']   = params[:marker]    if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request('GET', 'distribution', opts)\n        last_response = request_info(link,  AcfDistributionListParser.new(:logger => @logger))\n        opts['Marker'] = last_response[:next_marker]\n        break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?\n      end \n      last_response \n    end\n\n    # Create a new distribution.\n    # Returns the just created distribution or RightAws::AwsError exception.\n    #\n    #  # S3 Origin\n    #\n    #  config =  { :comment   => \"kd: delete me please\",\n    #              :s3_origin => { :dns_name               => \"devs-us-east.s3.amazonaws.com\",\n    #                              :origin_access_identity => \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n    #              :enabled   => true,\n    #              :logging   => { :prefix => \"kd/log/\",\n    #                              :bucket => \"devs-us-west.s3.amazonaws.com\"}}\n    #  acf.create_distribution(config) #=>\n    #    { :status=>\"InProgress\",\n    #      :enabled=>true,\n    #      :caller_reference=>\"201012071910051044304704\",\n    #      :logging=>{:prefix=>\"kd/log/\", :bucket=>\"devs-us-west.s3.amazonaws.com\"},\n    #      :e_tag=>\"ESCTG5WJCFWJK\",\n    #      :location=> \"https://cloudfront.amazonaws.com/2010-11-01/distribution/E3KUBANZ7N1B2\",\n    #      :comment=>\"kd: delete me please\",\n    #      :domain_name=>\"d3stykk6upgs20.cloudfront.net\",\n    #      :aws_id=>\"E3KUBANZ7N1B2\",\n    #      :s3_origin=>\n    #        {:origin_access_identity=>\"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\",\n    #         :dns_name=>\"devs-us-east.s3.amazonaws.com\"},\n    #      :last_modified_time=>\"2010-12-07T16:10:07.087Z\",\n    #      :in_progress_invalidation_batches=>0}\n    #\n    #  # Custom Origin\n    #\n    #    custom_config = { :comment       => \"kd: delete me please\",\n    #                      :custom_origin => { :dns_name  => \"custom_origin.my-site.com\",\n    #                                          :http_port => 80,\n    #                                          :https_port => 443,\n    #                                          :origin_protocol_policy => 'match-viewer' },\n    #                      :enabled       => true,\n    #                      :logging       => { :prefix => \"kd/log/\",\n    #                                          :bucket => \"my-bucket.s3.amazonaws.com\"}} #=>\n    #    { :last_modified_time=>\"2010-12-08T14:23:43.522Z\",\n    #      :status=>\"InProgress\",\n    #      :custom_origin=>\n    #        {:http_port=>\"80\",\n    #        :https_port=>\"443\",\n    #        :origin_protocol_policy=>\"match-viewer\",\n    #        :dns_name=>\"custom_origin.my-site.com\"},\n    #      :enabled=>true,\n    #      :caller_reference=>\"201012081723428499167245\",\n    #      :in_progress_invalidation_batches=>0,\n    #      :e_tag=>\"E1ZCJ8N5E52KO6\",\n    #      :location=>\n    #        \"https://cloudfront.amazonaws.com/2010-11-01/distribution/EK0AJ4RMNIF2P\",\n    #      :logging=>{:prefix=>\"kd/log/\", :bucket=>\"my-bucket.s3.amazonaws.com\"},\n    #      :domain_name=>\"do36k7s2wxklg.cloudfront.net\",\n    #      :comment=>\"kd: delete me please\",\n    #      :aws_id=>\"EK0AJ4RMNIF2P\"}\n    #\n    def create_distribution(config)\n      config[:caller_reference] ||= generate_call_reference\n      link = generate_request('POST', 'distribution', {}, distribution_config_to_xml(config))\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n    alias_method :create_distribution_by_config, :create_distribution\n\n    # Get a distribution's information.\n    # Returns a distribution's information or RightAws::AwsError exception.\n    #\n    #  acf.get_distribution('E2REJM3VUN5RSI') #=>\n    #    {:last_modified_time=>\"2010-05-19T18:54:38.242Z\",\n    #     :status=>\"Deployed\",\n    #     :domain_name=>\"dpzl38cuix402.cloudfront.net\",\n    #     :caller_reference=>\"201005181943052207677116\",\n    #     :e_tag=>\"EJSXFGM5JL8ER\",\n    #     :s3_origin=>\n    #      {:dns_name=>\"bucket-for-konstantin-eu.s3.amazonaws.com\",\n    #       :origin_access_identity=>\n    #        \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n    #     :aws_id=>\"E5P8HQ3ZAZIXD\",\n    #     :enabled=>false}\n    #\n    #  acf.get_distribution('E2FNSBHNVVF11E') #=>\n    #    {:e_tag=>\"E1Q2DJEPTQOLJD\",\n    #     :status=>\"InProgress\",\n    #     :last_modified_time=>\"2010-04-17T17:24:25.000Z\",\n    #     :cnames=>[\"web1.my-awesome-site.net\", \"web2.my-awesome-site.net\"],\n    #     :aws_id=>\"E2FNSBHNVVF11E\",\n    #     :logging=>{:prefix=>\"xlog/\", :bucket=>\"my-bucket.s3.amazonaws.com\"},\n    #     :enabled=>true,\n    #     :active_trusted_signers=>\n    #      [{:aws_account_number=>\"120288270000\",\n    #        :key_pair_ids=>[\"APKAJTD5OHNDX0000000\", \"APKAIK74BJWCL0000000\"]},\n    #       {:aws_account_number=>\"self\"},\n    #       {:aws_account_number=>\"648772220000\"}],\n    #     :caller_reference=>\"201004171154450740700072\",\n    #     :domain_name=>\"d1f6lpevremt5m.cloudfront.net\",\n    #     :s3_origin=>\n    #      {:dns_name=>\"bucket-for-konstantin-eu.s3.amazonaws.com\",\n    #       :origin_access_identity=>\n    #        \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n    #     :trusted_signers=>[\"self\", \"648772220000\", \"120288270000\"]}\n    #\n    def get_distribution(aws_id)\n      link = generate_request('GET', \"distribution/#{aws_id}\")\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n\n    # Get a distribution's configuration.\n    # Returns a distribution's configuration or RightAws::AwsError exception.\n    #\n    #  acf.get_distribution_config('E2REJM3VUN5RSI') #=>\n    #    {:caller_reference=>\"201005181943052207677116\",\n    #     :e_tag=>\"EJSXFGM5JL8ER\",\n    #     :s3_origin=>\n    #      {:dns_name=>\"bucket-for-konstantin-eu.s3.amazonaws.com\",\n    #       :origin_access_identity=>\n    #        \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n    #     :enabled=>false}\n    #\n    #  acf.get_distribution_config('E2FNSBHNVVF11E') #=>\n    #    {:e_tag=>\"E1Q2DJEPTQOLJD\",\n    #     :logging=>{:prefix=>\"xlog/\", :bucket=>\"my-bucket.s3.amazonaws.com\"},\n    #     :enabled=>true,\n    #     :caller_reference=>\"201004171154450740700072\",\n    #     :trusted_signers=>[\"self\", \"648772220000\", \"120288270000\"],\n    #     :s3_origin=>\n    #      {:dns_name=>\"bucket-for-konstantin-eu.s3.amazonaws.com\",\n    #       :origin_access_identity=>\n    #        \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"}}\n    #\n    def get_distribution_config(aws_id)\n      link = generate_request('GET', \"distribution/#{aws_id}/config\")\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n\n    # Set a distribution's configuration \n    # Returns +true+ on success or RightAws::AwsError exception.\n    #\n    #  config = acf.get_distribution_config('E2REJM3VUN5RSI') #=>\n    #    {:enabled          => true,\n    #     :caller_reference => \"200809102100536497863003\",\n    #     :e_tag            => \"E39OHHU1ON65SI\",\n    #     :cnames           => [\"web1.my-awesome-site.net\", \"web2.my-awesome-site.net\"]\n    #     :comment          => \"Woo-Hoo!\",\n    #     :s3_origin        => { :dns_name => \"my-bucket.s3.amazonaws.com\"}}\n    #\n    #  config[:comment]                = 'Olah-lah!'\n    #  config[:enabled]                = false\n    #  config[:s3_origin][:origin_access_identity] = \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"\n    #  # or just\n    #  # config[:s3_origin][:origin_access_identity] = \"E3JPJZ80ZBX24G\"\n    #  config[:trusted_signers]        = ['self', '648772220000', '120288270000']\n    #  config[:logging]                = { :bucket => 'my-bucket.s3.amazonaws.com', :prefix => 'xlog/' }\n    #  \n    #  acf.set_distribution_config('E2REJM3VUN5RSI', config) #=> true\n    #\n    def set_distribution_config(aws_id, config)\n      link = generate_request('PUT', \"distribution/#{aws_id}/config\", {}, distribution_config_to_xml(config),\n                                     'If-Match' => config[:e_tag])\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Delete a distribution. The enabled distribution cannot be deleted.\n    # Returns +true+ on success or RightAws::AwsError exception.\n    #\n    #  acf.delete_distribution('E2REJM3VUN5RSI', 'E39OHHU1ON65SI') #=> true\n    #\n    def delete_distribution(aws_id, e_tag)\n      link = generate_request('DELETE', \"distribution/#{aws_id}\", {}, nil,\n                                        'If-Match' => e_tag)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class AcfDistributionListParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :distributions => [] }\n      end\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/Signer$}\n          @active_signer = {}\n        when %r{(Streaming)?DistributionSummary$},\n             %r{^(Streaming)?Distribution$},\n             %r{^(Streaming)?DistributionConfig$}\n          @distribution = { }\n        when %r{/S3Origin$}     then @distribution[:s3_origin] = {}\n        when %r{/CustomOrigin$} then @distribution[:custom_origin] = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'           then @result[:marker]       = @text\n        when 'NextMarker'       then @result[:next_marker]  = @text\n        when 'MaxItems'         then @result[:max_items]    = @text.to_i\n        when 'IsTruncated'      then @result[:is_truncated] = (@text == 'true')\n        when 'Id'               then @distribution[:aws_id]                    = @text\n        when 'Status'           then @distribution[:status]                    = @text\n        when 'LastModifiedTime' then @distribution[:last_modified_time]        = @text\n        when 'DomainName'       then @distribution[:domain_name]               = @text\n        when 'Comment'          then @distribution[:comment]                   = AwsUtils::xml_unescape(@text)\n        when 'CallerReference'  then @distribution[:caller_reference]          = @text\n        when 'CNAME'            then (@distribution[:cnames] ||= [])          << @text\n        when 'Enabled'          then @distribution[:enabled]                   = (@text == 'true')\n        when 'Bucket'           then (@distribution[:logging] ||= {})[:bucket] = @text\n        when 'Prefix'           then (@distribution[:logging] ||= {})[:prefix] = @text\n        when 'Protocol'         then (@distribution[:required_protocols] ||= {})[:protocol]        = @text\n        when 'InProgressInvalidationBatches' then @distribution[:in_progress_invalidation_batches] = @text.to_i\n        when 'DefaultRootObject'             then @distribution[:default_root_object]              = @text\n        else\n          case full_tag_name\n          when %r{/S3Origin/DNSName$}                  then @distribution[:s3_origin][:dns_name]                   = @text\n          when %r{/S3Origin/OriginAccessIdentity$}     then @distribution[:s3_origin][:origin_access_identity]     = @text\n          when %r{/CustomOrigin/DNSName$}              then @distribution[:custom_origin][:dns_name]               = @text\n          when %r{/CustomOrigin/HTTPPort}              then @distribution[:custom_origin][:http_port]              = @text\n          when %r{/CustomOrigin/HTTPSPort$}            then @distribution[:custom_origin][:https_port]             = @text\n          when %r{/CustomOrigin/OriginProtocolPolicy$} then @distribution[:custom_origin][:origin_protocol_policy] = @text\n          when %r{/TrustedSigners/Self$}               then (@distribution[:trusted_signers] ||= [])              << 'self'\n          when %r{/TrustedSigners/AwsAccountNumber$}   then (@distribution[:trusted_signers] ||= [])              << @text\n          when %r{/Signer/Self$}                       then @active_signer[:aws_account_number]                    = 'self'\n          when %r{/Signer/AwsAccountNumber$}           then @active_signer[:aws_account_number]                    = @text\n          when %r{/Signer/KeyPairId$}                  then (@active_signer[:key_pair_ids] ||= [])                << @text\n          when %r{/Signer$}                            then (@distribution[:active_trusted_signers] ||= [])       << @active_signer\n          when %r{(Streaming)?DistributionSummary$},\n               %r{^(Streaming)?Distribution$},\n               %r{^(Streaming)?DistributionConfig$}\n            @result[:distributions] << @distribution\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/acf/right_acf_invalidations.rb",
    "content": "#\n# Copyright (c) 2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\nmodule RightAws\n\n  class AcfInterface\n\n    # List Invalidations\n    # \n    #  acf.list_invalidations('E3LTBMK4EAQS7D') #=>\n    #    [{:status=>\"InProgress\", :aws_id=>\"I3AW9PPQS0CBKV\"},\n    #     {:status=>\"InProgress\", :aws_id=>\"I1HV23N5KD3XH9\"}]\n    #\n    def list_invalidations(distribution_aws_id)\n      result = []\n      incrementally_list_invalidations(distribution_aws_id) do |response|\n        result += response[:invalidations]\n        true\n      end\n      result\n    end\n\n    # Incrementally list Invalidations.\n    # Optional params: +:marker+ and +:max_items+.\n    #\n    def incrementally_list_invalidations(distribution_aws_id, params={}, &block)\n      opts = {}\n      opts['MaxItems'] = params[:max_items] if params[:max_items]\n      opts['Marker']   = params[:marker]    if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request('GET', \"distribution/#{distribution_aws_id}/invalidation\", opts)\n        last_response = request_info(link,  AcfInvalidationsListParser.new(:logger => @logger))\n        opts['Marker'] = last_response[:next_marker]\n        break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?\n      end\n      last_response\n    end\n\n    #-----------------------------------------------------------------\n    #      Origin Access Identity\n    #-----------------------------------------------------------------\n\n    # Create a new Invalidation batch.\n    #\n    #  acf.create_invalidation('E3LTBMK4EAQS7D', :path => ['/boot.jpg', '/kd/boot.public.1.jpg']) #=>\n    #    {:status=>\"InProgress\",\n    #     :create_time=>\"2010-12-08T14:03:38.449Z\",\n    #     :location=> \"https://cloudfront.amazonaws.com/2010-11-01/distribution/E3LTBMK4EAQS7D/invalidation/I3AW9PPQS0CBKV\",\n    #     :aws_id=>\"I3AW9PPQS0CBKV\",\n    #     :invalidation_batch=>\n    #      {:caller_reference=>\"201012081703372555972012\",\n    #       :path=>[\"/boot.jpg\", \"/kd/boot.public.1.jpg\"]}}\n    #\n    def create_invalidation(distribution_aws_id, invalidation_batch)\n      invalidation_batch[:caller_reference] ||= generate_call_reference\n      link = generate_request('POST', \"/distribution/#{distribution_aws_id}/invalidation\", {}, invalidation_batch_to_xml(invalidation_batch))\n      merge_headers(request_info(link, AcfInvalidationsListParser.new(:logger => @logger))[:invalidations].first)\n    end\n\n    # Get Invalidation\n    #\n    #  acf.get_invalidation('E3LTBMK4EAQS7D', 'I3AW9PPQS0CBKV') #=>\n    #    {:create_time=>\"2010-12-08T14:03:38.449Z\",\n    #     :status=>\"InProgress\",\n    #     :aws_id=>\"I3AW9PPQS0CBKV\",\n    #     :invalidation_batch=>\n    #      {:caller_reference=>\"201012081703372555972012\",\n    #       :path=>[\"/boot.jpg\", \"/kd/boot.public.1.jpg\"]}}\n    #\n    def get_invalidation(distribution_aws_id, aws_id)\n      link = generate_request('GET', \"distribution/#{distribution_aws_id}/invalidation/#{aws_id}\")\n      merge_headers(request_info(link, AcfInvalidationsListParser.new(:logger => @logger))[:invalidations].first)\n    end\n\n    #-----------------------------------------------------------------\n    #      Batch\n    #-----------------------------------------------------------------\n\n    def invalidation_batch_to_xml(invalidation_batch) # :nodoc:\n      paths = ''\n      Array(invalidation_batch[:path]).each do |path|\n        paths << \"  <Path>#{AwsUtils::xml_escape(path)}</Path>\\n\"\n      end\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" +\n      \"<InvalidationBatch xmlns=\\\"http://#{@params[:server]}/doc/#{API_VERSION}/\\\">\\n\" +\n      \"  <CallerReference>#{invalidation_batch[:caller_reference]}</CallerReference>\\n\" +\n      paths +\n      \"</InvalidationBatch>\"\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class AcfInvalidationsListParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :invalidations => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when %r{(InvalidationSummary|Invalidation)$} then @item = {}\n        when %r{InvalidationBatch}                   then @item[:invalidation_batch] = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'      then @result[:marker]       = @text\n        when 'NextMarker'  then @result[:next_marker]  = @text\n        when 'MaxItems'    then @result[:max_items]    = @text.to_i\n        when 'IsTruncated' then @result[:is_truncated] = (@text == 'true')\n        when 'Id'              then @item[:aws_id]                                = @text\n        when 'Status'          then @item[:status]                                = @text\n        when 'CreateTime'      then @item[:create_time]                           = @text\n        when 'Path'            then (@item[:invalidation_batch][:path] ||= [])   << @text\n        when 'CallerReference' then @item[:invalidation_batch][:caller_reference] = @text\n        when %r{(InvalidationSummary|Invalidation)$}\n          @item[:invalidation_batch][:path].sort! if @item[:invalidation_batch] && !@item[:invalidation_batch][:path].right_blank?\n          @result[:invalidations] << @item\n        end\n      end\n    end\n\n  end\nend"
  },
  {
    "path": "lib/acf/right_acf_origin_access_identities.rb",
    "content": "#\n# Copyright (c) 2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\nmodule RightAws\n  \n  class AcfInterface\n\n    # List Origin Access Identities.\n    #\n    #  acf.list_origin_access_identities #=>\n    #    [{:comment=>\"kd: TEST\",\n    #      :s3_canonical_user_id=>\n    #       \"c7ca36f6c5d384e60aeca02032ac748bae3c458c5322a2e279382935f1f71b16d9ac251f7f71f1ea91c37d3c214645b8\",\n    #      :aws_id=>\"E3TL4XWF5KTGH\"},\n    #     {:comment=>\"kd: TEST-2\",\n    #      :s3_canonical_user_id=>\n    #       \"9af7058b1d197c2c03fdcc3ddad07012a7822f5fc4a8156025409ffac646bdae4dc714820482c92e6988e5703c8d9954\",\n    #      :aws_id=>\"E3HJ7V8C3324VF\"},\n    #     {:comment=>\"MyTestAccessIdentity\",\n    #      :s3_canonical_user_id=>\n    #       \"de4361b33dbaf499d3d77159bfa1571d3451eaec25a2b16553de5e534da8089bb8c31a4898d73d1a658155d0e48872a7\",\n    #      :aws_id=>\"E3JPJZ80ZBX24G\"}]\n    #\n    def list_origin_access_identities\n      result = []\n      incrementally_list_origin_access_identities do |response|\n        result += response[:origin_access_identities]\n        true\n      end\n      result\n    end\n\n    # Incrementally list Origin Access Identities.\n    # Optional params: +:marker+ and +:max_items+.\n    #\n    #  acf.incrementally_list_origin_access_identities(:max_items => 2) #=>\n    #    {:origin_access_identities=>\n    #      [{:comment=>\"kd: TEST\",\n    #        :s3_canonical_user_id=>\n    #         \"c7ca36f6c5d384e60aeca02032ac748bae3c458c5322a2e279382935f1f71b16d9ac251f7f71f1ea91c37d3c214645b8\",\n    #        :aws_id=>\"E3TL4XWF5KTGH\"},\n    #       {:comment=>\"kd: TEST-2\",\n    #        :s3_canonical_user_id=>\n    #         \"9af7058b1d197c2c03fdcc3ddad07012a7822f5fc4a8156025409ffac646bdae4dc714820482c92e6988e5703c8d9954\",\n    #        :aws_id=>\"E3HJ7V8C3324VF\"}],\n    #     :is_truncated=>true,\n    #     :max_items=>2,\n    #     :marker=>\"\",\n    #     :next_marker=>\"E3HJ7V8C3324VF\"}\n    #\n    #   # get max 100 origin access identities (the list will be restricted by a default MaxItems value ==100 )\n    #   incrementally_list_origin_access_identities\n    #\n    #   # list origin access identities by 10\n    #   acf.incrementally_list_origin_access_identities(:max_items => 10) do |response|\n    #     puts response.inspect # a list of 10 distributions\n    #     true # return false if the listing should be broken otherwise use true\n    #   end\n    #\n    def incrementally_list_origin_access_identities(params={}, &block)\n      opts = {}\n      opts['MaxItems'] = params[:max_items] if params[:max_items]\n      opts['Marker']   = params[:marker]    if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request('GET', 'origin-access-identity/cloudfront', opts)\n        last_response = request_info(link,  AcfOriginAccesIdentitiesListParser.new(:logger => @logger))\n        opts['Marker'] = last_response[:next_marker]\n        break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?\n      end\n      last_response\n    end\n\n    #-----------------------------------------------------------------\n    #      Origin Access Identity\n    #-----------------------------------------------------------------\n\n    # Create a new CloudFront Origin Access Identity.\n    #\n    #  acf.create_origin_access_identity('MyTestAccessIdentity') #=>\n    #    {:e_tag=>\"E2QOKZEXCUWHJX\",\n    #     :comment=>\"MyTestAccessIdentity\",\n    #     :location=>\n    #       \"https://cloudfront.amazonaws.com/origin-access-identity/cloudfront/E3JPJZ80ZBX24G\",\n    #     :caller_reference=>\"201004161657467493031273\",\n    #     :s3_canonical_user_id=>\n    #       \"de4361b33dbaf499d3d77159bfa1571d3451eaec25a2b16553de5e534da8089bb8c31a4898d73d1a658155d0e48872a7\",\n    #     :aws_id=>\"E3JPJZ80ZBX24G\"}\n    #\n    def create_origin_access_identity(comment='', caller_reference=nil)\n      config = { :comment          => comment,\n                 :caller_reference => caller_reference }\n      create_origin_access_identity_by_config(config)\n    end\n\n    def create_origin_access_identity_by_config(config)\n      config[:caller_reference] ||= generate_call_reference\n      link = generate_request('POST', 'origin-access-identity/cloudfront', {}, origin_access_identity_config_to_xml(config))\n      merge_headers(request_info(link, AcfOriginAccesIdentitiesListParser.new(:logger => @logger))[:origin_access_identities].first)\n    end\n\n    # Get Origin Access Identity\n    #\n    #  acf.get_origin_access_identity('E3HJ7V8C3324VF') #=>\n    #    {:comment=>\"kd: TEST-2\",\n    #     :caller_reference=>\"201004161655035372351604\",\n    #     :aws_id=>\"E3HJ7V8C3324VF\",\n    #     :s3_canonical_user_id=>\n    #      \"9af7058b1d197c2c03fdcc3ddad07012a7822f5fc4a8156025409ffac646bdae4dc714820482c92e6988e5703c8d9954\",\n    #     :e_tag=>\"E309Q4IM450498\"}\n    #\n    def get_origin_access_identity(aws_id)\n      link = generate_request('GET', \"origin-access-identity/cloudfront/#{aws_id}\")\n      merge_headers(request_info(link, AcfOriginAccesIdentitiesListParser.new(:logger => @logger))[:origin_access_identities].first)\n    end\n\n    # Get Origin Access Identity\n    #\n    #  acf.get_origin_access_identity('E3HJ7V8C3324VF') #=>\n    #    {:comment=>\"kd: TEST-2\",\n    #     :caller_reference=>\"201004161655035372351604\",\n    #     :aws_id=>\"E3HJ7V8C3324VF\",\n    #     :s3_canonical_user_id=>\n    #      \"9af7058b1d197c2c03fdcc3ddad07012a7822f5fc4a8156025409ffac646bdae4dc714820482c92e6988e5703c8d9954\",\n    #     :e_tag=>\"E309Q4IM450498\"}\n    #\n    #  acf.delete_origin_access_identity(\"E3HJ7V8C3324VF\",\"E309Q4IM450498\") #=> true\n    #\n    def delete_origin_access_identity(aws_id, e_tag)\n      link = generate_request('DELETE', \"origin-access-identity/cloudfront/#{aws_id}\", {}, nil,\n                                        'If-Match' => e_tag)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Config\n    #-----------------------------------------------------------------\n\n    def origin_access_identity_config_to_xml(config) # :nodoc:\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" +\n      \"<CloudFrontOriginAccessIdentityConfig xmlns=\\\"http://#{@params[:server]}/doc/#{API_VERSION}/\\\">\\n\" +\n      \"  <CallerReference>#{config[:caller_reference]}</CallerReference>\\n\" +\n      \"  <Comment>#{AwsUtils::xml_escape(config[:comment].to_s)}</Comment>\\n\" +\n      \"</CloudFrontOriginAccessIdentityConfig>\"\n    end\n\n    # Get Origin Access Identity config\n    #\n    #  acf.get_origin_access_identity_config(\"E3JPJZ80ZBX24G\") #=>\n    #    {:comment=>\"MyTestAccessIdentity\",\n    #     :caller_reference=>\"201004161657467493031273\",\n    #     :e_tag=>\"E2QOKZEXCUWHJX\"}\n    #\n    def get_origin_access_identity_config(aws_id)\n      link = generate_request('GET', \"origin-access-identity/cloudfront/#{aws_id}/config\")\n      merge_headers(request_info(link, AcfOriginAccesIdentitiesListParser.new(:logger => @logger))[:origin_access_identities].first)\n    end\n\n    # Set Origin Access Identity config\n    #\n    #\n    #  acf.set_origin_access_identity_config(\"E2QOKZEXCUWHJX\",\n    #                                        :comment => \"MyBestOriginAccessConfig\",\n    #                                        :caller_reference => '01234567890',\n    #                                        :e_tag=>\"E2QOKZEXCUWHJX\") #=> true\n    #                                        \n    # P.S. This guy is not tested yet: http://developer.amazonwebservices.com/connect/thread.jspa?threadID=45256\n    def set_origin_access_identity_config(aws_id, config)\n      link = generate_request('PUT', \"origin-access-identity/cloudfront/#{aws_id}/config\", {}, origin_access_identity_config_to_xml(config),\n                                     'If-Match' => config[:e_tag])\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class AcfOriginAccesIdentitiesListParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :origin_access_identities => [] }\n      end\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{CloudFrontOriginAccessIdentitySummary$},\n             %r{^CloudFrontOriginAccessIdentity$},\n             %r{^CloudFrontOriginAccessIdentityConfig$}\n          @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'            then @result[:marker]       = @text\n        when 'NextMarker'        then @result[:next_marker]  = @text\n        when 'MaxItems'          then @result[:max_items]    = @text.to_i\n        when 'IsTruncated'       then @result[:is_truncated] = (@text == 'true')\n        when 'Id'                then @item[:aws_id]               = @text\n        when 'S3CanonicalUserId' then @item[:s3_canonical_user_id] = @text\n        when 'CallerReference'   then @item[:caller_reference]     = @text\n        when 'Comment'           then @item[:comment]              = AwsUtils::xml_unescape(@text)\n        end\n        case full_tag_name\n        when %r{CloudFrontOriginAccessIdentitySummary$},\n             %r{^CloudFrontOriginAccessIdentity$},\n             %r{^CloudFrontOriginAccessIdentityConfig$}\n          @result[:origin_access_identities] << @item\n        end\n      end\n    end\n\n  end\nend"
  },
  {
    "path": "lib/acf/right_acf_streaming_interface.rb",
    "content": "#\n# Copyright (c) 2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\nmodule RightAws\n  \n  class AcfInterface\n    \n    def streaming_distribution_config_to_xml(config) # :nodoc:\n      distribution_config_to_xml(config, 'StreamingDistributionConfig')\n    end\n\n    #-----------------------------------------------------------------\n    #      API Calls:\n    #-----------------------------------------------------------------\n\n    # List all streaming distributions.\n    # Returns an array of distributions or RightAws::AwsError exception.\n    #\n    #  acf.list_streaming_distributions #=>\n    #    [{:status=>\"Deployed\",\n    #      :aws_id=>\"E3CWE2Z9USOS6B\",\n    #      :enabled=>true,\n    #      :domain_name=>\"s2jz1ourvss1fj.cloudfront.net\",\n    #      :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #      :last_modified_time=>\"2010-04-19T08:53:32.574Z\",\n    #      :comment=>\"Woo-Hoo!\",\n    #      :cnames=>[\"stream.web.my-awesome-site.net\"]},\n    #      ...\n    #     {:status=>\"Deployed\",\n    #      :aws_id=>\"E3NPQZY4LKAYQ8\",\n    #      :enabled=>true,\n    #      :domain_name=>\"sw9nrsq9pudk3.cloudfront.net\",\n    #      :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #      :last_modified_time=>\"2010-04-19T08:59:09.600Z\",\n    #      :comment=>\"Woo-Hoo!\",\n    #      :cnames=>[\"stream-6.web.my-awesome-site.net\"]}]\n    #\n    def list_streaming_distributions\n      result = []\n      incrementally_list_streaming_distributions do |response|\n        result += response[:distributions]\n        true\n      end\n      result\n    end\n\n    # Incrementally list streaming distributions.\n    #\n    # Optional params: +:marker+ and +:max_items+.\n    #\n    #   # get first streaming distribution\n    #   incrementally_list_distributions(:max_items => 1) #=>\n    #    {:marker=>\"\",\n    #     :next_marker=>\"E3CWE2Z9USOS6B\",\n    #     :distributions=>\n    #      [{:status=>\"Deployed\",\n    #        :cnames=>[\"stream.web.my-awesome-site.net\"],\n    #        :aws_id=>\"E3CWE2Z9USOS6B\",\n    #        :enabled=>true,\n    #        :last_modified_time=>\"2010-04-19T08:53:32.574Z\",\n    #        :domain_name=>\"s2jz1ourvss1fj.cloudfront.net\",\n    #        :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #        :comment=>\"Woo-Hoo!\"}],\n    #     :max_items=>1,\n    #     :is_truncated=>true}\n    #\n    #   # get max 100 streaming distributions (the list will be restricted by a default MaxItems value ==100 )\n    #   incrementally_list_streaming_distributions\n    #\n    #   # list streaming distributions by 10\n    #   incrementally_list_streaming_distributions(:max_items => 10) do |response|\n    #     puts response.inspect # a list of 10 distributions\n    #     true # return false if the listing should be broken otherwise use true\n    #   end\n    #\n    def incrementally_list_streaming_distributions(params={}, &block)\n      opts = {}\n      opts['MaxItems'] = params[:max_items] if params[:max_items]\n      opts['Marker']   = params[:marker]    if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request('GET', 'streaming-distribution', opts)\n        last_response = request_info(link,  AcfDistributionListParser.new(:logger => @logger))\n        opts['Marker'] = last_response[:next_marker]\n        break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?\n      end\n      last_response\n    end\n\n    # Create a new streaming distribution.\n    # Returns the just created distribution or RightAws::AwsError exception.\n    #\n    #  acf.create_streaming_distribution('bucket-for-konstantin-00.s3.amazonaws.com', 'Woo-Hoo!', true,\n    #                                    ['stream-1.web.my-awesome-site.net']) #=>\n    #    {:status=>\"InProgress\",\n    #     :caller_reference=>\"201004191254412191173215\",\n    #     :cnames=>[\"stream-1.web.my-awesome-site.net\"],\n    #     :aws_id=>\"E1M5LERJLU636F\",\n    #     :e_tag=>\"E2588L5QL4BLXH\",\n    #     :enabled=>true,\n    #     :domain_name=>\"s1di8imd85wgld.cloudfront.net\",\n    #     :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #     :last_modified_time=>Mon Apr 19 08:54:42 UTC 2010,\n    #     :location=>\n    #      \"https://cloudfront.amazonaws.com/streaming-distribution/E1M5LERJLU636F\",\n    #     :comment=>\"Woo-Hoo!\"}\n    #\n    def create_streaming_distribution(config)\n      config[:caller_reference] ||= generate_call_reference\n      link = generate_request('POST', 'streaming-distribution', {}, streaming_distribution_config_to_xml(config))\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n    alias_method :create_streaming_distribution_by_config, :create_streaming_distribution\n\n    # Get a streaming distribution's information.\n    # Returns a distribution's information or RightAws::AwsError exception.\n    #\n    #  acf.get_streaming_distribution('E3CWE2Z9USOS6B') #=>\n    #    {:status=>\"Deployed\",\n    #     :e_tag=>\"EXTZ2SXAQT39K\",\n    #     :cnames=>[\"stream.web.my-awesome-site.net\"],\n    #     :aws_id=>\"E3CWE2Z9USOS6B\",\n    #     :enabled=>true,\n    #     :domain_name=>\"s2jz1ourvss1fj.cloudfront.net\",\n    #     :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #     :last_modified_time=>\"2010-04-19T08:53:32.574Z\",\n    #     :comment=>\"Woo-Hoo!\",\n    #     :caller_reference=>\"201004191253311625537161\"}\n    #\n    #  acf.get_streaming_distribution('E1M5LERJLU636F') #=>\n    #    {:trusted_signers=>[\"self\", \"648772220000\", \"120288270000\"],\n    #     :status=>\"InProgress\",\n    #     :e_tag=>\"E2K6XD13RCJQ6E\",\n    #     :cnames=>[\"stream-1.web.my-awesome-site.net\"],\n    #     :active_trusted_signers=>\n    #      [{:key_pair_ids=>[\"APKAIK74BJWCLXZUMEJA\"],\n    #        :aws_account_number=>\"120288270000\"},\n    #       {:aws_account_number=>\"self\"},\n    #       {:aws_account_number=>\"648772220000\"}],\n    #     :aws_id=>\"E1M5LERJLU636F\",\n    #     :enabled=>false,\n    #     :domain_name=>\"s1di8imd85wgld.cloudfront.net\",\n    #     :s3_origin=> {\n    #       :dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\",\n    #       :origin_access_identity=>\"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"},\n    #     :last_modified_time=>\"2010-04-19T09:14:07.160Z\",\n    #     :comment=>\"Olah-lah!\",\n    #     :caller_reference=>\"201004191254412191173215\"}\n    #\n    def get_streaming_distribution(aws_id)\n      link = generate_request('GET', \"streaming-distribution/#{aws_id}\")\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n\n    # Get a streaming distribution's configuration.\n    # Returns a distribution's configuration or RightAws::AwsError exception.\n    #\n    #  acf.get_streaming_distribution_config('E1M5LERJLU636F') #=>\n    #    {:trusted_signers=>[\"self\", \"648772220000\", \"120288270000\"],\n    #     :e_tag=>\"E2K6XD13RCJQ6E\",\n    #     :cnames=>[\"stream-1.web.my-awesome-site.net\"],\n    #     :enabled=>false,\n    #     :s3_origin=> {\n    #       :dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\",\n    #       :origin_access_identity=>\"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\",},\n    #     :comment=>\"Olah-lah!\",\n    #     :caller_reference=>\"201004191254412191173215\"}\n    #\n    def get_streaming_distribution_config(aws_id)\n      link = generate_request('GET', \"streaming-distribution/#{aws_id}/config\")\n      merge_headers(request_info(link, AcfDistributionListParser.new(:logger => @logger))[:distributions].first)\n    end\n\n    # Set a streaming distribution's configuration\n    # Returns +true+ on success or RightAws::AwsError exception.\n    #\n    #  acf.get_streaming_distribution_config('E1M5LERJLU636F') #=>\n    #    {:e_tag=>\"E2588L5QL4BLXH\",\n    #     :cnames=>[\"stream-1.web.my-awesome-site.net\"],\n    #     :enabled=>true,\n    #     :s3_origin=> {:dns_name=>\"bucket-for-konstantin-00.s3.amazonaws.com\"},\n    #     :comment=>\"Woo-Hoo!\",\n    #     :caller_reference=>\"201004191254412191173215\"}\n    #\n    #  config[:comment]                            = 'Olah-lah!'\n    #  config[:enabled]                            = false\n    #  config[:s3_origin][:origin_access_identity] = \"origin-access-identity/cloudfront/E3JPJZ80ZBX24G\"\n    #  config[:trusted_signers]                    = ['self', '648772220000', '120288270000']\n    #\n    #  acf.set_distribution_config('E2REJM3VUN5RSI', config) #=> true\n    #\n    def set_streaming_distribution_config(aws_id, config)\n      link = generate_request('PUT', \"streaming-distribution/#{aws_id}/config\", {}, streaming_distribution_config_to_xml(config),\n                                     'If-Match' => config[:e_tag])\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Delete a streaming distribution. The enabled distribution cannot be deleted.\n    # Returns +true+ on success or RightAws::AwsError exception.\n    #\n    #  acf.delete_streaming_distribution('E1M5LERJLU636F', 'E2588L5QL4BLXH') #=> true\n    #\n    def delete_streaming_distribution(aws_id, e_tag)\n      link = generate_request('DELETE', \"streaming-distribution/#{aws_id}\", {}, nil,\n                                        'If-Match' => e_tag)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/acw/right_acw_interface.rb",
    "content": "#\n# Copyright (c) 2007-2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::AcwInterface -- RightScale Amazon Cloud Watch interface\n  # The RightAws::AcwInterface class provides a complete interface to Amazon Cloud Watch service.\n  #\n  # For explanations of the semantics of each call, please refer to Amazon's documentation at\n  # http://docs.amazonwebservices.com/AmazonCloudWatch/latest/DeveloperGuide/\n  #\n  class AcwInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    # Amazon ACW API version being used\n    API_VERSION       = \"2009-05-15\"\n    DEFAULT_HOST      = \"monitoring.amazonaws.com\"\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to an ACW account. All handles share the same per process or per thread\n    # HTTP connection to Amazon ACW. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://monitoring.amazonaws.com/'\n    # * <tt>:server</tt>: ACW service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: ACW service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    # * <tt>:cache</tt>: true/false(default): list_metrics\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'ACW',\n             :default_host        => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['ACW_URL'] ? URI.parse(ENV['ACW_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['ACW_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:ams_connection, @@bench, request, parser)\n    end\n\n    #-----------------------------------------------------------------\n    #      MetricStatistics\n    #-----------------------------------------------------------------\n\n    # Get time-series data for one or more statistics of given a Metric\n    # Returns a hash of stat data.\n    #\n    #  Options are:\n    #\n    #    :period       - x*60 seconds interval (where x > 0)\n    #    :statistics   - Average, Minimum. Maximum, Sum, Samples\n    #    :start_time   - The timestamp of the first datapoint to return, inclusive.\n    #    :end_time     - The timestamp to use for determining the last datapoint to return. This is the last datapoint to fetch, exclusive.\n    #    :namespace    - The namespace corresponding to the service of interest. For example, AWS/EC2 represents Amazon EC2.\n    #    :unit         - Seconds, Percent, Bytes, Bits, Count, Bytes/Second, Bits/Second, Count/Second, and None\n    #    :custom_unit  - The user-defined CustomUnit applied to a Measure. Please see the key term Unit.\n    #    \n    #    :dimentions\n    #      Dimensions for EC2 Metrics:\n    #      * ImageId              - shows the requested metric for all instances running this EC2 Amazon Machine Image(AMI)\n    #      * AvailabilityZone     - shows the requested metric for all instances running in that EC2 Availability Zone\n    #      * CapacityGroupName    - shows the requested metric for all instances in the specified capacity group - this dimension is\n    #                               only available for EC2 metrics when the instances are in an Amazon Automatic Scaling Service\n    #                               Capacity Group\n    #      * InstanceId           - shows the requested metric for only the identified instance\n    #      * InstanceType         - shows the requested metric for all instances running with that instance type\n    #      * Service (required)   - the name of the service that reported the monitoring data - for EC2 metrics, use \"EC2\"\n    #      * Namespace (required) - in private beta, the available metrics are all reported by AWS services, so set this to \"AWS\"\n    #      Dimensions for Load Balancing Metrics:\n    #      * AccessPointName      - shows the requested metric for the specified AccessPoint name\n    #      * AvailabilityZone     - shows the requested metric for all instances running in that EC2 Availability Zone\n    #      * Service (required)   - the name of the service that reported the monitoring data - for LoadBalancing metrics, use \"LBS\"\n    #      * Namespace (required) - in private beta, the available metrics are all reported by AWS services, so set this to \"AWS\"\n    #\n    #    :measure_name\n    #      EC2 Metrics:\n    #      * CPUUtilization  the percentage of allocated EC2 Compute Units that are currently in use on the instance. Units are Percent.\n    #      * NetworkIn      - the number of bytes received on all network interfaces by the instance. Units are Bytes.\n    #      * NetworkOut     - the number of bytes sent out on all network interfaces by the instance. Units are Bytes.\n    #      * DiskReadOps    - completed read operations from all disks available to the instance in one minute. Units are Count/Second.\n    #      * DiskWriteOps   - completed writes operations to all disks available to the instance in one minute. Units are Count/Second.\n    #      * DiskReadBytes  - bytes read from all disks available to the instance in one minute. Units are Bytes/Second.\n    #      * DiskWriteBytes - bytes written to all disks available to the instance in one minute. Units are Bytes/Second.\n    #      Load Balancing Metrics:\n    #      * Latency            - time taken between a request and the corresponding response as seen by the load balancer. Units are in\n    #                             seconds, and the available statistics include minimum, maximum, average and count.\n    #      * RequestCount       - number of requests processed by the AccessPoint over the valid period. Units are count per second, and\n    #                             the available statistics include minimum, maximum and sum. A valid period can be anything equal to or\n    #                             multiple of sixty (60) seconds.\n    #      * HealthyHostCount   - number of healthy EndPoints for the valid Period. A valid period can be anything equal to or a multiple\n    #                             of sixty (60) seconds. Units are the count of EndPoints. The meaningful statistic for HealthyHostCount\n    #                             is the average for an AccessPoint within an Availability Zone. Both Load Balancing dimensions,\n    #                             AccessPointName and AvailabilityZone, should be specified when retreiving HealthyHostCount.\n    #      * UnHealthyHostCount - number of unhealthy EndPoints for the valid Period. A valid period can be anything equal to or a multiple\n    #                             of sixty (60) seconds. Units are the count of EndPoints. The meaningful statistic for UnHealthyHostCount\n    #                             is the average for an AccessPoint within Availability Amazon Monitoring Service Developer Guide Load\n    #                             Balancing Metrics Version PRIVATE BETA 2009-01-22 19 Zone. Both Load Balancing dimensions, AccessPointName\n    #                             and AvailabilityZone, should be specified when retreiving UnHealthyHostCount.\n    #\n    def get_metric_statistics(options={})\n      # Period (60 sec by default)\n      period = (options[:period] && options[:period].to_i) || 60\n      # Statistics ('Average' by default)\n      statistics = Array(options[:statistics]).flatten\n      statistics = statistics.right_blank? ? ['Average'] : statistics.map{|statistic| statistic.to_s.capitalize }\n      # Times (5.min.ago up to now by default)\n      start_time = options[:start_time] || (Time.now.utc - 5*60)\n      start_time = start_time.utc.strftime(\"%Y-%m-%dT%H:%M:%S+00:00\") if start_time.is_a?(Time)\n      end_time = options[:end_time] || Time.now.utc\n      end_time = end_time.utc.strftime(\"%Y-%m-%dT%H:%M:%S+00:00\") if end_time.is_a?(Time)\n      # Measure name\n      measure_name = options[:measure_name] || 'CPUUtilization'\n      # Dimentions (a hash, empty by default)\n      dimentions = options[:dimentions] || {}\n      #\n      request_hash = { 'Period'      => period,\n                       'StartTime'   => start_time,\n                       'EndTime'     => end_time,\n                       'MeasureName' => measure_name }\n      request_hash['Unit']       = options[:unit]        if options[:unit]\n      request_hash['CustomUnit'] = options[:custom_unit] if options[:custom_unit]\n      request_hash['Namespace']  = options[:namespace]   if options[:namespace]\n      request_hash.merge!(amazonize_list('Statistics.member', statistics))\n      # dimentions\n      dim = []\n      dimentions.each do |key, values|\n        Array(values).each { |value|  dim << [key, value] }\n      end\n      request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'], dim))\n      #\n      link = generate_request(\"GetMetricStatistics\", request_hash)\n      request_info(link, GetMetricStatisticsParser.new(:logger => @logger))\n    end\n\n    # This call returns a list of the valid metrics for which there is recorded data available to a you.\n    #\n    #  acw.list_metrics #=>\n    #      [ { :namespace    => \"AWS/ELB\",\n    #          :measure_name => \"HealthyHostCount\",\n    #          :dimentions   => { \"LoadBalancerName\"=>\"test-kd1\" } },\n    #        { :namespace    => \"AWS/ELB\",\n    #          :measure_name => \"UnHealthyHostCount\",\n    #          :dimentions   => { \"LoadBalancerName\"=>\"test-kd1\" } } ]\n    def list_metrics\n      link = generate_request(\"ListMetrics\")\n      request_cache_or_info :list_metrics, link,  ListMetricsParser, @@bench, true\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: MetricStatistics\n    #-----------------------------------------------------------------\n\n    class GetMetricStatisticsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'member'\n      end\n      def tagend(name)\n        case name\n        when 'Timestamp'  then @item[:timestamp]   = @text\n        when 'Unit'       then @item[:unit]        = @text\n        when 'CustomUnit' then @item[:custom_unit] = @text\n        when 'Samples'    then @item[:samples]     = @text.to_f\n        when 'Average'    then @item[:average]     = @text.to_f\n        when 'Minimum'    then @item[:minimum]     = @text.to_f\n        when 'Maximum'    then @item[:maximum]     = @text.to_f\n        when 'Sum'        then @item[:sum]         = @text.to_f\n        when 'member'     then @result[:datapoints] << @item\n        when 'Label'      then @result[:label]     = @text\n        end\n      end\n      def reset\n        @result = { :datapoints => [] }\n      end\n    end\n\n    class ListMetricsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'member'\n          case @xmlpath\n            when @p then @item = { :dimentions => {} }\n          end\n        end\n      end\n      def tagend(name)\n        case name\n        when 'MeasureName' then @item[:measure_name] = @text\n        when 'Namespace'   then @item[:namespace] = @text\n        when 'Name'        then @dname  = @text\n        when 'Value'       then @dvalue = @text\n        when 'member'\n          case @xmlpath\n          when \"#@p/member/Dimensions\" then @item[:dimentions][@dname] = @dvalue\n          when @p then @result << @item\n          end\n        end\n      end\n      def reset\n        @p      = 'ListMetricsResponse/ListMetricsResult/Metrics'\n        @result = []\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/as/right_as_interface.rb",
    "content": "#\n# Copyright (c) 2007-2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::AsInterface -- RightScale Amazon Auto Scaling interface\n  # The RightAws::AsInterface class provides a complete interface to Amazon Auto Scaling service.\n  #\n  # For explanations of the semantics of each call, please refer to Amazon's documentation at\n  # http://docs.amazonwebservices.com/AutoScaling/latest/DeveloperGuide/\n  #\n  # Create an interface handle:\n  #\n  #  as = RightAws::AsInterface.new(aws_access_key_id, aws_security_access_key)\n  #\n  # Create a launch configuration:\n  #\n  #  as.create_launch_configuration('CentOS.5.1-c', 'ami-08f41161', 'm1.small',\n  #                                 :key_name        => 'kd-moo-test',\n  #                                 :security_groups => ['default'],\n  #                                 :user_data       => \"Woohoo: CentOS.5.1-c\" )\n  #\n  # Create an AutoScaling group:\n  #\n  #  as.create_auto_scaling_group('CentOS.5.1-c-array', 'CentOS.5.1-c', 'us-east-1c',\n  #                               :min_size => 2,\n  #                               :max_size => 5)\n  #\n  # Create a new trigger:\n  # \n  #  as.create_or_update_scaling_trigger('kd.tr.1', 'CentOS.5.1-c-array',\n  #                                      :measure_name => 'CPUUtilization',\n  #                                      :statistic => :average,\n  #                                      :dimensions => {\n  #                                         'AutoScalingGroupName' => 'CentOS.5.1-c-array',\n  #                                         'Namespace' => 'AWS',\n  #                                         'Service' => 'EC2' },\n  #                                      :period => 60,\n  #                                      :lower_threshold => 5,\n  #                                      :lower_breach_scale_increment => -1,\n  #                                      :upper_threshold => 60,\n  #                                      :upper_breach_scale_increment => 1,\n  #                                      :breach_duration => 300 )\n  #\n  # Describe scaling activity:\n  #\n  #  as.incrementally_describe_scaling_activities('CentOS.5.1-c-array') #=> List of activities\n  #\n  # Describe the Auto Scaling group status:\n  #\n  #  as.describe_auto_scaling_groups('CentOS.5.1-c-array') #=> Current group status\n  #\n  class AsInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    # Amazon AS API version being used\n    API_VERSION       = '2009-05-15'\n    DEFAULT_HOST      = 'autoscaling.amazonaws.com'\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to an CSLS account. All handles share the same per process or per thread\n    # HTTP connection to Amazon CSLS. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://autoscaling.amazonaws.com/'\n    # * <tt>:server</tt>: AS service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: AS service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    # * <tt>:cache</tt>: true/false(default): describe_auto_scaling_groups\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'AS',\n             :default_host        => ENV['AS_URL'] ? URI.parse(ENV['AS_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['AS_URL'] ? URI.parse(ENV['AS_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['AS_URL'] ? URI.parse(ENV['AS_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['AS_URL'] ? URI.parse(ENV['AS_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['AS_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:aass_connection, @@bench, request, parser)\n    end\n\n    #-----------------------------------------------------------------\n    #      Auto Scaling Groups\n    #-----------------------------------------------------------------\n\n    # Describe auto scaling groups.\n    # Returns a full description of the AutoScalingGroups from the given list.\n    # This includes all EC2 instances that are members of the group. If a list\n    # of names is not provided, then the full details of all AutoScalingGroups\n    # is returned. This style conforms to the EC2 DescribeInstances API behavior.\n    #\n    def describe_auto_scaling_groups(*auto_scaling_group_names)\n      auto_scaling_group_names = auto_scaling_group_names.flatten.compact\n      request_hash = amazonize_list('AutoScalingGroupNames.member', auto_scaling_group_names)\n      link = generate_request(\"DescribeAutoScalingGroups\", request_hash)\n      request_cache_or_info(:describe_auto_scaling_groups, link,  DescribeAutoScalingGroupsParser, @@bench, auto_scaling_group_names.right_blank?)\n    end\n\n    # Creates a new auto scaling group with the specified name.\n    # Returns +true+ or raises an exception.\n    #\n    # Options: +:min_size+, +:max_size+, +:cooldown+, +:load_balancer_names+\n    #\n    #  as.create_auto_scaling_group('CentOS.5.1-c-array', 'CentOS.5.1-c', 'us-east-1c',\n    #                               :min_size => 2,\n    #                               :max_size => 5)  #=> true\n    #\n    # Amazon's notice: Constraints: Restricted to one Availability Zone\n    def create_auto_scaling_group(auto_scaling_group_name, launch_configuration_name, availability_zones, options={})\n      options[:min_size] ||= 1\n      options[:max_size] ||= 20\n      options[:cooldown] ||= 0\n      request_hash = amazonize_list('AvailabilityZones.member', availability_zones)\n      request_hash.merge!( amazonize_list('LoadBalancerNames.member', options[:load_balancer_names]) )\n      request_hash.merge!( 'AutoScalingGroupName'    => auto_scaling_group_name,\n                           'LaunchConfigurationName' => launch_configuration_name,\n                           'MinSize'                 => options[:min_size],\n                           'MaxSize'                 => options[:max_size],\n                           'Cooldown'                => options[:cooldown] )\n      link = generate_request(\"CreateAutoScalingGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Deletes all configuration for this auto scaling group and also deletes the group.\n    # Returns +true+ or raises an exception.\n    #\n    def delete_auto_scaling_group(auto_scaling_group_name)\n      link = generate_request('DeleteAutoScalingGroup', 'AutoScalingGroupName' => auto_scaling_group_name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Adjusts the desired size of the Capacity Group by using scaling actions, as necessary. When\n    # adjusting the size of the group downward, it is not possible to define which EC2 instances will be\n    # terminated. This also applies to any auto-scaling decisions that might result in the termination of\n    # instances.\n    #\n    # Returns +true+ or raises an exception.\n    #\n    #  as.set_desired_capacity('CentOS.5.1-c',3) #=> 3\n    #\n    def set_desired_capacity(auto_scaling_group_name, desired_capacity)\n      link = generate_request('SetDesiredCapacity', 'AutoScalingGroupName' => auto_scaling_group_name,\n                                                    'DesiredCapacity'      => desired_capacity )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Updates the configuration for the given AutoScalingGroup. If MaxSize is lower than the current size,\n    # then there will be an implicit call to SetDesiredCapacity to set the group to the new MaxSize. The\n    # same is true for MinSize there will also be an implicit call to SetDesiredCapacity. All optional\n    # parameters are left unchanged if not passed in the request.\n    #\n    # The new settings are registered upon the completion of this call. Any launch configuration settings\n    # will take effect on any triggers after this call returns. However, triggers that are currently in\n    # progress can not be affected. See key term Trigger.\n    # \n    # Returns +true+ or raises an exception.\n    #\n    # Options: +:launch_configuration_name+, +:min_size+, +:max_size+, +:cooldown+, +:availability_zones+.\n    # (Amazon's notice: +:availability_zones+ is reserved for future use.)\n    #\n    #  as.update_auto_scaling_group('CentOS.5.1-c', :min_size => 1, :max_size => 4) #=> true\n    #\n    def update_auto_scaling_group(auto_scaling_group_name, options={})\n      request_hash = amazonize_list('AvailabilityZones.member', options[:availability_zones])\n      request_hash['AutoScalingGroupName']    = auto_scaling_group_name\n      request_hash['LaunchConfigurationName'] = options[:launch_configuration_name] if options[:launch_configuration_name]\n      request_hash['MinSize']  = options[:min_size] if options[:min_size]\n      request_hash['MaxSize']  = options[:max_size] if options[:max_size]\n      request_hash['Cooldown'] = options[:cooldown] if options[:cooldown]\n      link = generate_request(\"UpdateAutoScalingGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Scaling Activities\n    #-----------------------------------------------------------------\n\n    # Describe all Scaling Activities.\n    #\n    #  describe_scaling_activities('CentOS.5.1-c-array') #=>\n    #        [{:cause=>\n    #            \"At 2009-05-28 10:11:35Z trigger kd.tr.1 breached high threshold value for\n    #             CPUUtilization, 10.0, adjusting the desired capacity from 1 to 2.  At 2009-05-28 10:11:35Z\n    #             a breaching trigger explicitly set group desired capacity changing the desired capacity\n    #             from 1 to 2.  At 2009-05-28 10:11:40Z an instance was started in response to a difference\n    #             between desired and actual capacity, increasing the capacity from 1 to 2.\",\n    #          :activity_id=>\"067c9abb-f8a7-4cf8-8f3c-dc6f280457c4\",\n    #          :progress=>0,\n    #          :description=>\"Launching a new EC2 instance\",\n    #          :status_code=>\"InProgress\",\n    #          :start_time=>Thu May 28 10:11:40 UTC 2009},\n    #         {:end_time=>Thu May 28 09:35:23 UTC 2009,\n    #          :cause=>\n    #            \"At 2009-05-28 09:31:21Z a user request created an AutoScalingGroup changing the desired\n    #             capacity from 0 to 1.  At 2009-05-28 09:32:35Z an instance was started in response to a\n    #             difference between desired and actual capacity, increasing the capacity from 0 to 1.\",\n    #          :activity_id=>\"90d506ba-1b75-4d29-8739-0a75b1ba8030\",\n    #          :progress=>100,\n    #          :description=>\"Launching a new EC2 instance\",\n    #          :status_code=>\"Successful\",\n    #          :start_time=>Thu May 28 09:32:35 UTC 2009}]}\n    #\n    def describe_scaling_activities(auto_scaling_group_name, *activity_ids)\n      result = []\n      incrementally_describe_scaling_activities(auto_scaling_group_name, *activity_ids) do |response|\n        result += response[:scaling_activities]\n        true\n      end\n      result\n    end\n\n    # Incrementally describe Scaling Activities.\n    # Returns the scaling activities specified for the given group. If the input list is empty, all the\n    # activities from the past six weeks will be returned. Activities will be sorted by completion time.\n    # Activities that have no completion time will be considered as using the most recent possible time.\n    #\n    # Optional params: +:max_records+, +:next_token+.\n    #\n    #  # get max 100 first activities\n    #  as.incrementally_describe_scaling_activities('CentOS.5.1-c-array') #=>\n    #      {:scaling_activities=>\n    #        [{:cause=>\n    #            \"At 2009-05-28 10:11:35Z trigger kd.tr.1 breached high threshold value for\n    #             CPUUtilization, 10.0, adjusting the desired capacity from 1 to 2.  At 2009-05-28 10:11:35Z\n    #             a breaching trigger explicitly set group desired capacity changing the desired capacity\n    #             from 1 to 2.  At 2009-05-28 10:11:40Z an instance was started in response to a difference\n    #             between desired and actual capacity, increasing the capacity from 1 to 2.\",\n    #          :activity_id=>\"067c9abb-f8a7-4cf8-8f3c-dc6f280457c4\",\n    #          :progress=>0,\n    #          :description=>\"Launching a new EC2 instance\",\n    #          :status_code=>\"InProgress\",\n    #          :start_time=>Thu May 28 10:11:40 UTC 2009},\n    #         {:end_time=>Thu May 28 09:35:23 UTC 2009,\n    #          :cause=>\n    #            \"At 2009-05-28 09:31:21Z a user request created an AutoScalingGroup changing the desired\n    #             capacity from 0 to 1.  At 2009-05-28 09:32:35Z an instance was started in response to a\n    #             difference between desired and actual capacity, increasing the capacity from 0 to 1.\",\n    #          :activity_id=>\"90d506ba-1b75-4d29-8739-0a75b1ba8030\",\n    #          :progress=>100,\n    #          :description=>\"Launching a new EC2 instance\",\n    #          :status_code=>\"Successful\",\n    #          :start_time=>Thu May 28 09:32:35 UTC 2009}]}\n    #\n    #  # list by 5 records\n    #  incrementally_describe_scaling_activities('CentOS.5.1-c-array', :max_records => 5) do |response|\n    #    puts response.inspect\n    #    true\n    #  end\n    #\n    def incrementally_describe_scaling_activities(auto_scaling_group_name, *activity_ids, &block)\n      activity_ids = activity_ids.flatten.compact\n      params = activity_ids.last.kind_of?(Hash) ? activity_ids.pop : {}\n      request_hash = amazonize_list('ActivityIds.member', activity_ids)\n      request_hash['AutoScalingGroupName'] = auto_scaling_group_name\n      request_hash['MaxRecords'] = params[:max_records] if params[:max_records]\n      request_hash['NextToken']  = params[:next_token]  if params[:next_token]\n      last_response = nil\n      loop do\n        link = generate_request(\"DescribeScalingActivities\", request_hash)\n        last_response = request_info( link,  DescribeScalingActivitiesParser.new(:logger => @logger))\n        request_hash['NextToken'] = last_response[:next_token]\n        break unless block && block.call(last_response) && !last_response[:next_token].right_blank?\n      end\n      last_response\n    end\n\n    #-----------------------------------------------------------------\n    #      Instance and Instance Workflow Operations\n    #-----------------------------------------------------------------\n\n    # This call will terminate the specified Instance. Optionally, the desired group size can be adjusted.\n    # If set to true, the default, the AutoScalingGroup size will decrease by one. If the AutoScalingGroup\n    # is associated with a LoadBalancer, the system will deregister the instance before terminating it.\n    # This call simply registers a termination request. The termination of the instance can not happen\n    # immediately.\n    #\n    # Returns the activity to terminate the instance.\n    #\n    def terminate_instance_in_auto_scaling_group(instance_id, should_decrement_desired_capacity=true)\n      request_hash = { 'InstanceId' => instance_id }\n      request_hash['ShouldDecrementDesiredCapacity'] = should_decrement_desired_capacity\n      link = generate_request('TerminateInstanceInAutoScalingGroup', request_hash )\n      request_info(link, DescribeScalingActivitiesParser.new(:logger => @logger))[:scaling_activities].first\n    end\n\n    #-----------------------------------------------------------------\n    #      Launch Configuration Operations\n    #-----------------------------------------------------------------\n\n    # Creates a new Launch Configuration. Please note that the launch configuration name used must\n    # be unique, within the scope of your Amazon Web Services AWS account, and the maximum limit of\n    # launch configurations must not yet have been met, or else the call will fail.\n    #\n    # Once created, the new launch configuration is available for immediate use.\n    #\n    # Options: +:security_groups+, +:block_device_mappings+, +:key_name+,\n    # +:user_data+, +:kernel_id+, +:ramdisk_id+\n    #\n    #  as.create_launch_configuration('kd: CentOS.5.1-c.1', 'ami-08f41161', 'c1.medium',\n    #    :key_name        => 'tim',\n    #    :security_groups => ['default'],\n    #    :user_data       => \"Woohoo: CentOS.5.1-c\",\n    #    :block_device_mappings => [ { :device_name     => '/dev/sdk',\n    #                                  :ebs_snapshot_id => 'snap-145cbc7d',\n    #          :ebs_delete_on_termination => true,\n    #          :ebs_volume_size => 3,\n    #          :virtual_name => 'ephemeral2'\n    #                                } ]\n    #    ) #=> true\n    #\n    def create_launch_configuration(launch_configuration_name, image_id, instance_type, options={})\n      request_hash = { 'LaunchConfigurationName' => launch_configuration_name,\n                       'ImageId'                 => image_id,\n                       'InstanceType'            => instance_type }\n      request_hash.merge!(amazonize_list('SecurityGroups.member',      options[:security_groups]))       unless options[:security_groups].right_blank?\n      request_hash.merge!(amazonize_block_device_mappings(options[:block_device_mappings], 'BlockDeviceMappings.member'))\n      request_hash['KeyName']   = options[:key_name]   if options[:key_name]\n      request_hash['UserData']  = Base64.encode64(options[:user_data]).delete(\"\\n\") unless options[:user_data].right_blank? if options[:user_data]\n      request_hash['KernelId']  = options[:kernel_id]  if options[:kernel_id]\n      request_hash['RamdiskId'] = options[:ramdisk_id] if options[:ramdisk_id]\n      link = generate_request(\"CreateLaunchConfiguration\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n\n    # Describe all Launch Configurations.\n    # Returns an array of configurations.\n    #\n    #  as.describe_launch_configurations #=>\n    #    [{:security_groups=>[\"default\"],\n    #      :ramdisk_id=>\"\",\n    #      :user_data=>\"V29vaG9vOiBDZW50T1MuNS4xLWM=\",\n    #      :instance_type=>\"c1.medium\",\n    #      :block_device_mappings=>\n    #       [{:virtual_name=>\"ephemeral2\", :device_name=>\"/dev/sdk\"}],\n    #      :launch_configuration_name=>\"kd: CentOS.5.1-c.1\",\n    #      :created_time=>\"2010-03-29T10:00:32.742Z\",\n    #      :image_id=>\"ami-08f41161\",\n    #      :key_name=>\"tim\",\n    #      :kernel_id=>\"\"}, ...]\n    #\n    def describe_launch_configurations(*launch_configuration_names)\n      result = []\n      incrementally_describe_launch_configurations(*launch_configuration_names) do |response|\n        result += response[:launch_configurations]\n        true\n      end\n      result\n    end\n\n    # Incrementally describe Launch Configurations.\n    # Returns a full description of the launch configurations given the specified names. If no names\n    # are specified, then the full details of all launch configurations are returned.\n    # \n    # Optional params: +:max_records+, +:next_token+.\n    #\n    #  # get max 100 first configurations\n    #  as.incrementally_describe_launch_configurations #=>\n    #      {:launch_configurations=>\n    #        [{:created_time=>Thu May 28 09:31:20 UTC 2009,\n    #          :kernel_id=>\"\",\n    #          :launch_configuration_name=>\"CentOS.5.1-c\",\n    #          :ramdisk_id=>\"\",\n    #          :security_groups=>[\"default\"],\n    #          :key_name=>\"kd-moo-test\",\n    #          :user_data=>\"Woohoo: CentOS.5.1-c-array\",\n    #          :image_id=>\"ami-08f41161\",\n    #          :block_device_mappings=>[],\n    #          :instance_type=>\"m1.small\"}, ... ]}\n    #\n    #  # list by 5 records\n    #  incrementally_describe_launch_configurations(:max_records => 5) do |response|\n    #    puts response.inspect\n    #    true\n    #  end\n    #\n    def incrementally_describe_launch_configurations(*launch_configuration_names, &block)\n      launch_configuration_names = launch_configuration_names.flatten.compact\n      params = launch_configuration_names.last.kind_of?(Hash) ? launch_configuration_names.pop : {}\n      request_hash = amazonize_list('LaunchConfigurationNames.member', launch_configuration_names)\n      request_hash['MaxRecords'] = params[:max_records] if params[:max_records]\n      request_hash['NextToken']  = params[:next_token]  if params[:next_token]\n      last_response = nil\n      loop do\n        link = generate_request(\"DescribeLaunchConfigurations\", request_hash)\n        last_response = request_info( link, DescribeLaunchConfigurationsParser.new(:logger => @logger) )\n        request_hash['NextToken'] = last_response[:next_token]\n        break unless block && block.call(last_response) && !last_response[:next_token].right_blank?\n      end\n      last_response\n    end\n\n    # Delete launch configuration.\n    # Returns +true+ or an exception.\n    #\n    #   as.delete_launch_configuration('CentOS.5.1') #=> true\n    #\n    def delete_launch_configuration(launch_configuration_name)\n      link = generate_request('DeleteLaunchConfiguration', 'LaunchConfigurationName' => launch_configuration_name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Trigger Operations\n    #-----------------------------------------------------------------\n\n    # Create or update specified trigger.\n    # This call sets the parameters that governs when and how to scale an AutoScalingGroup.\n    # If the Trigger, within the scope of the caller's AWS account, specified already exists,\n    # it will be updated. If a trigger with a different name already exists, this call will fail.\n    #\n    # Returns +true+ or an exception.\n    #\n    # Options: +:measure_name+, +:statistic+, +:period+, +:lower_threshold+, +:lower_breach_scale_increment+,\n    # +:upper_threshold+, +:upper_breach_scale_increment+, +:dimensions+, +:breach_duration+, +:unit+, +:custom_unit+\n    #\n    #  as.create_or_update_scaling_trigger('kd.tr.1', 'CentOS.5.1-c-array',\n    #                                      :measure_name => 'CPUUtilization',\n    #                                      :statistic => :average,\n    #                                      :dimensions => {\n    #                                         'AutoScalingGroupName' => 'CentOS.5.1-c-array',\n    #                                         'Namespace' => 'AWS',\n    #                                         'Service' => 'EC2' },\n    #                                      :period => 60,\n    #                                      :lower_threshold => 5,\n    #                                      :lower_breach_scale_increment => -1,\n    #                                      :upper_threshold => 60,\n    #                                      :upper_breach_scale_increment => 1,\n    #                                      :breach_duration => 300 ) #=> true\n    #\n    def create_or_update_scaling_trigger(trigger_name, auto_scaling_group_name, options={})\n      request_hash = { 'TriggerName'               => trigger_name,\n                       'AutoScalingGroupName'      => auto_scaling_group_name,\n                       'MeasureName'               => options[:measure_name],\n                       'Statistic'                 => options[:statistic].to_s.capitalize,\n                       'Period'                    => options[:period],\n                       'LowerThreshold'            => options[:lower_threshold],\n                       'LowerBreachScaleIncrement' => options[:lower_breach_scale_increment],\n                       'UpperThreshold'            => options[:upper_threshold],\n                       'UpperBreachScaleIncrement' => options[:upper_breach_scale_increment],\n                       'BreachDuration'            => options[:breach_duration] }\n      request_hash['Unit']       = options[:unit]        if options[:unit]\n      request_hash['CustomUnit'] = options[:custom_unit] if options[:custom_unit]\n      dimensions = []\n      (options[:dimensions] || {}).each do |key, values|\n        Array(values).each { |value| dimensions << [key, value] }\n      end\n      request_hash.merge!(amazonize_list(['Dimensions.member.?.Name', 'Dimensions.member.?.Value'], dimensions))\n      link = generate_request(\"CreateOrUpdateScalingTrigger\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Describe triggers.\n    # Returns a full description of the trigger in the specified Auto Scaling Group.\n    #\n    #  as.describe_triggers('CentOS.5.1-c-array') #=>\n    #      [{:status=>\"HighBreaching\",\n    #        :breach_duration=>300,\n    #        :measure_name=>\"CPUUtilization\",\n    #        :trigger_name=>\"kd.tr.1\",\n    #        :period=>60,\n    #        :lower_threshold=>0.0,\n    #        :lower_breach_scale_increment=>-1,\n    #        :dimensions=>\n    #         {\"Namespace\"=>\"AWS\",\n    #          \"AutoScalingGroupName\"=>\"CentOS.5.1-c-array\",\n    #          \"Service\"=>\"EC2\"},\n    #        :statistic=>\"Average\",\n    #        :upper_threshold=>10.0,\n    #        :created_time=>Thu May 28 09:48:46 UTC 2009,\n    #        :auto_scaling_group_name=>\"CentOS.5.1-c-array\",\n    #        :upper_breach_scale_increment=>1}]\n    #\n    def describe_triggers(auto_scaling_group_name)\n      link = generate_request(\"DescribeTriggers\", 'AutoScalingGroupName' => auto_scaling_group_name)\n      request_info(link, DescribeTriggersParser.new(:logger => @logger))\n    end\n\n    # Delete specified trigger.\n    # Returns +true+ or an exception.\n    #\n    #  as.delete_trigger('kd.tr.1', 'CentOS.5.1-c-array') #=> true\n    #\n    def delete_trigger(trigger_name, auto_scaling_group_name)\n      link = generate_request('DeleteTrigger', 'TriggerName'          => trigger_name,\n                                               'AutoScalingGroupName' => auto_scaling_group_name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Scaling Activity\n    #-----------------------------------------------------------------\n\n    class DescribeScalingActivitiesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'member', 'Activity' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'ActivityId'    then @item[:activity_id]    = @text\n        when 'StartTime'     then @item[:start_time]     = @text\n        when 'EndTime'       then @item[:end_time]       = @text\n        when 'Progress'      then @item[:progress]       = @text.to_i\n        when 'StatusCode'    then @item[:status_code]    = @text\n        when 'Cause'         then @item[:cause]          = @text\n        when 'Description'   then @item[:description]    = @text\n        when 'member', 'Activity'  then @result[:scaling_activities] << @item\n        when 'NextToken' then @result[:next_token] = @text\n        end\n      end\n      def reset\n        @result = { :scaling_activities => []}\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Auto Scaling Groups\n    #-----------------------------------------------------------------\n\n    class DescribeAutoScalingGroupsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'member'\n          case @xmlpath\n            when @p then @item = { :instances => [ ],\n                                   :availability_zones => [],\n                                   :load_balancer_names => [] }\n            when \"#@p/member/Instances\" then @instance = { }\n          end\n        end\n      end\n      def tagend(name)\n        case name\n        when 'CreatedTime'             then @item[:created_time]              = @text\n        when 'MinSize'                 then @item[:min_size]                  = @text.to_i\n        when 'MaxSize'                 then @item[:max_size]                  = @text.to_i\n        when 'DesiredCapacity'         then @item[:desired_capacity]          = @text.to_i\n        when 'Cooldown'                then @item[:cooldown]                  = @text.to_i\n        when 'LaunchConfigurationName' then @item[:launch_configuration_name] = @text\n        when 'AutoScalingGroupName'    then @item[:auto_scaling_group_name]   = @text\n        when 'InstanceId'              then @instance[:instance_id]       = @text\n        when 'LifecycleState'          then @instance[:lifecycle_state]   = @text\n        when 'AvailabilityZone'        then @instance[:availability_zone] = @text\n        when 'member'\n          case @xmlpath\n          when @p then\n            @item[:availability_zones].sort!\n            @result << @item\n          when \"#@p/member/AvailabilityZones\" then @item[:availability_zones] << @text\n          when \"#@p/member/LoadBalancerNames\" then @item[:load_balancer_names] << @text\n          when \"#@p/member/Instances\"         then @item[:instances] << @instance\n          end\n        end\n      end\n      def reset\n        @p      = 'DescribeAutoScalingGroupsResponse/DescribeAutoScalingGroupsResult/AutoScalingGroups'\n        @result = []\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Launch Configurations\n    #-----------------------------------------------------------------\n\n    class DescribeLaunchConfigurationsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/LaunchConfigurations/member$}\n          @item = { :block_device_mappings => [],\n                    :security_groups       => [] }\n        when %r{/BlockDeviceMappings/member$}\n          @block_device_mapping = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'CreatedTime'             then @item[:created_time]              = @text\n        when 'InstanceType'            then @item[:instance_type]             = @text\n        when 'KeyName'                 then @item[:key_name]                  = @text\n        when 'ImageId'                 then @item[:image_id]                  = @text\n        when 'KernelId'                then @item[:kernel_id]                 = @text\n        when 'RamdiskId'               then @item[:ramdisk_id]                = @text\n        when 'LaunchConfigurationName' then @item[:launch_configuration_name] = @text\n        when 'UserData'                then @item[:user_data]                 = @text\n        when 'NextToken'               then @result[:next_token]              = @text\n        else\n          case full_tag_name\n          when %r{/BlockDeviceMappings/member} # no trailing $\n            case name\n            when 'DeviceName'          then @block_device_mapping[:device_name]  = @text\n            when 'VirtualName'         then @block_device_mapping[:virtual_name] = @text\n            when 'member'              then @item[:block_device_mappings]        << @block_device_mapping\n            end\n          when %r{member/SecurityGroups/member$} \n            @item[:security_groups] << @text\n          when %r{/LaunchConfigurations/member$}\n            @item[:security_groups].sort!\n            @result[:launch_configurations] << @item\n          end\n        end\n      end\n      def reset\n        @result = { :launch_configurations => []}\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Triggers\n    #-----------------------------------------------------------------\n\n    class DescribeTriggersParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'member'\n          case @xmlpath\n          when 'DescribeTriggersResponse/DescribeTriggersResult/Triggers'\n            @item = { :dimensions => {} }\n          when 'DescribeTriggersResponse/DescribeTriggersResult/Triggers/member/Dimensions'\n            @dimension = {}\n          end\n        end\n      end\n      def tagend(name)\n        case name\n        when 'AutoScalingGroupName'      then @item[:auto_scaling_group_name]      = @text\n        when 'MeasureName'               then @item[:measure_name]                 = @text\n        when 'CreatedTime'               then @item[:created_time]                 = @text\n        when 'BreachDuration'            then @item[:breach_duration]              = @text.to_i\n        when 'UpperBreachScaleIncrement' then @item[:upper_breach_scale_increment] = @text.to_i\n        when 'UpperThreshold'            then @item[:upper_threshold]              = @text.to_f\n        when 'LowerThreshold'            then @item[:lower_threshold]              = @text.to_f\n        when 'LowerBreachScaleIncrement' then @item[:lower_breach_scale_increment] = @text.to_i\n        when 'Period'                    then @item[:period]                       = @text.to_i\n        when 'Status'                    then @item[:status]                       = @text\n        when 'TriggerName'               then @item[:trigger_name]                 = @text\n        when 'Statistic'                 then @item[:statistic]                    = @text\n        when 'Unit'                      then @item[:unit]                         = @text\n        when 'Name'                      then @dimension[:name]                    = @text\n        when 'Value'                     then @dimension[:value]                   = @text\n        when 'member'\n          case @xmlpath\n          when \"#@p/member/Dimensions\" then @item[:dimensions][@dimension[:name]] = @dimension[:value]\n          when @p                      then @result << @item\n          end\n        end\n      end\n      def reset\n        @p      = 'DescribeTriggersResponse/DescribeTriggersResult/Triggers'\n        @result = []\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/awsbase/benchmark_fix.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n#\n\n\n# A hack because there's a bug in add! in Benchmark::Tms\nmodule Benchmark  #:nodoc:\n  class Tms #:nodoc:\n    def add!(&blk)\n      t = Benchmark::measure(&blk)\n      @utime  = utime + t.utime\n      @stime  = stime + t.stime\n      @cutime = cutime + t.cutime\n      @cstime = cstime + t.cstime\n      @real   = real + t.real\n      self\n    end\n  end\nend\n"
  },
  {
    "path": "lib/awsbase/right_awsbase.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\n# Test\nmodule RightAws\n  require 'digest/md5'\n  \n  class AwsUtils #:nodoc:\n    @@digest1   = OpenSSL::Digest.new(\"sha1\")\n    @@digest256 = nil\n    if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000\n      @@digest256 = OpenSSL::Digest.new(\"sha256\") rescue nil # Some installation may not support sha256\n    end\n\n    def self.utc_iso8601(time)\n      if    time.is_a?(Fixnum) then time = Time::at(time)\n      elsif time.is_a?(String) then time = Time::parse(time)\n      end\n      time.utc.strftime(\"%Y-%m-%dT%H:%M:%S.000Z\")\n    end\n    \n    def self.sign(aws_secret_access_key, auth_string)\n      Base64.encode64(OpenSSL::HMAC.digest(@@digest1, aws_secret_access_key, auth_string)).strip\n    end\n\n    # Calculates 'Content-MD5' header value for some content\n    def self.content_md5(content)\n      Base64.encode64(Digest::MD5::new.update(content).digest).strip\n    end\n\n    # Escape a string accordingly Amazon rulles\n    # http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html\n    def self.amz_escape(param)\n      param = param.flatten.join('') if param.is_a?(Array) # ruby 1.9.x Array#to_s fix\n      param.to_s.gsub(/([^a-zA-Z0-9._~-]+)/n) do\n        '%' + $1.unpack('H2' * $1.size).join('%').upcase\n      end\n    end\n\n    def self.xml_escape(text) # :nodoc:\n      REXML::Text::normalize(text)\n    end\n\n    def self.xml_unescape(text) # :nodoc:\n      REXML::Text::unnormalize(text)\n    end\n\n    # Set a timestamp and a signature version\n    def self.fix_service_params(service_hash, signature)\n      service_hash[\"Timestamp\"] ||= utc_iso8601(Time.now) unless service_hash[\"Expires\"]\n      service_hash[\"SignatureVersion\"] = signature\n      service_hash\n    end\n\n    def self.fix_headers(headers)\n      result = {}\n      headers.each do |header, value|\n        next if !header.is_a?(String) || value.nil?\n        header = header.downcase\n        result[header] = value if result[header].right_blank?\n      end\n      result\n    end\n\n    # Signature Version 0\n    # A deprecated guy (should work till septemper 2009)\n    def self.sign_request_v0(aws_secret_access_key, service_hash)\n      fix_service_params(service_hash, '0')\n      string_to_sign = \"#{service_hash['Action']}#{service_hash['Timestamp'] || service_hash['Expires']}\"\n      service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)\n      service_hash.to_a.collect{|key,val| \"#{amz_escape(key)}=#{amz_escape(val.to_s)}\" }.join(\"&\")\n    end\n\n    # Signature Version 1\n    # Another deprecated guy (should work till septemper 2009)\n    def self.sign_request_v1(aws_secret_access_key, service_hash)\n      fix_service_params(service_hash, '1')\n      string_to_sign = service_hash.sort{|a,b| (a[0].to_s.downcase)<=>(b[0].to_s.downcase)}.to_s\n      service_hash['Signature'] = AwsUtils::sign(aws_secret_access_key, string_to_sign)\n      service_hash.to_a.collect{|key,val| \"#{amz_escape(key)}=#{amz_escape(val.to_s)}\" }.join(\"&\")\n    end\n\n    # Signature Version 2\n    # EC2, SQS and SDB requests must be signed by this guy.\n    # See:  http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?REST_RESTAuth.html\n    #       http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1928\n    def self.sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, uri)\n      fix_service_params(service_hash, '2')\n      # select a signing method (make an old openssl working with sha1)\n      # make 'HmacSHA256' to be a default one\n      service_hash['SignatureMethod'] = 'HmacSHA256' unless ['HmacSHA256', 'HmacSHA1'].include?(service_hash['SignatureMethod'])\n      service_hash['SignatureMethod'] = 'HmacSHA1'   unless @@digest256\n      # select a digest\n      digest = (service_hash['SignatureMethod'] == 'HmacSHA256' ? @@digest256 : @@digest1)\n      # form string to sign\n      canonical_string = service_hash.keys.sort.map do |key|\n        \"#{amz_escape(key)}=#{amz_escape(service_hash[key])}\"\n      end.join('&')\n      string_to_sign = \"#{http_verb.to_s.upcase}\\n#{host.downcase}\\n#{uri}\\n#{canonical_string}\"\n      # sign the string\n      signature      = amz_escape(Base64.encode64(OpenSSL::HMAC.digest(digest, aws_secret_access_key, string_to_sign)).strip)\n      \"#{canonical_string}&Signature=#{signature}\"\n    end\n\n    # From Amazon's SQS Dev Guide, a brief description of how to escape:\n    # \"URL encode the computed signature and other query parameters as specified in \n    # RFC1738, section 2.2. In addition, because the + character is interpreted as a blank space \n    # by Sun Java classes that perform URL decoding, make sure to encode the + character \n    # although it is not required by RFC1738.\"\n    # Avoid using CGI::escape to escape URIs. \n    # CGI::escape will escape characters in the protocol, host, and port\n    # sections of the URI.  Only target chars in the query\n    # string should be escaped.\n    def self.URLencode(raw)\n      e = URI.escape(raw)\n      e.gsub(/\\+/, \"%2b\")\n    end\n    \n    def self.allow_only(allowed_keys, params)\n      bogus_args = []\n      params.keys.each {|p| bogus_args.push(p) unless allowed_keys.include?(p) }\n      raise AwsError.new(\"The following arguments were given but are not legal for the function call #{caller_method}: #{bogus_args.inspect}\") if bogus_args.length > 0\n    end\n    \n    def self.mandatory_arguments(required_args, params)\n      rargs = required_args.dup\n      params.keys.each {|p| rargs.delete(p)}\n      raise AwsError.new(\"The following mandatory arguments were not provided to #{caller_method}: #{rargs.inspect}\") if rargs.length > 0\n    end\n    \n    def self.caller_method\n      caller[1]=~/`(.*?)'/\n      $1\n    end\n\n    def self.split_items_and_params(array)\n      items  = Array(array).flatten.compact\n      params = items.last.kind_of?(Hash) ? items.pop : {}\n      [items, params]\n    end\n\n    # Generates a token in format of:\n    #  1. \"1dd8d4e4-db6b-11df-b31d-0025b37efad0 (if UUID gem is loaded)\n    #  2. \"1287483761-855215-zSv2z-bWGj2-31M5t-ags9m\" (if UUID gem is not loaded)\n    TOKEN_GENERATOR_CHARSET = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a\n    def self.generate_unique_token\n      time  = Time.now\n      token = \"%d-%06d\" % [time.to_i, time.usec]\n      4.times do\n        token << \"-\"\n        5.times { token << TOKEN_GENERATOR_CHARSET[rand(TOKEN_GENERATOR_CHARSET.size)] }\n      end\n      token\n    end\n  end\n\n  class AwsBenchmarkingBlock #:nodoc:\n    attr_accessor :xml, :service\n    def initialize\n      # Benchmark::Tms instance for service (Ec2, S3, or SQS) access benchmarking.\n      @service = Benchmark::Tms.new()\n      # Benchmark::Tms instance for XML parsing benchmarking.\n      @xml = Benchmark::Tms.new()\n    end\n  end\n\n  class AwsNoChange < RuntimeError\n  end\n  \n  class RightAwsBase\n\n    # Amazon HTTP Error handling\n\n    # Text, if found in an error message returned by AWS, indicates that this may be a transient\n    # error. Transient errors are automatically retried with exponential back-off.\n    AMAZON_PROBLEMS = [ 'internal service error', \n                        'is currently unavailable',\n                        'no response from',\n                        'Please try again',\n                        'InternalError',\n                        'Internal Server Error',\n                        'ServiceUnavailable', #from SQS docs\n                        'Unavailable',\n                        'This application is not currently available',\n                        'InsufficientInstanceCapacity'\n                      ]\n    @@amazon_problems = AMAZON_PROBLEMS\n      # Returns a list of Amazon service responses which are known to be transient problems. \n      # We have to re-request if we get any of them, because the problem will probably disappear. \n      # By default this method returns the same value as the AMAZON_PROBLEMS const.\n    def self.amazon_problems\n      @@amazon_problems\n    end\n    \n      # Sets the list of Amazon side problems.  Use in conjunction with the\n      # getter to append problems.\n    def self.amazon_problems=(problems_list)\n      @@amazon_problems = problems_list\n    end\n\n    # Raise an exception if a timeout occures while an API call is in progress.\n    # This helps to avoid a duplicate resources creation when Amazon hangs for some time and\n    # RightHttpConnection is forced to use retries to get a response from it.\n    #\n    # If an API call action is in the list then no attempts to retry are performed.\n    #\n    RAISE_ON_TIMEOUT_ON_ACTIONS = %w{ \n      AllocateAddress\n      CreateSnapshot\n      CreateVolume\n      PurchaseReservedInstancesOffering\n      RequestSpotInstances\n      RunInstances\n    }\n    @@raise_on_timeout_on_actions = RAISE_ON_TIMEOUT_ON_ACTIONS.dup\n\n    def self.raise_on_timeout_on_actions\n      @@raise_on_timeout_on_actions\n    end\n\n    def self.raise_on_timeout_on_actions=(actions_list)\n      @@raise_on_timeout_on_actions = actions_list\n    end\n\n  end\n\n  module RightAwsBaseInterface\n    DEFAULT_SIGNATURE_VERSION = '2'\n    \n    @@caching = false\n    def self.caching\n      @@caching\n    end\n    def self.caching=(caching)\n      @@caching = caching\n    end\n\n      # Current aws_access_key_id\n    attr_reader :aws_access_key_id\n      # Current aws_secret_access_key\n    attr_reader :aws_secret_access_key\n      # Last HTTP request object\n    attr_reader :last_request\n      # Last HTTP response object\n    attr_reader :last_response\n      # Last AWS errors list (used by AWSErrorHandler)\n    attr_accessor :last_errors\n      # Last AWS request id (used by AWSErrorHandler)\n    attr_accessor :last_request_id\n      # Logger object\n    attr_accessor :logger\n      # Initial params hash\n    attr_accessor :params\n      # RightHttpConnection instance\n    attr_reader :connection\n      # Cache\n    attr_reader :cache\n      # Signature version (all services except s3)\n    attr_reader :signature_version\n\n    def init(service_info, aws_access_key_id, aws_secret_access_key, params={}) #:nodoc:\n      @params = params\n      # If one defines EC2_URL he may forget to use a single slash as an \"empty service\" path.\n      # Amazon does not like this therefore add this bad boy if he is missing...\n      service_info[:default_service] = '/' if service_info[:default_service].right_blank?\n      raise AwsError.new(\"AWS access keys are required to operate on #{service_info[:name]}\") \\\n        if aws_access_key_id.right_blank? || aws_secret_access_key.right_blank?\n      @aws_access_key_id     = aws_access_key_id\n      @aws_secret_access_key = aws_secret_access_key\n      # if the endpoint was explicitly defined - then use it\n      if @params[:endpoint_url]\n        uri = URI.parse(@params[:endpoint_url])\n        @params[:server]   = uri.host\n        @params[:port]     = uri.port\n        @params[:service]  = uri.path\n        @params[:protocol] = uri.scheme\n        # make sure the 'service' path is not empty\n        @params[:service]  = service_info[:default_service] if @params[:service].right_blank?\n        @params[:region]   = nil\n        default_port       = uri.default_port\n      else\n        @params[:server]   ||= service_info[:default_host]\n        @params[:server]     = \"#{@params[:region]}.#{@params[:server]}\" if @params[:region]\n        @params[:port]     ||= service_info[:default_port]\n        @params[:service]  ||= service_info[:default_service]\n        @params[:protocol] ||= service_info[:default_protocol]\n        default_port         = @params[:protocol] == 'https' ? 443 : 80\n      end\n      # build a host name to sign\n      @params[:host_to_sign]  = @params[:server].dup\n      @params[:host_to_sign] << \":#{@params[:port]}\" unless default_port == @params[:port].to_i\n      # a set of options to be passed to RightHttpConnection object\n      @params[:connection_options] = {} unless @params[:connection_options].is_a?(Hash) \n      @with_connection_options = {}\n      @params[:connections] ||= :shared # || :dedicated\n      @params[:max_connections] ||= 10\n      @params[:connection_lifetime] ||= 20*60\n      @params[:api_version]  ||= service_info[:default_api_version]\n      @logger = @params[:logger]\n      @logger = ::Rails.logger       if !@logger && defined?(::Rails) && ::Rails.respond_to?(:logger)\n      @logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)\n      @logger = Logger.new(STDOUT)   if !@logger\n      @logger.info \"New #{self.class.name} using #{@params[:connections]} connections mode\"\n      @error_handler = nil\n      @cache = {}\n      @signature_version = (params[:signature_version] || DEFAULT_SIGNATURE_VERSION).to_s\n    end\n\n    def signed_service_params(aws_secret_access_key, service_hash, http_verb=nil, host=nil, service=nil )\n      case signature_version.to_s\n      when '0' then AwsUtils::sign_request_v0(aws_secret_access_key, service_hash)\n      when '1' then AwsUtils::sign_request_v1(aws_secret_access_key, service_hash)\n      when '2' then AwsUtils::sign_request_v2(aws_secret_access_key, service_hash, http_verb, host, service)\n      else raise AwsError.new(\"Unknown signature version (#{signature_version.to_s}) requested\")\n      end\n    end\n\n    # Returns +true+ if the describe_xxx responses are being cached \n    def caching?\n      @params.key?(:cache) ? @params[:cache] : @@caching\n    end\n    \n    # Check if the aws function response hits the cache or not.\n    # If the cache hits:\n    # - raises an +AwsNoChange+ exception if +do_raise+ == +:raise+.\n    # - returnes parsed response from the cache if it exists or +true+ otherwise.\n    # If the cache miss or the caching is off then returns +false+.\n    def cache_hits?(function, response, do_raise=:raise)\n      result = false\n      if caching?\n        function = function.to_sym\n        # get rid of requestId (this bad boy was added for API 2008-08-08+ and it is uniq for every response)\n        # feb 04, 2009 (load balancer uses 'RequestId' hence use 'i' modifier to hit it also)\n        response = response.sub(%r{<requestId>.+?</requestId>}i, '')\n        # this should work for both ruby 1.8.x and 1.9.x\n        response_md5 = Digest::MD5::new.update(response).to_s\n        # check for changes\n        unless @cache[function] && @cache[function][:response_md5] == response_md5\n          # well, the response is new, reset cache data\n          update_cache(function, {:response_md5 => response_md5, \n                                  :timestamp    => Time.now, \n                                  :hits         => 0, \n                                  :parsed       => nil})\n        else\n          # aha, cache hits, update the data and throw an exception if needed\n          @cache[function][:hits] += 1\n          if do_raise == :raise\n            raise(AwsNoChange, \"Cache hit: #{function} response has not changed since \"+\n                               \"#{@cache[function][:timestamp].strftime('%Y-%m-%d %H:%M:%S')}, \"+\n                               \"hits: #{@cache[function][:hits]}.\")\n          else\n            result = @cache[function][:parsed] || true\n          end\n        end\n      end\n      result\n    end\n    \n    def update_cache(function, hash)\n      (@cache[function.to_sym] ||= {}).merge!(hash) if caching?\n    end\n    \n    def on_exception(options={:raise=>true, :log=>true}) # :nodoc:\n      raise if $!.is_a?(AwsNoChange)\n      AwsError::on_aws_exception(self, options)\n    end\n\n    #----------------------------\n    # HTTP Connections handling\n    #----------------------------\n\n    def get_server_url(request) # :nodoc:\n      \"#{request[:protocol]}://#{request[:server]}:#{request[:port]}\"\n    end\n\n    def get_connections_storage(aws_service) # :nodoc:\n      case @params[:connections].to_s\n      when 'dedicated' then @connections_storage        ||= {}\n      else                  Thread.current[aws_service] ||= {}\n      end\n    end\n\n    def destroy_connection(request, reason) # :nodoc:\n      connections = get_connections_storage(request[:aws_service])\n      server_url  = get_server_url(request)\n      if connections[server_url]\n        connections[server_url][:connection].finish(reason)\n        connections.delete(server_url)\n      end\n    end\n\n    # Expire the connection if it has expired.\n    def get_connection(request) # :nodoc:\n      server_url         = get_server_url(request)\n      connection_storage = get_connections_storage(request[:aws_service])\n      life_time_scratch  = Time.now-@params[:connection_lifetime]\n      # Delete out-of-dated connections\n      connections_in_list = 0\n      connection_storage.to_a.sort{|conn1, conn2| conn2[1][:last_used_at] <=> conn1[1][:last_used_at]}.each do |serv_url, conn_opts|\n        if    @params[:max_connections] <= connections_in_list\n          conn_opts[:connection].finish('out-of-limit')\n          connection_storage.delete(server_url)\n        elsif conn_opts[:last_used_at] < life_time_scratch\n          conn_opts[:connection].finish('out-of-date')\n          connection_storage.delete(server_url)\n        else\n          connections_in_list += 1\n        end\n      end\n      connection = (connection_storage[server_url] ||= {})\n      connection[:last_used_at] = Time.now\n      connection[:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)\n    end\n\n    #----------------------------\n    # HTTP Requests handling\n    #----------------------------\n\n    # ACF, AMS, EC2, LBS and SDB uses this guy\n    # SQS and S3 use their own methods\n    def generate_request_impl(verb, action, options={}, custom_options={}) #:nodoc:\n      # Form a valid http verb: 'GET' or 'POST' (all the other are not supported now)\n      http_verb = verb.to_s.upcase\n      # remove empty keys from request options\n      options.delete_if { |key, value| value.nil? }\n      # prepare service data\n      service_hash = {\"Action\"         => action,\n                      \"AWSAccessKeyId\" => @aws_access_key_id,\n                      \"Version\"        => custom_options[:api_version] || @params[:api_version] }\n      service_hash.merge!(options)\n      service_hash[\"SecurityToken\"] = @params[:token] if @params[:token]\n      # Sign request options\n      service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:host_to_sign], @params[:service])\n      # Use POST if the length of the query string is too large\n      # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/MakingRESTRequests.html\n      if http_verb != 'POST' && service_params.size > 2000\n        http_verb = 'POST'\n        if signature_version == '2'\n          service_params = signed_service_params(@aws_secret_access_key, service_hash, http_verb, @params[:host_to_sign], @params[:service])\n        end\n      end\n      # create a request\n      case http_verb\n      when 'GET'\n        request = Net::HTTP::Get.new(\"#{@params[:service]}?#{service_params}\")\n      when 'POST'\n        request      = Net::HTTP::Post.new(@params[:service])\n        request.body = service_params\n        request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'\n      else\n        raise \"Unsupported HTTP verb #{verb.inspect}!\"\n      end\n      # prepare output hash\n      request_hash = { :request  => request,\n                       :server   => @params[:server],\n                       :port     => @params[:port],\n                       :protocol => @params[:protocol] }\n      request_hash.merge!(@params[:connection_options])\n      request_hash.merge!(@with_connection_options)\n      \n      # If an action is marked as \"non-retryable\" and there was no :raise_on_timeout option set\n      # explicitly then do set that option\n      if Array(RightAwsBase::raise_on_timeout_on_actions).include?(action) && !request_hash.has_key?(:raise_on_timeout)\n        request_hash.merge!(:raise_on_timeout => true)\n      end\n\n      request_hash\n    end\n\n    # All services uses this guy.\n    def request_info_impl(aws_service, benchblock, request, parser, &block) #:nodoc:\n      request[:aws_service] = aws_service\n      @connection    = get_connection(request)\n      @last_request  = request[:request]\n      @last_response = nil\n      response = nil\n      blockexception = nil\n\n      if(block != nil)\n        # TRB 9/17/07 Careful - because we are passing in blocks, we get a situation where\n        # an exception may get thrown in the block body (which is high-level\n        # code either here or in the application) but gets caught in the\n        # low-level code of HttpConnection.  The solution is not to let any\n        # exception escape the block that we pass to HttpConnection::request.\n        # Exceptions can originate from code directly in the block, or from user\n        # code called in the other block which is passed to response.read_body.\n        benchblock.service.add! do\n          begin\n            responsehdr = @connection.request(request) do |response|\n            #########\n              begin\n                @last_response = response\n                if response.is_a?(Net::HTTPSuccess)\n                  @error_handler = nil\n                  response.read_body(&block)\n                else\n                  @error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler\n                  check_result   = @error_handler.check(request)\n                  if check_result\n                    @error_handler = nil\n                    return check_result\n                  end\n                  raise AwsError.new(@last_errors, @last_response.code, @last_request_id)\n                end\n              rescue Exception => e\n                blockexception = e\n              end\n            end\n          rescue Exception => e\n            # Kill a connection if we run into a low level connection error\n            destroy_connection(request, \"error: #{e.message}\")\n            raise e\n          end\n          #########\n\n          #OK, now we are out of the block passed to the lower level\n          if(blockexception)\n            raise blockexception\n          end\n          benchblock.xml.add! do\n            parser.parse(responsehdr)\n          end\n          return parser.result\n        end\n      else\n        benchblock.service.add! do\n          begin\n            response = @connection.request(request)\n          rescue Exception => e\n            # Kill a connection if we run into a low level connection error\n            destroy_connection(request, \"error: #{e.message}\")\n            raise e\n          end\n        end\n          # check response for errors...\n        @last_response = response\n        if response.is_a?(Net::HTTPSuccess)\n          @error_handler = nil\n          benchblock.xml.add! { parser.parse(response) }\n          return parser.result\n        else\n          @error_handler = AWSErrorHandler.new(self, parser, :errors_list => self.class.amazon_problems) unless @error_handler\n          check_result   = @error_handler.check(request)\n          if check_result\n            @error_handler = nil\n            return check_result \n          end\n          raise AwsError.new(@last_errors, @last_response.code, @last_request_id)\n        end\n      end\n    rescue\n      @error_handler = nil\n      raise\n    end\n\n    def request_cache_or_info(method, link, parser_class, benchblock, use_cache=true, &block) #:nodoc:\n      # We do not want to break the logic of parsing hence will use a dummy parser to process all the standard\n      # steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.\n      # If the caching is enabled and hit then throw  AwsNoChange.\n      # P.S. caching works for the whole images list only! (when the list param is blank)\n      # check cache\n      response, params = request_info(link, RightDummyParser.new)\n      cache_hits?(method.to_sym, response.body) if use_cache\n      parser = parser_class.new(:logger => @logger)\n      benchblock.xml.add!{ parser.parse(response, params) }\n      result = block ? block.call(parser) : parser.result\n      # update parsed data\n      update_cache(method.to_sym, :parsed => result) if use_cache\n      result\n    end\n\n    # Returns Amazons request ID for the latest request\n    def last_request_id\n      @last_response && @last_response.body.to_s[%r{<requestId>(.+?)</requestId>}i] && $1\n    end\n\n    # Incrementally lists something.\n    def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:\n      params = params.dup\n      params['MaxItems'] = params.delete(:max_items) if params[:max_items]\n      params['Marker']   = params.delete(:marker)    if params[:marker]\n      last_response = nil\n      loop do\n        last_response    = request_info( generate_request(action, params), parser_class.new(:logger => @logger))\n        params['Marker'] = last_response[:marker]\n        break unless block && block.call(last_response) && !last_response[:marker].right_blank?\n      end\n      last_response\n    end\n\n    # Format array of items into Amazons handy hash ('?' is a place holder):\n    # Options:\n    #   :default => \"something\"   : Set a value to \"something\" when it is nil\n    #   :default => :skip_nils    : Skip nil values\n    #\n    #  amazonize_list('Item', ['a', 'b', 'c']) =>\n    #    { 'Item.1' => 'a', 'Item.2' => 'b', 'Item.3' => 'c' }\n    #\n    #  amazonize_list('Item.?.instance', ['a', 'c']) #=>\n    #    { 'Item.1.instance' => 'a', 'Item.2.instance' => 'c' }\n    #\n    #  amazonize_list(['Item.?.Name', 'Item.?.Value'], {'A' => 'a', 'B' => 'b'}) #=>\n    #    { 'Item.1.Name' => 'A', 'Item.1.Value' => 'a',\n    #      'Item.2.Name' => 'B', 'Item.2.Value' => 'b'  }\n    #\n    #  amazonize_list(['Item.?.Name', 'Item.?.Value'], [['A','a'], ['B','b']]) #=>\n    #    { 'Item.1.Name' => 'A', 'Item.1.Value' => 'a',\n    #      'Item.2.Name' => 'B', 'Item.2.Value' => 'b'  }\n    #\n    #  amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], {'A' => ['aa','ab'], 'B' => ['ba','bb']}) #=>\n    #  amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], [['A',['aa','ab']], ['B',['ba','bb']]])   #=>\n    #    {\"Filter.1.Key\"=>\"A\",\n    #     \"Filter.1.Value.1\"=>\"aa\",\n    #     \"Filter.1.Value.2\"=>\"ab\",\n    #     \"Filter.2.Key\"=>\"B\",\n    #     \"Filter.2.Value.1\"=>\"ba\",\n    #     \"Filter.2.Value.2\"=>\"bb\"}\n    def amazonize_list(masks, list, options={}) #:nodoc:\n      groups = {}\n      list_idx = options[:index] || 1\n      Array(list).each do |list_item|\n        Array(masks).each_with_index do |mask, mask_idx|\n          key = mask[/\\?/] ? mask.dup : mask.dup + '.?'\n          key.sub!('?', list_idx.to_s)\n          value = Array(list_item)[mask_idx]\n          if value.is_a?(Array)\n            groups.merge!(amazonize_list(key, value, options))\n          else\n            if value.nil?\n              next if options[:default] == :skip_nils\n              value = options[:default]\n            end\n            # Hack to avoid having unhandled '?' in keys : do replace them all with '1':\n            #  bad:  ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {\"Filter.1.Key\"=>:a, \"Filter.1.Value.?\"=>1}\n            #  good: ec2.amazonize_list(['Filter.?.Key', 'Filter.?.Value.?'], { a: => :b }) => {\"Filter.1.Key\"=>:a, \"Filter.1.Value.1\"=>1}\n            key.gsub!('?', '1')\n            groups[key] = value\n          end\n        end\n        list_idx += 1\n      end\n      groups\n    end\n\n    BLOCK_DEVICE_KEY_MAPPING = {                                                           # :nodoc:\n      :device_name               => 'DeviceName',\n      :virtual_name              => 'VirtualName',\n      :no_device                 => 'NoDevice',\n      :ebs_snapshot_id           => 'Ebs.SnapshotId',\n      :ebs_volume_size           => 'Ebs.VolumeSize',\n      :ebs_delete_on_termination => 'Ebs.DeleteOnTermination' }\n\n    def amazonize_block_device_mappings(block_device_mappings, key = 'BlockDeviceMapping') # :nodoc:\n      result = {}\n      unless block_device_mappings.right_blank?\n        block_device_mappings = [block_device_mappings] unless block_device_mappings.is_a?(Array)\n        block_device_mappings.each_with_index do |b, idx|\n          BLOCK_DEVICE_KEY_MAPPING.each do |local_name, remote_name|\n            value = b[local_name]\n            case local_name\n            when :no_device then value = value ? '' : nil   # allow to pass :no_device as boolean\n            end\n            result[\"#{key}.#{idx+1}.#{remote_name}\"] = value unless value.nil?\n          end\n        end\n      end\n      result\n    end\n\n    # Build API request keys set.\n    #\n    # Options is a hash, expectations is a set of keys [and rules] how to represent options.\n    # Mappings is an Array (may include hashes) or a Hash.\n    #\n    #  Example:\n    #\n    #  options = { :valid_from              => Time.now - 10,\n    #              :instance_count          => 3,\n    #              :image_id                => 'ami-08f41161',\n    #              :spot_price              => 0.059,\n    #              :instance_type           => 'c1.medium',\n    #              :instance_count          => 1,\n    #              :key_name                => 'tim',\n    #              :availability_zone       => 'us-east-1a',\n    #              :monitoring_enabled      => true,\n    #              :launch_group            => 'lg1',\n    #              :availability_zone_group => 'azg1',\n    #              :groups                  => ['a', 'b', 'c'],\n    #              :group_ids               => 'sg-1',\n    #              :user_data               => 'konstantin',\n    #              :block_device_mappings   => [ { :device_name     => '/dev/sdk',\n    #                                              :ebs_snapshot_id => 'snap-145cbc7d',\n    #                                              :ebs_delete_on_termination => true,\n    #                                              :ebs_volume_size => 3,\n    #                                              :virtual_name => 'ephemeral2' }]}\n    #  mappings = { :spot_price,\n    #               :availability_zone_group,\n    #               :launch_group,\n    #               :type,\n    #               :instance_count,\n    #               :image_id              => 'LaunchSpecification.ImageId',\n    #               :instance_type         => 'LaunchSpecification.InstanceType',\n    #               :key_name              => 'LaunchSpecification.KeyName',\n    #               :addressing_type       => 'LaunchSpecification.AddressingType',\n    #               :kernel_id             => 'LaunchSpecification.KernelId',\n    #               :ramdisk_id            => 'LaunchSpecification.RamdiskId',\n    #               :subnet_id             => 'LaunchSpecification.SubnetId',\n    #               :availability_zone     => 'LaunchSpecification.Placement.AvailabilityZone',\n    #               :monitoring_enabled    => 'LaunchSpecification.Monitoring.Enabled',\n    #               :valid_from            => { :value => Proc.new { !options[:valid_from].right_blank?  && AwsUtils::utc_iso8601(options[:valid_from]) }},\n    #               :valid_until           => { :value => Proc.new { !options[:valid_until].right_blank? && AwsUtils::utc_iso8601(options[:valid_until]) }},\n    #               :user_data             => { :name  => 'LaunchSpecification.UserData',\n    #                                           :value => Proc.new { !options[:user_data].right_blank? && Base64.encode64(options[:user_data]).delete(\"\\n\") }},\n    #               :groups                => { :amazonize_list => 'LaunchSpecification.SecurityGroup'},\n    #               :group_ids             => { :amazonize_list => 'LaunchSpecification.SecurityGroupId'},\n    #               :block_device_mappings => { :amazonize_bdm  => 'LaunchSpecification.BlockDeviceMapping'})\n    #\n    #  map_api_keys_and_values( options, mappings) #=>\n    #    {\"LaunchSpecification.BlockDeviceMapping.1.Ebs.DeleteOnTermination\" => true,\n    #     \"LaunchSpecification.BlockDeviceMapping.1.VirtualName\"             => \"ephemeral2\",\n    #     \"LaunchSpecification.BlockDeviceMapping.1.Ebs.VolumeSize\"          => 3,\n    #     \"LaunchSpecification.BlockDeviceMapping.1.Ebs.SnapshotId\"          => \"snap-145cbc7d\",\n    #     \"LaunchSpecification.BlockDeviceMapping.1.DeviceName\"              => \"/dev/sdk\",\n    #     \"LaunchSpecification.SecurityGroupId.1\"                            => \"sg-1\",\n    #     \"LaunchSpecification.InstanceType\"                                 => \"c1.medium\",\n    #     \"LaunchSpecification.KeyName\"                                      => \"tim\",\n    #     \"LaunchSpecification.ImageId\"                                      => \"ami-08f41161\",\n    #     \"LaunchSpecification.SecurityGroup.1\"                              => \"a\",\n    #     \"LaunchSpecification.SecurityGroup.2\"                              => \"b\",\n    #     \"LaunchSpecification.SecurityGroup.3\"                              => \"c\",\n    #     \"LaunchSpecification.Placement.AvailabilityZone\"                   => \"us-east-1a\",\n    #     \"LaunchSpecification.Monitoring.Enabled\"                           => true,\n    #     \"LaunchGroup\"                                                      => \"lg1\",\n    #     \"InstanceCount\"                                                    => 1,\n    #     \"SpotPrice\"                                                        => 0.059,\n    #     \"AvailabilityZoneGroup\"                                            => \"azg1\",\n    #     \"ValidFrom\"                                                        => \"2011-06-30T08:06:30.000Z\",\n    #     \"LaunchSpecification.UserData\"                                     => \"a29uc3RhbnRpbg==\"}\n    #\n    def map_api_keys_and_values(options, *mappings) # :nodoc:\n      result = {}\n      vars   = {}\n      # Fix inputs and make them all to be hashes\n      mappings.flatten.each do |mapping|\n        unless mapping.is_a?(Hash)\n          # mapping is just a :key_name\n          mapping = { mapping => { :name  => mapping.to_s.right_camelize, :value => options[mapping] }}\n        else\n          mapping.each do |local_key, api_opts|\n            unless api_opts.is_a?(Hash)\n              # mapping is a { :key_name => 'ApiKeyName' }\n              mapping[local_key] = { :name  => api_opts.to_s, :value => options[local_key]}\n            else\n              # mapping is a { :key_name => { :name => 'ApiKeyName', :value => 'Value', ... etc}  }\n              api_opts[:name]  = local_key.to_s.right_camelize if (api_opts.keys & [:name, :amazonize_list, :amazonize_bdm]).right_blank?\n              api_opts[:value] = options[local_key] unless api_opts.has_key?(:value)\n            end\n          end\n        end\n        vars.merge! mapping\n      end\n      # Build API keys set\n      #  vars now is a Hash:\n      #    { :key1 => { :name           => 'ApiKey1',   :value => 'BlahBlah'},\n      #      :key2 => { :amazonize_list => 'ApiKey2.?', :value => [1, ...] },\n      #      :key3 => { :amazonize_bdm  => 'BDM',       :value => [{..}, ...] }, ... }\n      #\n      vars.each do |local_key, api_opts|\n        if api_opts[:amazonize_list]\n          result.merge!(amazonize_list( api_opts[:amazonize_list], api_opts[:value] )) unless api_opts[:value].right_blank?\n        elsif api_opts[:amazonize_bdm]\n          result.merge!(amazonize_block_device_mappings( api_opts[:value], api_opts[:amazonize_bdm] )) unless api_opts[:value].right_blank?\n        else\n          api_key = api_opts[:name]\n          value   = api_opts[:value]\n          value   = value.call if value.is_a?(Proc)\n          next if value.right_blank?\n          result[api_key] = value\n        end\n      end\n      #\n      result\n    end\n\n    # Transform a hash of parameters into a hash suitable for sending\n    # to Amazon using a key mapping.\n    #\n    #  amazonize_hash_with_key_mapping('Group.Filter',\n    #    {:some_param => 'SomeParam'},\n    #    {:some_param => 'value'}) #=> {'Group.Filter.SomeParam' => 'value'}\n    #\n    def amazonize_hash_with_key_mapping(key, mapping, hash, options={})\n      result = {}\n      unless hash.right_blank?\n        mapping.each do |local_name, remote_name|\n          value = hash[local_name]\n          next if value.nil?\n          result[\"#{key}.#{remote_name}\"] = value\n        end\n      end\n      result\n    end\n\n    # Transform a list of hashes of parameters into a hash suitable for sending\n    # to Amazon using a key mapping.\n    #\n    #  amazonize_list_with_key_mapping('Group.Filter',\n    #    [{:some_param => 'SomeParam'}, {:some_param => 'SomeParam'}],\n    #    {:some_param => 'value'}) #=>\n    #      {'Group.Filter.1.SomeParam' => 'value',\n    #       'Group.Filter.2.SomeParam' => 'value'}\n    #\n    def amazonize_list_with_key_mapping(key, mapping, list, options={})\n      result = {}\n      unless list.right_blank?\n        list.each_with_index do |item, index|\n          mapping.each do |local_name, remote_name|\n            value = item[local_name]\n            next if value.nil?\n            result[\"#{key}.#{index+1}.#{remote_name}\"] = value\n          end\n        end\n      end\n    end\n    \n    # Execute a block of code with custom set of settings for right_http_connection.\n    # Accepts next options (see Rightscale::HttpConnection for explanation):\n    #  :raise_on_timeout\n    #  :http_connection_retry_count\n    #  :http_connection_open_timeout\n    #  :http_connection_read_timeout\n    #  :http_connection_retry_delay\n    #  :user_agent\n    #  :exception\n    #\n    #  Example #1:\n    #\n    #  # Try to create a snapshot but stop with exception if timeout is received\n    #  # to avoid having a duplicate API calls that create duplicate snapshots.\n    #  ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key)\n    #  ec2.with_connection_options(:raise_on_timeout => true) do\n    #    ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!')\n    #  end\n    #\n    #  Example #2:\n    #\n    #  # Opposite case when the setting is global:\n    #  @ec2 = Rightscale::Ec2::new(aws_access_key_id, aws_secret_access_key,\n    #                           :connection_options => { :raise_on_timeout => true })\n    #  # Create an SSHKey but do tries on timeout\n    #  ec2.with_connection_options(:raise_on_timeout => false) do\n    #    new_key = ec2.create_key_pair('my_test_key')\n    #  end\n    #\n    #  Example #3:\n    #\n    #  # Global settings (HttpConnection level):\n    #  Rightscale::HttpConnection::params[:http_connection_open_timeout] = 5\n    #  Rightscale::HttpConnection::params[:http_connection_read_timeout] = 250\n    #  Rightscale::HttpConnection::params[:http_connection_retry_count]  = 2\n    #\n    #  # Local setings (RightAws level)\n    #  ec2 = Rightscale::Ec2::new(AWS_ID, AWS_KEY,\n    #    :region => 'us-east-1',\n    #    :connection_options => {\n    #      :http_connection_read_timeout => 2,\n    #      :http_connection_retry_count  => 5,\n    #      :user_agent => 'Mozilla 4.0'\n    #    })\n    #\n    #  # Custom settings (API call level)\n    #  ec2.with_connection_options(:raise_on_timeout => true,\n    #                              :http_connection_read_timeout => 10,\n    #                              :user_agent => '') do\n    #    pp ec2.describe_images\n    #  end\n    #\n    def with_connection_options(options, &block)\n      @with_connection_options = options\n      block.call self\n    ensure\n      @with_connection_options = {}\n    end\n  end\n\n\n  # Exception class to signal any Amazon errors. All errors occuring during calls to Amazon's\n  # web services raise this type of error.\n  # Attribute inherited by RuntimeError:\n  #  message    - the text of the error, generally as returned by AWS in its XML response.\n  class AwsError < RuntimeError\n    \n    # either an array of errors where each item is itself an array of [code, message]),\n    # or an error string if the error was raised manually, as in <tt>AwsError.new('err_text')</tt>\n    attr_reader :errors\n    \n    # Request id (if exists)\n    attr_reader :request_id\n    \n    # Response HTTP error code\n    attr_reader :http_code\n    \n    def initialize(errors=nil, http_code=nil, request_id=nil)\n      @errors      = errors\n      @request_id  = request_id\n      @http_code   = http_code\n      super(@errors.is_a?(Array) ? @errors.map{|code, msg| \"#{code}: #{msg}\"}.join(\"; \") : @errors.to_s)\n    end\n    \n    # Does any of the error messages include the regexp +pattern+?\n    # Used to determine whether to retry request.\n    def include?(pattern)\n      if @errors.is_a?(Array)\n        @errors.each{ |code, msg| return true if code =~ pattern } \n      else\n        return true if @errors_str =~ pattern \n      end\n      false\n    end\n    \n    # Generic handler for AwsErrors. +aws+ is the RightAws::S3, RightAws::EC2, or RightAws::SQS\n    # object that caused the exception (it must provide last_request and last_response). Supported\n    # boolean options are:\n    # * <tt>:log</tt> print a message into the log using aws.logger to access the Logger\n    # * <tt>:puts</tt> do a \"puts\" of the error\n    # * <tt>:raise</tt> re-raise the error after logging\n    def self.on_aws_exception(aws, options={:raise=>true, :log=>true})\n \t    # Only log & notify if not user error\n      if !options[:raise] || system_error?($!)\n        error_text = \"#{$!.inspect}\\n#{$@}.join('\\n')}\"\n        puts error_text if options[:puts]\n          # Log the error\n        if options[:log]\n          request  = aws.last_request  ? aws.last_request.path :  '-none-'\n          response = aws.last_response ? \"#{aws.last_response.code} -- #{aws.last_response.message} -- #{aws.last_response.body}\" : '-none-'\n          aws.logger.error error_text\n          aws.logger.error \"Request was:  #{request}\"\n          aws.logger.error \"Response was: #{response}\"\n        end\n      end\n      raise if options[:raise]  # re-raise an exception\n      return nil\n    end\n    \n    # True if e is an AWS system error, i.e. something that is for sure not the caller's fault.\n    # Used to force logging.\n    def self.system_error?(e)\n \t    !e.is_a?(self) || e.message =~ /InternalError|InsufficientInstanceCapacity|Unavailable/\n    end\n\n  end\n\n\n  class AWSErrorHandler\n    # 0-100 (%) \n    DEFAULT_CLOSE_ON_4XX_PROBABILITY = 10     \n    \n    @@reiteration_start_delay = 0.2\n    def self.reiteration_start_delay\n      @@reiteration_start_delay\n    end\n    def self.reiteration_start_delay=(reiteration_start_delay)\n      @@reiteration_start_delay = reiteration_start_delay\n    end\n\n    @@reiteration_time = 5\n    def self.reiteration_time\n      @@reiteration_time\n    end\n    def self.reiteration_time=(reiteration_time)\n      @@reiteration_time = reiteration_time\n    end\n    \n    @@close_on_error = true \n    def self.close_on_error \n      @@close_on_error \n    end \n    def self.close_on_error=(close_on_error) \n      @@close_on_error = close_on_error \n    end \n \n    @@close_on_4xx_probability = DEFAULT_CLOSE_ON_4XX_PROBABILITY \n    def self.close_on_4xx_probability \n      @@close_on_4xx_probability \n    end \n    def self.close_on_4xx_probability=(close_on_4xx_probability) \n      @@close_on_4xx_probability = close_on_4xx_probability \n    end \n \n    # params: \n    #  :reiteration_time \n    #  :errors_list \n    #  :close_on_error           = true | false \n    #  :close_on_4xx_probability = 1-100 \n    def initialize(aws, parser, params={}) #:nodoc:     \n      @aws           = aws              # Link to RightEc2 | RightSqs | RightS3 instance\n      @parser        = parser           # parser to parse Amazon response\n      @started_at    = Time.now\n      @stop_at       = @started_at  + (params[:reiteration_time] || @@reiteration_time) \n      @errors_list   = params[:errors_list] || [] \n      @reiteration_delay = @@reiteration_start_delay\n      @retries       = 0\n      # close current HTTP(S) connection on 5xx, errors from list and 4xx errors \n      @close_on_error           = params[:close_on_error].nil? ? @@close_on_error : params[:close_on_error]\n      @close_on_4xx_probability = params[:close_on_4xx_probability] || @@close_on_4xx_probability       \n    end\n    \n      # Returns false if \n    def check(request)  #:nodoc:\n      result           = false\n      error_found      = false\n      redirect_detected= false\n      error_match      = nil\n      last_errors_text = ''\n      response         = @aws.last_response\n      # log error\n      request_text_data = \"#{request[:protocol]}://#{request[:server]}:#{request[:port]}#{request[:request].path}\"\n      # is this a redirect?\n      # yes!\n      if response.is_a?(Net::HTTPRedirection)\n        redirect_detected = true \n      else\n        # no, it's an error ...\n        @aws.logger.warn(\"##### #{@aws.class.name} returned an error: #{response.code} #{response.message}\\n#{response.body} #####\")\n        @aws.logger.warn(\"##### #{@aws.class.name} request: #{request_text_data} ####\")\n      end\n\n      # Extract error/redirection message from the response body\n      # Amazon claims that a redirection must have a body but somethimes it is nil....\n      if response.body && response.body[/^(<\\?xml|<ErrorResponse)/]\n        error_parser = RightErrorResponseParser.new\n        @aws.class.bench_xml.add! do\n          error_parser.parse(response.body)\n        end\n        @aws.last_errors     = error_parser.errors\n        @aws.last_request_id = error_parser.requestID\n        last_errors_text     = @aws.last_errors.flatten.join(\"\\n\")\n      else\n        @aws.last_errors     = [[response.code, \"#{response.message} (#{request_text_data})\"]]\n        @aws.last_request_id = '-undefined-'\n        last_errors_text     = response.message\n      end\n      \n      # Ok, it is a redirect, find the new destination location\n      if redirect_detected\n        location = response['location']\n        # As for 301 ( Moved Permanently) Amazon does not return a 'Location' header but\n        # it is possible to extract a new endpoint from the response body\n        if location.right_blank? && response.code=='301' && response.body\n          new_endpoint = response.body[/<Endpoint>(.*?)<\\/Endpoint>/] && $1\n          location     = \"#{request[:protocol]}://#{new_endpoint}:#{request[:port]}#{request[:request].path}\"\n        end\n        # ... log information and ...\n        @aws.logger.info(\"##### #{@aws.class.name} redirect requested: #{response.code} #{response.message} #####\")\n        @aws.logger.info(\"      Old location: #{request_text_data}\")\n        @aws.logger.info(\"      New location: #{location}\")\n        @aws.logger.info(\"      Request Verb: #{request[:request].class.name}\")\n        # ... fix the connection data\n        request[:server]   = URI.parse(location).host\n        request[:protocol] = URI.parse(location).scheme\n        request[:port]     = URI.parse(location).port\n      else\n        # Not a redirect but an error: try to find the error in our list\n        @errors_list.each do |error_to_find|\n          if last_errors_text[/#{error_to_find}/i]\n            error_found = true\n            error_match = error_to_find\n            @aws.logger.warn(\"##### Retry is needed, error pattern match: #{error_to_find} #####\")\n            break\n          end\n        end\n      end\n      \n        # check the time has gone from the first error come\n      if redirect_detected || error_found\n        # Close the connection to the server and recreate a new one. \n        # It may have a chance that one server is a semi-down and reconnection \n        # will help us to connect to the other server \n        if !redirect_detected && @close_on_error\n          @aws.destroy_connection(request, \"#{self.class.name}: error match to pattern '#{error_match}'\")\n        end \n                 \n        if (Time.now < @stop_at)\n          @retries += 1\n          unless redirect_detected\n            @aws.logger.warn(\"##### Retry ##{@retries} is being performed. Sleeping for #{@reiteration_delay} sec. Whole time: #{Time.now-@started_at} sec ####\")\n            sleep @reiteration_delay \n            @reiteration_delay *= 2\n\n          else\n            @aws.logger.info(\"##### Retry ##{@retries} is being performed due to a redirect.  ####\")\n          end\n\n          # Always make sure that the fp is set to point to the beginning(?)\n          # of the File/IO. TODO: it assumes that offset is 0, which is bad.\n          if(request[:request].body_stream && request[:request].body_stream.respond_to?(:pos))\n            begin\n              request[:request].body_stream.pos = 0\n            rescue Exception => e\n              @logger.warn(\"Retry may fail due to unable to reset the file pointer\" +\n                           \" -- #{self.class.name} : #{e.inspect}\")\n            end\n          end\n\n          result = @aws.request_info(request, @parser)\n        else\n          @aws.logger.warn(\"##### Ooops, time is over... ####\")\n        end \n      # aha, this is unhandled error: \n      elsif @close_on_error \n        # On 5xx(Server errors), 403(RequestTimeTooSkewed) and 408(Request Timeout) a conection has to be closed\n        if @aws.last_response.code.to_s[/^(5\\d\\d|403|408)$/]\n          @aws.destroy_connection(request, \"#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}'\")\n        # Is this a 4xx error ? \n        elsif @aws.last_response.code.to_s[/^4\\d\\d$/] && @close_on_4xx_probability > rand(100) \n          @aws.destroy_connection(request, \"#{self.class.name}: code: #{@aws.last_response.code}: '#{@aws.last_response.message}', \" +\n                                           \"probability: #{@close_on_4xx_probability}%\")\n        end\n      end\n      result\n    end\n    \n  end\n\n\n  #-----------------------------------------------------------------\n\n  class RightSaxParserCallbackTemplate #:nodoc:\n    def initialize(right_aws_parser) \n      @right_aws_parser = right_aws_parser \n    end \n    def on_characters(chars) \n      @right_aws_parser.text(chars)\n    end \n    def on_start_document; end \n    def on_comment(msg); end \n    def on_processing_instruction(target, data); end \n    def on_cdata_block(cdata); end \n    def on_end_document; end \n  end \n\n  class RightSaxParserCallback < RightSaxParserCallbackTemplate\n    def self.include_callback\n      include XML::SaxParser::Callbacks\n    end\n    def on_start_element(name, attr_hash)\n      @right_aws_parser.tag_start(name, attr_hash)\n    end\n    def on_end_element(name)\n      @right_aws_parser.tag_end(name)\n    end\n  end\n\n  class RightSaxParserCallbackNs < RightSaxParserCallbackTemplate\n    def on_start_element_ns(name, attr_hash, prefix, uri, namespaces)\n      @right_aws_parser.tag_start(name, attr_hash)\n    end\n    def on_end_element_ns(name, prefix, uri)\n      @right_aws_parser.tag_end(name)\n    end\n  end\n\n  class RightAWSParser  #:nodoc:\n      # default parsing library \n    DEFAULT_XML_LIBRARY = 'rexml' \n      # a list of supported parsers \n    @@supported_xml_libs = [DEFAULT_XML_LIBRARY, 'libxml'] \n     \n    @@xml_lib = DEFAULT_XML_LIBRARY # xml library name: 'rexml' | 'libxml' \n    def self.xml_lib\n      @@xml_lib\n    end\n    def self.xml_lib=(new_lib_name)\n      @@xml_lib = new_lib_name\n    end\n    \n    attr_accessor :result\n    attr_reader   :xmlpath\n    attr_accessor :xml_lib\n    attr_reader   :full_tag_name\n    attr_reader   :tag\n    \n    def initialize(params={})\n      @xmlpath = ''\n      @full_tag_name = ''\n      @result  = false\n      @text    = ''\n      @tag     = ''\n      @xml_lib = params[:xml_lib] || @@xml_lib\n      @logger  = params[:logger]\n      reset\n    end\n    def tag_start(name, attributes)\n      @text = ''\n      @tag  = name\n      @full_tag_name += @full_tag_name.empty? ? name : \"/#{name}\"\n      tagstart(name, attributes)\n      @xmlpath = @full_tag_name\n    end\n    def tag_end(name)\n      @xmlpath = @full_tag_name[/^(.*?)\\/?#{name}$/] && $1\n      tagend(name)\n      @full_tag_name = @xmlpath\n    end\n    def text(text)\n      @text += text\n      tagtext(text)\n    end\n      # Parser method.\n      # Params:\n      #   xml_text         - xml message text(String) or Net:HTTPxxx instance (response)\n      #   params[:xml_lib] - library name: 'rexml' | 'libxml'\n    def parse(xml_text, params={})\n        # Get response body\n      xml_text = xml_text.body unless xml_text.is_a?(String)\n      @xml_lib = params[:xml_lib] || @xml_lib\n        # check that we had no problems with this library otherwise use default \n      @xml_lib = DEFAULT_XML_LIBRARY unless @@supported_xml_libs.include?(@xml_lib)       \n        # load xml library\n      if @xml_lib=='libxml' && !defined?(XML::SaxParser)\n        begin\n          require 'xml/libxml'\n          # Setup SaxParserCallback \n          if XML::Parser::VERSION >= '0.5.1' &&\n             XML::Parser::VERSION  < '0.9.7'\n            RightSaxParserCallback.include_callback\n          end           \n        rescue LoadError => e\n          @@supported_xml_libs.delete(@xml_lib)\n          @xml_lib = DEFAULT_XML_LIBRARY\n          if @logger\n            @logger.error e.inspect\n            @logger.error e.backtrace\n            @logger.info \"Can not load 'libxml' library. '#{DEFAULT_XML_LIBRARY}' is used for parsing.\"\n          end\n        end\n      end\n        # Parse the xml text\n      case @xml_lib\n      when 'libxml'\n        if XML::Parser::VERSION >= '0.9.9'\n          # avoid warning on every usage\n          xml        = XML::SaxParser.string(xml_text)\n        else\n          xml        = XML::SaxParser.new\n          xml.string = xml_text \n        end\n        # check libxml-ruby version \n        if    XML::Parser::VERSION >= '0.9.7'\n          xml.callbacks = RightSaxParserCallbackNs.new(self)\n        elsif XML::Parser::VERSION >= '0.5.1'\n          xml.callbacks = RightSaxParserCallback.new(self) \n        else \n          xml.on_start_element{|name, attr_hash| self.tag_start(name, attr_hash)} \n          xml.on_characters{   |text|            self.text(text)}\n          xml.on_end_element{  |name|            self.tag_end(name)} \n        end \n        xml.parse\n      else\n        REXML::Document.parse_stream(xml_text, self)\n      end\n    end\n      # Parser must have a lots of methods \n      # (see /usr/lib/ruby/1.8/rexml/parsers/streamparser.rb)\n      # We dont need most of them in RightAWSParser and method_missing helps us\n      # to skip their definition\n    def method_missing(method, *params)\n        # if the method is one of known - just skip it ...\n      return if [:comment, :attlistdecl, :notationdecl, :elementdecl, \n                 :entitydecl, :cdata, :xmldecl, :attlistdecl, :instruction, \n                 :doctype].include?(method)\n        # ... else - call super to raise an exception\n      super(method, params)\n    end\n      # the functions to be overriden by children (if nessesery)\n    def reset                     ; end\n    def tagstart(name, attributes); end\n    def tagend(name)              ; end\n    def tagtext(text)             ; end\n  end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Errors\n  #-----------------------------------------------------------------\n\n#<Error>\n#  <Code>TemporaryRedirect</Code>\n#  <Message>Please re-send this request to the specified temporary endpoint. Continue to use the original request endpoint for future requests.</Message>\n#  <RequestId>FD8D5026D1C5ABA3</RequestId>\n#  <Endpoint>bucket-for-k.s3-external-3.amazonaws.com</Endpoint>\n#  <HostId>ItJy8xPFPli1fq/JR3DzQd3iDvFCRqi1LTRmunEdM1Uf6ZtW2r2kfGPWhRE1vtaU</HostId>\n#  <Bucket>bucket-for-k</Bucket>\n#</Error>\n\n  class RightErrorResponseParser < RightAWSParser #:nodoc:\n    attr_accessor :errors  # array of hashes: error/message\n    attr_accessor :requestID\n#    attr_accessor :endpoint, :host_id, :bucket\n    def tagend(name)\n      case name\n        when 'RequestID' ; @requestID = @text\n        when 'Code'      ; @code      = @text\n        when 'Message'   ; @message   = @text\n#       when 'Endpoint'  ; @endpoint  = @text\n#       when 'HostId'    ; @host_id   = @text\n#       when 'Bucket'    ; @bucket    = @text\n        when 'Error'     ; @errors   << [ @code, @message ]\n      end\n    end\n    def reset\n      @errors = []\n    end\n  end\n\n  # Dummy parser - does nothing\n  # Returns the original params back\n  class RightDummyParser  # :nodoc:\n    attr_accessor :result\n    def parse(response, params={})\n      @result = [response, params]\n    end\n  end\n\n  class RightHttp2xxParser < RightAWSParser # :nodoc:\n    def parse(response)\n      @result = response.is_a?(Net::HTTPSuccess)\n    end\n  end\n\n  class RightBoolResponseParser < RightAWSParser #:nodoc:\n    def tagend(name)\n      @result = (@text=='true') if name == 'return'\n    end\n  end\n\nend\n\n"
  },
  {
    "path": "lib/awsbase/support.rb",
    "content": "  # These are ActiveSupport-;like extensions to do a few handy things in the gems\n  # Derived from ActiveSupport, so the AS copyright notice applies:\n  #\n  #\n  #\n  # Copyright (c) 2005 David Heinemeier Hansson\n  #\n  # Permission is hereby granted, free of charge, to any person obtaining\n  # a copy of this software and associated documentation files (the\n  # \"Software\"), to deal in the Software without restriction, including\n  # without limitation the rights to use, copy, modify, merge, publish,\n  # distribute, sublicense, and/or sell copies of the Software, and to\n  # permit persons to whom the Software is furnished to do so, subject to\n  # the following conditions:\n  #\n  # The above copyright notice and this permission notice shall be\n  # included in all copies or substantial portions of the Software.\n  #\n  # THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n  #++\n  #\n  #\n  class String #:nodoc:\n\n    def right_underscore\n      self.gsub(/[A-Z]/){|match| \"#{$`=='' ? '' : '_'}#{match.downcase}\" }\n    end\n    \n  end\n"
  },
  {
    "path": "lib/awsbase/version.rb",
    "content": "module RightAws #:nodoc:\n  module VERSION #:nodoc:\n    MAJOR = 3  unless defined?(MAJOR)\n    MINOR = 1  unless defined?(MINOR)\n    TINY  = 0  unless defined?(TINY)\n\n    STRING = [MAJOR, MINOR, TINY].join('.') unless defined?(STRING)\n  end\nend\n"
  },
  {
    "path": "lib/ec2/right_ec2.rb",
    "content": "#\n# Copyright (c) 2007-2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::EC2 -- RightScale Amazon EC2 interface\n  # The RightAws::EC2 class provides a complete interface to Amazon's\n  # Elastic Compute Cloud service, as well as the associated EBS (Elastic Block\n  # Store).\n  # For explanations of the semantics\n  # of each call, please refer to Amazon's documentation at\n  # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=87\n  #\n  # Examples:\n  #\n  # Create an EC2 interface handle:\n  #   \n  #   @ec2   = RightAws::Ec2.new(aws_access_key_id,\n  #                               aws_secret_access_key)\n  # Create a new SSH key pair:\n  #  @key   = 'right_ec2_awesome_test_key'\n  #  new_key = @ec2.create_key_pair(@key)\n  #  keys = @ec2.describe_key_pairs\n  #\n  # Create a security group:\n  #  @group = 'right_ec2_awesome_test_security_group'\n  #  @ec2.create_security_group(@group,'My awesome test group')\n  #  group = @ec2.describe_security_groups([@group])[0]\n  #\n  # Configure a security group:\n  #  @ec2.authorize_security_group_named_ingress(@group, account_number, 'default')\n  #  @ec2.authorize_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')\n  #\n  # Describe the available images:\n  #  images = @ec2.describe_images\n  #\n  # Launch an instance:\n  #  ec2.run_instances('ami-9a9e7bf3', 1, 1, ['default'], @key, 'SomeImportantUserData', 'public')\n  # \n  #\n  # Describe running instances:\n  #  @ec2.describe_instances\n  #\n  # Error handling: all operations raise an RightAws::AwsError in case\n  # of problems. Note that transient errors are automatically retried.\n    \n  class Ec2 < RightAwsBase\n    include RightAwsBaseInterface\n    \n    # Amazon EC2 API version being used\n    API_VERSION       = \"2011-02-28\"\n    DEFAULT_HOST      = \"ec2.amazonaws.com\"\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n    \n    # Default addressing type (public=NAT, direct=no-NAT) used when launching instances.\n    DEFAULT_ADDRESSING_TYPE =  'public'\n    DNS_ADDRESSING_SET      = ['public','direct']\n    \n    # Amazon EC2 Instance Types : http://www.amazon.com/b?ie=UTF8&node=370375011\n    # Default EC2 instance type (platform) \n    DEFAULT_INSTANCE_TYPE   =  'm1.small' \n    INSTANCE_TYPES          = [  't1.micro'  ,\n                                 'm1.small'  ,\n                                 'm1.medium' ,\n                                 'm1.large'  ,\n                                 'm1.xlarge' ,\n                                 'c1.medium' ,\n                                 'c1.xlarge' ,\n                                 'm2.xlarge' ,\n                                 'm2.2xlarge',\n                                 'm2.4xlarge',\n                                 'm3.xlarge' ,\n                                 'm3.2xlarge',\n                                 'cc1.4xlarge',\n                                 'cg1.4xlarge',\n                                 'cc2.8xlarge',\n                                 'hi1.4xlarge',\n                                 'hs1.8xlarge',\n                                 'cr1.8xlarge'\n                              ]\n    \n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_ec2\n      @@bench.service\n    end\n    \n     # Current API version (sometimes we have to check it outside the GEM).\n    @@api = ENV['EC2_API_VERSION'] || API_VERSION\n    def self.api \n      @@api\n    end\n    \n    # Create a new handle to an EC2 account. All handles share the same per process or per thread\n    # HTTP connection to Amazon EC2. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol and :region). Example: 'https://eu-west-1.ec2.amazonaws.com/'\n    # * <tt>:server</tt>: EC2 service host, default: DEFAULT_HOST\n    # * <tt>:region</tt>: EC2 region (North America by default)\n    # * <tt>:port</tt>: EC2 service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    # * <tt>:cache</tt>: true/false: caching for: ec2_describe_images, describe_instances,\n    # * <tt>:token</tt>: Option SecurityToken for temporary credentials\n    # describe_images_by_owner, describe_images_by_executable_by, describe_availability_zones,\n    # describe_security_groups, describe_key_pairs, describe_addresses, \n    # describe_volumes, describe_snapshots methods, default: false.\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'EC2',\n             :default_host        => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['EC2_URL'] ? URI.parse(ENV['EC2_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => @@api },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] , \n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n      # Eucalyptus supports some yummy features but Amazon does not\n      #if @params[:eucalyptus]\n      #  @params[:port_based_group_ingress] = true unless @params.has_key?(:port_based_group_ingress)\n      #end\n    end\n\n    def generate_request(action, params={}, custom_options={}) #:nodoc:\n      generate_request_impl(:get, action, params, custom_options)\n    end\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:ec2_connection, @@bench, request, parser)\n    end\n\n    def describe_resources_with_list_and_options(remote_function_name, remote_item_name, parser_class, list_and_options, &block) # :nodoc:\n      # 'RemoteFunctionName' -> :remote_funtion_name\n      cache_name = remote_function_name.right_underscore.to_sym\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      custom_options = {}\n      # Resource IDs to fetch\n      request_hash  = amazonize_list(remote_item_name, list)\n      # Other custom options\n      options.each do |key, values|\n        next if values.right_blank?\n        case key\n        when :options\n          custom_options = values\n        when :filters then\n          request_hash.merge!(amazonize_list(['Filter.?.Name', 'Filter.?.Value.?'], values))\n        else\n          request_hash.merge!(amazonize_list(key.to_s.right_camelize, values))\n        end\n      end\n      cache_for = (list.right_blank? && options.right_blank?) ? cache_name : nil\n      link = generate_request(remote_function_name, request_hash, custom_options)\n      request_cache_or_info(cache_for, link,  parser_class, @@bench, cache_for, &block)\n    rescue Exception\n      on_exception\n    end\n\n    # Incrementally lists given API call.\n    #\n    # All params are the same as for describe_resources_with_list_and_options call.\n    #\n    # Block is called on every chunk of resources received. If you need to stop the loop\n    # just make the block to return nil or false.\n    #\n    # The API call should support 'NextToken' parameter and the response should return :next_token\n    # as well.\n    #\n    # Returns the last response from the cloud.\n    #\n    def incrementally_list_items(remote_function_name, remote_item_name, parser_class, list_and_options, &block) # :nodoc:\n      last_response = nil\n      loop do\n        last_response = describe_resources_with_list_and_options(remote_function_name, remote_item_name, parser_class, list_and_options)\n        break unless block && block.call(last_response) && !last_response[:next_token].right_blank?\n        list, options = AwsUtils::split_items_and_params(list_and_options)\n        options[:next_token] = last_response[:next_token]\n        list_and_options = list + [options]\n      end\n      last_response\n    end\n\n    def merge_new_options_into_list_and_options(list_and_options, new_options)\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      list << options.merge(new_options)\n    end\n\n  #-----------------------------------------------------------------\n  #      Keys\n  #-----------------------------------------------------------------\n  \n      # Retrieve a list of SSH keys.\n      #\n      # Accepts a list of ssh keys and/or a set of filters as the last parameter.\n      #\n      # Filters: fingerprint, key-name\n      #\n      # Returns an array of keys or an exception. Each key is represented as a two-element hash.\n      #\n      #  ec2.describe_key_pairs #=>\n      #    [{:aws_fingerprint=> \"01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03\", :aws_key_name=>\"key-1\"},\n      #     {:aws_fingerprint=> \"1e:29:30:47:58:6d:7b:8c:9f:08:11:20:3c:44:52:69:74:80:97:08\", :aws_key_name=>\"key-2\"},\n      #      ..., {...} ]\n      #\n      #  ec2.describe_key_pairs(:filters => {'fingerprint' => [\"53:0b:73:c9:c8:18:98:6e:bc:98:9e:51:97:04:74:4b:07:f9:00:00\",\n      #                                                        \"9f:57:a5:bb:4b:e8:a7:f8:3c:fe:d6:db:41:f5:7e:97:b5:b2:00:00\"]})\n      #\n      # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeKeyPairs.html\n      #\n    def describe_key_pairs(*list_and_options)\n      describe_resources_with_list_and_options('DescribeKeyPairs', 'KeyName', QEc2DescribeKeyPairParser, list_and_options)\n    end\n      \n      # Import new SSH key. Returns a hash of the key's data or an exception.\n      #\n      #  ec2.import_key_pair('my_awesome_key', 'C:\\keys\\myfavoritekeypair_public.ppk') #=>\n      #    {:aws_key_name    => \"my_awesome_key\",\n      #     :aws_fingerprint => \"01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03\"}\n      #\n    def import_key_pair(name, public_key_material)\n      link = generate_request(\"ImportKeyPair\",\n                              'KeyName' => name.to_s,\n                              'PublicKeyMaterial' => Base64.encode64(public_key_material.to_s))\n      request_info(link, QEc2ImportKeyPairParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n      # Create new SSH key. Returns a hash of the key's data or an exception.\n      #\n      #  ec2.create_key_pair('my_awesome_key') #=>\n      #    {:aws_key_name    => \"my_awesome_key\",\n      #     :aws_fingerprint => \"01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03\",\n      #     :aws_material    => \"-----BEGIN RSA PRIVATE KEY-----\\nMIIEpQIBAAK...Q8MDrCbuQ=\\n-----END RSA PRIVATE KEY-----\"}\n      #\n    def create_key_pair(name)\n      link = generate_request(\"CreateKeyPair\",\n                              'KeyName' => name.to_s)\n      request_info(link, QEc2CreateKeyPairParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n      # Delete a key pair. Returns +true+ or an exception.\n      #\n      #  ec2.delete_key_pair('my_awesome_key') #=> true\n      #\n    def delete_key_pair(name)\n      link = generate_request(\"DeleteKeyPair\", \n                              'KeyName' => name.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n    \n  #-----------------------------------------------------------------\n  #      Elastic IPs\n  #-----------------------------------------------------------------\n\n    # Acquire a new elastic IP address for use with your account.\n    # Options: :domain.\n    # Returns allocated IP address or or an exception.\n    #\n    #  ec2.allocate_address #=>\n    #    { :public_ip => \"50.19.214.224\",\n    #      :domain    => \"standard\"}\n    #\n    #  ec2.allocate_address(:domain => 'vpc') #=>\n    #    { :allocation_id => \"eipalloc-c6abfeaf\",\n    #      :domain        => \"vpc\",\n    #      :public_ip     => \"184.72.112.39\"}\n    #\n    def allocate_address(options={})\n      request_hash = {}\n      request_hash['Domain'] = options[:domain] unless options[:domain].right_blank?\n      link = generate_request(\"AllocateAddress\", request_hash)\n      request_info(link, QEc2AllocateAddressParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Associate an elastic IP address with an instance.\n    # Options: :public_ip, :allocation_id.\n    # Returns a hash of data or an exception.\n    #\n    #  ec2.associate_address('i-d630cbbf', :public_ip => '75.101.154.140') #=>\n    #    { :return => true }\n    #\n    #  ec2.associate_address(inst, :allocation_id => \"eipalloc-c6abfeaf\") #=>\n    #    { :return         => true,\n    #      :association_id => 'eipassoc-fc5ca095'}\n    #\n    def associate_address(instance_id, options={})\n      request_hash = { \"InstanceId\" => instance_id.to_s }\n      request_hash['PublicIp']     = options[:public_ip]     unless options[:public_ip].right_blank?\n      request_hash['AllocationId'] = options[:allocation_id] unless options[:allocation_id].right_blank?\n      link = generate_request(\"AssociateAddress\", request_hash)\n      request_info(link, QEc2AssociateAddressParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # List elastic IPs by public addresses.\n    #\n    # Accepts a list of addresses and/or a set of filters as the last parameter.\n    #\n    # Filters: instance-id, public-ip\n    #\n    # Returns an array of 2 keys (:instance_id and :public_ip) hashes:\n    #\n    #  ec2.describe_addresses  #=> [{:instance_id=>\"i-75ebd41b\", :domain=>\"standard\", :public_ip=>\"50.17.211.96\"},\n    #                                :domain=>\"vpc\", :public_ip=>\"184.72.112.39\",  :allocation_id=>\"eipalloc-c6abfeaf\"}]\n    #\n    #  ec2.describe_addresses('75.101.154.140') #=> [{:instance_id=>\"i-d630cbbf\", :public_ip=>\"75.101.154.140\", :domain=>\"standard\"}]\n    #\n    #  ec2.describe_addresses(:filters => { 'public-ip' => \"75.101.154.140\" })\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAddresses.html\n    #\n    def describe_addresses(*list_and_options)\n      describe_resources_with_list_and_options('DescribeAddresses', 'PublicIp', QEc2DescribeAddressesParser, list_and_options)\n    end\n\n\n    # List elastic IPs by allocation ids.\n    #\n    # Accepts a list of allocations and/or a set of filters as the last parameter.\n    #\n    #  describe_addresses_by_allocation_ids(\"eipalloc-c6abfeaf\") #=>\n    #    [{:domain=>\"vpc\",\n    #      :public_ip=>\"184.72.112.39\",\n    #      :allocation_id=>\"eipalloc-c6abfeaf\"}]\n    #\n    #  P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAddresses.html\n    #\n    def describe_addresses_by_allocation_ids(*list_and_options)\n      describe_resources_with_list_and_options('DescribeAddresses', 'AllocationId', QEc2DescribeAddressesParser, list_and_options)\n    end\n\n    # Disassociate the specified elastic IP address from the instance to which it is assigned.\n    # Options: :public_ip, :association_id.\n    # Returns +true+ or an exception.\n    # \n    #  ec2.disassociate_address(:public_ip => '75.101.154.140') #=> true\n    #\n    def disassociate_address(options = {})\n      request_hash = {}\n      request_hash['PublicIp']      = options[:public_ip]      unless options[:public_ip].right_blank?\n      request_hash['AssociationId'] = options[:association_id] unless options[:association_id].right_blank?\n      link = generate_request(\"DisassociateAddress\", request_hash)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Release an elastic IP address associated with your account.\n    # Options: :public_ip, :allocation_id.\n    # Returns +true+ or an exception.\n    #\n    #  ec2.release_address(:public_ip => '75.101.154.140') #=> true\n    #\n    def release_address(options = {})\n      request_hash = {}\n      request_hash['PublicIp']     = options[:public_ip]     unless options[:public_ip].right_blank?\n      request_hash['AllocationId'] = options[:allocation_id] unless options[:allocation_id].right_blank?\n      link = generate_request(\"ReleaseAddress\", request_hash)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n  #-----------------------------------------------------------------\n  #      Availability zones\n  #-----------------------------------------------------------------\n    \n    # Describes availability zones that are currently available to the account and their states.\n    #\n    # Accepts a list of availability zones and/or a set of filters as the last parameter.\n    #\n    # Filters: message, region-name, state, zone-name\n    #\n    # Returns an array of 2 keys (:zone_name and :zone_state) hashes:\n    #\n    #  ec2.describe_availability_zones  #=> [{:region_name=>\"us-east-1\",\n    #                                         :zone_name=>\"us-east-1a\",\n    #                                         :zone_state=>\"available\"}, ... ]\n    #\n    #  ec2.describe_availability_zones('us-east-1c') #=> [{:region_name=>\"us-east-1\", \n    #                                                      :zone_state=>\"available\",\n    #                                                      :zone_name=>\"us-east-1c\"}]\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeAvailabilityZones.html\n    #\n    def describe_availability_zones(*list_and_options)\n      describe_resources_with_list_and_options('DescribeAvailabilityZones', 'ZoneName', QEc2DescribeAvailabilityZonesParser, list_and_options)\n    end\n\n  #-----------------------------------------------------------------\n  #      Regions\n  #-----------------------------------------------------------------\n\n    # Describe regions.\n    #\n    # Accepts a list of regions and/or a set of filters as the last parameter.\n    #\n    # Filters: endpoint, region-name\n    #\n    #  ec2.describe_regions  #=>\n    #   [{:region_endpoint=>\"ec2.eu-west-1.amazonaws.com\",      :region_name=>\"eu-west-1\"},\n    #    {:region_endpoint=>\"ec2.us-east-1.amazonaws.com\",      :region_name=>\"us-east-1\"},\n    #    {:region_endpoint=>\"ec2.ap-northeast-1.amazonaws.com\", :region_name=>\"ap-northeast-1\"},\n    #    {:region_endpoint=>\"ec2.us-west-1.amazonaws.com\",      :region_name=>\"us-west-1\"},\n    #    {:region_endpoint=>\"ec2.ap-southeast-1.amazonaws.com\", :region_name=>\"ap-southeast-1\"}]\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeRegions.html\n    #\n    def describe_regions(*list_and_options)\n      describe_resources_with_list_and_options('DescribeRegions', 'RegionName', QEc2DescribeRegionsParser, list_and_options)\n    end\n\n    #-----------------------------------------------------------------\n    #      Accounts\n    #-----------------------------------------------------------------\n\n    # Describe the specified attribute of your AWS account.\n    #\n    #  ec2.describe_account_attributes(:attribute_name => ['default-vpc','supported-platforms']) #=> \n    #    {\"default-vpc\"         => \"vpc-8c3b00e7\",\n    #     \"supported-platforms\" => \"VPC\"}\n    #\n    #  ec2.describe_account_attributes #=>\n    #    {\"vpc-max-security-groups-per-interface\" => \"5\",\n    #     \"max-instances\"                         => \"150\",\n    #     \"supported-platforms\"                   => [\"EC2\", \"VPC\"],\n    #     \"default-vpc\"                           => \"none\"}\n    #\n    def describe_account_attributes(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => VPC_API_VERSION})\n      describe_resources_with_list_and_options('DescribeAccountAttributes', 'accountAttributeValues', QEc2DescribeAccountAttributesParser, list_and_options)\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Key Pair\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeKeyPairParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name \n          when 'keyName'        then @item[:aws_key_name]    = @text\n          when 'keyFingerprint' then @item[:aws_fingerprint] = @text\n          when 'item'           then @result                << @item\n        end\n      end\n      def reset\n        @result = [];    \n      end\n    end\n\n    class QEc2CreateKeyPairParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @result = {} if name == 'CreateKeyPairResponse'\n      end\n      def tagend(name)\n        case name \n          when 'keyName'        then @result[:aws_key_name]    = @text\n          when 'keyFingerprint' then @result[:aws_fingerprint] = @text\n          when 'keyMaterial'    then @result[:aws_material]    = @text\n        end\n      end\n    end\n\n    class QEc2ImportKeyPairParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @result = {} if name == 'ImportKeyPairResponse'\n      end\n      def tagend(name)\n        case name\n          when 'keyName'        then @result[:aws_key_name]    = @text\n          when 'keyFingerprint' then @result[:aws_fingerprint] = @text\n        end\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Elastic IPs\n  #-----------------------------------------------------------------\n  \n    class QEc2AllocateAddressParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'publicIp'     then @result[:public_ip]     = @text\n        when 'allocationId' then @result[:allocation_id] = @text\n        when 'domain'       then @result[:domain]        = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2AssociateAddressParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'return'        then @result[:return]         = @text == 'true'\n        when 'associationId' then @result[:association_id] = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2DescribeAddressesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n        when 'instanceId'    then (@item[:instance_id]    = @text unless @text.right_blank?)\n        when 'publicIp'      then @item[:public_ip]       = @text\n        when 'allocationId'  then @item[:allocation_id]   = @text\n        when 'associationId' then @item[:association_id]  = @text\n        when 'domain'        then @item[:domain]          = @text\n        when 'item'          then @result                << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: AvailabilityZones\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeAvailabilityZonesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/availabilityZoneInfo/item$} then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'regionName' then @item[:region_name] = @text\n        when 'zoneName'   then @item[:zone_name]   = @text\n        when 'zoneState'  then @item[:zone_state]  = @text\n        else\n          case full_tag_name\n          when %r{/messageSet/item/message$}   then (@item[:messages] ||= []) << @text\n          when %r{/availabilityZoneInfo/item$} then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Regions\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeRegionsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n        when 'regionName'     then @item[:region_name]     = @text\n        when 'regionEndpoint' then @item[:region_endpoint] = @text\n        when 'item'           then @result                << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Account\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeAccountAttributesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'attributeValueSet' then @values = []\n        end\n      end\n      def tagend(name)\n        case name\n        when 'attributeName'     then @name    = @text\n        when 'attributeValue'    then @values << @text\n        when 'attributeValueSet' then @result[@name] = (@values.size == 1) ? @values.first : @values\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n  end\n      \nend\n"
  },
  {
    "path": "lib/ec2/right_ec2_ebs.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      EBS: Volumes\n    #-----------------------------------------------------------------\n\n    VOLUME_API_VERSION = (API_VERSION > '2012-06-15') ? API_VERSION : '2012-06-15'\n    VOLUME_TYPES       = ['standard', 'io1']\n\n    # Describe EBS volumes.\n    #\n    # Accepts a list of volumes and/or a set of filters as the last parameter.\n    #\n    # Filters: attachement.attach-time, attachment.delete-on-termination, attachement.device, attachment.instance-id,\n    # attachment.status, availability-zone, create-time, size, snapshot-id, status, tag-key, tag-value, tag:key, volume-id\n    #\n    #  ec2.describe_volumes #=>\n    #      [{:aws_size              => 94,\n    #        :aws_device            => \"/dev/sdc\",\n    #        :aws_attachment_status => \"attached\",\n    #        :zone                  => \"merlot\",\n    #        :snapshot_id           => nil,\n    #        :aws_attached_at       => \"2008-06-18T08:19:28.000Z\",\n    #        :aws_status            => \"in-use\",\n    #        :aws_id                => \"vol-60957009\",\n    #        :aws_created_at        => \"2008-06-18T08:19:20.000Z\",\n    #        :aws_instance_id       => \"i-c014c0a9\"},\n    #       {:aws_id         => \"vol-71de8b1f\",\n    #        :aws_size       => 5,\n    #        :snapshot_id    => nil,\n    #        :zone           => \"us-east-1a\",\n    #        :aws_status     => \"available\",\n    #        :aws_created_at => \"2012-06-21T18:47:34.000Z\",\n    #        :volume_type    => \"io1\",\n    #        :iop            => \"5\"},#\n    #       {:aws_size       => 1,\n    #        :zone           => \"merlot\",\n    #        :snapshot_id    => nil,\n    #        :aws_status     => \"available\",\n    #        :aws_id         => \"vol-58957031\",\n    #        :aws_created_at => Wed Jun 18 08:19:21 UTC 2008,}, ... ]\n    #\n    #  ec2.describe_volumes(:filters => { 'availability-zone' => 'us-east-1a', 'size' => '10' })\n    #\n    #  P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeVolumes.html\n    #\n    def describe_volumes(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => VOLUME_API_VERSION})\n      describe_resources_with_list_and_options('DescribeVolumes', 'VolumeId', QEc2DescribeVolumesParser, list_and_options)\n    end\n\n    # Create new EBS volume based on previously created snapshot.\n    # +Size+ in Gigabytes.\n    #\n    #  ec2.create_volume('snap-000000', 10, zone) #=>\n    #      {:snapshot_id    => \"snap-e21df98b\",\n    #       :aws_status     => \"creating\",\n    #       :aws_id         => \"vol-fc9f7a95\",\n    #       :zone           => \"merlot\",\n    #       :aws_created_at => \"2008-06-24T18:13:32.000Z\",\n    #       :aws_size       => 94}\n    #\n    #  ec2.create_volume(nil, 5, 'us-east-1a', :iops => '5', :volume_type => 'io1') #=>\n    #      {:aws_id=>\"vol-71de8b1f\",\n    #       :aws_size=>5,\n    #       :snapshot_id=>nil,\n    #       :zone=>\"us-east-1a\",\n    #       :aws_status=>\"creating\",\n    #       :aws_created_at=>\"2012-06-21T18:47:34.000Z\",\n    #       :volume_type=>\"io1\",\n    #       :iops=>\"5\"}\n    #\n    def create_volume(snapshot_id, size, zone, options={})\n      hash = { \"Size\"              => size.to_s,\n               \"AvailabilityZone\"  => zone.to_s }\n      # Get rig of empty snapshot: e8s guys do not like it\n      hash[\"SnapshotId\"] = snapshot_id.to_s unless snapshot_id.right_blank?\n      # Add IOPS support (default behavior) but skip it when an old API version call is requested\n      options[:options]                 ||= {}\n      options[:options][:api_version]   ||= VOLUME_API_VERSION\n      if options[:options][:api_version] >= VOLUME_API_VERSION\n        hash[\"VolumeType\"] = options[:volume_type] unless options[:volume_type].right_blank?\n        hash[\"Iops\"]       = options[:iops]        unless options[:iops].right_blank?\n      end\n      link = generate_request(\"CreateVolume\", hash, options[:options])\n      request_info(link, QEc2CreateVolumeParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Delete the specified EBS volume.\n    # This does not deletes any snapshots created from this volume.\n    #\n    #  ec2.delete_volume('vol-b48a6fdd') #=> true\n    #\n    def delete_volume(volume_id)\n      link = generate_request(\"DeleteVolume\",\n                              \"VolumeId\" => volume_id.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Attach the specified EBS volume to a specified instance, exposing the\n    # volume using the specified device name.\n    #\n    #  ec2.attach_volume('vol-898a6fe0', 'i-7c905415', '/dev/sdh') #=>\n    #    { :aws_instance_id => \"i-7c905415\",\n    #      :aws_device      => \"/dev/sdh\",\n    #      :aws_status      => \"attaching\",\n    #      :aws_attached_at => \"2008-03-28T14:14:39.000Z\",\n    #      :aws_id          => \"vol-898a6fe0\" }\n    #\n    def attach_volume(volume_id, instance_id, device)\n      link = generate_request(\"AttachVolume\",\n                              \"VolumeId\"   => volume_id.to_s,\n                              \"InstanceId\" => instance_id.to_s,\n                              \"Device\"     => device.to_s)\n      request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Detach the specified EBS volume from the instance to which it is attached.\n    #\n    #   ec2.detach_volume('vol-898a6fe0') #=>\n    #     { :aws_instance_id => \"i-7c905415\",\n    #       :aws_device      => \"/dev/sdh\",\n    #       :aws_status      => \"detaching\",\n    #       :aws_attached_at => \"2008-03-28T14:38:34.000Z\",\n    #       :aws_id          => \"vol-898a6fe0\"}\n    #\n    def detach_volume(volume_id, instance_id=nil, device=nil, force=nil)\n      hash = { \"VolumeId\" => volume_id.to_s }\n      hash[\"InstanceId\"] = instance_id.to_s unless instance_id.right_blank?\n      hash[\"Device\"]     = device.to_s      unless device.right_blank?\n      hash[\"Force\"]      = 'true'           if     force\n      #\n      link = generate_request(\"DetachVolume\", hash)\n      request_info(link, QEc2AttachAndDetachVolumeParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n\n    #-----------------------------------------------------------------\n    #      EBS: Snapshots\n    #-----------------------------------------------------------------\n\n    # Describe EBS snapshots.\n    #\n    # Accepts a list of snapshots and/or options: :restorable_by, :owner and :filters\n    # \n    # Options: :restorable_by => Array or String, :owner => Array or String, :filters => Hash\n    #\n    # Filters: description, owner-alias, owner-id, progress, snapshot-id, start-time, status, tag-key,\n    # tag-value, tag:key, volume-id, volume-size\n    #\n    #  ec2.describe_snapshots #=>\n    #    [{:aws_volume_size=>2,\n    #      :tags=>{},\n    #      :aws_id=>\"snap-d010f6b9\",\n    #      :owner_alias=>\"amazon\",\n    #      :aws_progress=>\"100%\",\n    #      :aws_status=>\"completed\",\n    #      :aws_description=>\n    #       \"Windows 2003 R2 Installation Media [Deprecated] - Enterprise Edition 64-bit\",\n    #      :aws_owner=>\"711940113766\",\n    #      :aws_volume_id=>\"vol-351efb5c\",\n    #      :aws_started_at=>\"2008-10-20T18:23:59.000Z\"},\n    #     {:aws_volume_size=>2,\n    #      :tags=>{},\n    #      :aws_id=>\"snap-a310f6ca\",\n    #      :owner_alias=>\"amazon\",\n    #      :aws_progress=>\"100%\",\n    #      :aws_status=>\"completed\",\n    #      :aws_description=>\"Windows 2003 R2 Installation Media 64-bit\",\n    #      :aws_owner=>\"711940113766\",\n    #      :aws_volume_id=>\"vol-001efb69\",\n    #      :aws_started_at=>\"2008-10-20T18:25:53.000Z\"}, ... ]\n    #  \n    #  ec2.describe_snapshots(\"snap-e676e28a\", \"snap-e176e281\")\n    #\n    #  ec2.describe_snapshots(:restorable_by => ['123456781234'],\n    #                         :owner         => ['self', 'amazon'],\n    #                         :filters       => {'tag:MyTag' => 'MyValue'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeSnapshots.html\n    #\n    def describe_snapshots(*list_and_options)\n      describe_resources_with_list_and_options('DescribeSnapshots', 'SnapshotId', QEc2DescribeSnapshotsParser, list_and_options)\n    end\n\n    def describe_snapshots_by_restorable_by(*list_and_options)\n      describe_resources_with_list_and_options('DescribeSnapshots', 'RestorableBy', QEc2DescribeSnapshotsParser, list_and_options)\n    end\n\n    # Create a snapshot of specified volume.\n    #\n    #  ec2.create_snapshot('vol-898a6fe0', 'KD: WooHoo!!') #=>\n    #    {:aws_volume_id=>\"vol-e429db8d\",\n    #     :aws_started_at=>\"2009-10-01T09:23:38.000Z\",\n    #     :aws_description=>\"KD: WooHoo!!\",\n    #     :aws_owner=>\"648770000000\",\n    #     :aws_progress=>\"\",\n    #     :aws_status=>\"pending\",\n    #     :aws_volume_size=>1,\n    #     :aws_id=>\"snap-3df54854\"}\n    #\n    def create_snapshot(volume_id, description='')\n      link = generate_request(\"CreateSnapshot\",\n                              \"VolumeId\" => volume_id.to_s,\n                              \"Description\" => description)\n      request_info(link, QEc2DescribeSnapshotsParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Create a snapshot of specified volume, but with the normal retry algorithms disabled.\n    # This method will return immediately upon error.  The user can specify connect and read timeouts (in s)\n    # for the connection to AWS.  If the user does not specify timeouts, try_create_snapshot uses the default values\n    # in Rightscale::HttpConnection.\n    #\n    #  ec2.try_create_snapshot('vol-898a6fe0', 'KD: WooHoo!!') #=>\n    #    {:aws_volume_id=>\"vol-e429db8d\",\n    #     :aws_started_at=>\"2009-10-01T09:23:38.000Z\",\n    #     :aws_description=>\"KD: WooHoo!!\",\n    #     :aws_owner=>\"648770000000\",\n    #     :aws_progress=>\"\",\n    #     :aws_status=>\"pending\",\n    #     :aws_volume_size=>1,\n    #     :aws_id=>\"snap-3df54854\"}\n    #\n    def try_create_snapshot(volume_id, connect_timeout = nil, read_timeout = nil, description='')\n      # For safety in the ensure block...we don't want to restore values\n      # if we never read them in the first place\n      orig_reiteration_time = nil\n      orig_http_params = nil\n\n      orig_reiteration_time = RightAws::AWSErrorHandler::reiteration_time\n      RightAws::AWSErrorHandler::reiteration_time = 0\n\n      orig_http_params = Rightscale::HttpConnection::params()\n      new_http_params = orig_http_params.dup\n      new_http_params[:http_connection_retry_count] = 0\n      new_http_params[:http_connection_open_timeout] = connect_timeout if !connect_timeout.nil?\n      new_http_params[:http_connection_read_timeout] = read_timeout if !read_timeout.nil?\n      Rightscale::HttpConnection::params = new_http_params\n\n      link = generate_request(\"CreateSnapshot\",\n                              \"VolumeId\"    => volume_id.to_s,\n                              \"Description\" => description)\n      request_info(link, QEc2DescribeSnapshotsParser.new(:logger => @logger)).first\n\n    rescue Exception\n      on_exception\n    ensure\n      RightAws::AWSErrorHandler::reiteration_time = orig_reiteration_time if orig_reiteration_time\n      Rightscale::HttpConnection::params = orig_http_params if orig_http_params\n    end\n\n    # Describe snapshot attribute.\n    #\n    #  ec2.describe_snapshot_attribute('snap-36fe435f') #=>\n    #    {:create_volume_permission=>\n    #       {:users=>[\"826690000000\", \"826690000001\"], :groups=>['all']}}\n    #\n    def describe_snapshot_attribute(snapshot_id, attribute='createVolumePermission')\n      link = generate_request(\"DescribeSnapshotAttribute\",\n                              'SnapshotId'=> snapshot_id,\n                              'Attribute' => attribute)\n      request_info(link, QEc2DescribeSnapshotAttributeParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Reset permission settings for the specified snapshot.\n    #\n    #  ec2.reset_snapshot_attribute('snap-cecd29a7') #=> true\n    #\n    def reset_snapshot_attribute(snapshot_id, attribute='createVolumePermission')\n      link = generate_request(\"ResetSnapshotAttribute\",\n                              'SnapshotId' => snapshot_id,\n                              'Attribute'  => attribute)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Modify snapshot attribute.\n    #\n    #  Attribute can take only 'createVolumePermission' value.\n    #  Value is a Hash {:add_user_ids, :add_groups, :remove_user_ids, :remove_groups }.\n    #\n    def modify_snapshot_attribute(snapshot_id, attribute, value)\n      params = { 'SnapshotId' => snapshot_id }\n      case attribute.to_s\n      when 'createVolumePermission'\n        params.update(amazonize_list('CreateVolumePermission.Add.?.UserId',    value[:add_user_ids]))\n        params.update(amazonize_list('CreateVolumePermission.Add.?.Group',     value[:add_groups]))\n        params.update(amazonize_list('CreateVolumePermission.Remove.?.UserId', value[:remove_user_ids]))\n        params.update(amazonize_list('CreateVolumePermission.Remove.?.Group',  value[:remove_groups]))\n      end\n      link = generate_request(\"ModifySnapshotAttribute\", params)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Grant create volume permission for a list of users.\n    #\n    #  ec2.modify_snapshot_attribute_create_volume_permission_add_users('snap-36fe435f', '000000000000', '000000000001') #=> true\n    #\n    def modify_snapshot_attribute_create_volume_permission_add_users(snapshot_id, *user_ids)\n      modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :add_user_ids => user_ids.flatten )\n    end\n    \n    # Revoke create volume permission for a list of users.\n    #\n    #  ec2.modify_snapshot_attribute_create_volume_permission_remove_users('snap-36fe435f', '000000000000', '000000000001') #=> true\n    #\n    def modify_snapshot_attribute_create_volume_permission_remove_users(snapshot_id, *user_ids)\n      modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :remove_user_ids => user_ids.flatten )\n    end\n\n    # Grant create volume permission for user groups (currently only 'all' is supported).\n    #\n    #  ec2.modify_snapshot_attribute_create_volume_permission_add_groups('snap-36fe435f') #=> true\n    #\n    def modify_snapshot_attribute_create_volume_permission_add_groups(snapshot_id, *groups)\n      groups.flatten!\n      groups = ['all'] if groups.right_blank?\n      modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :add_groups => groups )\n    end\n\n    # Remove create volume permission for user groups (currently only 'all' is supported).\n    #\n    #  ec2.modify_snapshot_attribute_create_volume_permission_remove_groups('snap-36fe435f') #=> true\n    #\n    def modify_snapshot_attribute_create_volume_permission_remove_groups(snapshot_id, *groups)\n      groups.flatten!\n      groups = ['all'] if groups.right_blank?\n      modify_snapshot_attribute(snapshot_id, 'createVolumePermission', :remove_groups => groups )\n    end\n\n    # Delete the specified snapshot.\n    #\n    #  ec2.delete_snapshot('snap-55a5403c') #=> true\n    #\n    def delete_snapshot(snapshot_id)\n      link = generate_request(\"DeleteSnapshot\",\n                              \"SnapshotId\" => snapshot_id.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: EBS - Volumes\n    #-----------------------------------------------------------------\n\n    class QEc2CreateVolumeParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'volumeId'         then @result[:aws_id]         = @text\n        when 'status'           then @result[:aws_status]     = @text\n        when 'createTime'       then @result[:aws_created_at] = @text\n        when 'size'             then @result[:aws_size]       = @text.to_i ###\n        when 'snapshotId'       then @result[:snapshot_id]    = @text.right_blank? ? nil : @text ###\n        when 'availabilityZone' then @result[:zone]           = @text ###\n        when 'volumeType'       then @result[:volume_type]    = @text\n        when 'iops'             then @result[:iops]           = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2AttachAndDetachVolumeParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'volumeId'   then @result[:aws_id]                = @text\n        when 'instanceId' then @result[:aws_instance_id]       = @text\n        when 'device'     then @result[:aws_device]            = @text\n        when 'status'     then @result[:aws_attachment_status] = @text\n        when 'attachTime' then @result[:aws_attached_at]       = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2DescribeVolumesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{volumeSet/item$} then @item    = { :tags => {} }\n        when %r{/tagSet/item$}   then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'size'             then @item[:aws_size]        = @text.to_i\n        when 'createTime'       then @item[:aws_created_at]  = @text\n        when 'instanceId'       then @item[:aws_instance_id] = @text\n        when 'device'           then @item[:aws_device]      = @text\n        when 'attachTime'       then @item[:aws_attached_at] = @text\n        when 'snapshotId'       then @item[:snapshot_id]     = @text.right_blank? ? nil : @text\n        when 'availabilityZone' then @item[:zone]            = @text\n        when 'deleteOnTermination' then @item[:delete_on_termination] = (@text == 'true')\n        when 'volumeType'       then @item[:volume_type]     = @text\n        when 'iops'             then @item[:iops]            = @text\n        else\n          case full_tag_name\n          when %r{/volumeSet/item/volumeId$}   then @item[:aws_id]                = @text\n          when %r{/volumeSet/item/status$}     then @item[:aws_status]            = @text\n          when %r{/attachmentSet/item/status$} then @item[:aws_attachment_status] = @text\n          when %r{/tagSet/item/key$}           then @aws_tag[:key]                = @text\n          when %r{/tagSet/item/value$}         then @aws_tag[:value]              = @text\n          when %r{/tagSet/item$}               then @item[:tags][@aws_tag[:key]]  = @aws_tag[:value]\n          when %r{/volumeSet/item$}            then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: EBS - Snapshots\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeSnapshotsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{CreateSnapshotResponse$},\n             %r{/snapshotSet/item$}  then @item = { :tags => {} }\n        when %r{/tagSet/item$}       then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'volumeId'    then @item[:aws_volume_id]   = @text\n        when 'snapshotId'  then @item[:aws_id]          = @text\n        when 'status'      then @item[:aws_status]      = @text\n        when 'startTime'   then @item[:aws_started_at]  = @text\n        when 'progress'    then @item[:aws_progress]    = @text\n        when 'description' then @item[:aws_description] = @text\n        when 'ownerId'     then @item[:aws_owner]       = @text\n        when 'volumeSize'  then @item[:aws_volume_size] = @text.to_i\n        when 'ownerAlias'  then @item[:owner_alias]     = @text\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}         then @aws_tag[:key]                = @text\n          when %r{/tagSet/item/value$}       then @aws_tag[:value]              = @text\n          when %r{/tagSet/item$}             then @item[:tags][@aws_tag[:key]]  = @aws_tag[:value]\n          when %r{CreateSnapshotResponse$},\n               %r{/snapshotSet/item$}        then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeSnapshotAttributeParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'createVolumePermission' then @result[:create_volume_permission] = { :groups => [], :users => [] }\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        when \"#{@create_volume_permission}/group\"  then @result[:create_volume_permission][:groups] << @text\n        when \"#{@create_volume_permission}/userId\" then @result[:create_volume_permission][:users]  << @text\n        end\n      end\n      def reset\n        @create_volume_permission = \"DescribeSnapshotAttributeResponse/createVolumePermission/item\"\n        @result = {}\n      end\n    end\n\n  end\n  \nend"
  },
  {
    "path": "lib/ec2/right_ec2_images.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      Images\n    #-----------------------------------------------------------------\n\n    # Describe images helper\n    # params:\n    #   { 'ImageId'      => ['id1', ..., 'idN'],\n    #     'Owner'        => ['self', ..., 'userN'],\n    #     'ExecutableBy' => ['self', 'all', ..., 'userN']\n    #   }\n    def ec2_describe_images(params={}, options={}, cache_for=nil) #:nodoc:\n      request_hash = {}\n      params.each { |list_by, list| request_hash.merge! amazonize_list(list_by, Array(list)) }\n      request_hash.merge!(amazonize_list(['Filter.?.Name', 'Filter.?.Value.?'], options[:filters])) unless options[:filters].right_blank?\n      link = generate_request(\"DescribeImages\", request_hash)\n      request_cache_or_info cache_for, link,  QEc2DescribeImagesParser, @@bench, cache_for\n    rescue Exception\n      on_exception\n    end\n\n    # Retrieve a list of images.\n    #\n    # Accepts a list of images and/or a set of filters as the last parameter.\n    # \n    # Filters: architecture, block-device-mapping.delete-on-termination block-device-mapping.device-name,\n    # block-device-mapping.snapshot-id, block-device-mapping.volume-size, description, image-id, image-type,\n    # is-public, kernel-id, manifest-location, name, owner-alias, owner-id, platform, product-code,\n    # ramdisk-id, root-device-name, root-device-type, state, state-reason-code, state-reason-message,\n    # tag-key, tag-value, tag:key, virtualization-type\n    #\n    #  ec2.describe_images #=>\n    #    [{:description=>\"EBS backed Fedora core 8 i386\",\n    #      :aws_architecture=>\"i386\",\n    #      :aws_id=>\"ami-c2a3f5d4\",\n    #      :aws_image_type=>\"machine\",\n    #      :root_device_name=>\"/dev/sda1\",\n    #      :image_class=>\"elastic\",\n    #      :aws_owner=>\"937766719418\",\n    #      :aws_location=>\"937766719418/EBS backed FC8 i386\",\n    #      :aws_state=>\"available\",\n    #      :block_device_mappings=>\n    #       [{:ebs_snapshot_id=>\"snap-829a20eb\",\n    #         :ebs_delete_on_termination=>true,\n    #         :device_name=>\"/dev/sda1\"}],\n    #      :name=>\"EBS backed FC8 i386\",\n    #      :aws_is_public=>true}, ... ]\n    #\n    #  ec2.describe_images(:filters => { 'image-type' => 'kernel', 'state' => 'available', 'tag:MyTag' => 'MyValue'})\n    #\n    #  ec2.describe_images(\"ari-fda54b94\", \"ami-2ee80247\", \"aki-00896a69\",\n    #                      :filters => { 'image-type' => 'kernel', 'state' => 'available' }) #=>\n    #    [{:root_device_type=>\"instance-store\",\n    #      :aws_id=>\"aki-00896a69\",\n    #      :aws_image_type=>\"kernel\",\n    #      :aws_location=>\n    #       \"karmic-kernel-zul/ubuntu-kernel-2.6.31-300-ec2-i386-20091002-test-04.manifest.xml\",\n    #      :virtualization_type=>\"paravirtual\",\n    #      :aws_state=>\"available\",\n    #      :aws_owner=>\"099720109477\",\n    #      :tags=>{},\n    #      :aws_is_public=>true,\n    #      :aws_architecture=>\"i386\"}]\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeImages.html\n    #\n    def describe_images(*list_and_options)\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      cache_for     = (list.right_blank? && options[:filters].right_blank?) ? :describe_images : nil\n      ec2_describe_images( {'ImageId'=>list}, options, cache_for)\n    end\n\n    # Retrieve a list of images by image owner.\n    #\n    # Accepts a list of images and/or a set of filters as the last parameter.\n    # \n    # Filters: architecture, block-device-mapping.delete-on-termination block-device-mapping.device-name,\n    # block-device-mapping.snapshot-id, block-device-mapping.volume-size, description, image-id, image-type,\n    # is-public, kernel-id, manifest-location, name, owner-alias, owner-id, platform, product-code,\n    # ramdisk-id, root-device-name, root-device-type, state, state-reason-code, state-reason-message,\n    # tag-key, tag-value, tag:key, virtualization-type\n    #\n    #   ec2.describe_images_by_owner('522821470517')\n    #   ec2.describe_images_by_owner('self', :filters => { 'block-device-mapping.delete-on-termination' => 'false' })\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeImages.html\n    #\n    def describe_images_by_owner(*list_and_options)\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      list          = ['self'] if list.right_blank?\n      cache_for     = (list==['self'] && options[:filters].right_blank?) ? :describe_images_by_owner : nil\n      ec2_describe_images( {'Owner'=>list}, options, cache_for)\n    end\n\n    # Retrieve a list of images by image executable by.\n    # \n    # Accepts a list of images and/or a set of filters as the last parameter.\n    # \n    # Filters: architecture, block-device-mapping.delete-on-termination block-device-mapping.device-name,\n    # block-device-mapping.snapshot-id, block-device-mapping.volume-size, description, image-id, image-type,\n    # is-public, kernel-id, manifest-location, name, owner-alias, owner-id, platform, product-code,\n    # ramdisk-id, root-device-name, root-device-type, state, state-reason-code, state-reason-message,\n    # tag-key, tag-value, tag:key, virtualization-type\n    #\n    #   ec2.describe_images_by_executable_by('522821470517')\n    #   ec2.describe_images_by_executable_by('self')\n    #   ec2.describe_images_by_executable_by('all', :filters => { 'architecture' => 'i386' })\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeImages.html\n    #\n    def describe_images_by_executable_by(*list_and_options)\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      list          = ['self'] if list.right_blank?\n      cache_for     = (list==['self'] && options[:filters].right_blank?) ? :describe_images_by_executable_by : nil\n      ec2_describe_images( {'ExecutableBy'=>list}, options, cache_for)\n    end\n\n    # Register new image at Amazon.\n    # Options: :image_location, :name, :description, :architecture, :kernel_id, :ramdisk_id,\n    #          :root_device_name, :block_device_mappings, :virtualizationt_type(hvm|paravirtual)\n    #\n    # Returns new image id.\n    #\n    #  # Register S3 image\n    #  ec2.register_image('bucket_for_k_dzreyev/image_bundles/kd__CentOS_1_10_2009_10_21_13_30_43_MSD/image.manifest.xml') #=> 'ami-e444444d'\n    #\n    #  # or\n    #  image_reg_params = {  :image_location => 'bucket_for_k_dzreyev/image_bundles/kd__CentOS_1_10_2009_10_21_13_30_43_MSD/image.manifest.xml',\n    #                        :name => 'my-test-one-1',\n    #                        :description => 'My first test image' }\n    #  ec2.register_image(image_reg_params) #=> \"ami-bca1f7aa\"\n    #\n    #  # Register EBS image\n    #  image_reg_params = { :name        => 'my-test-image',\n    #                       :description => 'My first test image',\n    #                       :root_device_name => \"/dev/sda1\",\n    #                       :block_device_mappings => [ { :ebs_snapshot_id=>\"snap-7360871a\",\n    #                                                     :ebs_delete_on_termination=>true,\n    #                                                     :device_name=>\"/dev/sda1\"},\n    #                                                   { :virtual_name => 'ephemeral0',}\n    #                                                     :device_name=>\"/dev/sdb\"} ]\n    #  ec2.register_image(image_reg_params) #=> \"ami-b2a1f7a4\"\n    #\n    def register_image(options)\n      case\n      when options.is_a?(String)\n        options = { :image_location => options }\n      when !options.is_a?(Hash)\n        raise \"Unsupported options type\"\n      end\n      params = {}\n      params['ImageLocation']  = options[:image_location]   if options[:image_location]\n      params['Name']           = options[:name]             if options[:name]\n      params['Description']    = options[:description]      if options[:description]\n      params['Architecture']   = options[:architecture]     if options[:architecture]\n      params['KernelId']       = options[:kernel_id]        if options[:kernel_id]\n      params['RamdiskId']      = options[:ramdisk_id]       if options[:ramdisk_id]\n      params['RootDeviceName'] = options[:root_device_name] if options[:root_device_name]\n      params['VirtualizationType'] = options[:virtualization_type] if options[:virtualization_type]\n#      params['SnapshotId']     = options[:snapshot_id]      if options[:snapshot_id]\n      params.merge!(amazonize_block_device_mappings(options[:block_device_mappings]))\n      link = generate_request(\"RegisterImage\", params)\n      request_info(link, QEc2RegisterImageParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Deregister image at Amazon. Returns +true+ or an exception.\n    #\n    #  ec2.deregister_image('ami-e444444d') #=> true\n    #\n    def deregister_image(image_id)\n      link = generate_request(\"DeregisterImage\",\n                              'ImageId' => image_id.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Describe image attributes.\n    # \n    # Returns: String (or nil) for 'description', 'kernel', 'ramdisk'; Hash for 'launchPermission'; Array for 'productCodes', 'blockDeviceMapping'\n    #\n    #  ec2.describe_image_attribute('ami-00000000', 'description')        #=> 'My cool Image'\n    #  ec2.describe_image_attribute('ami-00000000', 'launchPermission')   #=> {:user_ids=>[\"443739700000\", \"115864000000\", \"309179000000\", \"857501300000\"]}\n    #  ec2.describe_image_attribute('ami-00000000', 'productCodes')       #=> [\"8ED10000\"]\n    #  ec2.describe_image_attribute('ami-00000000', 'kernel')             #=> \"aki-9b00e5f2\"\n    #  ec2.describe_image_attribute('ami-00000000', 'ramdisk')            #=> nil\n    #  ec2.describe_image_attribute('ami-00000000', 'blockDeviceMapping') #=> [{:device_name=>\"sda2\", :virtual_name=>\"ephemeral0\"},\n    #                                                                          {:device_name=>\"sda1\", :virtual_name=>\"ami\"},\n    #                                                                          {:device_name=>\"/dev/sda1\", :virtual_name=>\"root\"},\n    #                                                                          {:device_name=>\"sda3\", :virtual_name=>\"swap\"}]\n    #\n    def describe_image_attribute(image_id, attribute='launchPermission')\n      link = generate_request(\"DescribeImageAttribute\",\n                              'ImageId'   => image_id,\n                              'Attribute' => attribute)\n      request_info(link, QEc2DescribeImageAttributeParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Reset image attribute. Currently, only 'launchPermission' is supported. Returns +true+ or an exception.\n    #\n    #  ec2.reset_image_attribute('ami-e444444d') #=> true\n    #\n    def reset_image_attribute(image_id, attribute='launchPermission')\n      link = generate_request(\"ResetImageAttribute\",\n                              'ImageId'   => image_id,\n                              'Attribute' => attribute)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Modify an image's attributes. It is recommended that you use\n    # modify_image_launch_perm_add_users, modify_image_launch_perm_remove_users, etc.\n    # instead of modify_image_attribute because the signature of\n    # modify_image_attribute may change with EC2 service changes.\n    #\n    #  Attribute can take next values: 'launchPermission', 'productCode', 'description'.\n    #  Value is a String for'description'. is a String or an Array for 'productCode' and\n    #  is a Hash {:add_user_ids, :add_groups, :remove_user_ids, :remove_groups } for 'launchPermission'.\n    #\n    def modify_image_attribute(image_id, attribute, value)\n      params = { 'ImageId' => image_id }\n      case attribute.to_s\n      when 'launchPermission'\n        params.update(amazonize_list('LaunchPermission.Add.?.UserId',    value[:add_user_ids]))\n        params.update(amazonize_list('LaunchPermission.Add.?.Group',     value[:add_groups]))\n        params.update(amazonize_list('LaunchPermission.Remove.?.UserId', value[:remove_user_ids]))\n        params.update(amazonize_list('LaunchPermission.Remove.?.Group',  value[:remove_groups]))\n      when 'productCode'\n        params.update(amazonize_list('ProductCode', value))\n      when 'description'\n        params['Description.Value'] = value\n      end\n      link = generate_request(\"ModifyImageAttribute\", params)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Grant image launch permissions to users.\n    # Parameter +user_id+ is a list of user AWS account ids.\n    # Returns +true+ or an exception.\n    #\n    #  ec2.modify_image_launch_perm_add_users('ami-e444444d',['000000000777','000000000778']) #=> true\n    def modify_image_launch_perm_add_users(image_id, *user_ids)\n      modify_image_attribute(image_id, 'launchPermission', :add_user_ids => user_ids.flatten)\n    end\n\n    # Revokes image launch permissions for users. +user_id+ is a list of users AWS accounts ids. Returns +true+ or an exception.\n    #\n    #  ec2.modify_image_launch_perm_remove_users('ami-e444444d',['000000000777','000000000778']) #=> true\n    #\n    def modify_image_launch_perm_remove_users(image_id, *user_ids)\n      modify_image_attribute(image_id, 'launchPermission', :remove_user_ids => user_ids.flatten)\n    end\n\n    # Add image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).\n    # Returns +true+ or an exception.\n    #\n    #  ec2.modify_image_launch_perm_add_groups('ami-e444444d') #=> true\n    #\n    def modify_image_launch_perm_add_groups(image_id, *groups)\n      modify_image_attribute(image_id, 'launchPermission', :add_groups => groups.flatten)\n    end\n\n    # Remove image launch permissions for users groups (currently only 'all' is supported, which gives public launch permissions).\n    #\n    #  ec2.modify_image_launch_perm_remove_groups('ami-e444444d') #=> true\n    #\n    def modify_image_launch_perm_remove_groups(image_id, *groups)\n      modify_image_attribute(image_id, 'launchPermission', :remove_groups => groups.flatten)\n    end\n\n    # Add product code to image\n    #\n    #  ec2.modify_image_product_code('ami-e444444d','0ABCDEF') #=> true\n    #\n    def modify_image_product_code(image_id, product_codes=[])\n      modify_image_attribute(image_id, 'productCodes',product_codes)\n    end\n\n    # Modify image description\n    #\n    #  ec2.modify_image_product_code('ami-e444444d','My cool image') #=> true\n    #\n    def modify_image_description(image_id, description)\n      modify_image_attribute(image_id, 'description', description)\n    end\n\n    # Create a new image.\n    # Options: :name, :description, :no_reboot(bool)\n    #\n    #  ec2.create_image(instance, :description => 'KD: test#1',\n    #                             :no_reboot => true,\n    #                             :name => 'kd-1' ) #=> \"ami-84a1f792\"\n    #\n    def create_image(instance_aws_id, options={})\n      params = { 'InstanceId' => instance_aws_id }\n      params['Name']        = options[:name]            unless options[:name].right_blank?\n      params['Description'] = options[:description]     unless options[:description].right_blank?\n      params['NoReboot']    = options[:no_reboot].to_s  unless options[:no_reboot].nil?\n      link = generate_request(\"CreateImage\", params)\n      request_info(link, QEc2RegisterImageParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Images\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeImagesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/imagesSet/item$}\n          @item = { :tags => {} }\n        when %r{/blockDeviceMapping/item$}\n          @item[:block_device_mappings] ||= []\n          @block_device_mapping = {}\n        when %r{/tagSet/item$}\n          @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'imageId'         then @item[:aws_id]            = @text\n        when 'imageLocation'   then @item[:aws_location]      = @text\n        when 'imageState'      then @item[:aws_state]         = @text\n        when 'imageOwnerId'    then @item[:aws_owner]         = @text\n        when 'isPublic'        then @item[:aws_is_public]     = @text == 'true' ? true : false\n        when 'productCode'     then (@item[:aws_product_codes] ||= []) << @text\n        when 'architecture'    then @item[:aws_architecture]  = @text\n        when 'imageType'       then @item[:aws_image_type]    = @text\n        when 'kernelId'        then @item[:aws_kernel_id]     = @text\n        when 'ramdiskId'       then @item[:aws_ramdisk_id]    = @text\n        when 'platform'        then @item[:aws_platform]      = @text\n        when 'imageOwnerAlias' then @item[:image_owner_alias] = @text\n        when 'name'            then @item[:name]              = @text\n        when 'description'     then @item[:description]       = @text\n        when 'rootDeviceType'  then @item[:root_device_type]  = @text\n        when 'rootDeviceName'  then @item[:root_device_name]  = @text\n        when 'imageClass'      then @item[:image_class]       = @text\n        when 'virtualizationType' then @item[:virtualization_type] = @text\n        when 'hypervisor'      then @item [:hypervisor]       = @text\n        else\n          case full_tag_name\n          when %r{/stateReason/code$}    then @item[:state_reason_code]    = @text.to_i\n          when %r{/stateReason/message$} then @item[:state_reason_message] = @text\n          when %r{/blockDeviceMapping/item} # no trailing $\n            case name\n            when 'deviceName'          then @block_device_mapping[:device_name]                = @text\n            when 'virtualName'         then @block_device_mapping[:virtual_name]               = @text\n            when 'volumeSize'          then @block_device_mapping[:ebs_volume_size]            = @text.to_i\n            when 'snapshotId'          then @block_device_mapping[:ebs_snapshot_id]            = @text\n            when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination]  = @text == 'true' ? true : false\n            when 'item'                then @item[:block_device_mappings]                    << @block_device_mapping\n            end\n          when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{/imagesSet/item$}    then @result                     << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2RegisterImageParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        @result = @text if name == 'imageId'\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Image Attribute\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeImageAttributeParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{launchPermission$}        then @result = {}\n        when %r{productCodes$}            then @result = []\n        when %r{blockDeviceMapping$}      then @result = []\n        when %r{blockDeviceMapping/item$} then @block_device_mapping = {}\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/kernel/value$}                then @result = @text\n        when %r{/ramdisk/value$}               then @result = @text\n        when %r{/description/value$}           then @result = @text\n        when %r{/productCode$}                 then @result << @text\n        when %r{launchPermission/item/group$}  then (@result[:groups]  ||=[])   << @text\n        when %r{launchPermission/item/userId$} then (@result[:user_ids]||=[]) << @text\n        when %r{/blockDeviceMapping/item} # no trailing $\n          case name\n          when 'deviceName'          then @block_device_mapping[:device_name]                = @text\n          when 'virtualName'         then @block_device_mapping[:virtual_name]               = @text\n          when 'noDevice'            then @block_device_mapping[:no_device]                  = @text\n          when 'snapshotId'          then @block_device_mapping[:ebs_snapshot_id]            = @text\n          when 'volumeSize'          then @block_device_mapping[:ebs_volume_size]            = @text\n          when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination]  = @text == 'true' ? true : false\n          when 'item'                then @result                                           << @block_device_mapping\n          end\n        end\n      end\n      def reset\n        @result = nil\n      end\n    end\n\n  end\n  \nend"
  },
  {
    "path": "lib/ec2/right_ec2_instances.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    INSTANCE_API_VERSION = (API_VERSION > '2012-07-20') ? API_VERSION : '2012-07-20'\n\n  #-----------------------------------------------------------------\n  #      Instances\n  #-----------------------------------------------------------------\n\n    def get_desc_instances(instances)  # :nodoc:\n      result = []\n      instances.each do |reservation|\n        reservation[:instances_set].each do |instance|\n          # Parse and remove timestamp from the reason string. The timestamp is of\n          # the request, not when EC2 took action, thus confusing & useless...\n          instance[:aws_reason]         = instance[:aws_reason].sub(/\\(\\d[^)]*GMT\\) */, '')\n          instance[:aws_owner]          = reservation[:aws_owner]\n          instance[:aws_reservation_id] = reservation[:aws_reservation_id]\n          # Security Groups\n          instance[:groups]             = instance[:groups].right_blank? ? reservation[:aws_groups] : instance[:groups]\n          result << instance\n        end\n      end\n      result\n    rescue Exception\n      on_exception\n    end\n\n    # Retrieve information about EC2 instances.\n    #\n    # Accepts a list of instances and/or a set of filters as the last parameter.\n    # \n    # Filters: architecture, availability-zone, block-device-mapping.attach-time, block-device-mapping.delete-on-termination,\n    # block-device-mapping.device-name, block-device-mapping.status, block-device-mapping.volume-id, client-token, dns-name,\n    # group-id, image-id, instance-id, instance-lifecycle, instance-state-code, instance-state-name, instance-type, ip-address,\n    # kernel-id, key-name, launch-index, launch-time, monitoring-state, owner-id, placement-group-name, platform,\n    # private-dns-name, private-ip-address, product-code, ramdisk-id, reason, requester-id, reservation-id, root-device-name,\n    # root-device-type, spot-instance-request-id, state-reason-code, state-reason-message, subnet-id, tag-key, tag-value,\n    # tag:key, virtualization-type, vpc-id,\n    #\n    #\n    #  ec2.describe_instances #=>\n    #    [{:source_dest_check=>true,\n    #        :subnet_id=>\"subnet-da6cf9b3\",\n    #        :aws_kernel_id=>\"aki-3932d150\",\n    #        :ami_launch_index=>\"0\",\n    #        :tags=>{},\n    #        :aws_reservation_id=>\"r-7cd25c11\",\n    #        :aws_owner=>\"826693181925\",\n    #        :state_reason_code=>\"Client.UserInitiatedShutdown\",\n    #        :aws_instance_id=>\"i-2d898e41\",\n    #        :hypervisor=>\"xen\",\n    #        :root_device_name=>\"/dev/sda1\",\n    #        :aws_ramdisk_id=>\"ari-c515f6ac\",\n    #        :aws_instance_type=>\"m1.large\",\n    #        :groups=>[{:group_name=>\"2009-07-15-default\", :group_id=>\"sg-90c5d6fc\"}],\n    #        :block_device_mappings=>\n    #          [{:device_name=>\"/dev/sda1\",\n    #            :ebs_status=>\"attached\",\n    #            :ebs_attach_time=>\"2011-03-04T18:51:58.000Z\",\n    #            :ebs_delete_on_termination=>true,\n    #            :ebs_volume_id=>\"vol-38f2bd50\"}],\n    #        :state_reason_message=>\n    #          \"Client.UserInitiatedShutdown: User initiated shutdown\",\n    #        :aws_image_id=>\"ami-a3638cca\",\n    #        :virtualization_type=>\"paravirtual\",\n    #        :aws_launch_time=>\"2011-03-04T18:13:59.000Z\",\n    #        :private_dns_name=>\"\",\n    #        :aws_product_codes=>[],\n    #        :aws_availability_zone=>\"us-east-1a\",\n    #        :aws_state_code=>80,\n    #        :architecture=>\"x86_64\",\n    #        :dns_name=>\"\",\n    #        :client_token=>\"1299262447-684266-NNgyH-ouPTI-MzG6h-5AIRk\",\n    #        :root_device_type=>\"ebs\",\n    #        :vpc_id=>\"vpc-e16cf988\",\n    #        :monitoring_state=>\"disabled\",\n    #        :ssh_key_name=>\"default\",\n    #        :private_ip_address=>\"192.168.0.52\",\n    #        :aws_reason=>\"User initiated \",\n    #        :aws_state=>\"stopped\"}, ...]\n    #\n    #   ec2.describe_instances(\"i-8ce84ae6\", \"i-8ce84ae8\", \"i-8ce84ae0\")\n    #   ec2.describe_instances(:filters => { 'availability-zone' => 'us-east-1a', 'instance-type' => 'c1.medium' })\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeInstances.html\n    #\n    def describe_instances(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => INSTANCE_API_VERSION})\n      describe_resources_with_list_and_options('DescribeInstances', 'InstanceId', QEc2DescribeInstancesParser, list_and_options) do |parser|\n        get_desc_instances(parser.result)\n      end\n    end\n\n    # Return the product code attached to instance or +nil+ otherwise.\n    #\n    #  ec2.confirm_product_instance('ami-e444444d','12345678') #=> nil\n    #  ec2.confirm_product_instance('ami-e444444d','00001111') #=> \"000000000888\"\n    #\n    def confirm_product_instance(instance, product_code)\n      link = generate_request(\"ConfirmProductInstance\", { 'ProductCode' => product_code,\n                                'InstanceId'  => instance })\n      request_info(link, QEc2ConfirmProductInstanceParser.new(:logger => @logger))\n    end\n\n    # Launch new EC2 instances. Returns a list of launched instances or an exception.\n    #\n    #  ec2.run_instances('ami-e444444d',1,1,['2009-07-15-default'],'my_awesome_key', 'Woohoo!!!', 'public') #=>\n    #   [{:aws_image_id       => \"ami-e444444d\",\n    #     :aws_reason         => \"\",\n    #     :aws_state_code     => \"0\",\n    #     :aws_owner          => \"000000000888\",\n    #     :aws_instance_id    => \"i-123f1234\",\n    #     :aws_reservation_id => \"r-aabbccdd\",\n    #     :aws_state          => \"pending\",\n    #     :dns_name           => \"\",\n    #     :ssh_key_name       => \"my_awesome_key\",\n    #     :groups             => [{:group_name=>\"2009-07-15-default\", :group_id=>\"sg-90c5d6fc\"}],\n    #     :private_dns_name   => \"\",\n    #     :aws_instance_type  => \"m1.small\",\n    #     :aws_launch_time    => \"2008-1-1T00:00:00.000Z\"\n    #     :aws_ramdisk_id     => \"ari-8605e0ef\"\n    #     :aws_kernel_id      => \"aki-9905e0f0\",\n    #     :ami_launch_index   => \"0\",\n    #     :aws_availability_zone => \"us-east-1b\"\n    #     }]\n    #\n    def run_instances(image_id, min_count, max_count, group_names, key_name, user_data='',\n                      addressing_type = nil, instance_type = nil,\n                      kernel_id = nil, ramdisk_id = nil, availability_zone = nil,\n                      monitoring_enabled = nil, subnet_id = nil, disable_api_termination = nil,\n                      instance_initiated_shutdown_behavior = nil, block_device_mappings = nil,\n                      placement_group_name = nil, client_token = nil)\n \t    launch_instances(image_id, { :min_count                            => min_count,\n \t                                 :max_count                            => max_count,\n \t                                 :user_data                            => user_data,\n                                   :group_names                          => group_names,\n                                   :key_name                             => key_name,\n                                   :instance_type                        => instance_type,\n                                   :kernel_id                            => kernel_id,\n                                   :ramdisk_id                           => ramdisk_id,\n                                   :availability_zone                    => availability_zone,\n                                   :monitoring_enabled                   => monitoring_enabled,\n                                   :subnet_id                            => subnet_id,\n                                   :disable_api_termination              => disable_api_termination,\n                                   :instance_initiated_shutdown_behavior => instance_initiated_shutdown_behavior,\n                                   :block_device_mappings                => block_device_mappings,\n                                   :placement_group_name                 => placement_group_name,\n                                   :client_token                         => client_token\n                                 })\n    end\n\n    # Launch new EC2 instances.\n    # \n    # Options: :image_id, :min_count, max_count, :key_name, :kernel_id, :ramdisk_id,\n    # :availability_zone, :monitoring_enabled, :subnet_id, :disable_api_termination, :instance_initiated_shutdown_behavior,\n    # :block_device_mappings, :placement_group_name, :license_pool, :group_ids, :group_names, :private_ip_address,\n    # :ebs_optimized\n    # \n    # Returns a list of launched instances or an exception.\n    #\n    #  ec2.launch_instances( \"ami-78779511\",\n    #                        :min_count => 1,\n    #                        :group_names => [\"default\", \"eugeg223123123\"],\n    #                        :user_data => 'Ohoho!',\n    #                        :availability_zone => \"us-east-1a\",\n    #                        :disable_api_termination => false,\n    #                        :instance_initiated_shutdown_behavior => 'terminate',\n    #                        :block_device_mappings => [ {:ebs_snapshot_id=>\"snap-e40fd188\",\n    #                                                     :ebs_delete_on_termination=>true,\n    #                                                     :device_name => \"/dev/sdk\",\n    #                                                     :virtual_name => \"mystorage\"} ] ) #=>\n    #    [{:hypervisor=>\"xen\",\n    #      :private_dns_name=>\"\",\n    #      :client_token=>\"1309532374-551037-gcsBj-gEypk-piG06-ODfQm\",\n    #      :monitoring_state=>\"disabled\",\n    #      :aws_availability_zone=>\"us-east-1a\",\n    #      :root_device_name=>\"/dev/sda1\",\n    #      :state_reason_code=>\"pending\",\n    #      :dns_name=>\"\",\n    #      :tags=>{},\n    #      :aws_reason=>\"\",\n    #      :virtualization_type=>\"paravirtual\",\n    #      :state_reason_message=>\"pending\",\n    #      :aws_reservation_id=>\"r-6fada703\",\n    #      :aws_ramdisk_id=>\"ari-a51cf9cc\",\n    #      :ami_launch_index=>\"0\",\n    #      :groups=>\n    #       [{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"},\n    #        {:group_id=>\"sg-70733019\", :group_name=>\"eugeg223123123\"}],\n    #      :aws_owner=>\"826693181925\",\n    #      :aws_instance_type=>\"m1.small\",\n    #      :aws_state=>\"pending\",\n    #      :root_device_type=>\"ebs\",\n    #      :aws_image_id=>\"ami-78779511\",\n    #      :aws_kernel_id=>\"aki-a71cf9ce\",\n    #      :aws_launch_time=>\"2011-07-01T14:59:35.000Z\",\n    #      :aws_state_code=>0,\n    #      :aws_instance_id=>\"i-4f202621\",\n    #      :aws_product_codes=>[]}]\n    #\n    def launch_instances(image_id, options={})\n      options[:user_data] = options[:user_data].to_s\n      params = map_api_keys_and_values( options,\n        :key_name, :kernel_id,\n        :ramdisk_id, :subnet_id, :instance_initiated_shutdown_behavior,\n        :private_ip_address, :additional_info, :license_pool, :ebs_optimized,\n        :image_id                => { :value => image_id },\n        :min_count               => { :value => options[:min_count] || 1 },\n        :max_count               => { :value => options[:max_count] || options[:min_count] || 1 },\n        :placement_tenancy       => 'Placement.Tenancy',\n        :placement_group_name    => 'Placement.GroupName',\n        :availability_zone       => 'Placement.AvailabilityZone',\n        :group_names             => { :amazonize_list => 'SecurityGroup' },\n        :group_ids               => { :amazonize_list => 'SecurityGroupId' },\n        :block_device_mappings   => { :amazonize_bdm  => 'BlockDeviceMapping' },\n        :instance_type           => { :value => options[:instance_type] || DEFAULT_INSTANCE_TYPE },\n        :disable_api_termination => { :value => Proc.new{ !options[:disable_api_termination].nil? && options[:disable_api_termination].to_s }},\n        :client_token            => { :value => !@params[:eucalyptus] && (options[:client_token] || AwsUtils::generate_unique_token)},\n        :user_data               => { :value => Proc.new { !options[:user_data].empty? && Base64.encode64(options[:user_data]).delete(\"\\n\") }},\n        :monitoring_enabled      => { :name  => 'Monitoring.Enabled',\n                                      :value => Proc.new{ options[:monitoring_enabled] && options[:monitoring_enabled].to_s }})\n      # Log debug information\n      @logger.info(\"Launching instance of image #{image_id}. Options: #{params.inspect}\")\n      # Add IOPS support (default behavior) but skip it when an old API version call is requested\n      options[:options]                 ||= {}\n      options[:options][:api_version]   ||= INSTANCE_API_VERSION\n      params.delete(\"EbsOptimized\") if options[:options][:api_version] < INSTANCE_API_VERSION\n      #\n      link = generate_request(\"RunInstances\", params, options[:options])\n      instances = request_info(link, QEc2DescribeInstancesParser.new(:logger => @logger))\n      get_desc_instances(instances)\n    rescue Exception\n      on_exception\n    end\n\n    # Start instances.\n    #\n    #  ec2.start_instances(\"i-36e84a5e\") #=>\n    #    [{:aws_prev_state_name=>\"stopped\",\n    #      :aws_instance_id=>\"i-36e84a5e\",\n    #      :aws_current_state_code=>16,\n    #      :aws_current_state_name=>\"running\",\n    #      :aws_prev_state_code=>80}]\n    #\n    def start_instances(*instance_aws_ids)\n      instance_aws_ids = instance_aws_ids.flatten\n      link = generate_request(\"StartInstances\", amazonize_list('InstanceId', instance_aws_ids))\n      request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))\n     end\n\n    # Stop instances.\n    #\n    # Options: :force => true|false\n    #\n    #  ec2.stop_instances(\"i-36e84a5e\") #=>\n    #    [{:aws_prev_state_code=>16,\n    #      :aws_prev_state_name=>\"running\",\n    #      :aws_instance_id=>\"i-36e84a5e\",\n    #      :aws_current_state_code=>64,\n    #      :aws_current_state_name=>\"stopping\"}]\n    #\n    def stop_instances(*instance_aws_ids_and_options)\n      list, options = AwsUtils::split_items_and_params(instance_aws_ids_and_options)\n      request_hash = {}\n      request_hash['Force'] = true if options[:force]\n      request_hash.merge!(amazonize_list('InstanceId', list))\n      link = generate_request(\"StopInstances\", request_hash)\n      request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))\n    end\n\n    # Terminates EC2 instances. Returns a list of termination params or an exception.\n    #\n    #  ec2.terminate_instances(['i-cceb49a4']) #=>\n    #    [{:aws_instance_id=>\"i-cceb49a4\",\n    #      :aws_current_state_code=>32,\n    #      :aws_current_state_name=>\"shutting-down\",\n    #      :aws_prev_state_code=>16,\n    #      :aws_prev_state_name=>\"running\"}]\n    #\n    def terminate_instances(*instance_aws_ids)\n      instance_aws_ids = instance_aws_ids.flatten\n      link = generate_request(\"TerminateInstances\", amazonize_list('InstanceId', instance_aws_ids))\n      request_info(link, QEc2TerminateInstancesParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Retreive EC2 instance OS logs. Returns a hash of data or an exception.\n    #\n    #  ec2.get_console_output('i-f222222d') =>\n    #    {:aws_instance_id => 'i-f222222d',\n    #     :aws_timestamp   => \"2007-05-23T14:36:07.000-07:00\",\n    #     :timestamp       => Wed May 23 21:36:07 UTC 2007,          # Time instance\n    #     :aws_output      => \"Linux version 2.6.16-xenU (builder@patchbat.amazonsa) (gcc version 4.0.1 20050727 ...\"\n    def get_console_output(instance_id)\n      link = generate_request(\"GetConsoleOutput\", { 'InstanceId.1' => instance_id })\n      request_info(link, QEc2GetConsoleOutputParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Reboot an EC2 instance. Returns +true+ or an exception.\n    #\n    #  ec2.reboot_instances(['i-f222222d','i-f222222e']) #=> true\n    #\n    def reboot_instances(*instances)\n      instances = instances.flatten\n      link = generate_request(\"RebootInstances\", amazonize_list('InstanceId', instances))\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Describe instance attribute.\n    #\n    # Attributes: 'instanceType', 'kernel', 'ramdisk', 'userData', 'rootDeviceName', 'disableApiTermination',\n    # 'instanceInitiatedShutdownBehavior', 'sourceDestCheck', 'blockDeviceMapping', 'groupSet'\n    #\n    #  ec2.describe_instance_attribute(instance, \"blockDeviceMapping\") #=>\n    #     [{:ebs_delete_on_termination=>true,\n    #       :ebs_volume_id=>\"vol-683dc401\",\n    #       :device_name=>\"/dev/sda1\"}]\n    #\n    #  ec2.describe_instance_attribute(instance, \"instanceType\") #=> \"m1.small\"\n    #\n    #  ec2.describe_instance_attribute(instance, \"instanceInitiatedShutdownBehavior\") #=> \"stop\"\n    #\n    def describe_instance_attribute(instance_id, attribute)\n      link = generate_request('DescribeInstanceAttribute',\n                              'InstanceId' => instance_id,\n                              'Attribute'  => attribute)\n      value = request_info(link, QEc2DescribeInstanceAttributeParser.new(:logger => @logger))\n      value = Base64.decode64(value) if attribute == \"userData\" && !value.right_blank?\n      value\n    rescue Exception\n      on_exception\n    end\n\n    # Describe instance attribute.\n    #\n    # Attributes: 'kernel', 'ramdisk', 'sourceDestCheck'\n    # \n    #  ec2.reset_instance_attribute(instance, 'kernel') #=> true\n    #\n    def reset_instance_attribute(instance_id, attribute)\n      link = generate_request('ResetInstanceAttribute',\n                              'InstanceId' => instance_id,\n                              'Attribute'  => attribute )\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Modify instance attribute.\n    #\n    # Attributes: 'InstanceType', 'Kernel', 'Ramdisk', 'UserData', 'DisableApiTermination',\n    # 'InstanceInitiatedShutdownBehavior', 'SourceDestCheck', 'GroupId'\n    #\n    #  ec2.modify_instance_attribute(instance, 'instanceInitiatedShutdownBehavior\", \"stop\") #=> true\n    #\n    def modify_instance_attribute(instance_id, attribute, value)\n      request_hash = {'InstanceId' => instance_id}\n      attribute = attribute.to_s.right_underscore.right_camelize\n      case attribute\n      when 'UserData' then request_hash[\"#{attribute}.Value\"] = Base64.encode64(value).delete(\"\\n\")\n      when 'GroupId'  then request_hash.merge!(amazonize_list('GroupId', value))\n      else                 request_hash[\"#{attribute}.Value\"] = value\n      end\n      link = generate_request('ModifyInstanceAttribute', request_hash, :api_version => INSTANCE_API_VERSION)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      Instances: Windows addons\n    #-----------------------------------------------------------------\n\n    # Get initial Windows Server setup password from an instance console output.\n    #\n    #  my_awesome_key = ec2.create_key_pair('my_awesome_key') #=>\n    #    {:aws_key_name    => \"my_awesome_key\",\n    #     :aws_fingerprint => \"01:02:03:f4:25:e6:97:e8:9b:02:1a:26:32:4e:58:6b:7a:8c:9f:03\",\n    #     :aws_material    => \"-----BEGIN RSA PRIVATE KEY-----\\nMIIEpQIBAAK...Q8MDrCbuQ=\\n-----END RSA PRIVATE KEY-----\"}\n    #\n    #  my_awesome_instance = ec2.run_instances('ami-a000000a',1,1,['my_awesome_group'],'my_awesome_key', 'WindowsInstance!!!') #=>\n    #   [{:aws_image_id       => \"ami-a000000a\",\n    #     :aws_instance_id    => \"i-12345678\",\n    #     ...\n    #     :aws_availability_zone => \"us-east-1b\"\n    #     }]\n    #\n    #  # wait until instance enters 'operational' state and get it's initial password\n    #\n    #  puts ec2.get_initial_password(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> \"MhjWcgZuY6\"\n    #\n    def get_initial_password(instance_id, private_key)\n      console_output = get_console_output(instance_id)\n      crypted_password = console_output[:aws_output][%r{<Password>(.+)</Password>}m] && $1\n      unless crypted_password\n        raise AwsError.new(\"Initial password was not found in console output for #{instance_id}\")\n      else\n        OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(crypted_password))\n      end\n    rescue Exception\n      on_exception\n    end\n\n    # Get Initial windows instance password using Amazon API call GetPasswordData.\n    #\n    #  puts ec2.get_initial_password_v2(my_awesome_instance[:aws_instance_id], my_awesome_key[:aws_material]) #=> \"MhjWcgZuY6\"\n    #\n    # P.S. To say the truth there is absolutely no any speedup if to compare to the old get_initial_password method... ;(\n    #\n    def get_initial_password_v2(instance_id, private_key)\n      link = generate_request('GetPasswordData',\n                              'InstanceId' => instance_id )\n      response = request_info(link, QEc2GetPasswordDataParser.new(:logger => @logger))\n      if response[:password_data].right_blank?\n        raise AwsError.new(\"Initial password is not yet created for #{instance_id}\")\n      else\n        OpenSSL::PKey::RSA.new(private_key).private_decrypt(Base64.decode64(response[:password_data]))\n      end\n    rescue Exception\n      on_exception\n    end\n\n    # Bundle a Windows image.\n    # Internally, it queues the bundling task and shuts down the instance.\n    # It then takes a snapshot of the Windows volume bundles it, and uploads it to\n    # S3. After bundling completes, Rightaws::Ec2#register_image may be used to\n    # register the new Windows AMI for subsequent launches.\n    #\n    #   ec2.bundle_instance('i-e3e24e8a', 'my-awesome-bucket', 'my-win-image-1') #=>\n    #    [{:aws_update_time => \"2008-10-16T13:58:25.000Z\",\n    #      :s3_bucket       => \"kd-win-1\",\n    #      :s3_prefix       => \"win2pr\",\n    #      :aws_state       => \"pending\",\n    #      :aws_id          => \"bun-26a7424f\",\n    #      :aws_instance_id => \"i-878a25ee\",\n    #      :aws_start_time  => \"2008-10-16T13:58:02.000Z\"}]\n    #\n    def bundle_instance(instance_id, s3_bucket, s3_prefix,\n                        s3_owner_aws_access_key_id=nil, s3_owner_aws_secret_access_key=nil,\n                        s3_expires = S3Interface::DEFAULT_EXPIRES_AFTER,\n                        s3_upload_policy='ec2-bundle-read')\n      # S3 access and signatures\n      s3_owner_aws_access_key_id     ||= @aws_access_key_id\n      s3_owner_aws_secret_access_key ||= @aws_secret_access_key\n      s3_expires = Time.now.utc + s3_expires if s3_expires.is_a?(Fixnum) && (s3_expires < S3Interface::ONE_YEAR_IN_SECONDS)\n      # policy\n      policy = { 'expiration' => AwsUtils::utc_iso8601(s3_expires),\n                 'conditions' => [ { 'bucket' => s3_bucket },\n                                   { 'acl'    => s3_upload_policy },\n                                   [ 'starts-with', '$key', s3_prefix ] ] }.to_json\n      policy64        = Base64.encode64(policy).gsub(\"\\n\",\"\")\n      signed_policy64 = AwsUtils.sign(s3_owner_aws_secret_access_key, policy64)\n      # fill request params\n      params = { 'InstanceId'                       => instance_id,\n                 'Storage.S3.AWSAccessKeyId'        => s3_owner_aws_access_key_id,\n                 'Storage.S3.UploadPolicy'          => policy64,\n                 'Storage.S3.UploadPolicySignature' => signed_policy64,\n                 'Storage.S3.Bucket'                => s3_bucket,\n                 'Storage.S3.Prefix'                => s3_prefix,\n                 }\n      link = generate_request(\"BundleInstance\", params)\n      request_info(link, QEc2BundleInstanceParser.new)\n    rescue Exception\n      on_exception\n    end\n\n    # Describe the status of the Windows AMI bundlings.\n    #\n    # Accepts a list of tasks and/or a set of filters as the last parameter.\n    #\n    # Filters\" bundle-id, error-code, error-message, instance-id, progress, s3-aws-access-key-id, s3-bucket, s3-prefix,\n    # start-time, state, update-time\n    #\n    #  ec2.describe_bundle_tasks('bun-4fa74226') #=>\n    #    [{:s3_bucket         => \"my-awesome-bucket\"\n    #      :aws_id            => \"bun-0fa70206\",\n    #      :s3_prefix         => \"win1pr\",\n    #      :aws_start_time    => \"2008-10-14T16:27:57.000Z\",\n    #      :aws_update_time   => \"2008-10-14T16:37:10.000Z\",\n    #      :aws_error_code    => \"Client.S3Error\",\n    #      :aws_error_message =>\n    #       \"AccessDenied(403)- Invalid according to Policy: Policy Condition failed: [\\\"eq\\\", \\\"$acl\\\", \\\"aws-exec-read\\\"]\",\n    #      :aws_state         => \"failed\",\n    #      :aws_instance_id   => \"i-e3e24e8a\"}]\n    #\n    #   ec2.describe_bundle_tasks(:filters => { 'state' => 'pending' })\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeBundleTasks.html\n    #\n    def describe_bundle_tasks(*list_and_options)\n      describe_resources_with_list_and_options('DescribeBundleTasks', 'BundleId', QEc2DescribeBundleTasksParser, list_and_options)\n    end\n\n    # Cancel an in‐progress or pending bundle task by id.\n    #\n    #  ec2.cancel_bundle_task('bun-73a7421a') #=>\n    #   [{:s3_bucket         => \"my-awesome-bucket\"\n    #     :aws_id            => \"bun-0fa70206\",\n    #     :s3_prefix         => \"win02\",\n    #     :aws_start_time    => \"2008-10-14T13:00:29.000Z\",\n    #     :aws_error_message => \"User has requested bundling operation cancellation\",\n    #     :aws_state         => \"failed\",\n    #     :aws_update_time   => \"2008-10-14T13:01:31.000Z\",\n    #     :aws_error_code    => \"Client.Cancelled\",\n    #     :aws_instance_id   => \"i-e3e24e8a\"}\n    #\n    def cancel_bundle_task(bundle_id)\n      link = generate_request(\"CancelBundleTask\", { 'BundleId' => bundle_id })\n      request_info(link, QEc2BundleInstanceParser.new)\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Instances\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeInstancesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{(RunInstancesResponse|DescribeInstancesResponse/reservationSet/item)$}\n          @reservation = { :aws_groups    => [],\n                           :instances_set => [] }\n        when %r{(/groupSet/item|instancesSet/item/placement)$}\n          @group = {}\n        when %r{instancesSet/item$}\n            # the optional params (sometimes are missing and we dont want them to be nil)\n          @item = { :aws_product_codes => [],\n                    :groups            => [],\n                    :tags              => {} }\n        when %r{blockDeviceMapping/item$}\n          @item[:block_device_mappings] ||= []\n          @block_device_mapping = {}\n        when %r{/tagSet/item$}\n          @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'reservationId'    then @reservation[:aws_reservation_id] = @text\n        when 'ownerId'          then @reservation[:aws_owner]          = @text\n        when 'instanceId'       then @item[:aws_instance_id]       = @text\n        when 'imageId'          then @item[:aws_image_id]          = @text\n        when 'privateDnsName'   then @item[:private_dns_name]      = @text\n        when 'dnsName'          then @item[:dns_name]              = @text\n        when 'reason'           then @item[:aws_reason]            = @text\n        when 'keyName'          then @item[:ssh_key_name]          = @text\n        when 'amiLaunchIndex'   then @item[:ami_launch_index]      = @text\n        when 'productCode'      then @item[:aws_product_codes]    << @text\n        when 'instanceType'     then @item[:aws_instance_type]     = @text\n        when 'launchTime'       then @item[:aws_launch_time]       = @text\n        when 'availabilityZone' then @item[:aws_availability_zone] = @text\n        when 'kernelId'         then @item[:aws_kernel_id]         = @text\n        when 'ramdiskId'        then @item[:aws_ramdisk_id]        = @text\n        when 'platform'         then @item[:aws_platform]          = @text\n        when 'subnetId'         then @item[:subnet_id]             = @text\n        when 'vpcId'            then @item[:vpc_id]                = @text\n        when 'privateIpAddress' then @item[:private_ip_address]    = @text\n        when 'ipAddress'        then @item[:ip_address]            = @text\n        when 'architecture'     then @item[:architecture]          = @text\n        when 'rootDeviceType'   then @item[:root_device_type]      = @text\n        when 'rootDeviceName'   then @item[:root_device_name]      = @text\n        when 'instanceClass'    then @item[:instance_class]        = @text\n        when 'instanceLifecycle'     then @item[:instance_lifecycle]       = @text\n        when 'spotInstanceRequestId' then @item[:spot_instance_request_id] = @text\n        when 'requesterId'           then @item[:requester_id]             = @text\n        when 'virtualizationType'    then @item[:virtualization_type]      = @text\n        when 'clientToken'           then @item[:client_token]      = @text\n        when 'sourceDestCheck'       then @item[:source_dest_check] = @text == 'true'\n        when 'tenancy'               then @item[:placement_tenancy] = @text\n        when 'hypervisor'            then @item[:hypervisor]        = @text\n        when 'ebsOptimized'          then @item[:ebs_optimized]     = @text == 'true'\n        else\n          case full_tag_name\n          # EC2 Groups\n          when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item/groupId$}   then @group[:group_id]          = @text\n          when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item/groupName$} then @group[:group_name]        = @text\n          when %r{(RunInstancesResponse|/reservationSet/item)/groupSet/item$}           then @reservation[:aws_groups] << @group\n          # VPC Groups\n          # KD: It seems that these groups are always present when the groups above present for non VPC instances only\n          when %r{/instancesSet/item/groupSet/item/groupId$}   then @group[:group_id]   = @text\n          when %r{/instancesSet/item/groupSet/item/groupName$} then @group[:group_name] = @text\n          when %r{/instancesSet/item/groupSet/item$}           then @item[:groups]     << @group\n          # Placement Group Name\n          when %r{/placement/groupName$} then @group[:placement_group_name]= @text\n          # Codes\n          when %r{/stateReason/code$}    then @item[:state_reason_code]    = @text\n          when %r{/stateReason/message$} then @item[:state_reason_message] = @text\n          when %r{/instanceState/code$}  then @item[:aws_state_code]       = @text.to_i\n          when %r{/instanceState/name$}  then @item[:aws_state]            = @text\n          when %r{/monitoring/state$}    then @item[:monitoring_state]     = @text\n          when %r{/license/pool$}        then @item[:license_pool]         = @text\n          when %r{/blockDeviceMapping/item} # no trailing $\n            case name\n            when 'deviceName'          then @block_device_mapping[:device_name]                = @text\n            when 'virtualName'         then @block_device_mapping[:virtual_name]               = @text\n            when 'volumeId'            then @block_device_mapping[:ebs_volume_id]              = @text\n            when 'status'              then @block_device_mapping[:ebs_status]                 = @text\n            when 'attachTime'          then @block_device_mapping[:ebs_attach_time]            = @text\n            when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination]  = @text == 'true'\n            when 'item'                then @item[:block_device_mappings]                     << @block_device_mapping\n            end\n          when %r{/instancesSet/item$} then @reservation[:instances_set] << @item\n          when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{(RunInstancesResponse|DescribeInstancesResponse/reservationSet/item)$} then @result << @reservation\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2ConfirmProductInstanceParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        @result = @text if name == 'ownerId'\n      end\n    end\n\n    class QEc2TerminateInstancesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @instance = {} if name == 'item'\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/instanceId$}         then @instance[:aws_instance_id]        = @text\n        when %r{/currentState/code$}  then @instance[:aws_current_state_code] = @text.to_i\n        when %r{/currentState/name$}  then @instance[:aws_current_state_name] = @text\n        when %r{/previousState/code$} then @instance[:aws_prev_state_code]    = @text.to_i\n        when %r{/previousState/name$} then @instance[:aws_prev_state_name]    = @text\n        when %r{/item$}               then @result << @instance\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeInstanceAttributeParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{groupSet$}                then @result = []\n        when %r{groupSet/item$}           then @group  = {}\n        when %r{blockDeviceMapping$}      then @result = []\n        when %r{blockDeviceMapping/item$} then @block_device_mapping = {}\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/instanceType/value$}   then @result = @text\n        when %r{/kernel/value$}         then @result = @text\n        when %r{/ramdisk/value$}        then @result = @text\n        when %r{/userData/value$}       then @result = @text\n        when %r{/rootDeviceName/value$} then @result = @text\n        when %r{/disableApiTermination/value}              then @result = @text == 'true'\n        when %r{/instanceInitiatedShutdownBehavior/value$} then @result = @text\n        when %r{/sourceDestCheck/value$}                   then @result = @text == 'true'\n        when %r{/groupSet/item} # no trailing $\n          case name\n          when 'groupId'   then @group[:group_id]   = @text\n          when 'groupName' then @group[:group_name] = @text\n          when 'item'      then @result << @group\n          end\n        when %r{/blockDeviceMapping/item} # no trailing $\n          case name\n          when 'deviceName'          then @block_device_mapping[:device_name]                = @text\n          when 'virtualName'         then @block_device_mapping[:virtual_name]               = @text\n          when 'noDevice'            then @block_device_mapping[:no_device]                  = @text\n          when 'volumeId'            then @block_device_mapping[:ebs_volume_id]              = @text\n          when 'status'              then @block_device_mapping[:ebs_status]                 = @text\n          when 'attachTime'          then @block_device_mapping[:ebs_attach_time]            = @text\n          when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination]  = @text == 'true'\n          when 'item'                then @result                                           << @block_device_mapping\n          end\n        end\n      end\n      def reset\n        @result = nil\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: Console\n  #-----------------------------------------------------------------\n\n    class QEc2GetConsoleOutputParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'instanceId' then @result[:aws_instance_id] = @text\n        when 'timestamp'  then @result[:aws_timestamp]   = @text\n                               @result[:timestamp]       = (Time.parse(@text)).utc\n        when 'output'     then @result[:aws_output]      = Base64.decode64(@text)\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n  #-----------------------------------------------------------------\n  #      Instances: Windows related part\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeBundleTasksParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @bundle = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n#        when 'requestId'  then @bundle[:request_id]    = @text\n        when 'instanceId' then @bundle[:aws_instance_id]   = @text\n        when 'bundleId'   then @bundle[:aws_id]            = @text\n        when 'bucket'     then @bundle[:s3_bucket]         = @text\n        when 'prefix'     then @bundle[:s3_prefix]         = @text\n        when 'startTime'  then @bundle[:aws_start_time]    = @text\n        when 'updateTime' then @bundle[:aws_update_time]   = @text\n        when 'state'      then @bundle[:aws_state]         = @text\n        when 'progress'   then @bundle[:aws_progress]      = @text\n        when 'code'       then @bundle[:aws_error_code]    = @text\n        when 'message'    then @bundle[:aws_error_message] = @text\n        when 'item'       then @result                    << @bundle\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2BundleInstanceParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n#        when 'requestId'  then @result[:request_id]    = @text\n        when 'instanceId' then @result[:aws_instance_id]   = @text\n        when 'bundleId'   then @result[:aws_id]            = @text\n        when 'bucket'     then @result[:s3_bucket]         = @text\n        when 'prefix'     then @result[:s3_prefix]         = @text\n        when 'startTime'  then @result[:aws_start_time]    = @text\n        when 'updateTime' then @result[:aws_update_time]   = @text\n        when 'state'      then @result[:aws_state]         = @text\n        when 'progress'   then @result[:aws_progress]      = @text\n        when 'code'       then @result[:aws_error_code]    = @text\n        when 'message'    then @result[:aws_error_message] = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2GetPasswordDataParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'instanceId'   then @result[:aws_instance_id] = @text\n        when 'timestamp'    then @result[:timestamp]       = @text\n        when 'passwordData' then @result[:password_data]   = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n  end\n\nend"
  },
  {
    "path": "lib/ec2/right_ec2_monitoring.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    # Enables monitoring for a running instances. For more information, refer to the Amazon CloudWatch Developer Guide.\n    # \n    #  ec2.monitor_instances('i-8437ddec') #=>\n    #    {:instance_id=>\"i-8437ddec\", :monitoring_state=>\"pending\"}\n    #\n    def monitor_instances(*list)\n      link = generate_request(\"MonitorInstances\", amazonize_list('InstanceId', list.flatten) )\n      request_info(link, QEc2MonitorInstancesParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Disables monitoring for a running instances. For more information, refer to the Amazon CloudWatch Developer Guide.\n    #\n    #  ec2.unmonitor_instances('i-8437ddec') #=>\n    #    {:instance_id=>\"i-8437ddec\", :monitoring_state=>\"disabling\"}\n    #\n    def unmonitor_instances(*list)\n      link = generate_request(\"UnmonitorInstances\", amazonize_list('InstanceId', list.flatten) )\n      request_info(link, QEc2MonitorInstancesParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    class QEc2MonitorInstancesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n        when 'instanceId'   then @item[:instance_id] = @text\n        when 'state'        then @item[:monitoring_state] = @text\n        when 'item'         then @result << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  end\n\nend"
  },
  {
    "path": "lib/ec2/right_ec2_placement_groups.rb",
    "content": "#\n# Copyright (c) 2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      Placement Groups\n    #-----------------------------------------------------------------\n\n    # Describe placement groups.\n    #\n    # Accepts a list of placement groups and/or a set of filters as the last parameter.\n    #\n    # Filters: group-name, state, strategy\n    #\n    # If you don’t specify a particular placement group, the response includes\n    # information about all of them. The information includes the group name, the strategy,\n    # and the group state (e.g., pending, available, etc.).\n    #\n    #  ec2.describe_placement_groups #=>\n    #    [{:state=>\"available\", :strategy=>\"cluster\", :group_name=>\"kd_first\"},\n    #     {:state=>\"available\", :strategy=>\"cluster\", :group_name=>\"kd_second\"}]\n    #\n    #  ec2.describe_placement_groups('kd_second') #=>\n    #    [{:strategy=>\"cluster\", :group_name=>\"kd_second\", :state=>\"available\"}]\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference_query_DescribePlacementGroups.html\n    #\n    def describe_placement_groups(*list_and_options)\n      describe_resources_with_list_and_options('DescribePlacementGroups', 'GroupName', QEc2DescribePlacementGroupsParser, list_and_options)\n    end\n\n    # Create placement group creates a placement group (i.e. logical cluster group)\n    # into which you can then launch instances. You must provide a name for the group\n    # that is unique within the scope of your account. You must also provide a strategy\n    # value. Currently the only value accepted is cluster.\n    #\n    #   ec2.create_placement_group('kd_second') #=> true\n    #\n    def create_placement_group(placement_group_name, strategy = 'cluster')\n      link = generate_request('CreatePlacementGroup',\n                              'GroupName' => placement_group_name.to_s,\n                              'Strategy'  => strategy.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Delete placement group deletes a placement group that you own. The group must not\n    # contain any instances.\n    #\n    #   ec2.delete_placement_group('kd_second') #=> true\n    #\n    def delete_placement_group(placement_group_name)\n      link = generate_request('DeletePlacementGroup',\n                              'GroupName' => placement_group_name.to_s)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Placement Groups\n    #-----------------------------------------------------------------\n\n    class QEc2DescribePlacementGroupsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case name\n        when 'item' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'groupName' then @item[:group_name] = @text\n        when 'strategy'  then @item[:strategy]   = @text\n        when 'state'     then @item[:state]      = @text\n        when 'item'      then @result           << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/ec2/right_ec2_reserved_instances.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    RESERVED_INSTANCE_API_VERSION = (API_VERSION > '2012-10-01') ? API_VERSION : '2012-10-01'\n\n  #-----------------------------------------------------------------\n  #      Reserved instances\n  #-----------------------------------------------------------------\n\n    # Retrieve reserved instances list.\n    #\n    # Accepts a list of reserved instances and/or a set of filters as the last parameter.\n    #\n    # Filters: availability-zone, duration, fixed-price, instance-type, product-description,\n    # reserved-instances-id, start, state, tag-key, tag-value, tag:key, usage-price\n    #\n    # ec2.describe_reserved_instances #=>\n    #    [{:tags=>{},\n    #      :aws_id=>\"4357912c-0000-0000-0000-15ca71a8e66d\",\n    #      :aws_instance_type=>\"m1.small\",\n    #      :aws_availability_zone=>\"us-east-1c\",\n    #      :aws_start=>\"2010-03-18T20:39:39.569Z\",\n    #      :aws_duration=>94608000,\n    #      :aws_fixed_price=>350.0,\n    #      :aws_usage_price=>0.03,\n    #      :aws_instance_count=>1,\n    #      :aws_product_description=>\"Linux/UNIX\",\n    #      :aws_state=>\"active\",\n    #      :instance_tenancy=>\"default\",\n    #      :currency_code=>\"USD\",\n    #      :offering_type=>\"Medium Utilization\"}]\n    #\n    #  ec2.describe_reserved_instances(:filters => {'availability-zone' => 'us-east-1a'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeReservedInstances.html\n    #\n    def describe_reserved_instances(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => RESERVED_INSTANCE_API_VERSION})\n      describe_resources_with_list_and_options('DescribeReservedInstances', 'ReservedInstancesId', QEc2DescribeReservedInstancesParser, list_and_options)\n    end\n\n    # Retrieve reserved instances offerings.\n    # \n    # Accepts a list of reserved instances offerings and/or a set of filters as the last parameter.\n    #\n    # Filters: availability-zone, duration, fixed-price, instance-type, product-description, reserved-instances-offering-id, usage-price\n    #\n    #  ec2.describe_reserved_instances_offerings #=>\n    #    [{:recurring_charges=>[{:frequency=>\"Hourly\", :amount=>\"0.095\"}],\n    #      :pricing_details_set=>[],\n    #      :aws_id=>\"438012d3-4031-43ff-9241-2964d1bf71d8\",\n    #      :aws_instance_type=>\"c1.medium\",\n    #      :aws_availability_zone=>\"us-east-1e\",\n    #      :aws_duration=>94608000,\n    #      :aws_fixed_price=>775.0,\n    #      :aws_usage_price=>0.0,\n    #      :aws_product_description=>\"Red Hat Enterprise Linux\",\n    #      :instance_tenancy=>\"default\",\n    #      :currency_code=>\"USD\",\n    #      :offering_type=>\"Heavy Utilization\",\n    #      :marketplace=>false},\n    #    { :recurring_charges=>[{:frequency=>\"Hourly\", :amount=>\"0.095\"}],\n    #      :pricing_details_set=>[],\n    #      :aws_id=>\"649fd0c8-6cb4-47bf-83db-7a844016afa7\",\n    #      :aws_instance_type=>\"c1.medium\",\n    #      :aws_availability_zone=>\"us-east-1e\",\n    #      :aws_duration=>94608000,\n    #      :aws_fixed_price=>775.0,\n    #      :aws_usage_price=>0.0,\n    #      :aws_product_description=>\"Red Hat Enterprise Linux (Amazon VPC)\",\n    #      :instance_tenancy=>\"default\",\n    #      :currency_code=>\"USD\",\n    #      :offering_type=>\"Heavy Utilization\",\n    #      :marketplace=>false}, ... ]\n    #\n    #  ec2.describe_reserved_instances_offerings(:filters => {'availability-zone' => 'us-east-1c'})\n    #\n    #  # Get all ReservedInstancesOfferings (list by 50 items)\n    #  result = ec2.describe_reserved_instances_offerings(:max_results => 50) do |response|\n    #    puts response[:items].count\n    #    true\n    #  end\n    #\n    #  # Get first 400 ReservedInstancesOfferings.\n    #  # P.S. it stops making calls one the block below returns false.\n    #  max_count_to_get = 400\n    #  counter          = 0\n    #  result = ec2.describe_reserved_instances_offerings do |response|\n    #    counter += response[:items].count\n    #    max_count_to_get <= counter\n    #  end\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeReservedInstancesOfferings.html\n    #\n    def describe_reserved_instances_offerings(*list_and_options, &block)\n      result = []\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => RESERVED_INSTANCE_API_VERSION})\n      incrementally_list_items('DescribeReservedInstancesOfferings', 'ReservedInstancesOfferingId', QEc2DescribeReservedInstancesOfferingsParser, list_and_options) do |response|\n        result += response[:items]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Purchase a Reserved Instance.\n    # Returns ReservedInstancesId value.\n    #\n    #  ec2.purchase_reserved_instances_offering('e5a2ff3b-f6eb-4b4e-83f8-b879d7060257', 3) # => '4b2293b4-5813-4cc8-9ce3-1957fc1dcfc8'\n    #\n    def purchase_reserved_instances_offering(reserved_instances_offering_id, instance_count=1, options={})\n      options[:options]               ||= {}\n      options[:options][:api_version] ||= RESERVED_INSTANCE_API_VERSION\n\n      api_params = { 'ReservedInstancesOfferingId' => reserved_instances_offering_id,\n                     'InstanceCount'               => instance_count  }\n\n      link = generate_request(\"PurchaseReservedInstancesOffering\", api_params, options)\n      request_info(link, QEc2PurchaseReservedInstancesOfferingParser.new)\n    rescue Exception\n      on_exception\n    end\n\n  #-----------------------------------------------------------------\n  #      PARSERS: ReservedInstances\n  #-----------------------------------------------------------------\n\n    class QEc2DescribeReservedInstancesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/recurringCharges/item$}     then @recurring_charge = {}\n        when %r{/tagSet/item$}               then @aws_tag = {}\n        when %r{/reservedInstancesSet/item$} then @item    = { :tags=> {} }\n        end\n      end\n      def tagend(name)\n        case name\n        when 'reservedInstancesId' then @item[:aws_id]                  = @text\n        when 'instanceType'        then @item[:aws_instance_type]       = @text\n        when 'availabilityZone'    then @item[:aws_availability_zone]   = @text\n        when 'duration'            then @item[:aws_duration]            = @text.to_i\n        when 'usagePrice'          then @item[:aws_usage_price]         = @text.to_f\n        when 'fixedPrice'          then @item[:aws_fixed_price]         = @text.to_f\n        when 'instanceCount'       then @item[:aws_instance_count]      = @text.to_i\n        when 'productDescription'  then @item[:aws_product_description] = @text\n        when 'state'               then @item[:aws_state]               = @text\n        when 'start'               then @item[:aws_start]               = @text\n        when 'instanceTenancy'     then @item[:instance_tenancy]        = @text\n        when 'currencyCode'        then @item[:currency_code]           = @text\n        when 'offeringType'        then @item[:offering_type]           = @text\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}                 then @aws_tag[:key]                = @text\n          when %r{/tagSet/item/value$}               then @aws_tag[:value]              = @text\n          when %r{/tagSet/item$}                     then @item[:tags][@aws_tag[:key]]  = @aws_tag[:value]\n          when %r{/recurringCharges/item/frequency$} then @recurring_charge[:frequency] = @text\n          when %r{/recurringCharges/item/amount$}    then @recurring_charge[:amount]    = @text\n          when %r{/recurringCharges/item$}           then (@item[:recurring_charges]  ||= []) << @recurring_charge\n          when %r{/reservedInstancesSet/item$}       then @result                      << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeReservedInstancesOfferingsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/pricingDetailsSet/item$}             then @pricing_details  = {}\n        when %r{/recurringCharges/item$}              then @recurring_charge = {}\n        when %r{/reservedInstancesOfferingsSet/item$} then @item             = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'nextToken'                   then @result[:next_token]            = @text\n        when 'reservedInstancesOfferingId' then @item[:aws_id]                  = @text\n        when 'instanceType'                then @item[:aws_instance_type]       = @text\n        when 'availabilityZone'            then @item[:aws_availability_zone]   = @text\n        when 'duration'                    then @item[:aws_duration]            = @text.to_i\n        when 'usagePrice'                  then @item[:aws_usage_price]         = @text.to_f\n        when 'fixedPrice'                  then @item[:aws_fixed_price]         = @text.to_f\n        when 'instanceTenancy'             then @item[:instance_tenancy]        = @text\n        when 'currencyCode'                then @item[:currency_code]           = @text\n        when 'productDescription'          then @item[:aws_product_description] = @text\n        when 'offeringType'                then @item[:offering_type]           = @text\n        when 'marketplace'                 then @item[:marketplace]             = (@text == 'true')\n        else\n          case full_tag_name\n          when %r{/recurringCharges/item/frequency$}    then @recurring_charge[:frequency]  = @text\n          when %r{/recurringCharges/item/amount$}       then @recurring_charge[:amount]     = @text\n          when %r{/recurringCharges/item$}              then (@item[:recurring_charges]   ||= []) << @recurring_charge\n          when %r{/pricingDetailsSet/item/price$}       then @pricing_details[:price]       = @text\n          when %r{/pricingDetailsSet/item/count$}       then @pricing_details[:count]       = @text\n          when %r{/pricingDetailsSet/item$}             then (@item[:pricing_details_set] ||= []) << @pricing_details\n          when %r{/reservedInstancesOfferingsSet/item$} then @result[:items] << @item\n          end\n        end\n      end\n      def reset\n        @result = { :items => [] }\n      end\n    end\n\n    class QEc2PurchaseReservedInstancesOfferingParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        if name == 'reservedInstancesId'\n          @result = @text\n        end\n      end\n      def reset\n        @result = ''\n      end\n    end\n\n  end\n\nend"
  },
  {
    "path": "lib/ec2/right_ec2_security_groups.rb",
    "content": "#\n# Copyright (c) 2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      Security groups\n    #-----------------------------------------------------------------\n\n    # Retrieve Security Groups information.\n    # Options: By default this methods expects security group ids but if you wanna pass their names then :describe_by => :group_name option must be set.\n    #\n    # Accepts a list of security groups and/or a set of filters as the last parameter.\n    #\n    # Filters: description, group-name, ip-permission.cidr, ip-permission.from-port, ip-permission.group-name,\n    # ip-permission.protocol, ip-permission.to-port, ip-permission.user-id, owner-id\n    #\n    #  # Amazon cloud:\n    #  ec2 = Rightscale::Ec2.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n    #  ec2.describe_security_groups #=>\n    #    [{:aws_perms=>\n    #        [{:protocol=>\"-1\", :cidr_ips=>\"0.0.0.0/0\", :direction=>:egress},\n    #        {:protocol=>\"tcp\",\n    #          :cidr_ips=>\"127.0.0.2/32\",\n    #          :direction=>:egress,\n    #          :from_port=>\"1111\",\n    #          :to_port=>\"1111\"},\n    #        {:protocol=>\"tcp\",\n    #          :cidr_ips=>\"127.0.0.1/32\",\n    #          :direction=>:egress,\n    #          :from_port=>\"1111\",\n    #          :to_port=>\"1111\"}],\n    #      :aws_group_name=>\"kd-vpc-egress-test-1\",\n    #      :vpc_id=>\"vpc-e16cf988\",\n    #      :aws_description=>\"vpc test\",\n    #      :aws_owner=>\"826693181925\",\n    #      :group_id=>\"sg-b72032db\"}]\n    #\n    #   # Describe by group ids\n    #   ec2.describe_security_groups(\"sg-a0b85dc9\", \"sg-00b05d39\", \"sg-a1b86dc8\")\n    #\n    #   # Describe by group names\n    #   ec2.describe_security_groups(\"default\", \"default1\", \"kd\", :describe_by => :group_name)\n    #\n    #  # Eucalyptus cloud:\n    #  ec2 = Rightscale::Ec2.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, :eucalyptus => true)\n    #  ec2.describe_security_groups #=>\n    #    [{:aws_perms=>\n    #       [{:to_port=>\"65535\",\n    #         :group_name=>\"default\",\n    #         :protocol=>\"tcp\",\n    #         :owner=>\"048291609141\",\n    #         :from_port=>\"1\"},\n    #        {:to_port=>\"65535\",\n    #         :group_name=>\"default\",\n    #         :protocol=>\"udp\",\n    #         :owner=>\"048291609141\",\n    #         :from_port=>\"1\"},\n    #        {:to_port=>\"-1\",\n    #         :group_name=>\"default\",\n    #         :protocol=>\"icmp\",\n    #         :owner=>\"048291609141\",\n    #         :from_port=>\"-1\"},\n    #        {:to_port=>\"22\",\n    #         :protocol=>\"tcp\",\n    #         :from_port=>\"22\",\n    #         :cidr_ip=>\"0.0.0.0/0\"},\n    #        {:to_port=>\"9997\",\n    #         :protocol=>\"tcp\",\n    #         :from_port=>\"9997\",\n    #         :cidr_ip=>\"0.0.0.0/0\"}],\n    #      :aws_group_name=>\"photo_us\",\n    #      :aws_description=>\"default group\",\n    #      :aws_owner=>\"826693181925\"}]\n    #\n    #  ec2.describe_security_groups(:filters => {'ip-permission.from-port' => '22'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeSecurityGroups.html\n    #\n    def describe_security_groups(*list_and_options)\n      list, options = AwsUtils::split_items_and_params(list_and_options)\n      describe_by   = options.delete(:describe_by) == :group_name ? 'GroupName' : 'GroupId'\n      describe_resources_with_list_and_options('DescribeSecurityGroups', describe_by, QEc2DescribeSecurityGroupsParser, list_and_options) do |parser|\n        result = []\n        parser.result.each do |item|\n          result_item = { :aws_owner       => item[:owner_id],\n                          :aws_group_name  => item[:group_name],\n                          :aws_description => item[:group_description] }\n          result_item[:group_id] = item[:group_id] unless item[:group_id].right_blank?\n          result_item[:vpc_id]   = item[:vpc_id]   unless item[:vpc_id].right_blank?\n          aws_perms = []\n          item[:ip_permissions].each do |permission|\n            result_perm = {}\n            result_perm[:from_port] = permission[:from_port]   unless permission[:from_port].right_blank?\n            result_perm[:to_port]   = permission[:to_port]     unless permission[:to_port].right_blank?\n            result_perm[:protocol]  = permission[:ip_protocol]\n            result_perm[:direction] = permission[:direction]\n            # IP permissions\n            Array(permission[:ip_ranges]).each do |ip_range|\n              perm = result_perm.dup\n              # Mhhh... For Eucalyptus we somehow get used to use \":cidr_ip\" instead of \":cidr_ips\"...\n              if @params[:eucalyptus] then  perm[:cidr_ip]  = ip_range\n              else                          perm[:cidr_ips] = ip_range\n              end\n              aws_perms << perm\n            end\n            # Group permissions\n            Array(permission[:groups]).each do |group|\n              perm = result_perm.dup\n              perm[:group_name] = group[:group_name] unless group[:group_name].right_blank?\n              perm[:group_id]   = group[:group_id]   unless group[:group_id].right_blank?\n              perm[:owner]      = group[:user_id]    unless group[:user_id].right_blank?\n              aws_perms << perm\n            end\n          end\n          result_item[:aws_perms] = aws_perms.uniq\n          result << result_item\n        end\n        result\n      end\n    end\n\n    def describe_security_groups_by_name(*list)\n      describe_security_groups(list, :describe_by => :group_name)\n    end\n\n    # Create new Security Group. Returns +true+ or an exception.\n    # Options: :vpc_id\n    #\n    #  ec2.create_security_group('default-1',\"Default allowing SSH, HTTP, and HTTPS ingress\") #=>\n    #    { :group_id=>\"sg-f0227599\", :return=>true }\n    #\n    #  ec2.create_security_group('default-2',\"my VPC group\", :vpc_id => 'vpc-e16c0000') #=>\n    #    { :group_id=>\"sg-76d1c31a\", :return=>true }\n    #\n    def create_security_group(name, description = nil, options = {})\n      options = options.dup\n      options[:group_name]        = name      \n      options[:group_description] = description.right_blank? ? '-' : description # EC2 rejects an empty description...\n      link = generate_request(\"CreateSecurityGroup\", map_api_keys_and_values(options, :group_name, :group_description, :vpc_id))\n      request_info(link, QEc2CreateSecurityGroupsParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Remove Security Group. Returns +true+ or an exception.\n    # Options: :group_name, :group_id\n    #\n    #  # Delete security group by group_id:\n    #  ec2.delete_security_group('sg-90054ef9') #=> true\n    #  ec2.delete_security_group(:group_id => 'sg-90054ef9') #=> true\n    #\n    #  # Delete security group by name (EC2 only):\n    #  ec2.delete_security_group(:group_name => 'my-group']) #=> true\n    #\n    def delete_security_group(group_id_or_options={})\n      options = group_id_or_options.is_a?(Hash) ? group_id_or_options : { :group_id => group_id_or_options } \n      link = generate_request(\"DeleteSecurityGroup\", map_api_keys_and_values(options, :group_name, :group_id))\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    def grant_security_group_ingress(group_id, permissions)\n      modify_security_group(:grant, :ingress, group_id, permissions)\n    end\n\n    def revoke_security_group_ingress(group_id, permissions)\n      modify_security_group(:revoke, :ingress, group_id, permissions)\n    end\n\n    def grant_security_group_egress(group_id, permissions)\n      modify_security_group(:grant, :egress, group_id, permissions)\n    end\n\n    def revoke_security_group_egress(group_id, permissions)\n      modify_security_group(:revoke, :egress, group_id, permissions)\n    end\n\n    # Modify AWS security group permissions.\n    #\n    #  Options:\n    #    action      - :authorize (or :grant) | :revoke (or :remove)\n    #    direction   - :ingress | :egress\n    #    group_name  - security group name\n    #    permissions - a combination of options below:\n    #      # Ports:\n    #      :from_port          => from port\n    #      :to_port            => to port\n    #      :port               => set both :from_port and to_port with the same value\n    #      # Protocol\n    #      :protocol           => :tcp | :udp | :icmp | -1\n    #      # or (ingress)\n    #      :groups             => { UserId1 => GroupId1, UserName2 => GroupId2 }\n    #      :groups             => [ [ UserId1, GroupId1 ], [ UserName2 => GroupId2 ] ]\n    #      # or (egress)\n    #      :groups             => [ GroupId1, GroupId2 ]\n    #      # CidrIp(s)\n    #      :cidr_ip            => '0.0.0.0/0'\n    #      :cidr_ips           => ['1.1.1.1/1', '2.2.2.2/2']\n    #\n    #  # CidrIP based permissions:\n    #\n    #  ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',\n    #                            :cidr_ip  =>  \"127.0.0.0/31\",\n    #                            :port     => 811,\n    #                            :protocol => 'tcp' ) #=> true\n    #\n    #  ec2.modify_security_group(:revoke, :ingress, 'sg-75d1c319',\n    #                            :cidr_ips =>  [\"127.0.0.1/32\", \"127.0.0.2/32\"],\n    #                            :port     => 812,\n    #                            :protocol => 'tcp' ) #=> true\n    #\n    #  # Group based permissions:\n    #\n    #  ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',\n    #                            :groups   => { \"586789340000\" => \"sg-75d1c300\",\n    #                                           \"635201710000\" => \"sg-75d1c301\" },\n    #                            :port     => 801,\n    #                            :protocol => 'tcp' ) #=> true\n    #\n    #  ec2.modify_security_group(:revoke, :ingress, 'sg-75d1c319',\n    #                            :groups   => [[ \"586789340000\", \"sg-75d1c300\" ],\n    #                                          [ \"586789340000\", \"sg-75d1c302\" ]],\n    #                            :port     => 809,\n    #                            :protocol => 'tcp' ) #=> true\n    #\n    #  # +Permissions+ can be an array of permission hashes:\n    #\n    #  ec2.modify_security_group(:authorize, :ingress, 'sg-75d1c319',\n    #                            [{ :groups   => { \"586789340000\" => \"sg-75d1c300\",\n    #                                              \"635201710000\" => \"sg-75d1c301\" },\n    #                                              :port          => 803,\n    #                                              :protocol      => 'tcp'},\n    #                             { :cidr_ips =>  [\"127.0.0.1/32\", \"127.0.0.2/32\"],\n    #                               :port     => 812,\n    #                               :protocol => 'tcp' }]) #=> true\n    #\n    def modify_security_group(action, direction, group_id, permissions)\n      hash = {}\n      raise \"Unknown action #{action.inspect}!\"       unless [:authorize, :grant, :revoke, :remove].include?(action)\n      raise \"Unknown direction #{direction.inspect}!\" unless [:ingress, :egress].include?(direction)\n      # Remote action\n      remote_action = case action\n                      when :authorize, :grant  then direction == :ingress ? \"AuthorizeSecurityGroupIngress\" : \"AuthorizeSecurityGroupEgress\"\n                      when :revoke,    :remove then direction == :ingress ? \"RevokeSecurityGroupIngress\"    : \"RevokeSecurityGroupEgress\"\n                      end\n      # Group Name\n      hash[\"GroupId\"] = group_id\n      # Permissions\n      permissions = [permissions] unless permissions.is_a?(Array)\n      permissions.each_with_index do |permission, idx|\n        pid = idx+1\n        # Protocol\n        hash[\"IpPermissions.#{pid}.IpProtocol\"] = permission[:protocol]\n        # Port\n        unless permission[:port].right_blank?\n          hash[\"IpPermissions.#{pid}.FromPort\"] = permission[:port]\n          hash[\"IpPermissions.#{pid}.ToPort\"]   = permission[:port]\n        else\n          hash[\"IpPermissions.#{pid}.FromPort\"] = permission[:from_port]\n          hash[\"IpPermissions.#{pid}.ToPort\"]   = permission[:to_port]\n        end\n        # Groups\n        case direction\n        when :ingress\n          #  :groups => {UserId1 => GroupId1, ... UserIdN => GroupIdN}\n          #  or (this allows using same UserId multiple times )\n          #  :groups => [[UserId1, GroupId1], ... [UserIdN, GroupIdN]]\n          #  or even (unset user is == current account user)\n          #  :groups => [GroupId1, GroupId2, ... GroupIdN]\n          #  :groups => [[UserId1, GroupId1], GroupId2, ... GroupIdN, ... [UserIdM, GroupIdM]]\n          #\n          index = 1\n          unless permission[:group_names].right_blank?\n            owner_and_groups = []\n            groups_only      = []\n            Array(permission[:group_names]).each do |item|\n              if item.is_a?(Array) && item.size == 2\n                owner_and_groups << item\n              else\n                groups_only << item\n              end\n            end\n            hash.merge!(amazonize_list( [\"IpPermissions.#{pid}.Groups.?.UserId\", \"IpPermissions.#{pid}.Groups.?.GroupName\"], owner_and_groups, :index => index ))\n            index += owner_and_groups.size\n            groups_only = groups_only.flatten\n            hash.merge!(amazonize_list( \"IpPermissions.#{pid}.Groups.?.GroupName\", groups_only, :index => index ))\n            index += groups_only.size\n          end\n          unless permission[:groups].right_blank?\n            owner_and_groups = []\n            groups_only      = []\n            Array(permission[:groups]).each do |item|\n              if item.is_a?(Array) && item.size == 2\n                owner_and_groups << item\n              else\n                groups_only << item\n              end\n            end\n            hash.merge!(amazonize_list( [\"IpPermissions.#{pid}.Groups.?.UserId\", \"IpPermissions.#{pid}.Groups.?.GroupId\"], owner_and_groups, :index => index ))\n            index += owner_and_groups.size\n            groups_only = groups_only.flatten\n            hash.merge!(amazonize_list( \"IpPermissions.#{pid}.Groups.?.GroupId\", groups_only, :index => index ))\n          end\n        when :egress\n          #  :groups => [GroupId1, ... GroupIdN]\n          hash.merge!(amazonize_list( \"IpPermissions.#{pid}.Groups.?.GroupId\", permission[:groups] ))\n        end\n        # CidrIp(s)\n        cidr_ips   = permission[:cidr_ips] unless permission[:cidr_ips].right_blank?\n        cidr_ips ||= permission[:cidr_ip]  unless permission[:cidr_ip].right_blank?\n        hash.merge!(amazonize_list(\"IpPermissions.1.IpRanges.?.CidrIp\", cidr_ips))\n      end\n      #\n      link = generate_request(remote_action, hash)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #   Eucalyptus\n    #-----------------------------------------------------------------\n\n    # Edit AWS/Eucaliptus security group permissions.\n    #\n    #  Options:\n    #    action      - :authorize (or :grant) | :revoke (or :remove)\n    #    group_name  - security group name\n    #    permissions - a combination of options below:\n    #      :source_group_owner => UserId\n    #      :source_group       => GroupName\n    #      :from_port          => from port\n    #      :to_port            => to port\n    #      :port               => set both :from_port and to_port with the same value\n    #      :protocol           => :tcp | :udp | :icmp\n    #      :cidr_ip            => '0.0.0.0/0'\n    #\n    #  ec2.edit_security_group( :grant,\n    #                           'kd-sg-test',\n    #                           :source_group       => \"sketchy\",\n    #                           :source_group_owner => \"600000000006\",\n    #                           :protocol           => 'tcp',\n    #                           :port               => '80',\n    #                           :cidr_ip            => '127.0.0.1/32') #=> true\n    #\n    # P.S. This method is deprecated for AWS and but still good for Eucaliptus clouds.\n    # Use +modify_security_group_ingress+ method for AWS clouds.\n    #\n    def edit_security_group(action, group_name, params)\n      hash = {}\n      case action\n      when :authorize, :grant then action = \"AuthorizeSecurityGroupIngress\"\n      when :revoke, :remove   then action = \"RevokeSecurityGroupIngress\"\n      else raise \"Unknown action #{action.inspect}!\"\n      end\n      hash['GroupName']               = group_name\n      hash['SourceSecurityGroupName'] = params[:source_group] unless params[:source_group].right_blank?\n      hash['IpProtocol']              = params[:protocol]     unless params[:protocol].right_blank?\n      unless params[:source_group_owner].right_blank?\n        # Do remove dashes only if the source owner is in format of \"7011-0219-8268\"\n        source_group_owner = params[:source_group_owner].to_s\n        source_group_owner.gsub!(/-/,'') if source_group_owner[/^\\d{4}-\\d{4}-\\d{4}$/]\n        hash['SourceSecurityGroupOwnerId'] = source_group_owner\n      end\n      unless params[:port].right_blank?\n        hash['FromPort'] = params[:port]\n        hash['ToPort']   = params[:port]\n      end\n      hash['FromPort']   = params[:from_port] unless params[:from_port].right_blank?\n      hash['ToPort']     = params[:to_port]   unless params[:to_port].right_blank?\n      hash['CidrIp']     = params[:cidr_ip]   unless params[:cidr_ip].right_blank?\n      #\n      link = generate_request(action, hash)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Authorize named ingress for security group. Allows instances that are member of someone\n    # else's security group to open connections to instances in my group.\n    #\n    #  ec2.authorize_security_group_named_ingress('my_awesome_group', '7011-0219-8268', 'their_group_name') #=> true\n    #\n    def authorize_security_group_named_ingress(name, owner, group)\n      edit_security_group( :authorize, name, :source_group_owner => owner, :source_group => group)\n    end\n\n    # Revoke named ingress for security group.\n    #\n    #  ec2.revoke_security_group_named_ingress('my_awesome_group', aws_user_id, 'another_group_name') #=> true\n    #\n    def revoke_security_group_named_ingress(name, owner, group)\n      edit_security_group( :revoke, name, :source_group_owner => owner, :source_group => group)\n    end\n\n    # Add permission to a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp'.\n    #\n    #  ec2.authorize_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true\n    #  ec2.authorize_security_group_IP_ingress('my_awesome_group', -1, -1, 'icmp') #=> true\n    #\n    def authorize_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')\n      edit_security_group( :authorize, name, :from_port => from_port, :to_port => to_port, :protocol => protocol, :cidr_ip => cidr_ip )\n    end\n\n    # Remove permission from a security group. Returns +true+ or an exception. +protocol+ is one of :'tcp'|'udp'|'icmp' ('tcp' is default).\n    #\n    #  ec2.revoke_security_group_IP_ingress('my_awesome_group', 80, 82, 'udp', '192.168.1.0/8') #=> true\n    #\n    def revoke_security_group_IP_ingress(name, from_port, to_port, protocol='tcp', cidr_ip='0.0.0.0/0')\n      edit_security_group( :revoke, name, :from_port => from_port, :to_port => to_port, :protocol => protocol, :cidr_ip => cidr_ip )\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Security Groups\n    #-----------------------------------------------------------------\n\n    class QEc2CreateSecurityGroupsParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'groupId' then @result[:group_id] = @text\n        when 'return'  then @result[:return]   = @text == 'true'\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    class QEc2DescribeSecurityGroupsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        if name == 'item'\n          case full_tag_name\n          when %r{securityGroupInfo/item$}                  then @item    = { :ip_permissions => [] }\n          when %r{ipPermissions/item$}                      then @ip_perm = { :groups => [], :ip_ranges => [], :direction => :ingress }\n          when %r{ipPermissionsEgress/item$}                then @ip_perm = { :groups => [], :ip_ranges => [], :direction => :egress  }\n          when %r{ipPermissions(Egress)?/item/groups/item$} then @group   = {}\n          end\n        end\n      end\n      def tagend(name)\n        case name\n        when 'ownerId'          then @item[:owner_id]          = @text\n        when 'groupDescription' then @item[:group_description] = @text\n        when 'vpcId'            then @item[:vpc_id]            = @text\n        else\n          case full_tag_name\n          when %r{securityGroupInfo/item/groupName$}                  then @item[:group_name]      = @text\n          when %r{securityGroupInfo/item/groupId$}                    then @item[:group_id]        = @text\n          # ipPermission[Egress]\n          when %r{ipPermissions(Egress)?/item/ipProtocol$}            then @ip_perm[:ip_protocol]  = @text\n          when %r{ipPermissions(Egress)?/item/fromPort$}              then @ip_perm[:from_port]    = @text\n          when %r{ipPermissions(Egress)?/item/toPort$}                then @ip_perm[:to_port]      = @text\n          when %r{ipPermissions(Egress)?/item/ipRanges/item/cidrIp$}  then @ip_perm[:ip_ranges]   << @text\n          # ipPermissions[Egress]/Groups\n          when %r{ipPermissions(Egress)?/item/groups/item/groupName$} then @group[:group_name]     = @text\n          when %r{ipPermissions(Egress)?/item/groups/item/groupId$}   then @group[:group_id]       = @text\n          when %r{ipPermissions(Egress)?/item/groups/item/userId$}    then @group[:user_id]        = @text\n          # Sets\n          when %r{ipPermissions(Egress)?/item/groups/item$}           then @ip_perm[:groups]      << @group\n          when %r{ipPermissions/item$}                                then @item[:ip_permissions] << @ip_perm\n          when %r{ipPermissionsEgress/item$}                          then @item[:ip_permissions] << @ip_perm\n          when %r{securityGroupInfo/item$}                            then @result                << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n    \n  end\nend"
  },
  {
    "path": "lib/ec2/right_ec2_spot_instances.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      Spot Instances\n    #-----------------------------------------------------------------\n\n    # Describe Spot Price history.\n    #\n    # Options: :start_time, :end_time, instance_types, product_description\n    # \n    # Filters: instance-type, product-description, spot-price, timestamp\n    #\n    #  ec2.describe_spot_price_history #=>\n    #    [{:spot_price=>0.054,\n    #      :timestamp=>\"2009-12-07T12:12:58.000Z\",\n    #      :product_description=>\"Windows\",\n    #      :instance_type=>\"m1.small\"},\n    #     {:spot_price=>0.06,\n    #      :timestamp=>\"2009-12-07T12:18:32.000Z\",\n    #      :product_description=>\"Linux/UNIX\",\n    #      :instance_type=>\"c1.medium\"},\n    #     {:spot_price=>0.198,\n    #      :timestamp=>\"2009-12-07T12:58:00.000Z\",\n    #      :product_description=>\"Windows\",\n    #      :instance_type=>\"m1.large\"},\n    #     {:spot_price=>0.028,\n    #      :timestamp=>\"2009-12-07T13:48:50.000Z\",\n    #      :product_description=>\"Linux/UNIX\",\n    #      :instance_type=>\"m1.small\"}, ... ]\n    #\n    #  ec2.describe_spot_price_history(:start_time => 1.day.ago,\n    #                                  :end_time => 10.minutes.ago,\n    #                                  :instance_types => [\"c1.medium\", \"m1.small\"],\n    #                                  :product_description => \"Linux/UNIX\" ) #=>\n    #    [{:product_description=>\"Linux/UNIX\",\n    #      :timestamp=>\"2010-02-04T05:44:36.000Z\",\n    #      :spot_price=>0.031,\n    #      :instance_type=>\"m1.small\"},\n    #     {:product_description=>\"Linux/UNIX\",\n    #      :timestamp=>\"2010-02-04T17:56:25.000Z\",\n    #      :spot_price=>0.058,\n    #      :instance_type=>\"c1.medium\"}, ... ]\n    #\n    #  ec2.describe_spot_price_history(:filters => {'spot-price' => '0.2' })\n    #\n    #  ec2.describe_spot_price_history(:instance_types => [\"c1.medium\"], :filters => {'spot-price' => '0.2' })\n    #\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeSpotPriceHistory.html\n    #\n    def describe_spot_price_history(options={})\n      options = options.dup\n      request_hash = {}\n      request_hash.merge!(amazonize_list(['Filter.?.Name', 'Filter.?.Value.?'], options[:filters])) unless options[:filters].right_blank?\n      request_hash['StartTime']          = AwsUtils::utc_iso8601(options[:start_time])      unless options[:start_time].right_blank?\n      request_hash['EndTime']            = AwsUtils::utc_iso8601(options[:end_time])        unless options[:end_time].right_blank?\n      request_hash['ProductDescription'] = options[:product_description]                   unless options[:product_description].right_blank?\n      request_hash.merge!(amazonize_list('InstanceType', Array(options[:instance_types]))) unless options[:instance_types].right_blank?\n      link = generate_request(\"DescribeSpotPriceHistory\", request_hash, :api_version => '2011-05-15')\n      request_info(link, QEc2DescribeSpotPriceHistoryParser.new)\n    rescue Exception\n      on_exception\n    end\n\n    # Describe Spot Instance requests.\n    #\n    # Accepts a list of requests and/or a set of filters as the last parameter.\n    #\n    # Filters: availability-zone-group, create-time, fault-code, fault-message, instance-id, launch-group,\n    # launch.block-device-mapping.delete-on-termination, launch.block-device-mapping.device-name,\n    # launch.block-device-mapping.snapshot-id, launch.group-id, launch.image-id, launch.instance-type,\n    # launch.kernel-id, launch.key-name, launch.monitoring-enabled, launch.ramdisk-id, product-description,\n    # spot-instance-request-id, spot-price, state, tag-key, tag-value, tag:key, type, valid-from, valid-until\n    #\n    #  ec2.describe_spot_instance_requests #=>\n    #    [{:product_description=>\"Linux/UNIX\",\n    #      :type=>\"one-time\",\n    #      :availability_zone=>\"us-east-1b\",\n    #      :monitoring_enabled=>false,\n    #      :tags=>{},\n    #      :image_id=>\"ami-08f41161\",\n    #      :groups=>[{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"}],\n    #      :spot_price=>0.01,\n    #      :create_time=>\"2010-03-24T10:41:28.000Z\",\n    #      :instance_type=>\"c1.medium\",\n    #      :state=>\"open\",\n    #      :spot_instance_request_id=>\"sir-9652a604\",\n    #      :key_name=>\"rightscale_test\"},\n    #     {:product_description=>\"Linux/UNIX\",\n    #      :type=>\"one-time\",\n    #      :availability_zone=>\"us-east-1b\",\n    #      :monitoring_enabled=>false,\n    #      :tags=>{},\n    #      :image_id=>\"ami-08f41161\",\n    #      :groups=>[{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"}],\n    #      :spot_price=>0.01,\n    #      :create_time=>\"2010-03-24T11:40:27.000Z\",\n    #      :instance_type=>\"c1.medium\",\n    #      :state=>\"open\",\n    #      :spot_instance_request_id=>\"sir-fa912802\",\n    #      :key_name=>\"rightscale_test\"}, ... ]\n    #\n    #  ec2.describe_spot_instance_requests(:filters => {'type'=>\"one-time\", 'state'=>\"open\"})\n    #  \n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeSpotInstanceRequests.html\n    #\n    def describe_spot_instance_requests(*list_and_options)\n      describe_resources_with_list_and_options('DescribeSpotInstanceRequests', 'SpotInstanceRequestId', QEc2DescribeSpotInstanceParser, list_and_options)\n    end\n\n    # Create a Spot Instance request.\n    #\n    # Mandatory params: :image_id, :spot_price, :instance_type\n    # Optional params: :valid_from, :valid_until, :instance_count, :type, :launch_group,\n    # :availability_zone_group, :key_name, :user_data, :addressing_type, :kernel_id,\n    # :ramdisk_id, :subnet_id, :availability_zone, :monitoring_enabled, :groups,\n    # :block_device_mappings\n    #\n    #  ec2.request_spot_instances(\n    #    :image_id => 'ami-08f41161',\n    #    :spot_price => 0.01,\n    #    :key_name => 'tim',\n    #    :instance_count => 2,\n    #    :group_ids => [\"sg-a0b85dc9\"],\n    #    :instance_type => 'c1.medium') #=>\n    #    \n    #    [{:product_description=>\"Linux/UNIX\",\n    #      :type=>\"one-time\",\n    #      :spot_instance_requestId=>\"sir-7a893003\",\n    #      :monitoring_enabled=>false,\n    #      :image_id=>\"ami-08f41161\",\n    #      :state=>\"open\",\n    #      :spot_price=>0.01,\n    #      :groups=>[{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"}],\n    #      :key_name=>\"tim\",\n    #      :create_time=>\"2010-03-10T10:33:09.000Z\",\n    #      :instance_type=>\"c1.medium\"},\n    #     {:product_description=>\"Linux/UNIX\",\n    #      :type=>\"one-time\",\n    #      :spot_instance_requestId=>\"sir-13dc9a03\",\n    #      :monitoring_enabled=>false,\n    #      :image_id=>\"ami-08f41161\",\n    #      :state=>\"open\",\n    #      :spot_price=>0.01,\n    #      :groups=>[{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"}],\n    #      :key_name=>\"tim\",\n    #      :create_time=>\"2010-03-10T10:33:09.000Z\",\n    #      :instance_type=>\"c1.medium\"}]\n    #\n    #  ec2.request_spot_instances(\n    #    :image_id => 'ami-08f41161',\n    #    :spot_price => 0.01,\n    #    :instance_type => 'm1.small',\n    #    :valid_from => 10.minutes.since,\n    #    :valid_until => 1.hour.since,\n    #    :instance_count => 1,\n    #    :key_name => 'tim',\n    #    :group_names => ['default'],\n    #    :availability_zone => 'us-east-1a',\n    #    :monitoring_enabled => true,\n    #    :launch_group => 'lg1',\n    #    :availability_zone_group => 'azg1',\n    #    :block_device_mappings => [ { :device_name => '/dev/sdk',\n    #                                  :ebs_snapshot_id => 'snap-145cbc7d',\n    #                                  :ebs_delete_on_termination => true,\n    #                                  :ebs_volume_size => 3,\n    #                                  :virtual_name => 'ephemeral2'\n    #                                 } ] ) #=>\n    #    [{:type=>\"one-time\",\n    #      :image_id=>\"ami-08f41161\",\n    #      :availability_zone_group=>\"azg1\",\n    #      :key_name=>\"default\",\n    #      :spot_instance_request_id=>\"sir-66c79a12\",\n    #      :block_device_mappings=>\n    #       [{:ebs_volume_size=>3,\n    #         :virtual_name=>\"ephemeral2\",\n    #         :device_name=>\"/dev/sdk\",\n    #         :ebs_snapshot_id=>\"snap-145cbc7d\",\n    #         :ebs_delete_on_termination=>true}],\n    #      :spot_price=>0.01,\n    #      :product_description=>\"Linux/UNIX\",\n    #      :state=>\"open\",\n    #      :instance_type=>\"m1.small\",\n    #      :availability_zone=>\"us-east-1a\",\n    #      :groups=>[{:group_id=>\"sg-a0b85dc9\", :group_name=>\"default\"}],\n    #      :valid_from=>\"2011-07-01T14:26:33.000Z\",\n    #      :tags=>{},\n    #      :monitoring_enabled=>true,\n    #      :valid_until=>\"2011-07-01T14:28:03.000Z\",\n    #      :create_time=>\"2011-07-01T14:26:24.000Z\",\n    #      :launch_group=>\"lg1\"}]\n    #\n    def request_spot_instances(options)\n      options[:user_data] = options[:user_data].to_s\n      request_hash = map_api_keys_and_values( options,\n        :spot_price, :availability_zone_group, :launch_group, :type, :instance_count,\n        :image_id              => 'LaunchSpecification.ImageId',\n        :instance_type         => 'LaunchSpecification.InstanceType',\n        :key_name              => 'LaunchSpecification.KeyName',\n        :addressing_type       => 'LaunchSpecification.AddressingType',\n        :kernel_id             => 'LaunchSpecification.KernelId',\n        :ramdisk_id            => 'LaunchSpecification.RamdiskId',\n        :subnet_id             => 'LaunchSpecification.SubnetId',\n        :availability_zone     => 'LaunchSpecification.Placement.AvailabilityZone',\n        :monitoring_enabled    => 'LaunchSpecification.Monitoring.Enabled',\n        :valid_from            => { :value => Proc.new { !options[:valid_from].right_blank?  && AwsUtils::utc_iso8601(options[:valid_from]) }},\n        :valid_until           => { :value => Proc.new { !options[:valid_until].right_blank? && AwsUtils::utc_iso8601(options[:valid_until]) }},\n        :user_data             => { :name  => 'LaunchSpecification.UserData',\n                                    :value => Proc.new { !options[:user_data].empty? && Base64.encode64(options[:user_data]).delete(\"\\n\") }},\n        :group_names           => { :amazonize_list => 'LaunchSpecification.SecurityGroup'},\n        :group_ids             => { :amazonize_list => 'LaunchSpecification.SecurityGroupId'},\n        :block_device_mappings => { :amazonize_bdm  => 'LaunchSpecification.BlockDeviceMapping'})\n      link = generate_request(\"RequestSpotInstances\", request_hash)\n      request_info(link, QEc2DescribeSpotInstanceParser.new(:logger => @logger))\n    end\n\n    # Cancel one or more Spot Instance requests.\n    #\n    #  ec2.cancel_spot_instance_requests('sir-60662c03',\"sir-d3c96e04\", \"sir-4fa8d804\",\"sir-6992ce04\") #=>\n    #    [{:state=>\"cancelled\", :spot_instance_request_id=>\"sir-60662c03\"},\n    #     {:state=>\"cancelled\", :spot_instance_request_id=>\"sir-6992ce04\"},\n    #     {:state=>\"cancelled\", :spot_instance_request_id=>\"sir-4fa8d804\"},\n    #     {:state=>\"cancelled\", :spot_instance_request_id=>\"sir-d3c96e04\"}]\n    #\n    def cancel_spot_instance_requests(*spot_instance_request_ids)\n      link = generate_request(\"CancelSpotInstanceRequests\", amazonize_list('SpotInstanceRequestId', spot_instance_request_ids.flatten))\n      request_info(link, QEc2CancelSpotInstanceParser.new(:logger => @logger))\n    end\n\n    # Create the data feed for Spot Instances\n    # (Enables to view Spot Instance usage logs)\n    #\n    #  ec2.create_spot_datafeed_subscription('bucket-for-konstantin-eu', 'splogs/') #=>\n    #    { :owner_id=>\"826693181925\",\n    #      :bucket=>\"bucket-for-konstantin-eu\",\n    #      :prefix=>\"splogs/\",\n    #      :state=>\"Active\"}\n    #\n    def create_spot_datafeed_subscription(bucket, prefix=nil)\n      request_hash = { 'Bucket' => bucket }\n      request_hash['Prefix'] = prefix unless prefix.right_blank?\n      link = generate_request(\"CreateSpotDatafeedSubscription\", request_hash)\n      request_info(link, QEc2DescribeSpotDatafeedSubscriptionParser.new(:logger => @logger))\n    end\n\n    # Describe the data feed for Spot Instances.\n    #\n    #  ec2.describe_spot_datafeed_subscription #=>\n    #    { :owner_id=>\"826693181925\",\n    #      :bucket=>\"bucket-for-konstantin-eu\",\n    #      :prefix=>\"splogs/\",\n    #      :state=>\"Active\"}\n    #\n    def describe_spot_datafeed_subscription\n      link = generate_request(\"DescribeSpotDatafeedSubscription\")\n      request_info(link, QEc2DescribeSpotDatafeedSubscriptionParser.new(:logger => @logger))\n    end\n\n    # Delete the data feed for Spot Instances.\n    #\n    #  ec2.delete_spot_datafeed_subscription #=> true\n    #\n    def delete_spot_datafeed_subscription()\n      link = generate_request(\"DeleteSpotDatafeedSubscription\")\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Spot Instances\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeSpotPriceHistoryParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n        when 'instanceType'       then @item[:instance_type]       = @text\n        when 'productDescription' then @item[:product_description] = @text\n        when 'spotPrice'          then @item[:spot_price]          = @text.to_f\n        when 'timestamp'          then @item[:timestamp]           = @text\n        when 'availabilityZone'   then @item[:availability_zone]   = @text\n        when 'item'               then @result                    << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeSpotInstanceParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{spotInstanceRequestSet/item$}\n          @item = { :tags => {} }\n        when %r{groupSet$}\n          @item[:groups] = []\n        when %r{groupSet/item$}\n          @group  = {}\n        when %r{/blockDeviceMapping/item$}\n          @item[:block_device_mappings] ||= []\n          @block_device_mapping = {}\n        when %r{/tagSet/item$}\n          @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'spotInstanceRequestId' then @item[:spot_instance_request_id]= @text\n        when 'spotPrice'             then @item[:spot_price]              = @text.to_f\n        when 'type'                  then @item[:type]                    = @text\n        when 'state'                 then @item[:state]                   = @text\n        when 'code'                  then @item[:fault_code]              = @text\n        when 'message'               then @item[:fault_message]           = @text\n        when 'validFrom'             then @item[:valid_from]              = @text\n        when 'validUntil'            then @item[:valid_until]             = @text\n        when 'launchGroup'           then @item[:launch_group]            = @text\n        when 'availabilityZoneGroup' then @item[:availability_zone_group] = @text\n        when 'imageId'               then @item[:image_id]                = @text\n        when 'keyName'               then @item[:key_name]                = @text\n        when 'userData'              then @item[:userData]                = @text\n        when 'data'                  then @item[:data]                    = @text\n        when 'addressingType'        then @item[:addressing_type]         = @text\n        when 'instanceType'          then @item[:instance_type]           = @text\n        when 'availabilityZone'      then @item[:availability_zone]       = @text\n        when 'kernelId'              then @item[:kernel_id]               = @text\n        when 'ramdiskId'             then @item[:ramdisk_id]              = @text\n        when 'subnetId'              then @item[:subnet_id]               = @text\n        when 'instanceId'            then @item[:instance_id]             = @text\n        when 'createTime'            then @item[:create_time]             = @text\n        when 'productDescription'    then @item[:product_description]     = @text\n        else\n          case full_tag_name\n          when %r{/groupSet/item} # no trailing $\n            case name\n            when 'groupId'   then @group[:group_id]   = @text\n            when 'groupName' then @group[:group_name] = @text\n            when 'item'      then @item[:groups]     << @group\n            end\n          when %r{monitoring/enabled$}\n            @item[:monitoring_enabled] = @text == 'true'\n          when %r{/blockDeviceMapping/item} # no trailing $\n            case name\n            when 'deviceName'          then @block_device_mapping[:device_name]                = @text\n            when 'virtualName'         then @block_device_mapping[:virtual_name]               = @text\n            when 'volumeSize'          then @block_device_mapping[:ebs_volume_size]            = @text.to_i\n            when 'snapshotId'          then @block_device_mapping[:ebs_snapshot_id]            = @text\n            when 'deleteOnTermination' then @block_device_mapping[:ebs_delete_on_termination]  = @text == 'true' ? true : false\n            when 'item'                then @item[:block_device_mappings]                     << @block_device_mapping\n            end\n          when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{spotInstanceRequestSet/item$} then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2CancelSpotInstanceParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'item'\n      end\n      def tagend(name)\n        case name\n        when 'spotInstanceRequestId' then @item[:spot_instance_request_id] = @text\n        when 'state'                 then @item[:state]                    = @text\n        when 'item'                  then @result                         << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeSpotDatafeedSubscriptionParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'ownerId' then @result[:owner_id] = @text\n        when 'bucket'  then @result[:bucket]   = @text\n        when 'prefix'  then @result[:prefix]   = @text\n        when 'state'   then @result[:state]    = @text\n        when 'code'    then @result[:fault_code]     = @text\n        when 'message' then @result[:fault_message]  = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n    \n  end\n\nend"
  },
  {
    "path": "lib/ec2/right_ec2_tags.rb",
    "content": "#\n# Copyright (c) 2007-2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n  class Ec2\n\n    #-----------------------------------------------------------------\n    #      Tags\n    #-----------------------------------------------------------------\n\n    # Describe tags.\n    #\n    # Accepts set of filters.\n    #\n    # Filters: key, resource-id, resource-type, value\n    #\n    #  ec2.describe_tags  #=> [{:resource_id   => \"i-12345678\",\n    #                           :value         => \"foo\",\n    #                           :resource_type => \"instance\",\n    #                           :key           => \"myKey\"}]\n    #\n    #  ec2.describe_tags(:filters => { 'resource-id' => \"i-12345678\"})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference_query_DescribeTags.html\n    def describe_tags(options={})\n      request_hash = {}\n      request_hash.merge!(amazonize_list(['Filter.?.Name', 'Filter.?.Value.?'], options[:filters])) unless options[:filters].right_blank?\n      cache_for = (options[:filters].right_blank?) ? :describe_tags : nil\n      link = generate_request(\"DescribeTags\", request_hash)\n      request_cache_or_info cache_for, link,  QEc2DescribeTagsParser, @@bench, cache_for\n    rescue Exception\n      on_exception\n    end\n\n    # Create tags.\n    # Options:\n    #   :default => 'something' : a default value for keys without (or with nil) values.\n    #\n    # Add a single tag with no value to a resource:\n    # ec2.create_tags(\"i-12345678\", \"myKey\") => true\n    #\n    # Add multiple tags with no values (actually Amazon sets the values to '')\n    # ec2.create_tags(\"i-12345678\", [\"myKey1\", \"myKey2\", \"myKey3\"]) => true\n    #\n    # Add multiple tags with 'true'\n    # ec2.create_tags(\"i-12345678\", [\"myKey1\", \"myKey2\", \"myKey3\"], :default => true ) => true\n    #\n    # Add multiple keys and values to a resource:\n    # ec2.create_tags(\"i-12345678\", {\"myKey1\" => \"foo\", \"myKey2\" => \"bar\", \"myKeyWithoutVal\" => nil }) #=> true\n    #\n    # Add a key and value to multiple resources:\n    # ec2.create_tags([\"i-12345678\",\"i-86fb3eec\",\"i-86fb3eed\"], {\"myKey\" => \"foo\"}) #=> true\n    #\n    def create_tags(resources, tags, options={})\n      default = options[:default].nil? ? '' : options[:default]\n      params = amazonize_list(\"ResourceId\", resources)\n      params.merge! amazonize_list(['Tag.?.Key', 'Tag.?.Value'], tags, :default => default)\n      link = generate_request(\"CreateTags\", params)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Delete tags.\n    # Options: \n    #   :default => 'something' : a default value for keys without (or with nil) values.\n    #\n    # Delete a tag from a resource regardless of value:\n    # ec2.delete_tags(\"i-12345678\", \"myKey\") => true\n    #\n    # Delete multiple tags (regardless of their values)\n    # ec2.delete_tags(\"i-12345678\", [\"myKey1\", \"myKey2\", \"myKey3\"]) => true\n    #\n    # Add multiple tags with value 'true'\n    # ec2.delete_tags(\"i-12345678\", [\"myKey1\", \"myKey2\", \"myKey3\"], :default => true) => true\n    #\n    # Delete multiple keys and values to a resource:\n    # ec2.delete_tags(\"i-12345678\", [{\"myKey1\" => \"foo\", \"myKey2\" => \"bar\",\"myKeyForAnyVal\" => nil }]) #=> true\n    #\n    # Delete a key and value on multiple resources:\n    # ec2.delete_tags([\"i-12345678\", \"i-a1234567\", \"i-b1234567\"], {\"myKey\" => \"foo\"}) #=> true\n    #\n    def delete_tags(resources, tags, options={})\n      default = options[:default].nil? ? :skip_nils : options[:default]\n      params = amazonize_list(\"ResourceId\", resources)\n      params.merge! amazonize_list(['Tag.?.Key', 'Tag.?.Value'], tags, :default => default)\n      link = generate_request(\"DeleteTags\", params)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Tags\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeTagsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @resource_tag = {} if name == 'item'\n      end\n\n      def tagend(name)\n        case name\n        when 'resourceId'   then @resource_tag[:resource_id]   = @text\n        when 'resourceType' then @resource_tag[:resource_type] = @text\n        when 'key'          then @resource_tag[:key]           = @text\n        when 'value'        then @resource_tag[:value]         = @text\n        when 'item'         then @result                      << @resource_tag\n        end\n      end\n\n      def reset\n        @result = []\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/ec2/right_ec2_vpc.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n\n    VPC_API_VERSION = (API_VERSION > '2012-10-15') ? API_VERSION : '2012-10-15'\n\n  public\n\n    #-----------------\n    # VPC\n    #-----------------\n\n    # Describe VPCs.\n    #\n    # Accepts a list of vpcs and/or a set of filters as the last parameter.\n    #\n    # Filters: cidr, dchp-options-id, state, tag-key, tag-value, tag:key, vpc-id\n    #\n    #  ec2.describe_vpcs #=>\n    #    [{:instance_tenancy=>\"default\",\n    #      :vpc_id=>\"vpc-e16cf988\",\n    #      :tags=>{},\n    #      :dhcp_options_id=>\"default\",\n    #      :cidr_block=>\"192.168.0.0/24\",\n    #      :state=>\"available\"}]\n    #\n    #  ec2.describe_vpcs(\"vpc-890ce2e0\")\n    #\n    #  ec2.describe_vpcs( :filters => {'tag:MyTag' => 'MyValue'} )\n    #  \n    #  ec2.describe_vpcs( :filters => {'cidr' => \"192.168.1.0/24\"} )\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeVpcs.html\n    #\n    def describe_vpcs(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => VPC_API_VERSION})\n      describe_resources_with_list_and_options('DescribeVpcs', 'VpcId', QEc2DescribeVpcsParser, list_and_options)\n    end\n\n    # Create VPC.\n    #\n    #  ec2.create_vpc('10.0.0.0/23') #=>\n    #    {:vpc_id=>\"vpc-890ce2e0\",\n    #     :dhcp_options_id=>\"default\",\n    #     :cidr_block=>\"10.0.0.0/23\",\n    #     :state=>\"pending\"}\n    #\n    def create_vpc(cidr_block, options = {})\n      request_hash = {'CidrBlock' => cidr_block}\n      request_hash['InstanceTenancy'] = options[:instance_tenancy] unless options[:instance_tenancy].right_blank?\n      link = generate_request(\"CreateVpc\", request_hash )\n      request_info(link, QEc2DescribeVpcsParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Delete VPC.\n    #\n    #  ec2.delete_vpc(\"vpc-890ce2e0\") #=> true\n    #\n    def delete_vpc(vpc_id)\n      link = generate_request(\"DeleteVpc\", 'VpcId' => vpc_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------\n    # Subnets\n    #-----------------\n\n    # Describe Subnet.\n    #\n    # Accepts a list of subnets and/or a set of filters as the last parameter.\n    #\n    # Filters: availability-zone, available-ip-address-count, cidr, state, subnet-id, tag-key, tag-value, tag:key, vpc-id\n    #\n    #  ec2.describe_subnets #=>\n    #    [{:available_ip_address_count=>\"251\",\n    #      :vpc_id=>\"vpc-890ce2e0\",\n    #      :availability_zone=>\"us-east-1a\",\n    #      :subnet_id=>\"subnet-770de31e\",\n    #      :cidr_block=>\"10.0.1.0/24\",\n    #      :state=>\"available\"}]\n    #\n    #  ec2.describe_subnets(:filters => {'cidr' => \"192.168.1.128/25\"})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeSubnets.html\n    #\n    def describe_subnets(*list_and_options)\n      list_and_options = merge_new_options_into_list_and_options(list_and_options, :options => {:api_version => VPC_API_VERSION})\n      describe_resources_with_list_and_options('DescribeSubnets', 'SubnetId', QEc2DescribeSubnetsParser, list_and_options)\n    end\n\n    # Create Subnet.\n    #\n    #  ec2.create_subnet(\"vpc-890ce2e0\",'10.0.1.0/24') #=>\n    #    {:available_ip_address_count=>\"251\",\n    #     :vpc_id=>\"vpc-890ce2e0\",\n    #     :availability_zone=>\"us-east-1a\",\n    #     :subnet_id=>\"subnet-770de31e\",\n    #     :cidr_block=>\"10.0.1.0/24\",\n    #     :state=>\"pending\"}\n    #\n    def create_subnet(vpc_id, cidr_block, availability_zone = nil)\n      request_hash = { 'VpcId'     => vpc_id,\n                       'CidrBlock' => cidr_block }\n      request_hash['AvailabilityZone'] = availability_zone unless availability_zone.right_blank?\n      link = generate_request(\"CreateSubnet\", request_hash)\n      request_info(link, QEc2DescribeSubnetsParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Delete Subnet.\n    #\n    #  ec2.delete_subnet(\"subnet-770de31e\") #=> true\n    #\n    def delete_subnet(subnet_id)\n      link = generate_request(\"DeleteSubnet\", 'SubnetId' => subnet_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------\n    # DHCP Options\n    #-----------------\n\n    # Describe DHCP options.\n    #\n    # Accepts a list of DHCP options and/or a set of filters as the last parameter.\n    #\n    # Filters: dchp-options-id, key, value, tag-key, tag-value, tag:key\n    # \n    #  ec2.describe_dhcp_options #=>\n    #    [{:dhcp_options_id=>\"dopt-cb0de3a2\",\n    #    :dhcp_configuration_set=>\n    #     {\"netbios-node-type\"=>[\"1\"], \"domain-name\"=>[\"my.awesomesite.ru\"]}}]\n    #\n    #  ec2.describe_dhcp_options(:filters => {'tag:MyTag' => 'MyValue'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeDhcpOptions.html\n    #\n    def describe_dhcp_options(*list_and_options)\n      describe_resources_with_list_and_options('DescribeDhcpOptions', 'DhcpOptionsId', QEc2DescribeDhcpOptionsParser, list_and_options)\n    end\n\n    # Create DHCP options.\n    #\n    #  ec2.create_dhcp_options('domain-name' => 'my.awesomesite.ru',\n    #                          'netbios-node-type' => 1) #=>\n    #    {:dhcp_options_id=>\"dopt-cb0de3a2\",\n    #     :dhcp_configuration_set=>\n    #      {\"netbios-node-type\"=>[\"1\"], \"domain-name\"=>[\"my.awesomesite.ru\"]}}\n    #\n    def create_dhcp_options(dhcp_configuration)\n      dhcp_configuration.each{ |key, values| dhcp_configuration[key] = Array(values) }\n      request_hash = amazonize_list(['DhcpConfiguration.?.Key','DhcpConfiguration.?.Value.?'], dhcp_configuration)\n      link = generate_request(\"CreateDhcpOptions\", request_hash)\n      request_info(link, QEc2DescribeDhcpOptionsParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Associate DHCP options\n    #\n    # ec2.associate_dhcp_options(\"dopt-cb0de3a2\", \"vpc-890ce2e0\" ) #=> true\n    # ec2.describe_vpcs #=>\n    #    [{:vpc_id=>\"vpc-890ce2e0\",\n    #      :dhcp_options_id=>\"dopt-cb0de3a2\",\n    #      :cidr_block=>\"10.0.0.0/23\",\n    #      :state=>\"available\"}]\n    #\n    def associate_dhcp_options(dhcp_options_id, vpc_id)\n      link = generate_request(\"AssociateDhcpOptions\", 'DhcpOptionsId' => dhcp_options_id,\n                                                      'VpcId'         => vpc_id)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Delete DHCP Options.\n    #\n    #  ec2.delete_dhcp_options(\"dopt-cb0de3a2\") #=> true\n    #\n    def delete_dhcp_options(dhcp_options_id)\n      link = generate_request(\"DeleteDhcpOptions\", 'DhcpOptionsId' => dhcp_options_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------\n    # Customer Gateways\n    #-----------------\n\n    # Describe customer gateways.\n    #\n    # Accepts a list of gateways and/or a set of filters as the last parameter.\n    #\n    # Filters: bgp-asn, customer-gateway-id, state, type, tag-key, tag-value, tag:key\n    #\n    #  ec2.describe_customer_gateways #=>\n    #    [{:type=>\"ipsec.1\",\n    #      :ip_address=>\"12.1.2.3\",\n    #      :bgp_asn=>\"65534\",\n    #      :state=>\"available\",\n    #      :customer_gateway_id=>\"cgw-d5a643bc\"}]\n    #\n    #  ec2.describe_customer_gateways(:filters => {'tag:MyTag' => 'MyValue'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeCustomerGateways.html\n    #\n    def describe_customer_gateways(*list_and_options)\n      describe_resources_with_list_and_options('DescribeCustomerGateways', 'CustomerGatewayId', QEc2DescribeCustomerGatewaysParser, list_and_options)\n    end\n\n    # Create customer gateway.\n    # \n    #  ec2.create_customer_gateway('ipsec.1', '12.1.2.3', 65534) #=>\n    #    {:type=>\"ipsec.1\",\n    #     :bgp_asn=>\"65534\",\n    #     :ip_address=>\"12.1.2.3\",\n    #     :state=>\"pending\",\n    #     :customer_gateway_id=>\"cgw-d5a643bc\"}\n    #\n    def create_customer_gateway(type, ip_address, bgp_asn)\n      link = generate_request(\"CreateCustomerGateway\", 'Type'      => type,\n                                                       'IpAddress' => ip_address,\n                                                       'BgpAsn'    => bgp_asn )\n      request_info(link, QEc2DescribeCustomerGatewaysParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Delete customer gateway.\n    #\n    #  ec2.delete_customer_gateway(\"cgw-d5a643bc\") #=> true\n    #\n    def delete_customer_gateway(customer_gateway_id)\n      link = generate_request(\"DeleteCustomerGateway\", 'CustomerGatewayId' => customer_gateway_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------\n    # VPN Gateways\n    #-----------------\n\n    # Describe VPN gateways.\n    #\n    # Accepts a list of VPN gateways and/or a set of filters as the last parameter.\n    #\n    # Filters: attachment.state, attachment.vpc-id, availability-zone, state, tag-key, tag-value, tag:key, type, vpn-gateway-id\n    #\n    #  ec2.describe_vpn_gateways #=>\n    #    [{:type=>\"ipsec.1\",\n    #      :availability_zone=>\"us-east-1a\",\n    #      :attachments=>[{:vpc_id=>\"vpc-890ce2e0\", :state=>\"attached\"}],\n    #      :vpn_gateway_id=>\"vgw-dfa144b6\"}]\n    #\n    #  ec2.describe_vpn_gateways(:filters => {'tag:MyTag' => 'MyValue'})\n    #      \n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeVpnGateways.html\n    #\n    def describe_vpn_gateways(*list_and_options)\n      describe_resources_with_list_and_options('DescribeVpnGateways', 'VpnGatewayId', QEc2DescribeVpnGatewaysParser, list_and_options)\n    end\n\n    # Create VPN gateway.\n    #\n    #  ec2.create_vpn_gateway('ipsec.1') #=>\n    #    {:type=>\"ipsec.1\",\n    #     :availability_zone=>\"us-east-1a\",\n    #     :attachments=>[nil],\n    #     :vpn_gateway_id=>\"vgw-dfa144b6\"}\n    #\n    def create_vpn_gateway(type, availability_zone=nil)\n      request_hash = { 'Type' => type }\n      request_hash['AvailabilityZone'] = availability_zone unless availability_zone.right_blank?\n      link = generate_request(\"CreateVpnGateway\", request_hash )\n      request_info(link, QEc2DescribeVpnGatewaysParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Attach VPN gateway.\n    #\n    #  ec2.attach_vpn_gateway('vgw-dfa144b6','vpc-890ce2e0') #=>\n    #     {:vpc_id=>\"vpc-890ce2e0\", :state=>\"attaching\"}\n    #\n    def attach_vpn_gateway(vpn_gateway_id, vpc_id)\n      link = generate_request(\"AttachVpnGateway\", 'VpnGatewayId' => vpn_gateway_id,\n                                                  'VpcId'        => vpc_id )\n      request_info(link, QEc2AttachVpnGatewayParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Detach VPN gateway.\n    #\n    #  ec2.detach_vpn_gateway('vgw-dfa144b6','vpc-890ce2e0') #=> true\n    #\n    def detach_vpn_gateway(vpn_gateway_id, vpc_id)\n      link = generate_request(\"DetachVpnGateway\", 'VpnGatewayId' => vpn_gateway_id,\n                                                  'VpcId'        => vpc_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Delete vpn gateway.\n    #\n    #  ec2.delete_vpn_gateway(\"vgw-dfa144b6\") #=> true\n    #\n    def delete_vpn_gateway(vpn_gateway_id)\n      link = generate_request(\"DeleteVpnGateway\", 'VpnGatewayId' => vpn_gateway_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------\n    # VPN Connections\n    #-----------------\n\n    # Describe VPN connections.\n    #\n    # Accepts a list of VPN gateways and/or a set of filters as the last parameter.\n    #\n    # Filters: customer-gateway-configuration, customer-gateway-id, state, tag-key, tag-value, tag:key,\n    # type, vpn-connection-id, vpn-gateway-id\n    #\n    #  ec2.describe_vpn_connections #=>\n    #    [{:type=>\"ipsec.1\",\n    #      :vpn_connection_id=>\"vpn-a9a643c0\",\n    #      :customer_gateway_configuration=>\n    #       \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<vpn_connection id=\\\"vpn-a9a643c0\\\">\\n...</vpn_connection>\\n\",\n    #      :state=>\"available\",\n    #      :vpn_gateway_id=>\"vgw-dfa144b6\",\n    #      :customer_gateway_id=>\"cgw-81a643e8\"}]\n    #\n    #  ec2.describe_vpn_gateways(:filters => {'tag:MyTag' => 'MyValue'})\n    #\n    # P.S. filters: http://docs.amazonwebservices.com/AmazonVPC/latest/APIReference/index.html?ApiReference-query-DescribeVpnConnections.html\n    #\n    def describe_vpn_connections(*list_and_options)\n      describe_resources_with_list_and_options('DescribeVpnConnections', 'VpnConnectionId', QEc2DescribeVpnConnectionsParser, list_and_options)\n    end\n\n    # Create VPN connection.\n    #\n    #  ec2.create_vpn_connection('ipsec.1', 'cgw-81a643e8' ,'vgw-dfa144b6')\n    #    {:customer_gateway_id=>\"cgw-81a643e8\",\n    #     :vpn_connection_id=>\"vpn-a9a643c0\",\n    #     :customer_gateway_configuration=>\n    #      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<vpn_connection id=\\\"vpn-a9a643c0\\\">\\n...</vpn_connection>\\n\",\n    #     :state=>\"pending\",\n    #     :vpn_gateway_id=>\"vgw-dfa144b6\"}\n    #\n    def create_vpn_connection(type, customer_gateway_id, vpn_gateway_id)\n      link = generate_request(\"CreateVpnConnection\", 'Type'              => type,\n                                                     'CustomerGatewayId' => customer_gateway_id,\n                                                     'VpnGatewayId'      => vpn_gateway_id )\n      request_info(link, QEc2DescribeVpnConnectionsParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Delete VPN connection.\n    #\n    #  ec2.delete_vpn_connection(\"vpn-a9a643c0\") #=> true\n    #\n    def delete_vpn_connection(vpn_connection_id)\n      link = generate_request(\"DeleteVpnConnection\", 'VpnConnectionId' => vpn_connection_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n\n    #-----------------\n    # Parsers\n    #-----------------\n\n    class QEc2DescribeVpcsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(vpcSet/item|vpc)$} then @item    = { :tags => {} }\n        when %r{/tagSet/item$}          then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'vpcId'           then @item[:vpc_id]          = @text\n        when 'state'           then @item[:state]           = @text\n        when 'dhcpOptionsId'   then @item[:dhcp_options_id] = @text\n        when 'cidrBlock'       then @item[:cidr_block]      = @text\n        when 'instanceTenancy' then @item[:instance_tenancy] = @text\n        when 'isDefault'       then @item[:is_default]      = @text == 'true'\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{(vpcSet/item|vpc)$}  then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeSubnetsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(subnetSet/item|subnet)$} then @item = { :tags => {} }\n        when %r{/tagSet/item$}   then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'subnetId'                then @item[:subnet_id]                  = @text\n        when 'state'                   then @item[:state]                      = @text\n        when 'vpcId'                   then @item[:vpc_id]                     = @text\n        when 'cidrBlock'               then @item[:cidr_block]                 = @text\n        when 'availabilityZone'        then @item[:availability_zone]          = @text\n        when 'availableIpAddressCount' then @item[:available_ip_address_count] = @text\n        when 'defaultForAz'            then @item[:default_for_az]             = @text == 'true'\n        when 'mapPublicIpOnLaunch'     then @item[:map_public_ip_on_launch]    = @text == 'true'\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{/(subnetSet/item|subnet)$} then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeDhcpOptionsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(dhcpOptionsSet/item|dhcpOptions)$} then @item = { :tags => {}, :dhcp_configuration_set => {} }\n        when %r{/tagSet/item$}                       then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/tagSet/item/key$}       then @aws_tag[:key]               = @text\n        when %r{/tagSet/item/value$}     then @aws_tag[:value]             = @text\n        when %r{/tagSet/item$}           then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n        when %r{/dhcpOptionsId$}         then @item[:dhcp_options_id]      = @text\n        when %r{/dhcpConfigurationSet/item/key$}                 then @conf_item_key = @text\n        when %r{/dhcpConfigurationSet/item/valueSet/item/value$} then (@item[:dhcp_configuration_set][@conf_item_key] ||= []) << @text\n        when %r{/(dhcpOptionsSet/item|dhcpOptions)$}             then @result << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeCustomerGatewaysParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(customerGatewaySet/item|customerGateway)$} then @item = { :tags => {} }\n        when %r{/tagSet/item$}                               then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'customerGatewayId' then @item[:customer_gateway_id] = @text\n        when 'state'             then @item[:state]               = @text\n        when 'type'              then @item[:type]                = @text\n        when 'ipAddress'         then @item[:ip_address]          = @text\n        when 'bgpAsn'            then @item[:bgp_asn]             = @text\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}       then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$}     then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}           then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{/(customerGatewaySet/item|customerGateway)$} then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2DescribeVpnGatewaysParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(vpnGatewaySet/item|vpnGateway)$} then @item = { :tags => {}, :attachments => [] }\n        when %r{/attachments/item$}                then @attachment = {}\n        when %r{/tagSet/item$}                     then @aws_tag    = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'vpnGatewayId'     then @item[:vpn_gateway_id]    = @text\n        when 'availabilityZone' then @item[:availability_zone] = @text\n        when 'type'             then @item[:type]              = @text\n        when 'vpcId'            then @attachment[:vpc_id]      = @text\n        else\n          case full_tag_name\n          when %r{/vpnGatewaySet/item/state$} then @item[:state]       = @text\n          when %r{/attachments/item/state$}   then @attachment[:state] = @text\n          when %r{/attachments/item$}         then @item[:attachments] << @attachment unless @attachment.right_blank?\n          when %r{/tagSet/item/key$}          then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$}        then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}              then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{/(vpnGatewaySet/item|vpnGateway)$} then @result << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class QEc2AttachVpnGatewayParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'vpcId' then @result[:vpc_id] = @text\n        when 'state' then @result[:state]  = @text\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n\n    class QEc2DescribeVpnConnectionsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(vpnConnectionSet/item|vpnConnection)$} then @item    = { :tags => {} }\n        when %r{/tagSet/item$}                           then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'vpnConnectionId'              then @item[:vpn_connection_id]              = @text\n        when 'state'                        then @item[:state]                          = @text\n        when 'type'                         then @item[:type]                           = @text\n        when 'vpnGatewayId'                 then @item[:vpn_gateway_id]                 = @text\n        when 'customerGatewayId'            then @item[:customer_gateway_id]            = @text\n        when 'customerGatewayConfiguration' then @item[:customer_gateway_configuration] = @text\n        else\n          case full_tag_name\n          when %r{/tagSet/item/key$}          then @aws_tag[:key]               = @text\n          when %r{/tagSet/item/value$}        then @aws_tag[:value]             = @text\n          when %r{/tagSet/item$}              then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n          when %r{/(vpnConnectionSet/item|vpnConnection)$} then @result         << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n\n  end\nend"
  },
  {
    "path": "lib/ec2/right_ec2_vpc2.rb",
    "content": "#\n# Copyright (c) 2011 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    # VPC v2: Limits\n    #\n    # http://docs.amazonwebservices.com/AmazonVPC/latest/UserGuide/index.html?VPC_Appendix_Limits.html\n    \n    #----------------------\n    #   InternetGateways\n    #----------------------\n\n    # Create internet gateway\n    #\n    #  ec2.create_internet_gateway #=> \n    #    { :internet_gateway_id=>\"igw-6585c10c\", :tags=>{}}\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-CreateInternetGateway.html\n    #\n    def create_internet_gateway\n      link = generate_request(\"CreateInternetGateway\")\n      request_info(link, QEc2DescribeInternetGatewaysParser.new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Describe internet gateways.\n    #\n    #  ec2.describe_internet_gateways #=>\n    #    [{:state=>\"available\",\n    #      :internet_gateway_id=>\"igw-6585c10c\",\n    #      :vpc_id=>\"vpc-df80a6b6\",\n    #      :tags=>{}},\n    #     {:internet_gateway_id=>\"igw-883503e1\",\n    #      :tags=>{}}]\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-CreateInternetGateway.html\n    #\n    def describe_internet_gateways(*list_and_options)\n      describe_resources_with_list_and_options('DescribeInternetGateways', 'InternetGatewayId', QEc2DescribeInternetGatewaysParser, list_and_options)\n    rescue Exception\n      on_exception\n    end\n\n    # Delete internet gateway.\n    #\n    #  ec2.delete_internet_gateway(\"igw-6585c10c\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteInternetGateway.html\n    #\n    def delete_internet_gateway(internet_gateway_id)\n      link = generate_request(\"DeleteInternetGateway\", 'InternetGatewayId' => internet_gateway_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Attaches an Internet gateway to a VPC, enabling connectivity between the Internet and the VPC. \n    # \n    #  ec2.attach_internet_gateway(\"igw-6585c10c\", \"vpc-df80a6b6\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-AttachInternetGateway.html\n    #\n    def attach_internet_gateway(internet_gateway_id, vpc_id)\n      request_hash = { 'InternetGatewayId' => internet_gateway_id,\n                       'VpcId'             => vpc_id }\n      link = generate_request(\"AttachInternetGateway\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Detaches an Internet gateway from a VPC, disabling connectivity between the Internet and the VPC.\n    # The VPC must not contain any running instances with Elastic IP addresses. \n    #\n    #  ec2.detach_internet_gateway(\"igw-6585c10c\", \"vpc-df80a6b6\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DetachInternetGateway.html\n    #\n    def detach_internet_gateway(internet_gateway_id, vpc_id)\n      request_hash = { 'InternetGatewayId' => internet_gateway_id,\n                       'VpcId'             => vpc_id }\n      link = generate_request(\"DetachInternetGateway\", request_hash)\n      request_info(link, RightHttp2xxParser::new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #----------------------\n    #   RouteTables\n    #----------------------\n\n    #  Describe route tables.\n    #\n    #  # List all tables\n    #  ec2.describe_route_tables #=>\n    #    [{:route_table_id=>\"rtb-be3006d7\",\n    #      :route_set=>\n    #       [{:state=>\"active\",\n    #         :destination_cidr_block=>\"10.0.3.0/24\",\n    #         :gateway_id=>\"local\"}],\n    #      :vpc_id=>\"vpc-df80a6b6\",\n    #      :association_set=>[],\n    #      :tags=>{}},\n    #     {:route_table_id=>\"rtb-e36cf98a\",\n    #      :route_set=>\n    #       [{:state=>\"active\",\n    #         :destination_cidr_block=>\"192.168.0.0/24\",\n    #         :gateway_id=>\"local\"}],\n    #      :vpc_id=>\"vpc-e16cf988\",\n    #      :association_set=>\n    #       [{:route_table_id=>\"rtb-e36cf98a\",\n    #         :main=>true,\n    #         :route_table_association_id=>\"rtbassoc-e26cf98b\"}],\n    #      :tags=>{}}, ... ]\n    #\n    #  # Filter tables by VpcId\n    #  ec2.describe_route_tables(:filters => {'vpc-id' => \"vpc-df80a6b6\"})\n    #\n    #  # Custom route table\n    #  ec2.describe_route_tables(\"rtb-be3006d7\") #=> \n    #    [{:vpc_id=>\"vpc-df80a6b6\",\n    #      :route_set=>\n    #       [{:state=>\"active\",\n    #         :destination_cidr_block=>\"0.0.0.1/32\",\n    #         :gateway_id=>\"igw-6585c10c\"},\n    #        {:state=>\"active\",\n    #         :destination_cidr_block=>\"10.0.3.0/24\",\n    #         :gateway_id=>\"local\"}],\n    #      :route_table_id=>\"rtb-be3006d7\",\n    #      :tags=>{},\n    #      :association_set=>\n    #       [{:route_table_association_id=>\"rtbassoc-a02610c9\",\n    #         :subnet_id=>\"subnet-b95f76d0\",\n    #         :route_table_id=>\"rtb-be3006d7\"}]}]\n    #  \n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DescribeRouteTables.html\n    # \n    def describe_route_tables(*list_and_options)\n      describe_resources_with_list_and_options('DescribeRouteTables', 'RouteTableId', QEc2DescribeRouteTablesParser, list_and_options)\n    rescue Exception\n      on_exception\n    end\n    \n    # Creates a new route table within a VPC. After you create a new route table, you can add routes and associate the table with a subne\n    #\n    #  ec2.create_route_table(\"vpc-df80a6b6\") #=>\n    #    {:route_table_id=>\"rtb-4331072a\",\n    #     :route_set=>\n    #      [{:state=>\"active\",\n    #        :destination_cidr_block=>\"10.0.3.0/24\",\n    #        :gateway_id=>\"local\"}],\n    #     :vpc_id=>\"vpc-df80a6b6\",\n    #     :association_set=>[],\n    #     :tags=>{}}\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-CreateRouteTable.html\n    #\n    def create_route_table(vpc_id)\n      link = generate_request(\"CreateRouteTable\", 'VpcId' => vpc_id )\n      request_info(link, QEc2DescribeRouteTablesParser::new(:logger => @logger)).first\n    rescue Exception\n      on_exception\n    end\n\n    # Deletes a route table from a VPC. \n    # The route table must not be associated with a subnet. You can't delete the main route table.\n    #\n    #  ec2.delete_route_table(\"rtb-4331072a\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-DeleteRouteTable.html\n    def delete_route_table(route_table_id)\n      link = generate_request(\"DeleteRouteTable\", 'RouteTableId' => route_table_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Associates a subnet with a route table. The subnet and route table must be in the same VPC. \n    # This association causes traffic originating from the subnet to be routed according to the routes in\n    # the route table. The action returns an association ID, which you need if you want to disassociate \n    # the route table from the subnet later. A route table can be associated with multiple subnets.\n    #\n    #  ec2.associate_route_table(\"rtb-be3006d7\", \"subnet-b95f76d0\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/index.html?ApiReference-query-AssociateRouteTable.html  \n    #\n    def associate_route_table(route_table_id, subnet_id)\n      request_hash = { 'RouteTableId' => route_table_id,\n                       'SubnetId'     => subnet_id }\n      link = generate_request(\"AssociateRouteTable\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Disassociates a subnet from a route table.\n    #\n    #  ec2.disassociate_route_table(route_table_association_id) #=> true\n    #  \n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DisassociateRouteTable.html\n    #\n    def disassociate_route_table(route_table_association_id)\n      link = generate_request(\"DisassociateRouteTable\", 'AssociationId' => route_table_association_id )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Changes the route table associated with a given subnet in a VPC. After you execute this action, the subnet \n    # uses the routes in the new route table it's associated with. \n    # You can also use this action to change which table is the main route table in the VPC. You just specify \n    # the main route table's association ID and the route table that you want to be the new main route table.\n    #\n    #  ec2.replace_route_table_association(\"rtb-be3006d7\", \"rtbassoc-a02610c9\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceRouteTableAssociation.html\n    #\n    def replace_route_table_association(route_table_id, route_table_association_id)\n      request_hash = { 'RouteTableId'  => route_table_id,\n                       'AssociationId' => route_table_association_id }\n      link = generate_request(\"ReplaceRouteTableAssociation\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n    \n    #---------------------\n    # Routes\n    #---------------------\n\n    # Creates a new route in a route table within a VPC. The route's target can be either a gateway attached to \n    # the VPC or a NAT instance in the VPC.\n    # Options: :gateway_id, :instance_id\n    # \n    #  ec2.create_route(\"rtb-be3006d7\",  \"0.0.0.1/32\", :gateway_id => 'igw-6585c10c') #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-CreateRoute.html\n    #\n    def create_route(route_table_id, destination_cidr_block, options = {})\n      request_hash = { 'RouteTableId'         => route_table_id,\n                       'DestinationCidrBlock' => destination_cidr_block }\n      request_hash['GatewayId']  = options[:gateway_id]  unless options[:gateway_id].right_blank?\n      request_hash['InstanceId'] = options[:instance_id] unless options[:instance_id].right_blank?      \n      link = generate_request(\"CreateRoute\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Deletes a route from a route table in a VPC.\n    #\n    #  ec2.delete_route(\"rtb-be3006d7\",  \"0.0.0.1/32\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DeleteRoute.html\n    #\n    def delete_route(route_table_id, destination_cidr_block)\n      link = generate_request(\"DeleteRoute\", 'RouteTableId'         => route_table_id,\n                                             'DestinationCidrBlock' => destination_cidr_block )\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    # Replaces an existing route within a route table in a VPC\n    # Options: :gateway_id, :instance_id\n    # \n    #  ec2.replace_route(\"rtb-be3006d7\",  \"0.0.0.2/32\", :gateway_id => 'igw-6585c10c') #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-ReplaceRoute.html\n    #\n    def replace_route(route_table_id, destination_cidr_block, options = {})\n      request_hash = { 'RouteTableId'         => route_table_id,\n                       'DestinationCidrBlock' => destination_cidr_block }\n      request_hash['GatewayId']  = options[:gateway_id]  unless options[:gateway_id].right_blank?\n      request_hash['InstanceId'] = options[:instance_id] unless options[:instance_id].right_blank?      \n      link = generate_request(\"ReplaceRoute\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue Exception\n      on_exception\n    end\n\n    #---------------------\n    # InternetGateways\n    #---------------------\n\n    class QEc2DescribeInternetGatewaysParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(internetGatewaySet/item|internetGateway)$} then @item = { :tags => {} }\n        when %r{/tagSet/item$} then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        # item\n        when %r{/(internetGatewaySet/item|internetGateway)$} then @result << @item\n        when %r{/internetGatewayId$} then @item[:internet_gateway_id] = @text\n        when %r{/vpcId$}             then @item[:vpc_id]              = @text\n        when %r{/state$}             then @item[:state]               = @text\n        # tags\n        when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n        when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n        when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    #---------------------\n    # Routes\n    #---------------------\n\n    class QEc2DescribeRouteTablesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/(routeTableSet/item|routeTable)$}\n          @item = { :route_set       => [],\n                    :association_set => [],\n                    :tags            => {}}\n        when %r{/routeSet/item$}       then @route_set       = {}\n        when %r{/associationSet/item$} then @association_set = {}\n        when %r{/tagSet/item$}         then @aws_tag = {}\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        # item\n        when %r{/(routeTableSet/item|routeTable)/routeTableId$} then @item[:route_table_id] = @text\n        when %r{/(routeTableSet/item|routeTable)/vpcId$}        then @item[:vpc_id]         = @text\n        when %r{/(routeTableSet/item|routeTable)$}              then @result               << @item\n        # route set\n        when %r{/routeSet/item/destinationCidrBlock$} then @route_set[:destination_cidr_block] = @text\n        when %r{/routeSet/item/gatewayId$}            then @route_set[:gateway_id]             = @text\n        when %r{/routeSet/item/instanceId$}           then @route_set[:instance_id]            = @text\n        when %r{/routeSet/item/state$}                then @route_set[:state]                  = @text\n        when %r{/routeSet/item$}                      then @item[:route_set]                  << @route_set\n        # association set\n        when %r{/associationSet/item/routeTableId$}            then @association_set[:route_table_id]             = @text\n        when %r{/associationSet/item/routeTableAssociationId$} then @association_set[:route_table_association_id] = @text\n        when %r{/associationSet/item/subnetId$}                then @association_set[:subnet_id]                  = @text\n        when %r{/associationSet/item/main}                     then @association_set[:main]                       = @text == 'true'\n        when %r{/associationSet/item$}                         then @item[:association_set]                      << @association_set\n        # tags\n        when %r{/tagSet/item/key$}   then @aws_tag[:key]               = @text\n        when %r{/tagSet/item/value$} then @aws_tag[:value]             = @text\n        when %r{/tagSet/item$}       then @item[:tags][@aws_tag[:key]] = @aws_tag[:value]\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\nend\n\nend\n"
  },
  {
    "path": "lib/ec2/right_ec2_windows_mobility.rb",
    "content": "#\n# Copyright (c) 2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class Ec2\n\n    def describe_licenses(*license_ids)\n      link = generate_request(\"DescribeLicenses\", amazonize_list('LicenseId', license_ids))\n      request_info(link, QEc2DescribeLicensesParser.new(:logger => @logger))\n    end\n\n    def activate_license(license_id, capacity)\n      link = generate_request(\"ActivateLicense\", 'LicenseId' => license_id,\n                                                 'Capacity'  => capacity)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    end\n\n#    def get_license_capacity(license_id)\n#      link = generate_request(\"GetLicenseCapacity\", 'LicenseId' => license_id)\n#      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n#    end\n\n    def deactivate_license(license_id, capacity)\n      link = generate_request(\"DeactivateLicense\", 'LicenseId' => license_id,\n                                                   'Capacity'  => capacity)\n      request_info(link, RightBoolResponseParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Images\n    #-----------------------------------------------------------------\n\n    class QEc2DescribeLicensesParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/licenseSet/item$}  then @item          = { :capacities => [] }\n        when %r{/capacitySet/item$} then @capacity_item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'licenseId'        then @item[:license_id] = @text\n        when 'type'             then @item[:type]       = @text\n        when 'pool'             then @item[:pool]       = @text\n        when 'capacity'         then @capacity_item[:capacity]          = @text.to_i\n        when 'instanceCapacity' then @capacity_item[:instance_capacity] = @text.to_i\n        when 'state'            then @capacity_item[:state]             = @text\n        when 'earliestAllowedDeactivationTime' then @capacity_item[:earliest_allowed_deactivation_time] = @text\n        else\n          case full_tag_name\n          when %r{/capacitySet/item$} then @item[:capacities] << @capacity_item\n          when %r{/licenseSet/item$}  then @result            << @item\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  end\n  \nend\n"
  },
  {
    "path": "lib/elb/right_elb_interface.rb",
    "content": "#\n# Copyright (c) 2007-2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::ElbInterface -- RightScale Amazon Elastic Load Balancer interface\n  # The RightAws::ElbInterface class provides a complete interface to Amazon's\n  # Elastic Load Balancer service.\n  # \n  # For explanations of the semantics of each call, please refer to Amazon's documentation at\n  # http://docs.amazonwebservices.com/ElasticLoadBalancing/latest/DeveloperGuide/\n  # \n  # Create an interface handle:\n  #\n  #  elb = RightAws::ElbInterface.new(aws_access_key_id, aws_security_access_key)\n  #\n  # Create an new load balancer:\n  #\n  #  elb.create_load_balancer( 'test-kd1',\n  #                           ['us-east-1a', 'us-east-1b'],\n  #                           [ { :protocol => :http, :load_balancer_port => 80,  :instance_port => 80 },\n  #\t\t                          { :protocol => :tcp,  :load_balancer_port => 443, :instance_port => 443 } ])\n  #\n  # Configure its health checking:\n  #\n  #  elb.configure_health_check( 'test-kd1',\n  #                              { :healthy_threshold => 9,\n  #                                :unhealthy_threshold => 3,\n  #                                :target => \"TCP:433\",\n  #                                :timeout => 6,\n  #                                :interval => 31}\n  #\n  # Register instances with the balancer:\n  #\n  #  elb.register_instances_with_load_balancer('test-kd1', 'i-8b8bcbe2', 'i-bf8bcbd6') #=> [\"i-8b8bcbe2\", \"i-bf8bcbd6\"]\n  #\n  # Add new availability zones:\n  #\n  #  elb.enable_availability_zones_for_load_balancer(\"test-kd1\", \"us-east-1c\")\n  #\n  class ElbInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    # Amazon ELB API version being used\n    API_VERSION       = \"2011-04-05\"\n    DEFAULT_HOST      = \"elasticloadbalancing.amazonaws.com\"\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    LISTENER_PROTOCOLS = [ 'HTTP', 'HTTPS', 'TCP', 'SSL' ]\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to an ELB account. All handles share the same per process or per thread\n    # HTTP connection to Amazon ELB. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://elasticloadbalancing.amazonaws.com'\n    # * <tt>:server</tt>: ELB service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: ELB service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    # * <tt>:cache</tt>: true/false(default): caching works for: describe_load_balancers\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'ELB',\n             :default_host        => ENV['ELB_URL'] ? URI.parse(ENV['ELB_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['ELB_URL'] ? URI.parse(ENV['ELB_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['ELB_URL'] ? URI.parse(ENV['ELB_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['ELB_URL'] ? URI.parse(ENV['ELB_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['ELB_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:lbs_connection, @@bench, request, parser)\n    end\n\n    #-----------------------------------------------------------------\n    #      Load Balancers\n    #-----------------------------------------------------------------\n    # Describe load balancers.\n    # Returns an array of load balancers.\n    #\n    #  elb.describe_load_balancers #=>\n    #    [ { :health_check =>\n    #        { :healthy_threshold => 10,\n    #          :unhealthy_threshold => 2,\n    #          :target => \"TCP:80\",\n    #          :timeout => 5,\n    #          :interval => 30},\n    #        :load_balancer_name => \"test-kd1\",\n    #        :availability_zones => [\"us-east-1a\", \"us-east-1b\"],\n    #        :listeners =>\n    #         [ { :protocol => \"HTTP\", :load_balancer_port => \"80\",  :instance_port => \"80\" },\n    #           { :protocol => \"TCP\",  :load_balancer_port => \"443\", :instance_port => \"443\" } ],\n    #        :created_time => \"2009-05-27T11:59:11.000Z\",\n    #        :dns_name => \"test-kd1-1519253964.us-east-1.elb.amazonaws.com\",\n    #        :instances => [] } ]\n    #\n    #  elb.describe_load_balancers(\"test-kd1\") #=>\n    #    [{:load_balancer_name=>\"test-kd1\",\n    #      :instances=>[\"i-9fc056f4\", \"i-b3debfd8\"],\n    #      :health_check=>\n    #       {:interval=>30,\n    #        :healthy_threshold=>10,\n    #        :target=>\"TCP:80\",\n    #        :unhealthy_threshold=>2,\n    #        :timeout=>5},\n    #      :dns_name=>\"test-kd1-869291821.us-east-1.elb.amazonaws.com\",\n    #      :listeners=>\n    #       [{:load_balancer_port=>\"80\",\n    #         :policy_names=>[\"my-policy-1\"],\n    #         :instance_port=>\"80\",\n    #         :protocol=>\"HTTP\"},\n    #        {:load_balancer_port=>\"8080\",\n    #         :policy_names=>[\"my-policy-lb-1\"],\n    #         :instance_port=>\"8080\",\n    #         :protocol=>\"HTTP\"},\n    #        {:load_balancer_port=>\"443\",\n    #         :policy_names=>[],\n    #         :instance_port=>\"443\",\n    #         :protocol=>\"TCP\"}],\n    #      :created_time=>\"2010-04-15T12:04:49.000Z\",\n    #      :availability_zones=>[\"us-east-1a\", \"us-east-1b\"],\n    #      :app_cookie_stickiness_policies=>\n    #       [{:policy_name=>\"my-policy-1\", :cookie_name=>\"my-cookie-1\"}],\n    #      :lb_cookie_stickiness_policies=>\n    #       [{:cookie_expiration_period=>60, :policy_name=>\"my-policy-lb-1\"}]}]\n    #\n    def describe_load_balancers(*load_balancers)\n      load_balancers = load_balancers.flatten.compact\n      request_hash = amazonize_list(\"LoadBalancerNames.member\", load_balancers)\n      link = generate_request(\"DescribeLoadBalancers\", request_hash)\n      request_cache_or_info(:describe_load_balancers, link,  DescribeLoadBalancersParser, @@bench, load_balancers.right_blank?)\n    end\n\n    # Create new load balancer.\n    # Returns a new load balancer DNS name.\n    #\n    # Listener options: :protocol, :load_balancer_port, :instance_port and :ssl_certificate_id\n    # Protocols: :tcp, :http, :https or :ssl\n    # \n    #  elb.create_load_balancer( 'test-kd1',\n    #                            ['us-east-1a', 'us-east-1b'],\n    #                            [ { :protocol => :http,  :load_balancer_port => 80,  :instance_port => 80 },\n\t\t#                              { :protocol => :https, :load_balancer_port => 443, :instance_port => 443,\n    #                                :ssl_certificate_id => 'arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob' } ])\n    #                                #=> \"test-kd1-1519253964.us-east-1.elb.amazonaws.com\"\n    #\n    def create_load_balancer(load_balancer_name, availability_zones=[], listeners=[])\n      request_hash = { 'LoadBalancerName' => load_balancer_name }\n      # merge zones\n      request_hash.merge!( amazonize_list(\"AvailabilityZones.member\", availability_zones) )\n      # merge listeners\n      if listeners.right_blank?\n        listeners = { :protocol           => :http,\n                      :load_balancer_port => 80,\n                      :instance_port      => 80 }\n      end\n      request_hash = merge_listeners_into_request_hash(request_hash, listeners)\n      link = generate_request(\"CreateLoadBalancer\", request_hash)\n      request_info(link, CreateLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Delete load balancer.\n    # Returns +true+ on success.\n    #\n    #  elb.delete_load_balancer('test-kd1') #=> true\n    #\n    # Amazon: Because this API has been designed to be idempotent, even if the LoadBalancer does not exist or\n    # has been deleted, DeleteLoadBalancer still returns a success.\n    #\n    def delete_load_balancer(load_balancer_name)\n      link = generate_request(\"DeleteLoadBalancer\", 'LoadBalancerName' => load_balancer_name)\n      request_info(link, DeleteLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Creates one or more new listeners on a LoadBalancer for the specified port. If a listener with the given\n    # port does not already exist, it will be created; otherwise, the properties of the new listener must match\n    # the the properties of the existing listener.\n    #\n    # Listener options: :protocol, :load_balancer_port, :instance_port and :ssl_certificate_id\n    # Protocols: :tcp, :http, :https or :ssl\n    #\n    #  elb.create_load_balancer_listeners( 'test-kd1',\n    #                                      [ { :protocol => :http,  :load_balancer_port => 80,  :instance_port => 80 },\n\t\t#                                        { :protocol => :https, :load_balancer_port => 443, :instance_port => 443,\n    #                                          :ssl_certificate_id => 'arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob' } ]) #=> true\n    #\n    def create_load_balancer_listeners(load_balancer_name, listeners)\n      request_hash = { 'LoadBalancerName' => load_balancer_name }\n      request_hash = merge_listeners_into_request_hash(request_hash, listeners)\n      link = generate_request(\"CreateLoadBalancerListeners\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Removes listeners from the load balancer for the specified port number.\n    #\n    #  elb.delete_load_balancer_listeners( 'kd_test', 80, 443) #=> true\n    #\n    def delete_load_balancer_listeners(load_balancer_name, *load_balancer_ports)\n      load_balancer_ports.flatten!\n      request_hash = { 'LoadBalancerName' => load_balancer_name }\n      request_hash.merge!( amazonize_list(\"LoadBalancerPorts.member\", load_balancer_ports ) )\n      link = generate_request(\"DeleteLoadBalancerListeners\", request_hash )\n      request_info(link, DeleteLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Add one or more zones to a load balancer.\n    # Returns a list of updated availability zones for the load balancer.\n    #\n    #  elb.enable_availability_zones_for_load_balancer(\"test-kd1\", \"us-east-1c\") #=> [\"us-east-1a\", \"us-east-1c\"]\n    #\n    def enable_availability_zones_for_load_balancer(load_balancer_name, *availability_zones)\n      availability_zones.flatten!\n      request_hash = amazonize_list(\"AvailabilityZones.member\", availability_zones)\n      request_hash.merge!( 'LoadBalancerName' => load_balancer_name )\n      link = generate_request(\"EnableAvailabilityZonesForLoadBalancer\", request_hash)\n      request_info(link, AvailabilityZonesForLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Remove one or more zones from a load balancer.\n    # Returns a list of updated availability zones for the load balancer.\n    #\n    #  elb.disable_availability_zones_for_load_balancer(\"test-kd1\", \"us-east-1c\") #=> [\"us-east-1a\"]\n    #\n    def disable_availability_zones_for_load_balancer(load_balancer_name, *availability_zones)\n      availability_zones.flatten!\n      request_hash = amazonize_list(\"AvailabilityZones.member\", availability_zones)\n      request_hash.merge!( 'LoadBalancerName' => load_balancer_name )\n      link = generate_request(\"DisableAvailabilityZonesForLoadBalancer\", request_hash)\n      request_info(link, AvailabilityZonesForLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Define an application healthcheck for the instances.\n    # Returns an updated health check configuration for the load balancer.\n    #\n    #  hc = elb.configure_health_check( 'test-kd1',\n    #                                   { :healthy_threshold => 9,\n    #                                     :unhealthy_threshold => 3,\n    #                                     :target => \"TCP:433\",\n    #                                     :timeout => 6,\n    #                                     :interval => 31}\n    #  pp hc #=> { :target=>\"TCP:433\", :timeout=>6, :interval=>31, :healthy_threshold=>9, :unhealthy_threshold=>3 }\n    #\n    def configure_health_check(load_balancer_name, health_check)\n      request_hash = { 'LoadBalancerName' => load_balancer_name }\n      health_check.each{ |key, value| request_hash[\"HealthCheck.#{key.to_s.right_camelize}\"] = value }\n      link = generate_request(\"ConfigureHealthCheck\", request_hash)\n      request_info(link, HealthCheckParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Instances\n    #-----------------------------------------------------------------\n\n    # Describe the current state of the instances of the specified load balancer.\n    # Returns a list of the instances.\n    #\n    #  elb.describe_instance_health('test-kd1', 'i-8b8bcbe2', 'i-bf8bcbd6') #=>\n    #      [ { :description => \"Instance registration is still in progress\",\n    #          :reason_code => \"ELB\",\n    #          :instance_id => \"i-8b8bcbe2\",\n    #          :state       => \"OutOfService\" },\n    #        { :description => \"Instance has failed at least the UnhealthyThreshold number of health checks consecutively.\",\n    #          :reason_code => \"Instance\",\n    #          :instance_id => \"i-bf8bcbd6\",\n    #          :state       => \"OutOfService\" } ]\n    #\n    def describe_instance_health(load_balancer_name, *instances)\n      instances.flatten!\n      request_hash = amazonize_list(\"Instances.member.?.InstanceId\", instances)\n      request_hash.merge!( 'LoadBalancerName' => load_balancer_name )\n      link = generate_request(\"DescribeInstanceHealth\", request_hash)\n      request_info(link, DescribeInstanceHealthParser.new(:logger => @logger))\n    end\n\n    # Add new instance(s) to the load balancer.\n    # Returns an updated list of instances for the load balancer.\n    #\n    #  elb.register_instances_with_load_balancer('test-kd1', 'i-8b8bcbe2', 'i-bf8bcbd6') #=> [\"i-8b8bcbe2\", \"i-bf8bcbd6\"]\n    #\n    def register_instances_with_load_balancer(load_balancer_name, *instances)\n      instances.flatten!\n      request_hash = amazonize_list(\"Instances.member.?.InstanceId\", instances)\n      request_hash.merge!( 'LoadBalancerName' => load_balancer_name )\n      link = generate_request(\"RegisterInstancesWithLoadBalancer\", request_hash)\n      request_info(link, InstancesWithLoadBalancerParser.new(:logger => @logger))\n    end\n\n    # Remove instance(s) from the load balancer.\n    # Returns an updated list of instances for the load balancer.\n    #\n    #  elb.deregister_instances_with_load_balancer('test-kd1', 'i-8b8bcbe2') #=> [\"i-bf8bcbd6\"]\n    #\n    def deregister_instances_with_load_balancer(load_balancer_name, *instances)\n      instances.flatten!\n      request_hash = amazonize_list(\"Instances.member.?.InstanceId\", instances)\n      request_hash.merge!( 'LoadBalancerName' => load_balancer_name )\n      link = generate_request(\"DeregisterInstancesFromLoadBalancer\", request_hash)\n      request_info(link, InstancesWithLoadBalancerParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Cookies\n    #-----------------------------------------------------------------\n\n    # Generates a stickiness policy with sticky session lifetimes that follow\n    # that of an application-generated cookie.\n    # This policy can only be associated with HTTP listeners.\n    #\n    #  elb.create_app_cookie_stickiness_policy('my-load-balancer', 'MyLoadBalancerPolicy', 'MyCookie') #=> true\n    #\n    def create_app_cookie_stickiness_policy(load_balancer_name, policy_name, cookie_name)\n      request_hash = { 'LoadBalancerName' => load_balancer_name,\n                       'PolicyName'       => policy_name,\n                       'CookieName'       => cookie_name }\n      link = generate_request(\"CreateAppCookieStickinessPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Generates a stickiness policy with sticky session lifetimes controlled by the\n    # lifetime of the browser (user-agent) or a specified expiration period.\n    # This policy can only be associated only with HTTP listeners.\n    #\n    #  elb.create_lb_cookie_stickiness_policy('my-load-balancer', 'MyLoadBalancerPolicy', 60) #=> true\n    #\n    def create_lb_cookie_stickiness_policy(load_balancer_name, policy_name, cookie_expiration_period)\n      request_hash = { 'LoadBalancerName'        => load_balancer_name,\n                       'PolicyName'              => policy_name,\n                       'CookieExpirationPeriod'  => cookie_expiration_period }\n      link = generate_request(\"CreateLBCookieStickinessPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Associates, updates, or disables a policy with a listener on the load balancer.\n    # Only zero(0) or one(1) policy can be associated with a listener.\n    #\n    #  elb.set_load_balancer_policies_of_listener('my-load-balancer', 80, 'MyLoadBalancerPolicy') #=> true\n    #\n    def set_load_balancer_policies_of_listener(load_balancer_name, load_balancer_port, *policy_names)\n      policy_names.flatten!\n      request_hash = { 'LoadBalancerName' => load_balancer_name,\n                       'LoadBalancerPort' => load_balancer_port }\n      if policy_names.right_blank?\n        request_hash['PolicyNames'] = ''\n      else\n        request_hash.merge!(amazonize_list('PolicyNames.member', policy_names))\n      end\n      link = generate_request(\"SetLoadBalancerPoliciesOfListener\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Deletes a policy from the load balancer. The specified policy must not be enabled for any listeners.\n    #\n    #  elb.delete_load_balancer_policy('my-load-balancer', 'MyLoadBalancerPolicy') #=> true\n    #\n    def delete_load_balancer_policy(load_balancer_name, policy_name)\n      request_hash = { 'LoadBalancerName'        => load_balancer_name,\n                       'PolicyName'              => policy_name }\n      link = generate_request(\"DeleteLoadBalancerPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    def set_load_balancer_listener_ssl_certificate(load_balancer_name, load_balancer_port, ssl_sertificate_id)\n      request_hash = { 'LoadBalancerName' => load_balancer_name,\n                       'LoadBalancerPort' => load_balancer_port,\n                       'SSLCertificateId' => ssl_sertificate_id }\n      link = generate_request(\"SetLoadBalancerListenerSSLCertificate\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Helpers\n    #-----------------------------------------------------------------\n\n    def merge_listeners_into_request_hash(request_hash, listeners) # :nodoc:\n      listeners = [listeners] unless listeners.is_a?(Array)\n      request_hash.merge(amazonize_list( ['Listeners.member.?.Protocol',\n                                          'Listeners.member.?.LoadBalancerPort',\n                                          'Listeners.member.?.InstancePort',\n                                          'Listeners.member.?.SSLCertificateId'],\n                                          listeners.map{ |i|\n                                            [ (i[:protocol]           || 'HTTP').to_s.upcase,\n                                               i[:load_balancer_port] || 80,\n                                               i[:instance_port]      || 80,\n                                               i[:ssl_certificate_id]]\n                                          },\n                                          :default => :skip_nils\n                                       )\n                        )\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Load Balancers\n    #-----------------------------------------------------------------\n \n    class DescribeLoadBalancersParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{LoadBalancerDescriptions/member$}\n          @item = { :availability_zones => [],\n                    :health_check       => {},\n                    :listeners          => [],\n                    :instances          => [],\n                    :app_cookie_stickiness_policies => [],\n                    :lb_cookie_stickiness_policies  => []}\n        when %r{ListenerDescriptions/member$}        then @listener = {:policy_names => []}\n        when %r{AppCookieStickinessPolicies/member$} then @app_cookie_stickiness_policy = {}\n        when %r{LBCookieStickinessPolicies/member$}  then @lb_cookie_stickiness_policy = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'LoadBalancerName'   then @item[:load_balancer_name]   = @text\n        when 'DNSName'            then @item[:dns_name]             = @text\n        when 'CreatedTime'        then @item[:created_time]         = @text\n        when 'Interval'           then @item[:health_check][:interval]            = @text.to_i\n        when 'Target'             then @item[:health_check][:target]              = @text\n        when 'HealthyThreshold'   then @item[:health_check][:healthy_threshold]   = @text.to_i\n        when 'Timeout'            then @item[:health_check][:timeout]             = @text.to_i\n        when 'UnhealthyThreshold' then @item[:health_check][:unhealthy_threshold] = @text.to_i       \n        when 'Protocol'           then @listener[:protocol]           = @text\n        when 'LoadBalancerPort'   then @listener[:load_balancer_port] = @text\n        when 'InstancePort'       then @listener[:instance_port]      = @text\n        when 'SSLCertificateId'   then @listener[:ssl_certificate_id] = @text\n        when 'CanonicalHostedZoneName'   then @item[:canonical_hosted_zone_name] = @text\n        when 'CanonicalHostedZoneNameID' then @item[:canonical_hosted_zone_name_id] = @text\n        end\n        case full_tag_name\n        when %r{AvailabilityZones/member$}    then @item[:availability_zones] << @text\n        when %r{Instances/member/InstanceId$} then @item[:instances]          << @text\n        when %r{ListenerDescriptions/member$} then @item[:listeners]          << @listener\n        when %r{ListenerDescriptions/member/PolicyNames/member$} then @listener[:policy_names] << @text\n        when %r{AppCookieStickinessPolicies/member}\n          case name\n          when 'PolicyName' then @app_cookie_stickiness_policy[:policy_name] = @text\n          when 'CookieName' then @app_cookie_stickiness_policy[:cookie_name] = @text\n          when 'member'     then @item[:app_cookie_stickiness_policies] << @app_cookie_stickiness_policy\n          end\n        when %r{LBCookieStickinessPolicies/member}\n          case name\n          when 'PolicyName'             then @lb_cookie_stickiness_policy[:policy_name] = @text\n          when 'CookieExpirationPeriod' then @lb_cookie_stickiness_policy[:cookie_expiration_period] = @text.to_i\n          when 'member'                 then @item[:lb_cookie_stickiness_policies] << @lb_cookie_stickiness_policy\n          end\n        when %r{LoadBalancerDescriptions/member$}\n          @item[:availability_zones].sort!\n          @item[:instances].sort!\n          @result << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class CreateLoadBalancerParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        @result = @text if name == 'DNSName'\n      end\n    end\n\n    class DeleteLoadBalancerParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        @result = true if name == 'DeleteLoadBalancerResult'\n      end\n    end\n\n    class AvailabilityZonesForLoadBalancerParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'member'\n          @result << @text\n        when 'AvailabilityZones'\n          @result.sort!\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class HealthCheckParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'Interval'           then @result[:interval]            = @text.to_i\n        when 'Target'             then @result[:target]              = @text\n        when 'HealthyThreshold'   then @result[:healthy_threshold]   = @text.to_i\n        when 'Timeout'            then @result[:timeout]             = @text.to_i\n        when 'UnhealthyThreshold' then @result[:unhealthy_threshold] = @text.to_i\n        end\n      end\n      def reset\n        @result = {}\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Instances\n    #-----------------------------------------------------------------\n\n    class DescribeInstanceHealthParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'member'\n      end\n      def tagend(name)\n        case name\n        when 'Description' then @item[:description] = @text\n        when 'State'       then @item[:state]       = @text\n        when 'InstanceId'  then @item[:instance_id] = @text\n        when 'ReasonCode'  then @item[:reason_code] = @text\n        when 'member'      then @result            << @item\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    class InstancesWithLoadBalancerParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'InstanceId'\n          @result << @text\n        when 'Instances'\n          @result.sort!\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/emr/right_emr_interface.rb",
    "content": "#\n# Copyright (c) 2011 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::EmrInterface -- RightScale Amazon Elastic Map Reduce interface\n  #\n  # The RightAws::EmrInterface class provides a complete interface to Amazon\n  # Elastic Map Reduce service.\n  #\n  # For explanations of the semantics of each call, please refer to Amazon's\n  # documentation at\n  # http://aws.amazon.com/documentation/elasticmapreduce/\n  #\n  # Create an interface handle:\n  #\n  #  emr = RightAws::EmrInterface.new(aws_access_key_id, aws_secret_access_key)\n  #\n  # Create a job flow:\n  #\n  #  emr.run_job_flow(\n  #    :name => 'job flow 1',\n  #    :master_instance_type => 'm1.large',\n  #    :slave_instance_type => 'm1.large',\n  #    :instance_count => 5,\n  #    :log_uri => 's3n://bucket/path/to/logs',\n  #    :steps => [{\n  #      :name => 'step 1',\n  #      :jar => 's3n://bucket/path/to/code.jar',\n  #      :main_class => 'com.foobar.emr.Step1',\n  #      :args => ['arg', 'arg'],\n  #    }]) #=> \"j-9K18HM82Q0AE7\"\n  #\n  # Describe a job flow:\n  #\n  #  emr.describe_job_flows('j-9K18HM82Q0AE7') #=> {...}\n  #\n  # Terminate a job flow:\n  #\n  #  emr.terminate_job_flows('j-9K18HM82Q0AE7') #=> true\n  #\n  class EmrInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    # Amazon EMR API version being used\n    API_VERSION       = '2009-03-31'\n    DEFAULT_HOST      = 'elasticmapreduce.amazonaws.com'\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to a EMR service.\n    #\n    # All handles share the same per process or per thread HTTP connection\n    # to EMR. Each handle is for a specific account. The params have\n    # the following options:\n    #\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint\n    #   (this overwrites: :server, :port, :service, :protocol). Example:\n    #   'https://elasticmapreduce.amazonaws.com'\n    # * <tt>:server</tt>: EMR service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: EMR service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    #\n    #  emr = RightAws::EmrInterface.new('xxxxxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n    #    {:logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::EmrInterface::0xb7b3c30c>\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'EMR',\n             :default_host        => ENV['EMR_URL'] ? URI.parse(ENV['EMR_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['EMR_URL'] ? URI.parse(ENV['EMR_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['EMR_URL'] ? URI.parse(ENV['EMR_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['EMR_URL'] ? URI.parse(ENV['EMR_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['EMR_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n    # Sends request to Amazon and parses the response\n    # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:emr_connection, @@bench, request, parser)\n    end\n\n    #-----------------------------------------------------------------\n    #      Job Flows\n    #-----------------------------------------------------------------\n\n    EMR_INSTANCES_KEY_MAPPING = {                                                           # :nodoc:\n      :additional_info => 'AdditionalInfo',\n      :log_uri => 'LogUri',\n      :name => 'Name',\n      :ami_version => 'AmiVersion',\n      # JobFlowInstancesConfig\n      :ec2_key_name          => 'Instances.Ec2KeyName',\n      :hadoop_version => 'Instances.HadoopVersion',\n      :instance_count        => 'Instances.InstanceCount',\n      :keep_job_flow_alive_when_no_steps   => 'Instances.KeepJobFlowAliveWhenNoSteps',\n      :master_instance_type  => 'Instances.MasterInstanceType',\n      :slave_instance_type   => 'Instances.SlaveInstanceType',\n      :termination_protected => 'Instances.TerminationProtected',\n      # PlacementType\n      :availability_zone     => 'Instances.Placement.AvailabilityZone',\n    }\n\n    BOOTSTRAP_ACTION_KEY_MAPPING = {                                                           # :nodoc:\n      :name => 'Name',\n      # ScriptBootstrapActionConfig\n      :args => 'ScriptBootstrapAction.Args',\n      :path => 'ScriptBootstrapAction.Path',\n    }\n\n    INSTANCE_GROUP_KEY_MAPPING = {                                                           # :nodoc:\n      :bid_price => 'BidPrice',\n      :instance_count => 'InstanceCount',\n      :instance_role => 'InstanceRole',\n      :instance_type => 'InstanceType',\n      :market => 'Market',\n      :name => 'Name',\n    }\n\n    STEP_CONFIG_KEY_MAPPING = {                                                           # :nodoc:\n      :action_on_failure => 'ActionOnFailure',\n      :name => 'Name',\n      # HadoopJarStepConfig\n      :args => 'HadoopJarStep.Args',\n      :jar => 'HadoopJarStep.Jar',\n      :main_class => 'HadoopJarStep.MainClass',\n      :properties => 'HadoopJarStep.Properties',\n    }\n    \n    KEY_VALUE_KEY_MAPPINGS = {\n      :key => 'Key',\n      :value => 'Value',\n    }\n\n    # Creates and starts running a new job flow.\n    #\n    # The job flow will run the steps specified and terminate (unless\n    # keep alive option is set).\n    #\n    # A maximum of 256 steps are allowed in a job flow.\n    #\n    # At least the name, instance types, instance count and one step\n    # must be specified.\n    #\n    #  # simple usage:\n    #  emr.run_job_flow(\n    #    :name => 'job flow 1',\n    #    :master_instance_type => 'm1.large',\n    #    :slave_instance_type => 'm1.large',\n    #    :instance_count => 5,\n    #    :log_uri => 's3n://bucket/path/to/logs',\n    #    :steps => [{\n    #      :name => 'step 1',\n    #      :jar => 's3n://bucket/path/to/code.jar',\n    #      :main_class => 'com.foobar.emr.Step1',\n    #      :args => ['arg', 'arg'],\n    #    }]) #=> \"j-9K18HM82Q0AE7\"\n    #\n    #  # advanced usage:\n    #  emr.run_job_flow(\n    #    :name => 'job flow 1',\n    #    :ec2_key_name => 'gsg-keypair',\n    #    :hadoop_version => '0.20',\n    #    :instance_groups => [{\n    #      :bid_price => '0.1',\n    #      :instance_count => '1',\n    #      :instance_role => 'MASTER',\n    #      :instance_type => 'm1.small',\n    #      :market => 'SPOT',\n    #      :name => 'master group',\n    #    }, {\n    #      :bid_price => '0.1',\n    #      :instance_count => '2',\n    #      :instance_role => 'CORE',\n    #      :instance_type => 'm1.small',\n    #      :market => 'SPOT',\n    #      :name => 'core group',\n    #    }, {\n    #      :bid_price => '0.1',\n    #      :instance_count => '2',\n    #      :instance_role => 'TASK',\n    #      :instance_type => 'm1.small',\n    #      :market => 'SPOT',\n    #      :name => 'task group',\n    #    }],\n    #    :keep_job_flow_alive_when_no_steps => true,\n    #    :availability_zone => 'us-east-1a',\n    #    :termination_protected => true,\n    #    :log_uri => 's3n://bucket/path/to/logs',\n    #    :steps => [{\n    #      :name => 'step 1',\n    #      :jar => 's3n://bucket/path/to/code.jar',\n    #      :main_class => 'com.foobar.emr.Step1',\n    #      :args => ['arg', 'arg'],\n    #      :properties => {\n    #        'property' => 'value',\n    #      },\n    #      :action_on_failure => 'TERMINATE_JOB_FLOW',\n    #    }],\n    #    :additional_info => '',\n    #    :bootstrap_actions => [{\n    #      :name => 'bootstrap action 1',\n    #      :path => 's3n://bucket/path/to/bootstrap',\n    #      :args => ['hello', 'world'],\n    #    }],\n    #  ) #=> \"j-9K18HM82Q0AE7\"\n    #\n    def run_job_flow(options={})\n      request_hash = amazonize_run_job_flow(options)\n      request_hash.update(amazonize_bootstrap_actions(options[:bootstrap_actions]))\n      request_hash.update(amazonize_instance_groups(options[:instance_groups]))\n      request_hash.update(amazonize_steps(options[:steps]))\n      link = generate_request(\"RunJobFlow\", request_hash)\n      request_info(link, RunJobFlowParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Returns a list of job flows that match all of supplied parameters.\n    #\n    # Without parameters, returns job flows started in the last two weeks\n    # or running job flows started in the last two months.\n    #\n    # Regardless of parameters, only jobs started in the last two months\n    # are returned.\n    #\n    #  # default list:\n    #  emr.describe_job_flows #=> [\n    #    {:keep_job_flow_alive_when_no_steps=>false,\n    #      :log_uri=>\"s3n://bucket/path/to/logs\",\n    #      :master_instance_type=>\"m1.small\",\n    #      :availability_zone=>\"us-east-1d\",\n    #      :last_state_change_reason=>\"Steps completed\",\n    #      :termination_protected=>false,\n    #      :master_instance_id=>\"i-1fe51278\",\n    #      :instance_count=>1,\n    #      :ready_date_time=>\"2011-08-31T18:58:58Z\",\n    #      :bootstrap_actions=>[],\n    #      :master_public_dns_name=>\"ec2-184-78-29-127.compute-1.amazonaws.com\",\n    #      :instance_groups=>\n    #       [{:instance_request_count=>1,\n    #         :last_state_change_reason=>\"Job flow terminated\",\n    #         :instance_role=>\"MASTER\",\n    #         :ready_date_time=>\"2011-08-31T18:58:56Z\",\n    #         :instance_running_count=>0,\n    #         :start_date_time=>\"2011-08-31T18:58:19Z\",\n    #         :market=>\"ON_DEMAND\",\n    #         :creation_date_time=>\"2011-08-31T18:55:36Z\",\n    #         :name=>\"master\",\n    #         :instance_group_id=>\"ig-1D91GQR7A9H2K\",\n    #         :state=>\"ENDED\",\n    #         :instance_type=>\"m1.small\",\n    #         :end_date_time=>\"2011-08-31T19:01:09Z\"}],\n    #      :start_date_time=>\"2011-08-31T18:58:58Z\",\n    #      :steps=>\n    #       [{:jar=>\"s3n://bucket/path/to/code.jar\",\n    #         :main_class=>\"com.foobar.emr.Step1\",\n    #         :start_date_time=>\"2011-08-31T18:58:58Z\",\n    #         :properties=>{},\n    #         :args=>[],\n    #         :creation_date_time=>\"2011-08-31T18:55:36Z\",\n    #         :action_on_failure=>\"TERMINATE_JOB_FLOW\",\n    #         :name=>\"step 1\",\n    #         :state=>\"COMPLETED\",\n    #         :end_date_time=>\"2011-08-31T19:00:34Z\"}],\n    #      :normalized_instance_hours=>1,\n    #      :ami_version=>\"1.0\",\n    #      :creation_date_time=>\"2011-08-31T18:55:36Z\",\n    #      :name=>\"jobflow 1\",\n    #      :hadoop_version=>\"0.18\",\n    #      :job_flow_id=>\"j-9K18HM82Q0AE7\",\n    #      :state=>\"COMPLETED\",\n    #      :end_date_time=>\"2011-08-31T19:01:09Z\"}]\n    #\n    #  # describe specific job flows:\n    #  emr.describe_job_flows('j-9K18HM82Q0AE7', 'j-2QE0KHA1LP4GS') #=> [...]\n    #\n    #  # specify parameters:\n    #  emr.describe_job_flows(\n    #    :created_after => Time.now - 86400,\n    #    :created_before => Time.now - 3600,\n    #    :job_flow_ids => ['j-9K18HM82Q0AE7', 'j-2QE0KHA1LP4GS'],\n    #    :job_flow_states => ['RUNNING']\n    #  ) #=> [...]\n    #\n    #  # combined job flow list and parameters syntax:\n    #  emr.describe_job_flows('j-9K18HM82Q0AE7', 'j-2QE0KHA1LP4GS',\n    #    :job_flow_states => ['RUNNING']\n    #  ) #=> [...]\n    #\n    def describe_job_flows(*job_flow_ids_and_options)\n      job_flow_ids, options = AwsUtils::split_items_and_params(job_flow_ids_and_options)\n      # merge job flow ids passed in as arguments and in options\n      unless job_flow_ids.empty?\n        # do not modify passed in options\n        options = options.dup\n        if job_flow_ids_in_options = options[:job_flow_ids]\n          # allow the same ids to be passed in either location;\n          # remove duplicates\n          options[:job_flow_ids] = (job_flow_ids_in_options + job_flow_ids).uniq\n        else\n          options[:job_flow_ids] = job_flow_ids\n        end\n      end\n      request_hash = {}\n      unless (job_flow_ids = options[:job_flow_ids]).right_blank?\n        request_hash.update(amazonize_list(\"JobFlowIds.member\", job_flow_ids))\n      end\n      unless (job_flow_states = options[:job_flow_states]).right_blank?\n        request_hash = amazonize_list(\"JobFlowStates.member\", job_flow_states)\n      end\n      request_hash['CreatedAfter'] = AwsUtils::utc_iso8601(options[:created_after]) unless options[:created_after].right_blank?\n      request_hash['CreatedBefore'] = AwsUtils::utc_iso8601(options[:created_before]) unless options[:created_before].right_blank?\n      link = generate_request(\"DescribeJobFlows\", request_hash)\n      request_cache_or_info(:describe_job_flows, link,  DescribeJobFlowsParser, @@bench, nil)\n    rescue\n      on_exception\n    end\n\n    # Terminates specified job flows.\n    #\n    #  emr.terminate_job_flows('j-9K18HM82Q0AE7') #=> true\n    #\n    def terminate_job_flows(*job_flow_ids)\n      link = generate_request(\"TerminateJobFlows\", amazonize_list('JobFlowIds.member', job_flow_ids))\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Locks a job flow so the EC2 instances in the cluster cannot be\n    # terminated by user intervention, an API call, or in the event of a\n    # job flow error. Cluster will still terminate upon successful completion\n    # of the job flow.\n    #\n    #  emr.set_termination_protection(\n    #    'j-9K18HM82Q0AE7', 'j-2QE0KHA1LP4GS', :termination_protected => true\n    #  ) #=> true\n    #\n    # Protection can be enabled using the shortcut syntax:\n    #\n    #  emr.set_termination_protection('j-9K18HM82Q0AE7') #=> true\n    #\n    def set_termination_protection(*job_flow_ids_and_options)\n      job_flow_ids, options = AwsUtils::split_items_and_params(job_flow_ids_and_options)\n      request_hash = amazonize_list('JobFlowIds.member', job_flow_ids)\n      request_hash['TerminationProtected'] = case value = options[:termination_protected]\n      when true\n        'true'\n      when false\n        'false'\n      when nil\n        # if :termination_protected => nil was given, then unprotect;\n        # if no :termination_protected option was given, protect\n        if options.has_key?(:termination_protected)\n          'false'\n        else\n          'true'\n        end\n      else\n        # pass value through\n        value\n      end\n      link = generate_request(\"SetTerminationProtection\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      Steps\n    #-----------------------------------------------------------------\n\n    # Adds steps to a running job flow.\n    #\n    # A maximum of 256 steps are allowed in a job flow. Steps can only be\n    # added to job flows that are starting, bootstrapping, running or waiting.\n    #\n    # Step configuration options are the same as the ones accepted by\n    # run_job_flow.\n    #\n    #  emr.add_job_flow_steps('j-2QE0KHA1LP4GS', {\n    #    :name => 'step 1',\n    #    :jar => 's3n://bucket/path/to/code.jar',\n    #    :main_class => 'com.foobar.emr.Step1',\n    #    :args => ['arg', 'arg'],\n    #    :properties => {\n    #      'property' => 'value',\n    #    },\n    #    :action_on_failure => 'TERMINATE_JOB_FLOW',\n    #  }) #=> true\n    #\n    def add_job_flow_steps(job_flow_id, *steps)\n      request_hash = amazonize_steps(steps)\n      request_hash['JobFlowId'] = job_flow_id\n      link = generate_request(\"AddJobFlowSteps\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      Instance Groups\n    #-----------------------------------------------------------------\n\n    # Adds instance groups to a running job flow.\n    #\n    # Instance group configuration options are the same as the ones accepted\n    # by run_job_flow.\n    #\n    # Only task instance groups may be added at runtime.\n    # Instance groups cannot be added to job flows that have only a master\n    # instance (i.e. 1 instance in total).\n    #\n    #  emr.add_instance_groups('j-2QE0KHA1LP4GS', {\n    #    :bid_price => '0.1',\n    #    :instance_count => '2',\n    #    :instance_role => 'TASK',\n    #    :instance_type => 'm1.small',\n    #    :market => 'SPOT',\n    #    :name => 'core group',\n    #  }) #=> true\n    #\n    def add_instance_groups(job_flow_id, *instance_groups)\n      request_hash = amazonize_instance_groups(instance_groups, 'InstanceGroups')\n      request_hash['JobFlowId'] = job_flow_id\n      link = generate_request(\"AddInstanceGroups\", request_hash)\n      request_info(link, AddInstanceGroupsParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n    MODIFY_INSTANCE_GROUP_KEY_MAPPINGS = {\n      :instance_group_id => 'InstanceGroupId',\n      :instance_count => 'InstanceCount',\n    }\n\n    # Modifies instance groups.\n    #\n    # The only modifiable parameter is instance count.\n    #\n    # An instance group may only be modified when the job flow is running\n    # or waiting. Additionally, hadoop 0.20 is required to resize job flows.\n    #\n    #  # general syntax\n    #  emr.modify_instance_groups(\n    #    {:instance_group_id => 'ig-P2OPM2L9ZQ4P', :instance_count => 5},\n    #    {:instance_group_id => 'ig-J82ML0M94A7E', :instance_count => 1}\n    #  ) #=> true\n    #\n    #  # shortcut syntax\n    #  emr.modify_instance_groups('ig-P2OPM2L9ZQ4P', 5) #=> true\n    #\n    # Shortcut syntax supports modifying only one instance group at a time.\n    #\n    def modify_instance_groups(*args)\n      unless args.first.is_a?(Hash)\n        if args.length != 2\n          raise ArgumentError, \"Must be given two arguments if arguments are not hashes\"\n        end\n        args = [{:instance_group_id => args.first, :instance_count => args.last}]\n      end\n      request_hash = amazonize_list_with_key_mapping('InstanceGroups.member', MODIFY_INSTANCE_GROUP_KEY_MAPPINGS, args)\n      link = generate_request(\"ModifyInstanceGroups\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n    private\n\n    def amazonize_run_job_flow(options) # :nodoc:\n      result = {}\n      unless options.right_blank?\n        EMR_INSTANCES_KEY_MAPPING.each do |local_name, remote_name|\n          value = options[local_name]\n          result[remote_name] = value unless value.nil?\n        end\n      end\n      result\n    end\n\n    def amazonize_bootstrap_actions(bootstrap_actions, key = 'BootstrapActions.member') # :nodoc:\n      result = {}\n      unless bootstrap_actions.right_blank?\n        bootstrap_actions.each_with_index do |item, index|\n          BOOTSTRAP_ACTION_KEY_MAPPING.each do |local_name, remote_name|\n            value = item[local_name]\n            case local_name\n            when :args\n              result.update(amazonize_list(\"#{key}.#{index+1}.#{remote_name}.member\", value))\n            else\n              next if value.nil?\n              result[\"#{key}.#{index+1}.#{remote_name}\"] = value\n            end\n          end\n        end\n      end\n      result\n    end\n\n    def amazonize_instance_groups(instance_groups, key = 'Instances.InstanceGroups') # :nodoc:\n      result = {}\n      unless instance_groups.right_blank?\n        instance_groups.each_with_index do |item, index|\n          INSTANCE_GROUP_KEY_MAPPING.each do |local_name, remote_name|\n            value = item[local_name]\n            case local_name\n            when :instance_groups\n              result.update(amazonize_list_with_key_mapping(\"#{key}.member.#{index+1}.#{remote_name}\", INSTANCE_GROUP_KEY_MAPPING, value))\n            else\n              next if value.nil?\n              result[\"#{key}.member.#{index+1}.#{remote_name}\"] = value\n            end\n          end\n        end\n      end\n      result\n    end\n\n    def amazonize_steps(steps, key = 'Steps.member') # :nodoc:\n      result = {}\n      unless steps.right_blank?\n        steps.each_with_index do |item, index|\n          STEP_CONFIG_KEY_MAPPING.each do |local_name, remote_name|\n            value = item[local_name]\n            case local_name\n            when :args\n              result.update(amazonize_list(\"#{key}.#{index+1}.#{remote_name}.member\", value))\n            when :properties\n              next if value.right_blank?\n              list = value.inject([]) do |l, (k, v)|\n                l << {:key => k, :value => v}\n              end\n              result.update(amazonize_list_with_key_mapping(\"#{key}.#{index+1}.#{remote_name}.member\", KEY_VALUE_KEY_MAPPINGS, list))\n            else\n              next if value.nil?\n              result[\"#{key}.#{index+1}.#{remote_name}\"] = value\n            end\n          end\n        end\n      end\n      result\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Run Job Flow\n    #-----------------------------------------------------------------\n\n    class RunJobFlowParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'JobFlowId' then @result = @text\n        end\n      end\n      def reset\n        @result = nil\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Describe Job Flows\n    #-----------------------------------------------------------------\n\n    class DescribeJobFlowsParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        case full_tag_name\n        when %r{/JobFlows/member$}\n          @item = { :instance_groups => [],\n                    :steps       => [],\n                    :bootstrap_actions => [] }\n        when %r{/BootstrapActionConfig$}\n          @bootstrap_action = {}\n        when %r{/InstanceGroups/member$}\n          @instance_group = {}\n        when %r{/Steps/member$}\n          @step = { :args => [],\n                    :properties => {} }\n        end\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/BootstrapActionConfig} # no trailing $\n          case name\n          when 'Name'\n            @bootstrap_action[:name] = @text\n          when 'ScriptBootstrapAction'\n            @bootstrap_action[:script_bootstrap_action] = @text\n          when 'BootstrapActionConfig'\n            @step[:bootstrap_actions] << @bootstrap_action\n          end\n        when %r{/InstanceGroups/member} # no trailing $\n          case name\n          when 'BidPrice' then @instance_group[:bid_price] = @text\n          when 'CreationDateTime' then @instance_group[:creation_date_time] = @text\n          when 'EndDateTime' then @instance_group[:end_date_time] = @text\n          when 'InstanceGroupId' then @instance_group[:instance_group_id] = @text\n          when 'InstanceRequestCount' then @instance_group[:instance_request_count] = @text.to_i\n          when 'InstanceRole' then @instance_group[:instance_role] = @text\n          when 'InstanceRunningCount' then @instance_group[:instance_running_count] = @text.to_i\n          when 'InstanceType' then @instance_group[:instance_type] = @text\n          when 'LastStateChangeReason' then @instance_group[:last_state_change_reason] = @text\n          when 'Market' then @instance_group[:market] = @text\n          when 'Name' then @instance_group[:name] = @text\n          when 'ReadyDateTime' then @instance_group[:ready_date_time] = @text\n          when 'StartDateTime' then @instance_group[:start_date_time] = @text\n          when 'State' then @instance_group[:state] = @text\n          when 'member' then @item[:instance_groups]        << @instance_group\n          end\n        when %r{/Steps/member/StepConfig/HadoopJarStep/Args/member}\n          @step[:args] << @text\n        when %r{/Steps/member/StepConfig/HadoopJarStep/Properties$}\n            @step[:properties][@key] = @value\n        when %r{/Steps/member/StepConfig/HadoopJarStep/Properties}\n          case name\n          when 'Key'\n            @key = @text\n          when 'Value'\n            @value = @text\n          end\n        when %r{/Steps/member$}\n          @item[:steps] << @step\n        when %r{/Steps/member} # no trailing $\n          case name\n          # ExecutionStatusDetail\n          when 'CreationDateTime' then @step[:creation_date_time] = @text\n          when 'EndDateTime' then @step[:end_date_time] = @text\n          when 'LastStateChangeReason' then @step[:last_state_change_reason] = @text\n          when 'StartDateTime' then @step[:start_date_time] = @text\n          when 'State' then @step[:state] = @text\n          # StepConfig\n          when 'ActionOnFailure' then @step[:action_on_failure] = @text\n          when 'Name' then @step[:name] = @text\n          # HadoopJarStepConfig\n          when 'Jar' then @step[:jar] = @text\n          when 'MainClass' then @step[:main_class] = @text\n          end\n        when %r{/JobFlows/member$}\n          @result << @item\n        else\n          case name\n          when 'AmiVersion' then @item[:ami_version] = @text\n          when 'JobFlowId' then @item[:job_flow_id] = @text\n          when 'LogUri' then @item[:log_uri] = @text\n          when 'Name' then @item[:name] = @text\n          \n          # JobFlowExecutionStatusDetail\n          when 'CreationDateTime' then @item[:creation_date_time] = @text\n          when 'EndDateTime' then @item[:end_date_time] = @text\n          when 'LastStateChangeReason' then @item[:last_state_change_reason] = @text\n          when 'ReadyDateTime' then @item[:ready_date_time] = @text\n          when 'StartDateTime' then @item[:start_date_time] = @text\n          when 'State' then @item[:state] = @text\n          \n          # JobFlowInstancesDetail\n          when 'Ec2KeyName' then @item[:ec2_key_name] = @text\n          when 'HadoopVersion' then @item[:hadoop_version] = @text\n          when 'InstanceCount' then @item[:instance_count] = @text.to_i\n          when 'KeepJobFlowAliveWhenNoSteps' then @item[:keep_job_flow_alive_when_no_steps] = case @text when 'true' then true when 'false' then false else @text end\n          when 'MasterInstanceId' then @item[:master_instance_id] = @text\n          when 'MasterInstanceType' then @item[:master_instance_type] = @text\n          when 'MasterPublicDnsName' then @item[:master_public_dns_name] = @text\n          when 'NormalizedInstanceHours' then @item[:normalized_instance_hours] = @text.to_i\n          # Placement\n          when 'AvailabilityZone' then @item[:availability_zone] = @text\n          when 'SlaveInstanceType' then @item[:slave_instance_type] = @text\n          when 'TerminationProtected' then @item[:termination_protected] = case @text when 'true' then true when 'false' then false else @text end\n          end\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Add Instance Groups\n    #-----------------------------------------------------------------\n\n    class AddInstanceGroupsParser < RightAWSParser #:nodoc:\n      def tagend(name)\n        case name\n        when 'InstanceGroupIds' then @result << @text.strip\n        end\n      end\n      def reset\n        @result = []\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/iam/right_iam_access_keys.rb",
    "content": "module RightAws\n\n  class IamInterface < RightAwsBase\n\n    #-----------------------------------------------------------------\n    #      Access Keys\n    #-----------------------------------------------------------------\n\n    # Returns information about the Access Key IDs associated with the specified User.\n    #\n    # Options: :user_name, :max_items, :marker\n    #\n    #  iam.list_access_keys #=>\n    #    [{:create_date=>\"2007-01-09T06:16:30Z\",\n    #      :status=>\"Active\",\n    #      :access_key_id=>\"00000000000000000000\"}]\n    #\n    def list_access_keys(options={}, &block)\n      incrementally_list_iam_resources('ListAccessKeys', options, &block)\n    end\n\n    # Creates a new AWS Secret Access Key and corresponding AWS Access Key ID for the specified User.\n    #\n    # Options: :user_name\n    #\n    #  iam.create_access_key(:user_name => 'kd1') #=>\n    #    {:access_key_id=>\"AK0000000000000000ZQ\",\n    #     :status=>\"Active\",\n    #     :secret_access_key=>\"QXN0000000000000000000000000000000000Ioj\",\n    #     :create_date=>\"2010-10-29T07:16:32.210Z\",\n    #     :user_name=>\"kd1\"}\n    #\n    def create_access_key(options={})\n      request_hash = {}\n      request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?\n      link = generate_request(\"CreateAccessKey\", request_hash)\n      request_info(link, CreateAccessKeyParser.new(:logger => @logger))\n    end\n\n    # Deletes the access key associated with the specified User.\n    #\n    # Options: :user_name\n    #\n    #  iam.delete_access_key('AK00000000000000006A', :user_name => 'kd1') #=> true\n    #\n    def delete_access_key(access_key_id, options={})\n      request_hash = { 'AccessKeyId' => access_key_id }\n      request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?\n      link = generate_request(\"DeleteAccessKey\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS\n    #-----------------------------------------------------------------\n\n    class ListAccessKeysParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ AccessKeyId CreateDate Status UserName }\n      end\n    end\n\n    class CreateAccessKeyParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ AccessKeyId CreateDate SecretAccessKey Status UserName }\n      end\n    end\n\n  end\n\nend"
  },
  {
    "path": "lib/iam/right_iam_groups.rb",
    "content": "module RightAws\n\n  class IamInterface < RightAwsBase\n\n    #-----------------------------------------------------------------\n    #      Groups\n    #-----------------------------------------------------------------\n\n    # Lists the groups that have the specified path prefix.\n    #\n    # Options: :path_prefix, :max_items, :marker\n    #\n    #  iam.list_groups #=>\n    #    [{:group_id=>\"AGP000000000000000UTY\",\n    #      :arn=>\"arn:aws:iam::640000000037:group/kd_test\",\n    #      :path=>\"/\",\n    #      :group_name=>\"kd_test\"}]\n    #\n    def list_groups(options={}, &block)\n      incrementally_list_iam_resources('ListGroups', options, &block)\n    end\n\n    # Creates a new group.\n    #\n    #  iam.create_group('kd_group') #=>\n    #    {:group_id=>\"AGP000000000000000UTY\",\n    #     :arn=>\"arn:aws:iam::640000000037:group/kd_test\",\n    #     :path=>\"/\",\n    #     :group_name=>\"kd_test\"}\n    #\n    #  iam.create_group('kd_test_3', '/kd/') #=>\n    #    {:group_id=>\"AGP000000000000000G6Q\",\n    #     :arn=>\"arn:aws:iam::640000000037:group/kd/kd_test_3\",\n    #     :path=>\"/kd/\",\n    #     :group_name=>\"kd_test_3\"}\n    #\n    def create_group(group_name, path=nil)\n      request_hash = { 'GroupName' => group_name }\n      request_hash['Path'] = path unless path.right_blank?\n      link = generate_request(\"CreateGroup\", request_hash)\n      request_info(link, CreateGroupParser.new(:logger => @logger))\n    end\n\n    # Updates the name and/or the path of the specified group\n    #\n    # Options: :new_group_name, :new_path\n    #\n    #  iam.update_group('kd_test', :new_group_name => 'kd_test_1', :new_path => '/kd1/') #=> true\n    #\n    def update_group(group_name, options={})\n      request_hash = { 'GroupName' => group_name}\n      request_hash['NewGroupName'] = options[:new_group_name] unless options[:new_group_name].right_blank?\n      request_hash['NewPath']      = options[:new_path]       unless options[:new_path].right_blank?\n      link = generate_request(\"UpdateGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Returns a list of Users that are in the specified group.\n    #\n    # Options: :max_items, :marker\n    #\n    #  iam.get_group('kd_test') #=>\n    #    {:arn=>\"arn:aws:iam::640000000037:group/kd1/kd_test_1\",\n    #      :users=>\n    #        [{:arn=>\"arn:aws:iam::640000000037:user/kd\",\n    #          :path=>\"/\",\n    #          :user_name=>\"kd\",\n    #          :user_id=>\"AID000000000000000WZ2\"}],\n    #      :group_name=>\"kd_test_1\",\n    #      :group_id=>\"AGP000000000000000UTY\",\n    #      :path=>\"/kd1/\"}\n    #\n    def get_group(group_name, options={}, &block)\n      options[:group_name] = group_name\n      incrementally_list_iam_resources('GetGroup', options, :items => :users, :except => [:marker, :is_truncated], &block)\n    end\n\n    # Deletes the specified group. The group must not contain any Users or have any attached policies.\n    #\n    #  iam.delete_group('kd_test_3') #=> true\n    #\n    def delete_group(group_name)\n      request_hash = { 'GroupName' => group_name }\n      link = generate_request(\"DeleteGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Group Policies\n    #-----------------------------------------------------------------\n\n    # Lists the names of the policies associated with the specified group.\n    #\n    # Options: :max_items, :marker\n    #\n    #  iam.list_group_policies('kd_test') #=> [\"kd_policy_1\"]\n    #\n    def list_group_policies(group_name, options={}, &block)\n      options[:group_name] = group_name\n      incrementally_list_iam_resources('ListGroupPolicies', options, :parser => BasicIamListParser, &block)\n    end\n\n    # Adds (or updates) a policy document associated with the specified group.\n    #\n    #  iam.put_group_policy('kd_test', 'kd_policy_1', %Q({\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}]})) #=> true\n    #\n    def put_group_policy(group_name, policy_name, policy_document)\n      request_hash = { 'GroupName'      => group_name,\n                       'PolicyDocument' => policy_document,\n                       'PolicyName'     => policy_name }\n      link = generate_request_impl(:post, \"PutGroupPolicy\", request_hash)\n      result = request_info(link, RightHttp2xxParser.new(:logger => @logger))\n      result[:policy_document] = URI::decode(result[:policy_document])\n      result\n    end\n\n    # Retrieves the specified policy document for the specified group.\n    #\n    #  iam.get_group_policy('kd_test', 'kd_policy_1') #=>\n    #    {:policy_name=>\"kd_policy_1\",\n    #     :policy_document=>\"{\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Action\\\":\\\"*\\\",\\\"Resource\\\":\\\"*\\\"}]}\",\n    #     :group_name=>\"kd_test\"}\n    #\n    def get_group_policy(group_name, policy_name)\n      request_hash = { 'GroupName'  => group_name,\n                       'PolicyName' => policy_name }\n      link = generate_request(\"GetGroupPolicy\", request_hash)\n      request_info(link, GetGroupPolicyParser.new(:logger => @logger))\n    end\n\n    # Deletes the specified policy that is associated with the specified group\n    #\n    #  iam.delete_group_policy('kd_test', 'kd_policy_1') #=> true\n    #\n    def delete_group_policy(group_name, policy_name)\n      request_hash = { 'GroupName'  => group_name,\n                       'PolicyName' => policy_name }\n      link = generate_request(\"DeleteGroupPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class ListGroupsParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn GroupId GroupName Path }\n      end\n    end\n\n    class CreateGroupParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn GroupId GroupName Path }\n      end\n    end\n\n    class GetGroupParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @item = {} if name == 'member'\n      end\n      def tagend(name)\n        case name\n        when 'Marker'      then @result[:marker]       = @text\n        when 'IsTruncated' then @result[:is_truncated] = @text == 'true'\n\n        when 'GroupName' then @result[:group_name] = @text\n        when 'GroupId'   then @result[:group_id]   = @text\n        when 'UserName'  then @item[:user_name]    = @text\n        when 'UserId'    then @item[:user_id]      = @text\n        when 'member'    then @result[:users]     << @item\n        else\n          case full_tag_name\n          when %r{/Group/Path$}  then @result[:path] = @text\n          when %r{/Group/Arn$}   then @result[:arn]  = @text\n          when %r{/member/Path$} then @item[:path]   = @text\n          when %r{/member/Arn$}  then @item[:arn]    = @text\n          end\n        end\n      end\n      def reset\n        @result = { :users => [] }\n      end\n    end\n\n    class GetGroupPolicyParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ GroupName PolicyDocument PolicyName }\n      end\n    end\n\n  end\n  \nend\n\n"
  },
  {
    "path": "lib/iam/right_iam_interface.rb",
    "content": "#\n# Copyright (c) 2007-2010 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAWS::Iam -- RightScale AWS Identity and Access Management (IAM) interface\n  #\n  # The RightAws::Iam class provides a complete interface to Amazon's Identity and\n  # Access Management service.\n  #\n  # For explanations of the semantics of each call, please refer to Amazon's documentation at\n  # http://aws.amazon.com/documentation/iam/\n  #\n  # Examples:\n  #\n  # Create an EC2 interface handle:\n  #\n  #   iam = RightAws::IamInterface.new(aws_access_key_id, aws_secret_access_key)\n  #   iam.list_access_keys\n  #   iam.list_users\n  #   iam.list_groups\n  #\n  class IamInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    API_VERSION       = \"2010-05-08\"\n    DEFAULT_HOST      = \"iam.amazonaws.com\"\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to an IAM account. All handles share the same per process or per thread\n    # HTTP connection to Amazon IAM. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). \n    # * <tt>:server</tt>: IAM service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: IAM service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    # * <tt>:cache</tt>: true/false(default): caching works for: describe_load_balancers\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'IAM',\n             :default_host        => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['IAM_URL'] ? URI.parse(ENV['IAM_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['IAM_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n    # Sends request to Amazon and parses the response\n    # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:iam_connection, @@bench, request, parser)\n    end\n\n    # Options: :parser, :except, :items\n    #\n    def incrementally_list_iam_resources(api_function, params={}, options={},  &block) #:nodoc:\n      items        = options[:items] || :items\n      result       = { items => [] }\n      parser       = options[:parser] || \"RightAws::IamInterface::#{api_function}Parser\".right_constantize\n      request_hash = {}\n      params.each { |key,value| request_hash[key.to_s.right_camelize] = value unless value.right_blank? }\n      incrementally_list_items(api_function, parser, request_hash) do |response|\n        if result[items].right_blank?\n          result = response\n        else\n          result[items] += response[items]\n        end\n        block ? block.call(response) : true\n      end\n      if options[:except]\n        Array(options[:except]).each{ |key| result.delete(key)}\n        result\n      else\n        result[items]\n      end\n    end\n    \n    #-----------------------------------------------------------------\n    #      Server Certificates\n    #-----------------------------------------------------------------\n\n    # Lists the server certificates that have the specified path prefix. If none exist, the action returns an empty list.\n    #\n    # Options: :path_prefix, :max_items, :marker\n    #\n    #  iam.list_server_certificates #=>\n    #    {:server_certificate_id=>\"ASCDJN5K5HRGS1N2UJWWU\",\n    #     :server_certificate_name=>\"KdCert1\",\n    #     :upload_date=>\"2010-12-09T13:21:07.226Z\",\n    #     :path=>\"/kdcert/\",\n    #     :arn=>\"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1\"}\n    #\n    def list_server_certificates(options={}, &block)\n      incrementally_list_iam_resources('ListServerCertificates', options, &block)\n    end\n\n    # Uploads a server certificate entity for the AWS Account. The server certificate\n    # entity includes a public key certificate, a private key, and an optional certificate\n    # chain, which should all be PEM-encoded.\n    #\n    # Options: :certificate_chain, :path\n    #\n    #  certificate_body =<<-EOB\n    #  -----BEGIN CERTIFICATE-----\n    #  MIICdzCCAeCgAwIBAgIGANc+Ha2wMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYT\n    #  AlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMQwwCgYDVQQLEwNBV1MxITAfBgNVBAMT\n    #  GEFXUyBMaW1pdGVkLUFzc3VyYW5jZSBDQTAeFw0wOTAyMDQxNzE5MjdaFw0xMDAy\n    #  AEaHzTpmEXAMPLE=\n    #  EOB\n    #\n    #  private_key =<<EOK\n    #  -----BEGIN DSA PRIVATE KEY-----\n    #  MIIBugIBTTKBgQD33xToSXPJ6hr37L3+KNi3/7DgywlBcvlFPPSHIw3ORuO/22mT\n    #  8Cy5fT89WwNvZ3BPKWU6OZ38TQv3eWjNc/3U3+oqVNG2poX5nCPOtO1b96HYX2mR\n    #  62TITdw53KWJEXAMPLE=\n    #  EOK\n    #\n    #  iam.upload_server_certificate('KdCert1', certificate_body, private_key, :path=>'/kdcert/') #=>\n    #    {:server_certificate_id=>\"ASCDJN5K5HRGS1N2UJWWU\",\n    #     :server_certificate_name=>\"KdCert1\",\n    #     :upload_date=>\"2010-12-09T13:21:07.226Z\",\n    #     :path=>\"/kdcert/\",\n    #     :arn=>\"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1\"}\n    #\n    def upload_server_certificate(server_certificate_name, certificate_body, private_key, options={})\n      request_hash = { 'CertificateBody'       => certificate_body,\n                       'PrivateKey'            => private_key,\n                       'ServerCertificateName' => server_certificate_name }\n      request_hash['CertificateChain'] = options[:certificate_chain] unless options[:certificate_chain].right_blank?\n      request_hash['Path']             = options[:path]              unless options[:path].right_blank?\n      link = generate_request_impl(:post, \"UploadServerCertificate\", request_hash)\n      request_info(link, GetServerCertificateParser.new(:logger => @logger))\n    end\n    \n    # Updates the name and/or the path of the specified server certificate.\n    #\n    # Options: :new_server_certificate_name, :new_path\n    #\n    #  iam.update_server_certificate('ProdServerCert', :new_server_certificate_name => 'OldServerCert') #=> true\n    #\n    def update_server_certificate(server_certificate_name, options={})\n      request_hash = { 'ServerCertificateName' => server_certificate_name}\n      request_hash['NewServerCertificateName'] = options[:new_server_certificate_name] unless options[:new_server_certificate_name].right_blank?\n      request_hash['NewPath']                  = options[:new_path]                    unless options[:new_path].right_blank?\n      link = generate_request(\"UpdateServerCertificate\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Retrieves information about the specified server certificate.\n    #\n    #  iam.get_server_certificate('KdCert1')\n    #    {:certificate_body=>\n    #      \"-----BEGIN CERTIFICATE-----\\nMIICATC...TiU5TibMpD1g==\\n-----END CERTIFICATE-----\",\n    #     :server_certificate_id=>\"ASCDJN5K5HRGS1N2UJWWU\",\n    #     :server_certificate_name=>\"KdCert1\",\n    #     :upload_date=>\"2010-12-09T13:21:07Z\",\n    #     :path=>\"/kdcert/\",\n    #     :certificate_chain=>\"\",\n    #     :arn=>\"arn:aws:iam::600000000007:server-certificate/kdcert/KdCert1\"}\n    #\n    def get_server_certificate(server_certificate_name)\n      request_hash = { 'ServerCertificateName' => server_certificate_name}\n      link = generate_request(\"GetServerCertificate\", request_hash)\n      request_info(link, GetServerCertificateParser.new(:logger => @logger))\n    end\n\n    # Deletes the specified server certificate\n    #\n    #  iam.delete_server_certificate('ProdServerCert') #=> true\n    #\n    def delete_server_certificate(server_certificate_name)\n      request_hash = { 'ServerCertificateName' => server_certificate_name }\n      link = generate_request(\"DeleteServerCertificate\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Signing Certificates\n    #-----------------------------------------------------------------\n\n    # Returns information about the signing certificates associated with the specified User.\n    # \n    # Options: :user_name, :max_items, :marker\n    #\n    # iam.list_signing_certificates #=>\n    #    [{:upload_date      => \"2007-08-11T06:48:35Z\",\n    #      :status           => \"Active\",\n    #      :certificate_id   => \"00000000000000000000000000000000\",\n    #      :certificate_body => \"-----BEGIN CERTIFICATE-----\\nMIICd...PPHQ=\\n-----END CERTIFICATE-----\\n\"}]\n    #\n    def list_signing_certificates(options={}, &block)\n      incrementally_list_iam_resources('ListSigningCertificates', options, &block)\n    end\n\n    # Uploads an X.509 signing certificate and associates it with the specified User.\n    #\n    # Options: :user_name\n    #\n    #  certificate_body =<<-EOB\n    #  -----BEGIN CERTIFICATE-----\n    #  MIICdzCCAeCgAwIBAgIGANc+Ha2wMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNVBAYT\n    #  AlVTMRMwEQYDVQQKEwpBbWF6b24uY29tMQwwCgYDVQQLEwNBV1MxITAfBgNVBAMT\n    #  GEFXUyBMaW1pdGVkLUFzc3VyYW5jZSBDQTAeFw0wOTAyMDQxNzE5MjdaFw0xMDAy\n    #  AEaHzTpmEXAMPLE=\n    #  EOB\n    #\n    #  iam.upload_signing_certificate(certificate_body, :user_name => 'kd1') #=>\n    #    {:user_name        => \"kd1\",\n    #     :certificate_id   => \"OBG00000000000000000000000000DHY\",\n    #     :status           => \"Active\",\n    #     :certificate_body => \"-----BEGIN CERTIFICATE-----\\nMII...5GS\\n-----END CERTIFICATE-----\\n\",\n    #     :upload_date      => \"2010-10-29T10:02:05.929Z\"}\n    #\n    def upload_signing_certificate(certificate_body, options={})\n      request_hash = { 'CertificateBody' => certificate_body }\n      request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?\n      link = generate_request_impl(:post, \"UploadSigningCertificate\", request_hash)\n      request_info(link, GetSigningCertificateParser.new(:logger => @logger))\n    end\n\n    # Deletes the specified signing certificate associated with the specified User.\n    #\n    # Options: :user_name\n    #\n    #  pp iam.delete_signing_certificate('OB0000000000000000000000000000HY', :user_name => 'kd1')\n    #\n    def delete_signing_certificate(certificate_id, options={})\n      request_hash = { 'CertificateId' => certificate_id }\n      request_hash['UserName'] = options[:user_name] unless options[:user_name].right_blank?\n      link = generate_request(\"DeleteSigningCertificate\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class BasicIamParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @result ||= {}\n      end\n      def tagend(name)\n        if Array(@expected_tags).include?(name)\n          @result[name.right_underscore.to_sym] = @text\n        end\n      end\n    end\n\n    class BasicIamListParser < RightAWSParser #:nodoc:\n      def tagstart(name, attributes)\n        @result ||= { :items => [] }\n        @item     = {} if name == (@items_splitter || 'member')\n      end\n      def tagend(name)\n        case name\n        when 'Marker'      then @result[:marker]       = @text\n        when 'IsTruncated' then @result[:is_truncated] = @text == 'true'\n        when (@items_splitter || 'member')\n          @result[:items] << (@item.right_blank? ? @text : @item)\n        else\n          if Array(@expected_tags).include?(name)\n            @item[name.right_underscore.to_sym] = @text\n          end\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      Server Certificates\n    #-----------------------------------------------------------------\n\n    class GetServerCertificateParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn Path ServerCertificateId ServerCertificateName UploadDate CertificateBody CertificateChain }\n      end\n    end\n\n    class ListServerCertificatesParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn Path ServerCertificateId ServerCertificateName UploadDate }\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      Signing Certificates\n    #-----------------------------------------------------------------\n\n    class ListSigningCertificatesParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ CertificateBody CertificateId Status UploadDate UserName }\n      end\n    end\n\n    class GetSigningCertificateParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ CertificateBody CertificateId Status UploadDate UserName }\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/iam/right_iam_mfa_devices.rb",
    "content": "module RightAws\n\n  class IamInterface < RightAwsBase\n\n    #-----------------------------------------------------------------\n    #      MFADevices\n    #-----------------------------------------------------------------\n\n    # Lists the MFA devices associated with the specified User name.\n    #\n    # Options: :user_name, :max_items, :marker\n    #\n    def list_mfa_devices(options={}, &block)\n      incrementally_list_iam_resources('ListMFADevices', options, &block)\n    end\n\n    # Enables the specified MFA device and associates it with the specified User name.\n    # Once enabled, the MFA device is required for every subsequent login by the User name associated with the device.\n    #\n    #  iam.enable_mfa_device('kd1', 'x12345', '12345', '67890') #=> true\n    #\n    def enable_mfa_device(user_name, serial_number, auth_code1, auth_code2)\n      request_hash = { 'UserName'            => user_name,\n                       'SerialNumber'        => serial_number,\n                       'AuthenticationCode1' => auth_code1,\n                       'AuthenticationCode2' => auth_code2 }\n      link = generate_request(\"EnableMFADevice\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Synchronizes the specified MFA device with AWS servers.\n    #\n    #  iam.resync_mfa_device('kd1', 'x12345', '12345', '67890') #=> true\n    #\n    def resync_mfa_device(user_name, serial_number, auth_code1, auth_code2)\n      request_hash = { 'UserName'            => user_name,\n                       'SerialNumber'        => serial_number,\n                       'AuthenticationCode1' => auth_code1,\n                       'AuthenticationCode2' => auth_code2 }\n      link = generate_request(\"ResyncMFADevice\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Deactivates the specified MFA device and removes it from association with the User name for which it was originally enabled.\n    #\n    #  deactivate_mfa_device('kd1', 'dev1234567890') #=> true\n    #\n    def deactivate_mfa_device(user_name, serial_number)\n      request_hash = { 'UserName'     => user_name,\n                       'SerialNumber' => serial_number }\n      link = generate_request(\"DeactivateMFADevice\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS\n    #-----------------------------------------------------------------\n\n    class ListMFADevicesParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ SerialNumber UserName }\n      end\n    end\n\n  end\n\nend"
  },
  {
    "path": "lib/iam/right_iam_users.rb",
    "content": "module RightAws\n\n  class IamInterface < RightAwsBase\n\n    #-----------------------------------------------------------------\n    #      Users\n    #-----------------------------------------------------------------\n\n    # Lists the Users that have the specified path prefix.\n    #\n    # Options: :path_prefix, :max_items, :marker\n    #\n    #  iam.list_users #=>\n    #    [{:user_name=>\"kd\",\n    #      :user_id=>\"AI000000000000000006A\",\n    #      :arn=>\"arn:aws:iam::640000000037:user/kd\",\n    #      :path=>\"/\"}]\n    #\n    def list_users(options={}, &block)\n      incrementally_list_iam_resources('ListUsers', options, &block)\n    end\n\n    # Creates a new User for your AWS Account.\n    #\n    # Options: :path\n    #\n    #  iam.create_user('kd') #=>\n    #    {:user_name=>\"kd\",\n    #     :user_id=>\"AI000000000000000006A\",\n    #     :arn=>\"arn:aws:iam::640000000037:user/kd\",\n    #     :path=>\"/\"}\n    #\n    def create_user(user_name, options={})\n      request_hash = { 'UserName' => user_name }\n      request_hash['Path'] = options[:path] unless options[:path]\n      link = generate_request(\"CreateUser\", request_hash)\n      request_info(link, GetUserParser.new(:logger => @logger))\n    end\n\n    # Updates the name and/or the path of the specified User.\n    #\n    #  iam.update_user('kd1', :new_user_name => 'kd1', :new_path => '/kd1/') #=> true\n    #\n    def update_user(user_name, options={})\n      request_hash = { 'UserName' => user_name}\n      request_hash['NewUserName'] = options[:new_user_name] unless options[:new_user_name].right_blank?\n      request_hash['NewPath']     = options[:new_path]       unless options[:new_path].right_blank?\n      link = generate_request(\"UpdateUser\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Retrieves information about the specified User, including the User's path, GUID, and ARN.\n    #\n    #  iam.get_user('kd') #=>\n    #    {:user_name=>\"kd\",\n    #     :user_id=>\"AI000000000000000006A\",\n    #     :arn=>\"arn:aws:iam::640000000037:user/kd\",\n    #     :path=>\"/\"}\n    #\n    def get_user(user_name)\n      request_hash = { 'UserName' => user_name }\n      link = generate_request(\"GetUser\", request_hash)\n      request_info(link, GetUserParser.new(:logger => @logger))\n    end\n\n    # Deletes the specified User. The User must not belong to any groups, have any keys or signing certificates, or have any attached policies.\n    #\n    #  iam.delete_user('kd') #=> true\n    #\n    def delete_user(user_name)\n      request_hash = { 'UserName' => user_name }\n      link = generate_request(\"DeleteUser\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      User Policies\n    #-----------------------------------------------------------------\n\n    # Lists the names of the policies associated with the specified User.\n    #\n    # Options: :max_items, :marker\n    #\n    #  iam.list_user_policies('kd') #=> [\"kd_user_policy_1\"]\n    #\n    def list_user_policies(user_name, options={}, &block)\n      options[:user_name] = user_name\n      incrementally_list_iam_resources('ListUserPolicies', options, :parser => BasicIamListParser, &block)\n    end\n\n    # Adds (or updates) a policy document associated with the specified User\n    #\n    #  iam.put_user_policy('kd', 'kd_user_policy_1', %Q({\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}]})) #=> true\n    #\n    def put_user_policy(user_name, policy_name, policy_document)\n      request_hash = { 'UserName'       => user_name,\n                       'PolicyDocument' => policy_document,\n                       'PolicyName'     => policy_name }\n      link = generate_request_impl(:post, \"PutUserPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Retrieves the specified policy document for the specified User.\n    #\n    #  iam.get_user_policy('kd','kd_user_policy_1') #=>\n    #    {:user_name=>\"kd\",\n    #     :policy_name=>\"kd_user_policy_1\",\n    #     :policy_document=>\"{\\\"Statement\\\":[{\\\"Effect\\\":\\\"Allow\\\",\\\"Action\\\":\\\"*\\\",\\\"Resource\\\":\\\"*\\\"}]}\"}\n    #\n    def get_user_policy(user_name, policy_name)\n      request_hash = { 'UserName'   => user_name,\n                       'PolicyName' => policy_name }\n      link = generate_request(\"GetUserPolicy\", request_hash)\n      result = request_info(link, GetUserPolicyParser.new(:logger => @logger))\n      result[:policy_document] = URI::decode(result[:policy_document])\n      result\n    end\n\n    # Deletes the specified policy associated with the specified User.\n    #\n    #  iam.delete_user_policy('kd','kd_user_policy_1') #=> true\n    #\n    def delete_user_policy(user_name, policy_name)\n      request_hash = { 'UserName'   => user_name,\n                       'PolicyName' => policy_name }\n      link = generate_request(\"DeleteUserPolicy\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      User Groups\n    #-----------------------------------------------------------------\n\n    # Lists the names of the policies associated with the specified group. If there are none,\n    # the action returns an empty list.\n    #\n    # Options: :max_items, :marker\n    #\n    #  iam.list_groups_for_user('kd') #=>\n    #    [{:group_name=>\"kd_test_1\",\n    #      :group_id=>\"AGP000000000000000UTY\",\n    #      :arn=>\"arn:aws:iam::640000000037:group/kd1/kd_test_1\",\n    #      :path=>\"/kd1/\"}]\n    #\n    def list_groups_for_user(user_name, options={}, &block)\n      options[:user_name] = user_name\n      incrementally_list_iam_resources('ListGroupsForUser', options, :parser => ListGroupsParser, &block)\n    end\n\n    # Adds the specified User to the specified group.\n    #\n    #  iam.add_user_to_group('kd', 'kd_test_1') #=> true\n    #\n    def add_user_to_group(user_name, group_name)\n      request_hash = { 'UserName'  => user_name,\n                       'GroupName' => group_name }\n      link = generate_request(\"AddUserToGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Removes the specified User from the specified group.\n    #\n    #  iam.remove_user_from_group('kd', 'kd_test_1') #=> true\n    #\n    def remove_user_from_group(user_name, group_name)\n      request_hash = { 'UserName'  => user_name,\n                       'GroupName' => group_name }\n      link = generate_request(\"RemoveUserFromGroup\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      User Login Profiles\n    #-----------------------------------------------------------------\n\n    # Creates a login profile for the specified User, giving the User the ability to access\n    # AWS services such as the AWS Management Console.\n    #\n    #  iam.create_login_profile('kd','q1w2e3r4t5') #=> { :user_name => 'kd' }\n    #\n    def create_login_profile(user_name, password)\n      request_hash = { 'UserName' => user_name,\n                       'Password' => password}\n      link = generate_request(\"CreateLoginProfile\", request_hash)\n      request_info(link, GetLoginProfileParser.new(:logger => @logger))\n    end\n\n    # Updates the login profile for the specified User. Use this API to change the User's password.\n    #\n    #  update_login_profile('kd', '00000000') #=> true\n    #\n    def update_login_profile(user_name, options={})\n      request_hash = { 'UserName' => user_name}\n      request_hash['Password'] = options[:password] unless options[:passwrod].right_blank?\n      link = generate_request(\"UpdateLoginProfile\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Retrieves the login profile for the specified User\n    #\n    #  iam.create_login_profile('kd','q1w2e3r4t5') #=> { :user_name => 'kd' }\n    #\n    def get_login_profile(user_name)\n      request_hash = { 'UserName' => user_name }\n      link = generate_request(\"GetLoginProfile\", request_hash)\n      request_info(link, GetLoginProfileParser.new(:logger => @logger))\n    end\n\n    # Deletes the login profile for the specified User, which terminates the User's ability to access\n    # AWS services through the IAM login page.\n    #\n    #  iam.delete_login_profile('kd') #=> true\n    #\n    def delete_login_profile(user_name)\n      request_hash = { 'UserName' => user_name }\n      link = generate_request(\"DeleteLoginProfile\", request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS\n    #-----------------------------------------------------------------\n\n    class ListUsersParser < BasicIamListParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn Path UserId UserName }\n      end\n    end\n\n    class GetUserParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ Arn Path UserId UserName }\n      end\n    end\n\n    class GetUserPolicyParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ PolicyDocument PolicyName UserName }\n      end\n    end\n\n    class GetLoginProfileParser < BasicIamParser #:nodoc:\n      def reset\n        @expected_tags = %w{ UserName }\n      end\n    end\n\n  end\n\nend\n\n"
  },
  {
    "path": "lib/rds/right_rds_interface.rb",
    "content": "#\n# Copyright (c) 2009 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n    class RdsInterface < RightAwsBase\n    \n    include RightAwsBaseInterface\n\n    API_VERSION      = \"2012-09-17\"\n    DEFAULT_HOST     = 'rds.amazonaws.com'\n    DEFAULT_PORT     = 443\n    DEFAULT_PROTOCOL = 'https'\n    DEFAULT_PATH     = '/'\n\n    DEFAULT_INSTANCE_CLASS   =  'db.m1.small'\n    INSTANCE_CLASSES         = [ 'db.t1.micro',\n                                 'db.m1.small',\n                                 'db.m1.medium',\n                                 'db.m1.large',\n                                 'db.m1.xlarge',\n                                 'db.m2.xlarge',\n                                 'db.m2.2xlarge',\n                                 'db.m2.4xlarge']\n    LICENSE_MODELS           = ['bring-your-own-license', 'license-included', 'general-public-license']\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to a RDS account. All handles share the same per process or per thread\n    # HTTP connection to RDS. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://rds.amazonaws.com'\n    # * <tt>:server</tt>: RDS service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: RDS service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    #\n    #  rds = RightAws::RdsInterface.new('xxxxxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',\n    #    {:logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::RdsInterface::0xb7b3c30c>\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'RDS',\n             :default_host        => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['RDS_API_VERSION'] || API_VERSION },\n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n\n    #-----------------------------------------------------------------\n    #      Requests\n    #-----------------------------------------------------------------\n\n    # Generates request hash for REST API.\n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n      \n      # Sends request to Amazon and parses the response.\n      # Raises AwsError if any banana happened.\n    def request_info(request, parser, &block) # :nodoc:\n      request_info_impl(:rds_connection, @@bench, request, parser, &block)\n    end\n\n    # Incrementally lists something.\n    def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc:\n      params = params.dup\n      params['MaxRecords'] = params.delete(:max_records) if params[:max_records]\n      params['Marker']     = params.delete(:marker)      if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request(action, params)\n        last_response = request_info( link,  parser_class.new(:logger => @logger))\n        params['Marker'] = last_response[:marker]\n        break unless block && block.call(last_response) && !last_response[:marker].right_blank?\n      end\n      last_response\n    end\n\n    #-----------------------------------------------------------------\n    #      API Calls:\n    #-----------------------------------------------------------------\n\n    # --------------------------------------------\n    #  DB Instances\n    # --------------------------------------------\n\n    # List DB instances.\n    #\n    # Optional params: +:aws_id+, +:max_records+, +:marker+\n    #\n    #  # Get a list of DB instances. The response is an +Array+ of instances.\n    #  rds.describe_db_instances #=>\n    #    [{:instance_class=>\"db.m1.small\",\n    #      :status=>\"creating\",\n    #      :backup_retention_period=>1,\n    #      :read_replica_db_instance_identifiers=>[\"kd-delete-me-01-replica-01\"],\n    #      :master_username=>\"username\",\n    #      :preferred_maintenance_window=>\"sun:05:00-sun:09:00\",\n    #      :db_parameter_group=>{:status=>\"in-sync\", :name=>\"default.mysql5.1\"},\n    #      :multi_az=>true,\n    #      :engine=>\"mysql\",\n    #      :auto_minor_version_upgrade=>false,\n    #      :allocated_storage=>25,\n    #      :availability_zone=>\"us-east-1d\",\n    #      :aws_id=>\"kd-delete-me-01\",\n    #      :preferred_backup_window=>\"03:00-05:00\",\n    #      :engine_version=>\"5.1.50\",\n    #      :pending_modified_values=>{:master_user_password=>\"****\"},\n    #      :db_security_groups=>[{:status=>\"active\", :name=>\"default\"}]}]\n    #\n    #  # Retrieve a custom DB instance.\n    #  # The response is an +Array+ with a single instance record.\n    #  rds.describe_db_instances(\"kd-test-n3\")\n    #\n    #  # Incrementally a list DB instances. Every response part is a +Hash+.\n    #  rds.describe_db_instances(:max_records => 30) do |x|\n    #    puts x.inspect #=>\n    #      {:db_instances=>\n    #        [{:instance_class=>\"db.m1.small\",\n    #          :status=>\"creating\",\n    #          :backup_retention_period=>1,\n    #          :read_replica_db_instance_identifiers=>[\"kd-delete-me-01-replica-01\"],\n    #          :master_username=>\"username\",\n    #          :preferred_maintenance_window=>\"sun:05:00-sun:09:00\",\n    #          :db_parameter_group=>{:status=>\"in-sync\", :name=>\"default.mysql5.1\"},\n    #          :multi_az=>true,\n    #          :engine=>\"mysql\",\n    #          :auto_minor_version_upgrade=>false,\n    #          :allocated_storage=>25,\n    #          :availability_zone=>\"us-east-1d\",\n    #          :aws_id=>\"kd-delete-me-01\",\n    #          :preferred_backup_window=>\"03:00-05:00\",\n    #          :engine_version=>\"5.1.50\",\n    #          :pending_modified_values=>{:master_user_password=>\"****\"},\n    #          :db_security_groups=>[{:status=>\"active\", :name=>\"default\"}]}]}\n    #    true\n    #  end\n    #\n    def describe_db_instances(*params, &block)\n      item, params = AwsUtils::split_items_and_params(params)\n      params = params.dup\n      params['DBInstanceIdentifier'] = item.first unless item.right_blank?\n      result = []\n      incrementally_list_items('DescribeDBInstances', DescribeDbInstancesParser, params) do |response|\n        result += response[:db_instances]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Create a new RDS instance of the type and size specified by you. The default storage engine for RDS Instances is InnoDB.\n    #\n    # Mandatory arguments: +aws_id+, +master_username+, +master_user_password+\n    # Optional params: +:allocated_storage+ (25 by def), +:instance_class+, +:engine+ ('MySQL' by def),\n    # +:endpoint_port+, +:db_name+, +:db_security_groups+, +:db_parameter_group+,  +:availability_zone+, +:preferred_maintenance_window+\n    # +:backup_retention_period+, +:preferred_backup_window+, +:multi_az+, +:engine_version+, +:auto_minor_version_upgrade+,\n    # +:license_model+, +:iops+, +:db_subnet_group_name+, +:character_set_name+, +:option_group_name+\n    #\n    #   rds.create_db_instance('kd-delete-me-01', 'username', 'password',\n    #                           :instance_class    => 'db.m1.small',\n    #                           :multi_az          => true,\n    #                           :auto_minor_version_upgrade => false ) #=>\n    #    {:instance_class=>\"db.m1.small\",\n    #      :multi_az=>true,\n    #      :status=>\"creating\",\n    #      :backup_retention_period=>1,\n    #      :read_replica_db_instance_identifiers=>[],\n    #      :master_username=>\"username\",\n    #      :preferred_maintenance_window=>\"sun:05:00-sun:09:00\",\n    #      :auto_minor_version_upgrade=>false,\n    #      :db_parameter_group=>{:status=>\"in-sync\", :name=>\"default.mysql5.1\"},\n    #      :engine=>\"mysql\",\n    #      :allocated_storage=>25,\n    #      :aws_id=>\"kd-delete-me-01\",\n    #      :preferred_backup_window=>\"03:00-05:00\",\n    #      :engine_version=>\"5.1.50\",\n    #      :pending_modified_values=>{:master_user_password=>\"****\"},\n    #      :db_security_groups=>[{:status=>\"active\", :name=>\"default\"}]}\n    #\n    def create_db_instance(aws_id, master_username, master_user_password, params={})\n      request_hash = {}\n      # Mandatory\n      request_hash['DBInstanceIdentifier'] = aws_id\n      request_hash['MasterUsername']       = master_username\n      request_hash['MasterUserPassword']   = master_user_password\n      # Mandatory with default values\n      request_hash['DBInstanceClass']            = params[:instance_class].right_blank?    ? DEFAULT_INSTANCE_CLASS : params[:instance_class].to_s\n      request_hash['AllocatedStorage']           = params[:allocated_storage].right_blank? ? 25                     : params[:allocated_storage]\n      request_hash['Engine']                     = params[:engine].right_blank?            ? 'mysql'                : params[:engine]\n      # Optional\n      request_hash['Port']                       = params[:endpoint_port]                   unless params[:endpoint_port].right_blank?\n      request_hash['DBName']                     = params[:db_name]                         unless params[:db_name].right_blank?\n      request_hash['AvailabilityZone']           = params[:availability_zone]               unless params[:availability_zone].right_blank?\n      request_hash['MultiAZ']                    = params[:multi_az].to_s                   unless params[:multi_az].nil?\n      request_hash['PreferredMaintenanceWindow'] = params[:preferred_maintenance_window]    unless params[:preferred_maintenance_window].right_blank?\n      request_hash['BackupRetentionPeriod']      = params[:backup_retention_period]         unless params[:backup_retention_period].right_blank?\n      request_hash['PreferredBackupWindow']      = params[:preferred_backup_window]         unless params[:preferred_backup_window].right_blank?\n      request_hash['DBParameterGroupName']       = params[:db_parameter_group]              unless params[:db_parameter_group].right_blank?\n      request_hash['EngineVersion']              = params[:engine_version]                  unless params[:engine_version].right_blank?\n      request_hash['AutoMinorVersionUpgrade']    = params[:auto_minor_version_upgrade].to_s unless params[:auto_minor_version_upgrade].nil?\n      request_hash['LicenseModel']               = params[:license_model]                   unless params[:license_model].right_blank?\n      request_hash['CharacterSetName']           = params[:character_set_name]              unless params[:character_set_name].right_blank?\n      request_hash['DBSubnetGroupName']          = params[:db_subnet_group_name]            unless params[:db_subnet_group_name].right_blank?\n      request_hash['Iops']                       = params[:iops]                            unless params[:iops].right_blank?\n      request_hash['OptionGroupName']            = params[:option_group_name]               unless params[:option_group_name].right_blank?\n      request_hash.merge!(amazonize_list('DBSecurityGroups.member',  params[:db_security_groups]))\n      link = generate_request('CreateDBInstance', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # Modify a DB instance.\n    # \n    # Mandatory arguments: +aws_id+. \n    # Optional params: +:master_user_password+, +:instance_class+, +:db_security_groups+,\n    # +:db_parameter_group+, +:preferred_maintenance_window+, +:allocated_storage+, +:apply_immediately+,\n    # +:backup_retention_period+, +:preferred_backup_window+, +:multi_az+, +:engine_version+,\n    # +:auto_minor_version_upgrade+, +:allow_major_version_upgrade+, +:iops+, +::option_group_name+\n    #\n    #    rds.modify_db_instance('kd-delete-me-01',\n    #                           :master_user_password => 'newpassword',\n    #                           :instance_class => 'db.m1.large',\n    #                           :multi_az => false,\n    #                           :allocated_storage => 30,\n    #                           :allow_major_version_upgrade => true,\n    #                           :auto_minor_version_upgrade => true,\n    #                           :preferred_maintenance_window => 'sun:06:00-sun:10:00',\n    #                           :preferred_backup_window => '02:00-04:00',\n    #                           :apply_immediately => true,\n    #                           :backup_retention_period => 2) #=>\n    #        {:engine_version=>\"5.1.50\",\n    #         :aws_id=>\"kd-delete-me-01\",\n    #         :multi_az=>true,\n    #         :status=>\"available\",\n    #         :read_replica_db_instance_identifiers=>[],\n    #         :availability_zone=>\"us-east-1d\",\n    #         :auto_minor_version_upgrade=>true,\n    #         :master_username=>\"username\",\n    #         :preferred_maintenance_window=>\"sun:06:00-sun:10:00\",\n    #         :db_parameter_group=>{:status=>\"in-sync\", :name=>\"default.mysql5.1\"},\n    #         :create_time=>\"2010-11-17T10:21:59.720Z\",\n    #         :preferred_backup_window=>\"02:00-04:00\",\n    #         :engine=>\"mysql\",\n    #         :db_security_groups=>[{:status=>\"active\", :name=>\"default\"}],\n    #         :endpoint_address=>\"kd-delete-me-01.chxspydgchoo.us-east-1.rds.amazonaws.com\",\n    #         :instance_class=>\"db.m1.small\",\n    #         :latest_restorable_time=>\"2010-11-17T10:27:17.089Z\",\n    #         :backup_retention_period=>2,\n    #         :pending_modified_values=>\n    #          {:multi_az=>false, :master_user_password=>\"****\", :allocated_storage=>30, :instance_class=>\"db.m1.large\"},\n    #         :allocated_storage=>25}\n    #\n    def modify_db_instance(aws_id, params={})\n      request_hash = {}\n      # Mandatory\n      request_hash['DBInstanceIdentifier'] = aws_id\n      # Optional\n      request_hash['MasterUserPassword']         = params[:master_user_password]            unless params[:master_user_password].right_blank?\n      request_hash['DBInstanceClass']            = params[:instance_class].to_s.capitalize  unless params[:instance_class].right_blank?\n      request_hash['PreferredMaintenanceWindow'] = params[:preferred_maintenance_window]    unless params[:preferred_maintenance_window].right_blank?\n      request_hash['BackupRetentionPeriod']      = params[:backup_retention_period]         unless params[:backup_retention_period].right_blank?\n      request_hash['PreferredBackupWindow']      = params[:preferred_backup_window]         unless params[:preferred_backup_window].right_blank?\n      request_hash['AllocatedStorage']           = params[:allocated_storage]               unless params[:allocated_storage].right_blank?\n      request_hash['MultiAZ']                    = params[:multi_az].to_s                   unless params[:multi_az].nil?\n      request_hash['EngineVersion']              = params[:engine_version]                  unless params[:engine_version].right_blank?\n      request_hash['AutoMinorVersionUpgrade']    = params[:auto_minor_version_upgrade].to_s unless params[:auto_minor_version_upgrade].nil?\n      request_hash['AllowMajorVersionUpgrade']   = params[:allow_major_version_upgrade].to_s unless params[:allow_major_version_upgrade].nil?\n      request_hash['ApplyImmediately']           = params[:apply_immediately].to_s          unless params[:apply_immediately].right_blank?\n      request_hash.merge!(amazonize_list('DBSecurityGroups.member',  params[:db_security_groups]))\n      request_hash['DBParameterGroupName']       = params[:db_parameter_group]              unless params[:db_parameter_group].right_blank?\n      request_hash['Iops']                       = params[:iops]                            unless params[:iops].right_blank?\n      request_hash['OptionGroupName']            = params[:option_group_name]               unless params[:option_group_name].right_blank?\n      link = generate_request('ModifyDBInstance', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # Reboot Db instance.\n    #\n    # Options: +:force_failover+\n    #\n    #  rds.reboot_db_instance('kd-my-awesome-db') #=>\n    #    {:status=>\"rebooting\",\n    #     :pending_modified_values=>{},\n    #     :allocated_storage=>42,\n    #     :master_username=>\"kd\",\n    #     :db_security_groups=>[],\n    #     :instance_class=>\"Medium\",\n    #     :availability_zone=>\"us-east-1a\",\n    #     :aws_id=>\"kd-my-awesome-db\",\n    #     :create_time=>\"2009-08-28T08:34:21.858Z\",\n    #     :engine=>\"MySQL5.1\",\n    #     :preferred_maintenance_window=>\"Sun:05:00-Sun:09:00\"}\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_RebootDBInstance.html\n    #\n    def reboot_db_instance(aws_id, params={})\n      params = params.dup\n      params['DBInstanceIdentifier'] = aws_id\n      params['ForceFailover']        = !!params[:force_failover] if params.has_key?(:force_failover)\n      link = generate_request('RebootDBInstance', params)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # Delete a DB instance\n    #\n    # Mandatory arguments: aws_id\n    # Optional params: :skip_final_snapshot ('false' by def),\n    #                  :snapshot_aws_id ('{instance_aws_id}-final-snapshot-YYYYMMDDHHMMSS')\n    #\n    #  rds.delete_db_instance('my-awesome-db-g2') #=> true\n    #\n    def delete_db_instance(aws_id, params={})\n      request_hash = {}\n      request_hash['DBInstanceIdentifier'] = aws_id\n      request_hash['SkipFinalSnapshot']    = params.has_key?(:skip_final_snapshot) ? params[:skip_final_snapshot].to_s : 'false'\n      if request_hash['SkipFinalSnapshot'] == 'false' && params[:snapshot_aws_id].right_blank?\n        params = params.dup\n        params[:snapshot_aws_id] = \"#{aws_id}-final-snapshot-#{Time.now.utc.strftime('%Y%m%d%H%M%S')}\"\n      end\n      request_hash['FinalDBSnapshotIdentifier'] = params[:snapshot_aws_id] unless params[:snapshot_aws_id].right_blank?\n      link = generate_request('DeleteDBInstance', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # --------------------------------------------\n    #  DB SecurityGroups\n    # --------------------------------------------\n    #\n    #  rds.describe_db_security_groups #=>\n    #    [{:owner_id=>\"82...25\",\n    #      :description=>\"Default\",\n    #      :ec2_security_groups=>[],\n    #      :ip_ranges=>[],\n    #      :name=>\"Default\"},\n    #     {:owner_id=>\"82...25\",\n    #      :description=>\"kd\",\n    #      :ec2_security_groups=>[],\n    #      :ip_ranges=>[],\n    #      :name=>\"kd2\"},\n    #     {:owner_id=>\"82...25\",\n    #      :description=>\"kd\",\n    #      :ec2_security_groups=>\n    #       [{:status=>\"Authorized\", :owner_id=>\"82...23\", :name=>\"default\"},\n    #        {:status=>\"Authorized\", :owner_id=>\"82...24\", :name=>\"default1\"},\n    #        {:status=>\"Authorized\", :owner_id=>\"82...25\", :name=>\"default\"},\n    #        {:status=>\"Authorized\", :owner_id=>\"82...26\", :name=>\"default\"},\n    #        {:status=>\"Authorized\", :owner_id=>\"82...26\", :name=>\"default1\"},\n    #        {:status=>\"Authorized\", :owner_id=>\"82...29\", :name=>\"default22\"}],\n    #      :ip_ranges=>\n    #       [{:status=>\"Authorized\", :cidrip=>\"127.0.0.1/8\"},\n    #        {:status=>\"Authorized\", :cidrip=>\"128.0.0.1/8\"},\n    #        {:status=>\"Authorized\", :cidrip=>\"129.0.0.1/8\"},\n    #        {:status=>\"Authorized\", :cidrip=>\"130.0.0.1/8\"},\n    #        {:status=>\"Authorized\", :cidrip=>\"131.0.0.1/8\"}],\n    #      :name=>\"kd3\"}]\n    #\n    #  # get a custom group\n    #  rds.describe_db_security_groups('kd3')\n    #\n    def describe_db_security_groups(*db_security_group_name, &block)\n      items, params = AwsUtils::split_items_and_params(db_security_group_name)\n      params['DBSecurityGroupName'] = items.first unless items.right_blank?\n      result = []\n      incrementally_list_items('DescribeDBSecurityGroups', DescribeDbSecurityGroupsParser, params) do |response|\n        result += response[:db_security_groups]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Create a database security group so that ingress to an RDS Instance can be controlled.\n    # A new security group cannot have the same name as an existing group.\n    #\n    # Options: :vpc_id\n    #\n    #  ds.create_db_security_group('kd3', 'kd') #=>\n    #    {:ec2_security_groups=>[],\n    #     :description=>\"kd\",\n    #     :ip_ranges=>[],\n    #     :name=>\"kd3\",\n    #     :owner_id=>\"82...25\"}\n    #\n    def create_db_security_group(db_security_group_name, db_security_group_description, params={})\n      request_hash = { 'DBSecurityGroupName'        => db_security_group_name,\n                       'DBSecurityGroupDescription' => db_security_group_description }\n      request_hash['EC2VpcId'] = params[:vpc_id] unless params[:vpc_id].right_blank?\n      link = generate_request('CreateDBSecurityGroup', request_hash)\n      request_info(link, DescribeDbSecurityGroupsParser.new(:logger => @logger))[:db_security_groups].first\n    end\n\n    def modify_db_security_group_ingress(action, db_security_group_name, params={}) # :nodoc:\n      request_hash = { 'DBSecurityGroupName' => db_security_group_name}\n      request_hash['CIDRIP']                  = params[:cidrip]                   unless params[:cidrip].right_blank?\n      request_hash['EC2SecurityGroupName']    = params[:ec2_security_group_name]  unless params[:ec2_security_group_name].right_blank?\n      request_hash['EC2SecurityGroupOwnerId'] = params[:ec2_security_group_owner] unless params[:ec2_security_group_owner].right_blank?\n      request_hash['EC2SecurityGroupId']      = params[:ec2_security_group_id]    unless params[:ec2_security_group_id].right_blank?\n      link = generate_request(action, request_hash)\n      request_info(link, DescribeDbSecurityGroupsParser.new(:logger => @logger))[:db_security_groups].first\n    end\n\n    # Authorize an ingress. Params: +:cidrip+ or (+:ec2_security_group_name+ and +:ec2_security_group_owner+)\n    #  \n    #  rds.authorize_db_security_group_ingress('kd3', :cidrip => '131.0.0.1/8')\n    #    {:owner_id=>\"82...25\",\n    #     :ec2_security_groups=>[],\n    #     :description=>\"kd\",\n    #     :ip_ranges=>\n    #      [{:status=>\"Authorized\", :cidrip=>\"127.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"128.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"129.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"130.0.0.1/8\"},\n    #       {:status=>\"Authorizing\", :cidrip=>\"131.0.0.1/8\"}],\n    #     :name=>\"kd3\"}\n    #\n    #  rds.authorize_db_security_group_ingress('kd3',:ec2_security_group_owner => '82...27',\n    #                                                :ec2_security_group_name => 'default') #=>\n    #    {:owner_id=>\"82...25\",\n    #     :ec2_security_groups=>\n    #      [{:status=>\"Authorized\", :owner_id=>\"82...25\", :name=>\"g1\"},\n    #       {:status=>\"Authorized\", :owner_id=>\"82...26\", :name=>\"g2\"},\n    #       {:status=>\"Authorizing\", :owner_id=>\"82...27\", :name=>\"default\"}],\n    #     :ip_ranges=>\n    #      [{:status=>\"Authorized\", :cidrip=>\"127.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"128.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"129.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"130.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"131.0.0.1/8\"}],\n    #     :name=>\"kd3\"}\n    #\n    def authorize_db_security_group_ingress(db_security_group_name, params={})\n      modify_db_security_group_ingress('AuthorizeDBSecurityGroupIngress', db_security_group_name, params)\n    end\n\n    # Revoke an ingress.\n    # Optional params: +:cidrip+ or (+:ec2_security_group_name+ and +:ec2_security_group_owner+)\n    #\n    #  rds.revoke_db_security_group_ingress('kd3', :ec2_security_group_owner => '82...25',\n    #                                              :ec2_security_group_name => 'default') #=>\n    #    {:owner_id=>\"82...25\",\n    #     :ec2_security_groups=>\n    #      [{:status=>\"Revoking\", :owner_id=>\"826693181925\", :name=>\"default\"}],\n    #     :name=>\"kd3\",\n    #     :description=>\"kd\",\n    #     :ip_ranges=>\n    #      [{:status=>\"Authorized\", :cidrip=>\"127.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"128.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"129.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"130.0.0.1/8\"},\n    #       {:status=>\"Authorized\", :cidrip=>\"131.0.0.1/8\"}]}\n    #\n    def revoke_db_security_group_ingress(db_security_group_name, params={})\n      modify_db_security_group_ingress('RevokeDBSecurityGroupIngress', db_security_group_name, params)\n    end\n\n    # Delete a database security group. Database security group must not be associated with any\n    # RDS Instances.\n    #\n    #  rds.delete_db_security_group('kd3') #=> true\n    #\n    def delete_db_security_group(db_security_group_name)\n      link = generate_request('DeleteDBSecurityGroup', 'DBSecurityGroupName' => db_security_group_name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # --------------------------------------------\n    #  DB ParameterGroups\n    # --------------------------------------------\n\n    # Describe DBParameterGroups.\n    #\n    #  rds.describe_db_parameter_groups #=>\n    #    [{:engine=>\"MySQL5.1\",\n    #      :description=>\"Default parameter group for MySQL5.1\",\n    #      :name=>\"default.MySQL5.1\"}]\n    #\n    #  # List parameter groups by 20\n    #  rds.describe_db_parameter_groups(:max_records=>20) do |response|\n    #    puts response.inspect\n    #    true\n    #  end\n    #\n    def describe_db_parameter_groups(*db_parameter_group_name, &block)\n      items, params = AwsUtils::split_items_and_params(db_parameter_group_name)\n      params['DBParameterGroupName'] = items.first unless items.right_blank?\n      result = []\n      incrementally_list_items('DescribeDBParameterGroups', DescribeDbParameterGroupsParser, params) do |response|\n        result += response[:db_parameter_groups]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Creates a database parameter group so that configuration of an RDS Instance can be controlled.\n    #\n    #  rds.create_db_parameter_group('my-new-group-1','My new group') #=> {}\n    #\n    #  TODO: this call returns an empty hash, but should be a parameter group data - ask Amazon guys.\n    #\n    def create_db_parameter_group(db_parameter_group_name, db_parameter_group_description, db_parameter_group_family='mysql5.1', params={})\n      params['DBParameterGroupName']   = db_parameter_group_name\n      params['Description']            = db_parameter_group_description\n      params['DBParameterGroupFamily'] = db_parameter_group_family\n      link = generate_request('CreateDBParameterGroup', params )\n      request_info(link, DescribeDbParameterGroupsParser.new(:logger => @logger))[:db_parameter_groups].first\n    end\n\n    # Modify DBParameterGroup paramaters. Up to 20 params can be midified at once.\n    #\n    #  rds.modify_db_parameter_group('kd1', 'max_allowed_packet' => 2048) #=> true\n    #  \n    #  rds.modify_db_parameter_group('kd1', 'max_allowed_packet' => {:value => 2048, :method => 'immediate')  #=> true\n    #\n    def modify_db_parameter_group(db_parameter_group_name, params={}) # :nodoc:\n      request_hash = { 'DBParameterGroupName' => db_parameter_group_name}\n      parameters = []\n      params.each do |key, value|\n        method = 'pending-reboot'\n        if value.is_a?(Hash)\n          method = value[:method] unless value[:method].right_blank?\n          value  = value[:value]\n        end\n        parameters << [key, value, method]\n      end\n      request_hash.merge!( amazonize_list(['Parameters.member.?.ParameterName',\n                                           'Parameters.member.?.ParameterValue',\n                                           'Parameters.member.?.ApplyMethod'],\n                                           parameters ))\n      link = generate_request('ModifyDBParameterGroup', request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Delete DBParameter Group.\n    #\n    # rds.delete_db_parameter_group('kd1') #=> true\n    #\n    def delete_db_parameter_group(db_parameter_group_name)\n      link = generate_request('DeleteDBParameterGroup', 'DBParameterGroupName' => db_parameter_group_name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Modify the parameters of a DBParameterGroup to the engine/system default value.\n    # \n    #  # Reset all parameters\n    #  rds.reset_db_parameter_group('kd2', :all ) #=> true\n    #\n    #  # Reset custom parameters\n    #  rds.reset_db_parameter_group('kd2', 'max_allowed_packet', 'auto_increment_increment' ) #=> true\n    #  rds.reset_db_parameter_group('kd2', 'max_allowed_packet', 'auto_increment_increment' => 'immediate' ) #=> true\n    #\n    def reset_db_parameter_group(db_parameter_group_name, *params)\n      params = params.flatten\n      request_hash = { 'DBParameterGroupName' => db_parameter_group_name }\n      if params.first.to_s == 'all'\n        request_hash['ResetAllParameters'] = true\n      else\n        tmp = []\n        params.each{ |item| tmp |= item.to_a }\n        params = []\n        tmp.each do |key, method|\n          method = 'pending-reboot' unless method\n          params << [key, method]\n        end\n        request_hash.merge!( amazonize_list(['Parameters.member.?.ParameterName',\n                                             'Parameters.member.?.ApplyMethod'],\n                                             params ))\n      end\n      link = generate_request('ResetDBParameterGroup', request_hash)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # Get the detailed parameters list for a particular DBParameterGroup.\n    #\n    #  rds.describe_db_parameters('kd1') #=>\n    #    [{:is_modifiable=>true,\n    #      :apply_type=>\"static\",\n    #      :source=>\"engine-default\",\n    #      :allowed_values=>\"ON,OFF\",\n    #      :description=>\"Controls whether user-defined functions that have only an xxx symbol for the main function can be loaded\",\n    #      :name=>\"allow-suspicious-udfs\",\n    #      :data_type=>\"boolean\"},\n    #     {:is_modifiable=>true,\n    #      :apply_type=>\"dynamic\",\n    #      :source=>\"engine-default\",\n    #      :allowed_values=>\"1-65535\",\n    #      :description=>\"Intended for use with master-to-master replication, and can be used to control the operation of AUTO_INCREMENT columns\",\n    #      :name=>\"auto_increment_increment\",\n    #      :data_type=>\"integer\"}, ... ]\n    #\n    #  # List parameters by 20\n    #  rds.describe_db_parameters('kd1', :max_records=>20) do |response|\n    #    puts response.inspect\n    #    true\n    #  end\n    #\n    def describe_db_parameters(*db_parameter_group_name, &block)\n      item, params = AwsUtils::split_items_and_params(db_parameter_group_name)\n      params['DBParameterGroupName'] = item\n      result = []\n      incrementally_list_items('DescribeDBParameters', DescribeDbParametersParser, params) do |response|\n        result += response[:parameters]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Describe a default parameters for the parameter group family.\n    #\n    #  rds.describe_engine_default_parameters('MySQL5.1') #=>\n    #    [{:is_modifiable=>true,\n    #      :apply_type=>\"static\",\n    #      :source=>\"engine-default\",\n    #      :allowed_values=>\"ON,OFF\",\n    #      :description=>\"Controls whether user-defined functions that have only an xxx symbol for the main function can be loaded\",\n    #      :name=>\"allow-suspicious-udfs\",\n    #      :data_type=>\"boolean\"},\n    #     {:is_modifiable=>true,\n    #      :apply_type=>\"dynamic\",\n    #      :source=>\"engine-default\",\n    #      :allowed_values=>\"1-65535\",\n    #      :description=>\"Intended for use with master-to-master replication, and can be used to control the operation of AUTO_INCREMENT columns\",\n    #      :name=>\"auto_increment_increment\",\n    #      :data_type=>\"integer\"}, ... ]\n    #\n    def describe_engine_default_parameters(*db_parameter_group_family, &block)\n      db_parameter_group_family = ['MySQL5.1'] if db_parameter_group_family.right_blank?\n      item, params = AwsUtils::split_items_and_params(db_parameter_group_family)\n      params['DBParameterGroupFamily'] = item if item\n      result = []\n      incrementally_list_items('DescribeEngineDefaultParameters', DescribeDbParametersParser, params) do |response|\n        result += response[:parameters]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Describe a list of orderable DB Instance options for the specified engine.\n    # Optionals: +:instance_class+, +:engine_version+ , +:license_model+, +:vpc+\n    #\n    #  rds.describe_orderable_db_instance_options('oracle-ee', :engine_version => '11.2.0.2.v2') #=>\n    #    [{:read_replica_capable=>false,\n    #      :instance_class=>\"db.m1.large\",\n    #      :availability_zones=>[\"us-east-1a\", \"us-east-1b\", \"us-east-1d\"],\n    #      :engine=>\"oracle-ee\",\n    #      :license_model=>\"bring-your-own-license\",\n    #      :engine_version=>\"11.2.0.2.v2\",\n    #      :multi_az_capable=>\"false\"}, ... ]\n    #\n    def describe_orderable_db_instance_options(engine, params={}, &block)\n      request_hash = { 'Engine' => engine }\n      request_hash['DBInstanceClass'] = params[:instance_class] unless params[:instance_class].right_blank?\n      request_hash['EngineVersion']   = params[:engine_version] unless params[:engine_version].right_blank?\n      request_hash['LicenseModel']    = params[:license_model]  unless params[:license_model].right_blank?\n      request_hash['Vpc']             = !!params[:vpc]          if     params.has_key?(:vpc)\n      result = []\n      incrementally_list_items('DescribeOrderableDBInstanceOptions', DescribeOrderableDBInstanceOptionsParser, request_hash) do |response|\n        result += response[:items]\n        block ? block.call(response) : true\n      end\n      result\n    end\n    \n    # --------------------------------------------\n    #  DB Snapshots\n    # --------------------------------------------\n\n    # Get DBSecurityGroup details for a particular customer or for a particular DBSecurityGroup if a name is specified.\n    # Optional params: +:instance_aws_id+\n    #\n    #  # all snapshots\n    #  rds.describe_db_snapshots #=>\n    #    [{:status=>\"Available\",\n    #      :instance_aws_id=>\"kd-test-n1\",\n    #      :allocated_storage=>25,\n    #      :availability_zone=>\"us-east-1b\",\n    #      :aws_id=>\"kd-test-n1-final-snapshot-at-20090630131215\",\n    #      :engine=>\"MySQL5.1\",\n    #      :endpoint_port=>3306,\n    #      :instance_create_time=>\"2009-06-30T12:48:15.590Z\",\n    #      :master_username=>\"payless\",\n    #      :snapshot_time=>\"2009-06-30T13:16:48.496Z\"}, ...]\n    #\n    #  # all snapshots for a custom instance\n    #  rds.describe_db_snapshots(:instance_aws_id => 'kd-test-n3') #=>\n    #    [{:status=>\"Available\",\n    #      :instance_aws_id=>\"kd-test-n3\",\n    #      :allocated_storage=>25,\n    #      :availability_zone=>\"us-east-1a\",\n    #      :aws_id=>\"kd-test-n3-final-snapshot-20090713074916\",\n    #      :engine=>\"MySQL5.1\",\n    #      :endpoint_port=>3306,\n    #      :instance_create_time=>\"2009-06-30T12:51:32.540Z\",\n    #      :master_username=>\"payless\",\n    #      :snapshot_time=>\"2009-07-13T07:52:35.542Z\"}]\n    #\n    #  # a snapshot by id\n    #  rds.describe_db_snapshots('my-awesome-db-final-snapshot-20090713075554') #=>\n    #    [{:status=>\"Available\",\n    #      :allocated_storage=>25,\n    #      :engine=>\"MySQL5.1\",\n    #      :instance_aws_id=>\"my-awesome-db\",\n    #      :availability_zone=>\"us-east-1a\",\n    #      :instance_create_time=>\"2009-07-13T07:53:08.912Z\",\n    #      :endpoint_port=>3306,\n    #      :master_username=>\"medium\",\n    #      :aws_id=>\"my-awesome-db-final-snapshot-20090713075554\",\n    #      :snapshot_time=>\"2009-07-13T07:59:17.537Z\"}]\n    #\n    def describe_db_snapshots(params={}, &block)\n      item, params = AwsUtils::split_items_and_params(params)\n      params['DBSnapshotIdentifier'] = item if item\n      params['DBInstanceIdentifier'] = params.delete(:instance_aws_id) unless params[:instance_aws_id].right_blank?\n      result = []\n      incrementally_list_items('DescribeDBSnapshots', DescribeDbSnapshotsParser, params) do |response|\n        result += response[:db_snapshots]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Create a DBSnapshot. The source DBInstance must be in Available state\n    #\n    #  rds.create_db_snapshot('remove-me-tomorrow-2', 'my-awesome-db-g7' ) #=>\n    #    {:status=>\"PendingCreation\",\n    #     :allocated_storage=>50,\n    #     :availability_zone=>\"us-east-1b\",\n    #     :engine=>\"MySQL5.1\",\n    #     :aws_id=>\"remove-me-tomorrow-2\",\n    #     :instance_create_time=>\"2009-07-13T09:35:39.243Z\",\n    #     :endpoint_port=>3306,\n    #     :instance_aws_id=>\"my-awesome-db-g7\",\n    #     :db_master_username=>\"username\"}\n    #\n    def create_db_snapshot(aws_id, instance_aws_id)\n      link = generate_request('CreateDBSnapshot', 'DBSnapshotIdentifier' => aws_id,\n                                                  'DBInstanceIdentifier' => instance_aws_id)\n      request_info(link, DescribeDbSnapshotsParser.new(:logger => @logger))[:db_snapshots].first\n    end\n\n    # Create a new RDS instance from a DBSnapshot. The source DBSnapshot must be\n    # in the \"Available\" state. The new RDS instance is created with the Default security group.\n    #\n    # Optional params: +:instance_class+, +:endpoint_port+, +:availability_zone+, +:multi_az+,\n    # +:auto_minor_version_upgrade+, +:license_model+, +:db_name+, +:engine+, +:db_subnet_group_name+,\n    # +:iops+, +:option_group_name+\n    #\n    #  rds.restore_db_instance_from_db_snapshot('ahahahaha-final-snapshot-20090828081159', 'q1') #=>\n    #    {:status=>\"creating\",\n    #     :pending_modified_values=>{},\n    #     :allocated_storage=>42,\n    #     :db_security_groups=>[],\n    #     :master_username=>\"kd\",\n    #     :availability_zone=>\"us-east-1a\",\n    #     :aws_id=>\"q1\",\n    #     :create_time=>\"2009-08-29T18:07:01.510Z\",\n    #     :instance_class=>\"Medium\",\n    #     :preferred_maintenance_window=>\"Sun:05:00-Sun:09:00\",\n    #     :engine=>\"MySQL\",\n    #     :engine_version=>\"5.1.49\"}\n    #\n    def restore_db_instance_from_db_snapshot(snapshot_aws_id, instance_aws_id, params={})\n      request_hash = { 'DBSnapshotIdentifier' => snapshot_aws_id,\n                       'DBInstanceIdentifier' => instance_aws_id }\n      request_hash['DBInstanceClass']         = params[:instance_class]             unless params[:instance_class].right_blank?\n      request_hash['Port']                    = params[:endpoint_port]              unless params[:endpoint_port].right_blank?\n      request_hash['AvailabilityZone']        = params[:availability_zone]          unless params[:availability_zone].right_blank?\n      request_hash['MultiAZ']                 = params[:multi_az]                   unless params[:multi_az].nil?\n      request_hash['AutoMinorVersionUpgrade'] = params[:auto_minor_version_upgrade] unless params[:auto_minor_version_upgrade].nil?\n      request_hash['LicenseModel']            = params[:license_model]              unless params[:license_model].right_blank?\n      request_hash['DBName']                  = params[:db_name]                    unless params[:db_name].right_blank?\n      request_hash['Engine']                  = params[:engine]                     unless params[:enginel].right_blank?\n      request_hash['DBSubnetGroupName']       = params[:db_subnet_group_name]       unless params[:db_subnet_group_name].right_blank?\n      request_hash['Iops']                    = params[:iops]                       unless params[:iops].right_blank?\n      request_hash['OptionGroupName']         = params[:option_group_name]          unless params[:option_group_name].right_blank?\n      link = generate_request('RestoreDBInstanceFromDBSnapshot', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # Create a new RDS instance from a point-in-time system snapshot. The target\n    # database is created from the source database restore point with the same configuration as\n    # the original source database, except that the new RDS instance is created with the default\n    # security group.\n    #\n    # Optional params: +:instance_class+, +:endpoint_port+, +:availability_zone+, +:multi_az+, +:restore_time+,\n    # +:auto_minor_version_upgrade+, +:use_latest_restorable_time+, +:license_model+, +:db_name+, +:engine+\n    # +:db_subnet_group_name+, +:iops+, +:option_group_name+\n    #\n    def restore_db_instance_to_point_in_time(instance_aws_id, new_instance_aws_id, params={})\n      request_hash = { 'SourceDBInstanceIdentifier' => instance_aws_id,\n                       'TargetDBInstanceIdentifier' => new_instance_aws_id}\n      request_hash['UseLatestRestorableTime'] = params[:use_latest_restorable_time].to_s unless params[:use_latest_restorable_time].nil?\n      request_hash['RestoreTime']             = params[:restore_time]               unless params[:restore_time].right_blank?\n      request_hash['DBInstanceClass']         = params[:instance_class]             unless params[:instance_class].right_blank?\n      request_hash['MultiAZ']                 = params[:multi_az]                   unless params[:multi_az].nil?\n      request_hash['Port']                    = params[:endpoint_port]              unless params[:endpoint_port].right_blank?\n      request_hash['AvailabilityZone']        = params[:availability_zone]          unless params[:availability_zone].right_blank?\n      request_hash['AutoMinorVersionUpgrade'] = params[:auto_minor_version_upgrade] unless params[:auto_minor_version_upgrade].nil?\n      request_hash['LicenseModel']            = params[:license_model]              unless params[:license_model].right_blank?\n      request_hash['DBName']                  = params[:db_name]                    unless params[:db_name].right_blank?\n      request_hash['Engine']                  = params[:engine]                     unless params[:enginel].right_blank?\n      request_hash['DBSubnetGroupName']       = params[:db_subnet_group_name]       unless params[:db_subnet_group_name].right_blank?\n      request_hash['Iops']                    = params[:iops]                       unless params[:iops].right_blank?\n      request_hash['OptionGroupName']         = params[:option_group_name]          unless params[:option_group_name].right_blank?\n      link = generate_request('RestoreDBInstanceToPointInTime', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n    # Delete a DBSnapshot. The DBSnapshot must be in the Available state to be deleted.\n    #\n    #  rds.delete_db_snapshot('remove-me-tomorrow-1') #=>\n    #    {:status=>\"Deleted\",\n    #     :allocated_storage=>50,\n    #     :instance_create_time=>\"2009-07-13T09:27:01.053Z\",\n    #     :availability_zone=>\"us-east-1a\",\n    #     :db_master_username=>\"username\",\n    #     :aws_id=>\"remove-me-tomorrow-1\",\n    #     :snapshot_time=>\"2009-07-13T10:59:30.227Z\",\n    #     :endpoint_port=>3306,\n    #     :instance_aws_id=>\"my-awesome-db-g5\",\n    #     :engine=>\"MySQL5.1\"}\n    #\n    def delete_db_snapshot(aws_id)\n      link = generate_request('DeleteDBSnapshot', 'DBSnapshotIdentifier' => aws_id)\n      request_info(link, DescribeDbSnapshotsParser.new(:logger => @logger))[:db_snapshots].first\n    end\n\n    # --------------------------------------------\n    #  DB Events\n    # --------------------------------------------\n\n    # Get events related to RDS instances and DBSecurityGroups for the past 14 days.\n    # Optional params: +:duration+, +:start_time+, +:end_time+, +:aws_id+, \n    #                  +:source_type+('db-instance', 'db-security-group', 'db-snapshot', 'db-parameter-group')\n    #\n    #  # get all enevts\n    #  rds.describe_events #=>\n    #    [{:aws_id=>\"my-awesome-db-g4\",\n    #      :source_type=>\"DBInstance\",\n    #      :message=>\"Started user snapshot for database instance:my-awesome-db-g4\",\n    #      :date=>\"2009-07-13T10:54:13.661Z\"},\n    #     {:aws_id=>\"my-awesome-db-g5\",\n    #      :source_type=>\"DBInstance\",\n    #      :message=>\"Started user snapshot for database instance:my-awesome-db-g5\",\n    #      :date=>\"2009-07-13T10:55:13.674Z\"},\n    #     {:aws_id=>\"my-awesome-db-g7\",\n    #      :source_type=>\"DBInstance\",\n    #      :message=>\"Started user snapshot for database instance:my-awesome-db-g7\",\n    #      :date=>\"2009-07-13T10:56:34.226Z\"}]\n    #\n    #  # get all events since yesterday\n    #  rds.describe_events(:start_date => 1.day.ago)\n    #\n    #  # get last 60 min events\n    #  rds.describe_events(:duration => 60)\n    #\n    def describe_events(params={}, &block)\n      params = params.dup\n      params['SourceIdentifier'] = params.delete(:aws_id)                unless params[:aws_id].right_blank?\n      params['SourceType']       = params.delete(:source_type)           unless params[:source_type].right_blank?\n      params['Duration']         = params.delete(:duration)              unless params[:duration].right_blank?\n      params['StartDate']        = fix_date(params.delete(:start_date))  unless params[:start_date].right_blank?\n      params['EndDate']          = fix_date(params.delete(:end_date))    unless params[:end_date].right_blank?\n      result = []\n      incrementally_list_items('DescribeEvents', DescribeEventsParser, params) do |response|\n        result += response[:events]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    def fix_date(date) # :nodoc:\n      date = Time.at(date) if date.is_a?(Fixnum)\n      date = date.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if date.is_a?(Time)\n      date\n    end\n\n    # --------------------------------------------\n    #  DB Engine Versions\n    # --------------------------------------------\n\n    # Get a list of the available DB engines.\n    # Optional params: +:db_parameter_group_family+, +:default_only+, +:engine+, +:engine_version+. +:engine_version+\n    #\n    #  rds.describe_db_engine_versions #=>\n    #    [{:db_parameter_group_family=>\"mysql5.1\",\n    #      :engine=>\"mysql\",\n    #      :db_engine_description=>\"MySQL Community Edition\",\n    #      :db_engine_version_description=>\"Mysql 5.1.45\",\n    #      :engine_version=>\"5.1.45\"},\n    #     {:db_parameter_group_family=>\"oracle-se1-11.2\",\n    #      :engine=>\"oracle-se1\",\n    #      :db_engine_description=>\"Oracle Database Standard Edition One\",\n    #      :db_engine_version_description=>\n    #       \"Oracle Standard Edition One - DB Engine Version 11.2.0.2.v2\",\n    #      :engine_version=>\"11.2.0.2.v2\"}]\n    #\n    #  rds.describe_db_engine_versions(:list_supported_character_sets => true) #=> \n    #    [{:db_parameter_group_family=>\"mysql5.1\",\n    #      :engine=>\"mysql\",\n    #      :db_engine_description=>\"MySQL Community Edition\",\n    #      :engine_version=>\"5.1.45\",\n    #      :db_engine_version_description=>\"MySQL 5.1.45\"},\n    #     {:db_parameter_group_family=>\"oracle-ee-11.2\",\n    #      :engine=>\"oracle-ee\",\n    #      :supported_character_sets=>\n    #       [{:name=>\"AL32UTF8\",\n    #         :description=>\"Unicode 5.0 UTF-8 Universal character set\"},\n    #        {:name=>\"JA16EUC\", :description=>\"EUC 24-bit Japanese\"},\n    #        {:name=>\"JA16EUCTILDE\",\n    #         :description=>\n    #          \"The same as JA16EUC except for the way that the wave dash and the tilde are mapped to and from Unicode.\"},\n    #        {:name=>\"JA16SJIS\", :description=>\"Shift-JIS 16-bit Japanese\"},\n    #        {:name=>\"WE8ISO8859P9\",\n    #         :description=>\"ISO 8859-9 West European and Turkish\"},\n    #        {:name=>\"US7ASCII\", :description=>\"ASCII 7-bit American\"}],\n    #      :db_engine_description=>\"Oracle Database Enterprise Edition\",\n    #      :default_character_set=>\n    #       {:name=>\"AL32UTF8\",\n    #        :description=>\"Unicode 5.0 UTF-8 Universal character set\"},\n    #      :engine_version=>\"11.2.0.2.v3\",\n    #      :db_engine_version_description=>\"Oracle 11.2.0.2.v3\"}, ... ]\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DescribeDBEngineVersions.html\n    #\n    def describe_db_engine_versions(params={}, &block)\n      params = params.dup\n      params['DBParameterGroupFamily'] = params.delete(:db_parameter_group_family) unless params[:db_parameter_group_family].right_blank?\n      params['DefaultOnly']            = params.delete(:default_only).to_s         unless params[:default_only].nil?\n      params['Engine']                 = params.delete(:engine)                    unless params[:engine].right_blank?\n      params['EngineVersion']          = params.delete(:engine_version)            unless params[:engine_version].right_blank?\n      params['ListSupportedCharacterSets'] = !!params.delete(:list_supported_character_sets) if params.has_key?(:list_supported_character_sets)\n      result = []\n      incrementally_list_items('DescribeDBEngineVersions', DescribeDBEngineVersionsParser, params) do |response|\n        result += response[:db_engine_versions]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # --------------------------------------------\n    #  DB Replicas\n    # --------------------------------------------\n\n    # Create a DB Instance that acts as a Read Replica of a source DB Instance.\n    #\n    # Optional params: +:endpoint_port+, +:availability_zone+, +:instance_class+, +:auto_minor_version_upgrade+,\n    # +:iops+, +:option_group_name+\n    #\n    #    rds.create_db_instance_read_replica('kd-delete-me-01-replica-01', 'kd-delete-me-01',\n    #                                        :instance_class => 'db.m1.small',\n    #                                        :endpoint_port => '11000',\n    #                                        :auto_minor_version_upgrade => false ) #=>\n    #      {:auto_minor_version_upgrade=>false,\n    #       :read_replica_source_db_instance_identifier=>\"kd-delete-me-01\",\n    #       :status=>\"creating\",\n    #       :backup_retention_period=>0,\n    #       :allocated_storage=>30,\n    #       :read_replica_db_instance_identifiers=>[],\n    #       :engine_version=>\"5.1.50\",\n    #       :aws_id=>\"kd-delete-me-01-replica-01\",\n    #       :multi_az=>false,\n    #       :preferred_maintenance_window=>\"sun:06:00-sun:10:00\",\n    #       :master_username=>\"username\",\n    #       :preferred_backup_window=>\"02:00-04:00\",\n    #       :db_parameter_group=>{:status=>\"in-sync\", :name=>\"default.mysql5.1\"},\n    #       :engine=>\"mysql\",\n    #       :db_security_groups=>[{:status=>\"active\", :name=>\"default\"}],\n    #       :instance_class=>\"db.m1.small\",\n    #       :pending_modified_values=>{}}\n    #\n    def create_db_instance_read_replica(aws_id, source_db_instance_identifier, params={})\n      request_hash = { 'DBInstanceIdentifier'       => aws_id,\n                       'SourceDBInstanceIdentifier' => source_db_instance_identifier}\n      request_hash['Port']                    = params[:endpoint_port]                   unless params[:endpoint_port].right_blank?\n      request_hash['AvailabilityZone']        = params[:availability_zone]               unless params[:availability_zone].right_blank?\n      request_hash['DBInstanceClass']         = params[:instance_class]                  unless params[:instance_class].right_blank?\n      request_hash['AutoMinorVersionUpgrade'] = params[:auto_minor_version_upgrade].to_s unless params[:auto_minor_version_upgrade].nil?\n      request_hash['Iops']                    = params[:iops]                            unless params[:iops].right_blank?\n      request_hash['OptionGroupName']         = params[:option_group_name]               unless params[:option_group_name].right_blank?\n      link = generate_request('CreateDBInstanceReadReplica', request_hash)\n      request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first\n    end\n\n\n    #---------------------------------------------\n    #  Reserved Instances\n    #---------------------------------------------\n\n    # Lists available reserved DB Instance offerings.\n    # Options: :aws_id, :instance_class, :duration, :product_description, :multi_az\n    #\n    #  rds.describe_reserved_db_instances_offerings #=>\n    #    [{:recurring_charges=>[],\n    #        :offering_type=>\"Medium Utilization\",\n    #        :duration=>94608000,\n    #        :currency_code=>\"USD\",\n    #        :fixed_price=>82.0,\n    #        :product_description=>\"oracle-se(byol)\",\n    #        :usage_price=>0.01,\n    #        :aws_id=>\"248e7b75-01ff-4f1d-8fad-918b76337c13\",\n    #        :multi_az=>false,\n    #        :instance_class=>\"db.t1.micro\"},\n    #      {:recurring_charges=>[{:frequency=>\"Hourly\", :amount=>\"0.24\"}],\n    #        :offering_type=>\"Heavy Utilization\",\n    #        :duration=>31536000,\n    #        :currency_code=>\"USD\",\n    #        :fixed_price=>1040.0,\n    #        :product_description=>\"sqlserver-web(li)\",\n    #        :usage_price=>0.0,\n    #        :aws_id=>\"248e7b75-05eb-46df-a7b8-4117b5001667\",\n    #        :multi_az=>false,\n    #        :instance_class=>\"db.m2.xlarge\"}, ... ]\n    #\n    #  rds.describe_reserved_db_instances_offerings(:aws_id => \"248e7b75-49a7-4cd7-9a9b-354f4906a9b1\") #=>\n    #    [{:duration=>94608000,\n    #      :multi_az=>true,\n    #      :fixed_price=>700.0,\n    #      :usage_price=>0.092,\n    #      :currency_code=>\"USD\",\n    #      :aws_id=>\"248e7b75-49a7-4cd7-9a9b-354f4906a9b1\",\n    #      :instance_class=>\"db.m1.small\",\n    #      :product_description=>\"mysql\"}]\n    #\n    #  rds.describe_reserved_db_instances_offerings(:instance_class => \"db.m1.small\")\n    #  rds.describe_reserved_db_instances_offerings(:duration => 31536000)\n    #  rds.describe_reserved_db_instances_offerings(:product_description => 'mysql')\n    #  rds.describe_reserved_db_instances_offerings(:multi_az => true)\n    #\n    def describe_reserved_db_instances_offerings(params={}, &block)\n      params = params.dup\n      params['ReservedDBInstancesOfferingId'] = params.delete(:aws_id)              unless params[:aws_id].right_blank?\n      params['DBInstanceClass']               = params.delete(:instance_class)      unless params[:instance_class].right_blank?\n      params['Duration']                      = params.delete(:duration)            unless params[:duration].right_blank?\n      params['ProductDescription']            = params.delete(:product_description) unless params[:product_description].right_blank?\n      params['MultiAZ']                       = params.delete(:multi_az).to_s       unless params[:multi_az].nil?\n      result = []\n      incrementally_list_items('DescribeReservedDBInstancesOfferings', DescribeReservedDBInstancesOfferingsParser, params) do |response|\n        result += response[:reserved_db_instances_offerings]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Returns information about reserved DB Instances for this account, or about\n    # a specified reserved DB Instance.\n    # Options: :aws_id, :offering_aws_id, :instance_class, :duration, :product_description, :multi_az\n    #\n    #  rds.describe_reserved_db_instances\n    #  rds.describe_reserved_db_instances(:aws_id => \"myreservedinstance\")\n    #  rds.describe_reserved_db_instances(:offering_aws_id => \"248e7b75-49a7-4cd7-9a9b-354f4906a9b1\")\n    #  rds.describe_reserved_db_instances(:instance_class => \"db.m1.small\")\n    #  rds.describe_reserved_db_instances(:duration => 31536000)\n    #  rds.describe_reserved_db_instances(:product_description => 'mysql')\n    #  rds.describe_reserved_db_instances_offerings(:multi_az => true)\n    #\n    def describe_reserved_db_instances(params={}, &block)\n      params = params.dup\n      params['ReservedDBInstancesId']         = params.delete(:aws_id)              unless params[:aws_id].right_blank?\n      params['ReservedDBInstancesOfferingId'] = params.delete(:offering_aws_id)     unless params[:offering_aws_id].right_blank?\n      params['DBInstanceClass']               = params.delete(:instance_class)      unless params[:instance_class].right_blank?\n      params['Duration']                      = params.delete(:duration)            unless params[:duration].right_blank?\n      params['ProductDescription']            = params.delete(:product_description) unless params[:product_description].right_blank?\n      params['MultiAZ']                       = params.delete(:multi_az).to_s       unless params[:multi_az].nil?\n      result = []\n      incrementally_list_items('DescribeReservedDBInstances', DescribeReservedDBInstancesParser, params) do |response|\n        result += response[:reserved_db_instances]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    # Purchases a reserved DB Instance offering.\n    # Options: :aws_id, :count\n    def purchase_reserved_db_instances_offering(offering_aws_id, params={})\n      request_hash = { 'ReservedDBInstancesOfferingId' => offering_aws_id }\n      request_hash['ReservedDBInstanceId'] = params[:aws_id] unless params[:aws_id].right_blank?\n      request_hash['DBInstanceCount']      = params[:count]  unless params[:count].right_blank?\n      link = generate_request('PurchaseReservedDBInstancesOffering', request_hash)\n      request_info(link, DescribeReservedDBInstancesParser.new(:logger => @logger))[:reserved_db_instances].first\n    end\n\n    #---------------------------------------------\n    #  Subnet Groups\n    #---------------------------------------------\n\n    # Lists available DB Subnet Groups.\n    # Options: :name, :max_records, :marker\n    #\n    #  rds.describe_db_subnet_groups #=>\n    #    [{:subnets=>\n    #       [{:availability_zone=>\n    #          {:name=>\"us-east-1d\", :provisioned_iops_capable=>false},\n    #         :status=>\"Active\",\n    #         :subnet_id=>\"subnet-5259d03a\"},\n    #        {:availability_zone=>\n    #          {:name=>\"us-east-1a\", :provisioned_iops_capable=>false},\n    #         :status=>\"Active\",\n    #         :subnet_id=>\"subnet-eb518f83\"}],\n    #      :vpc_id=>\"vpc-10518f78\",\n    #      :status=>\"Complete\",\n    #      :description=>\"delete me please\",\n    #      :name=>\"kd-subnet-group\"},\n    #     {:subnets=>\n    #       [{:availability_zone=>\n    #          {:name=>\"us-east-1a\", :provisioned_iops_capable=>false},\n    #         :status=>\"Active\",\n    #         :subnet_id=>\"subnet-eb518f83\"},\n    #        {:availability_zone=>\n    #          {:name=>\"us-east-1d\", :provisioned_iops_capable=>false},\n    #         :status=>\"Active\",\n    #         :subnet_id=>\"subnet-5259d03a\"}],\n    #      :vpc_id=>\"vpc-10518f78\",\n    #      :status=>\"Complete\",\n    #      :description=>\"delete me please\",\n    #      :name=>\"kd-subnet-group-1\"}]\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DescribeDBSubnetGroups.html\n    #\n    def describe_db_subnet_groups(params={}, &block)\n      params = params.dup\n      params['DBSubnetGroupName'] = params.delete(:name) unless params[:name].right_blank?\n      result = []\n      incrementally_list_items('DescribeDBSubnetGroups', DescribeDBSubnetGroupsParser, params) do |response|\n        result += response[:subnet_groups]\n        block ? block.call(response) : true\n      end\n      result\n    end\n\n    def get_db_subnet_group(group_name, &block)\n      describe_db_subnet_groups(:name => group_name, &block)\n    end\n\n    # Create a new DB Subnet Group.\n    #\n    #  rds.create_db_subnet_group('kd-subnet-group-1', ['subnet-5259d03a', 'subnet-eb518f83'], 'delete me please') #=>\n    #    {:subnets=>\n    #      [{:availability_zone=>\n    #         {:name=>\"us-east-1a\", :provisioned_iops_capable=>false},\n    #        :status=>\"Active\",\n    #        :subnet_id=>\"subnet-eb518f83\"},\n    #       {:availability_zone=>\n    #         {:name=>\"us-east-1d\", :provisioned_iops_capable=>false},\n    #        :status=>\"Active\",\n    #        :subnet_id=>\"subnet-5259d03a\"}],\n    #     :vpc_id=>\"vpc-10518f78\",\n    #     :status=>\"Complete\",\n    #     :description=>\"delete me please\",\n    #     :name=>\"kd-subnet-group-1\"}\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_CreateDBSubnetGroup.html\n    #\n    def create_db_subnet_group(subnet_group_name, subnets, subnet_group_description = '-')\n      request_hash = { 'DBSubnetGroupName'        => subnet_group_name,\n                       'DBSubnetGroupDescription' => subnet_group_description }\n      request_hash.merge!(amazonize_list('SubnetIds.member',  subnets))\n      link = generate_request('CreateDBSubnetGroup', request_hash)\n      request_info(link, DescribeDBSubnetGroupsParser.new(:logger => @logger))[:subnet_groups].first\n    end\n\n    # Modify an existing DB Subnet Group.\n    #\n    #  rds.modify_db_subnet_group('kd-subnet-group', ['subnet-5259d03a', 'subnet-eb518f83'], 'hahaha!') #=>\n    #    {:subnets=>\n    #      [{:availability_zone=>\n    #         {:name=>\"us-east-1d\", :provisioned_iops_capable=>false},\n    #        :status=>\"Active\",\n    #        :subnet_id=>\"subnet-5259d03a\"},\n    #       {:availability_zone=>\n    #         {:name=>\"us-east-1a\", :provisioned_iops_capable=>false},\n    #        :status=>\"Active\",\n    #        :subnet_id=>\"subnet-eb518f83\"}],\n    #     :vpc_id=>\"vpc-10518f78\",\n    #     :status=>\"Complete\",\n    #     :description=>\"hahaha!\",\n    #     :name=>\"kd-subnet-group\"}\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_ModifyDBSubnetGroup.html\n    #\n    def modify_db_subnet_group(subnet_group_name, subnets, subnet_group_description = '')\n      request_hash = { 'DBSubnetGroupName' => subnet_group_name}\n      request_hash['DBSubnetGroupDescription'] = subnet_group_description unless subnet_group_description.right_blank?\n      request_hash.merge!(amazonize_list('SubnetIds.member',  subnets))\n      link = generate_request('ModifyDBSubnetGroup', request_hash)\n      request_info(link, DescribeDBSubnetGroupsParser.new(:logger => @logger))[:subnet_groups].first\n    end\n\n    # Delete a DB Subnet Group.\n    #\n    #  rds.delete_db_subnet_group(\"kd-subnet-group-1\") #=> true\n    #\n    # P.S. http://docs.amazonwebservices.com/AmazonRDS/latest/APIReference/API_DeleteDBSubnetGroup.html\n    #\n    def delete_db_subnet_group(name)\n      link = generate_request('DeleteDBSubnetGroup', 'DBSubnetGroupName' => name)\n      request_info(link, RightHttp2xxParser.new(:logger => @logger))\n    end\n\n    # --------------------------------------------\n    #  Parsers\n    # --------------------------------------------\n\n    # --------------------------------------------\n    #  DB Instances\n    # --------------------------------------------\n\n    class DescribeDbInstancesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :db_instances => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBInstance'             then @item = { :db_security_groups                   => [],\n                                                     :pending_modified_values              => {},\n                                                     :read_replica_db_instance_identifiers => [],\n                                                     :option_group_membership              => {} }\n        when 'DBSecurityGroup'        then @db_security_group = {}\n        when 'DBSubnetGroup'          then @item[:db_subnet_group] = {}\n        when 'Subnet'                 then @subnet = { :availability_zone => {} }\n        when 'DBParameterGroup',\n             'DBParameterGroupStatus' then @db_parameter_group = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                                then @result[:marker]               = @text\n        when 'MaxRecords'                            then @result[:max_records]          = @text.to_i\n        when 'DBInstanceIdentifier'                  then @item[:aws_id]                 = @text\n        when 'InstanceCreateTime'                    then @item[:create_time]            = @text\n        when 'Engine'                                then @item[:engine]                 = @text\n        when 'DBInstanceStatus'                      then @item[:status]                 = @text\n        when 'Address'                               then @item[:endpoint_address]       = @text\n        when 'Port'                                  then @item[:endpoint_port]          = @text.to_i\n        when 'MasterUsername'                        then @item[:master_username]        = @text\n        when 'AvailabilityZone'                      then @item[:availability_zone]      = @text\n        when 'LatestRestorableTime'                  then @item[:latest_restorable_time] = @text\n        when 'LicenseModel'                          then @item[:license_model]          = @text\n        when 'DBName'                                then @item[:db_name]                = @text\n        when 'Iops'                                  then @item[:iops]                   = @text\n        when 'CharacterSetName'                      then @item[:character_set_name]     = @text\n        when 'ReadReplicaSourceDBInstanceIdentifier' then @item[:read_replica_source_db_instance_identifier] = @text\n        when 'ReadReplicaDBInstanceIdentifier'       then @item[:read_replica_db_instance_identifiers]      << @text\n        when 'DBParameterGroupName'                  then @db_parameter_group[:name]            = @text\n        when 'ParameterApplyStatus'                  then @db_parameter_group[:status]          = @text\n        when 'DBSecurityGroup'                       then @item[:db_security_groups] << @db_security_group\n        when 'DBParameterGroup',\n             'DBParameterGroupStatus'                then @item[:db_parameter_group] = @db_parameter_group\n        when 'DBInstance'                            then @result[:db_instances]            << @item\n        else\n          case full_tag_name\n          when %r{DBInstance/DBInstanceClass$}                       then @item[:instance_class]               = @text\n          when %r{DBInstance/AllocatedStorage$}                      then @item[:allocated_storage]            = @text.to_i\n          when %r{DBInstance/MultiAZ$}                               then @item[:multi_az]                     = (@text == 'true')\n          when %r{DBInstance/BackupRetentionPeriod$}                 then @item[:backup_retention_period]      = @text.to_i\n          when %r{DBInstance/PreferredMaintenanceWindow$}            then @item[:preferred_maintenance_window] = @text\n          when %r{DBInstance/PreferredBackupWindow$}                 then @item[:preferred_backup_window]      = @text\n          when %r{DBInstance/EngineVersion$}                         then @item[:engine_version]               = @text\n          when %r{DBInstance/AutoMinorVersionUpgrade$}               then @item[:auto_minor_version_upgrade]   = (@text == 'true')\n          when %r{DBInstance/AllowMajorVersionUpgrade$}              then @item[:allow_major_version_upgrade]  = (@text == 'true')\n          when %r{PendingModifiedValues/DBInstanceClass$}            then @item[:pending_modified_values][:instance_class]               = @text\n          when %r{PendingModifiedValues/AllocatedStorage$}           then @item[:pending_modified_values][:allocated_storage]            = @text.to_i\n          when %r{PendingModifiedValues/MasterUserPassword$}         then @item[:pending_modified_values][:master_user_password]         = @text\n          when %r{PendingModifiedValues/MultiAZ$}                    then @item[:pending_modified_values][:multi_az]                     = (@text == 'true')\n          when %r{PendingModifiedValues/BackupRetentionPeriod$}      then @item[:pending_modified_values][:backup_retention_period]      = @text.to_i\n          when %r{PendingModifiedValues/PreferredMaintenanceWindow$} then @item[:pending_modified_values][:preferred_maintenance_window] = @text\n          when %r{PendingModifiedValues/PreferredBackupWindow$}      then @item[:pending_modified_values][:preferred_backup_window]      = @text\n          when %r{PendingModifiedValues/EngineVersion$}              then @item[:pending_modified_values][:engine_version]               = @text\n          when %r{PendingModifiedValues/AutoMinorVersionUpgrade$}    then @item[:pending_modified_values][:auto_minor_version_upgrade]   = (@text == 'true')\n          when %r{PendingModifiedValues/AllowMajorVersionUpgrade$}   then @item[:pending_modified_values][:allow_major_version_upgrade]  = (@text == 'true')\n          when %r{OptionGroupMembership/Status$}                     then @item[:option_group_membership][:status] = @text\n          when %r{OptionGroupMembership/OptionGroupName$}            then @item[:option_group_membership][:name]   = @text\n          when %r{DBSecurityGroup/Status$}                           then @db_security_group[:status]           = @text\n          when %r{DBSecurityGroup/DBSecurityGroupName$}              then @db_security_group[:name]             = @text\n          when %r{DBSubnetGroup/DBSubnetGroupDescription$}           then @item[:db_subnet_group][:description]        = @text\n          when %r{DBSubnetGroup/DBSubnetGroupName$}                  then @item[:db_subnet_group][:name]               = @text\n          when %r{DBSubnetGroup/SubnetGroupStatus$}                  then @item[:db_subnet_group][:status]             = @text\n          when %r{Subnet/SubnetIdentifier$}                          then @subnet[:subnet_id]                = @text\n          when %r{Subnet/SubnetStatus$}                              then @subnet[:status]                   = @text\n          when %r{Subnet/AvailabilityZone/Name$}                     then @subnet[:availability_zone][:name] = @text\n          when %r{Subnet/AvailabilityZone/ProvisionedIopsCapable$}   then @subnet[:availability_zone][:provisioned_iops_capable] = @text == 'true'\n          when %r{DBSubnetGroup/Subnet$}                             then (@item[:db_subnet_group][:subnets] ||= [])  << @subnet\n          when %r{DBSubnetGroup/VpcId$}                              then @item[:db_subnet_group][:vpc_id]             = @text\n          end\n        end\n      end\n    end\n\n    class DescribeOrderableDBInstanceOptionsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'OrderableDBInstanceOption' then @item = { :availability_zones => [] }\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                     then @result[:marker]             = @text\n        when 'MaxRecords'                 then @result[:max_records]        = @text.to_i\n        when 'DBInstanceClass'            then @item[:instance_class]       = @text\n        when 'Engine'                     then @item[:engine]               = @text\n        when 'EngineVersion'              then @item[:engine_version]       = @text\n        when 'LicenseModel'               then @item[:license_model]        = @text\n        when 'MultiAZCapable'             then @item[:multi_az_capable]     = @text\n        when 'ReadReplicaCapable'         then @item[:read_replica_capable] = @text == 'true'\n        when 'Vpc'                        then @item[:vpc]                  = @text == 'true'\n        when 'Name'                       then @item[:availability_zones]  << @text\n        when 'OrderableDBInstanceOption'  then @result[:items]             << @item\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Security Groups\n    # --------------------------------------------\n\n    class DescribeDbSecurityGroupsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :db_security_groups => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBSecurityGroup'  then @item               = { :ec2_security_groups => [], :ip_ranges => [] }\n        when 'IPRange'          then @ip_range           = {}\n        when 'EC2SecurityGroup' then @ec2_security_group = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                     then @result[:marker]               = @text\n        when 'MaxRecords'                 then @result[:max_records]          = @text.to_i\n        when 'DBSecurityGroupDescription' then @item[:description ]           = @text\n        when 'OwnerId'                    then @item[:owner_id]               = @text\n        when 'DBSecurityGroupName'        then @item[:name]                   = @text\n        when 'EC2SecurityGroupId'         then @ec2_security_group[:group_id] = @text\n        when 'EC2SecurityGroupName'       then @ec2_security_group[:name]     = @text\n        when 'EC2SecurityGroupOwnerId'    then @ec2_security_group[:owner_id] = @text\n        when 'CIDRIP'                     then @ip_range[:cidrip]             = @text\n        when 'IPRange'                    then @item[:ip_ranges]             << @ip_range\n        when 'EC2SecurityGroup'           then @item[:ec2_security_groups]   << @ec2_security_group\n        when 'VpcId'                      then @item[:vpc_id]                 = @text\n        when 'DBSecurityGroup'\n          # Sort the ip_ranges and ec2_security_groups\n          @item[:ip_ranges].sort!{ |i1,i2| \"#{i1[:cidrip]}\" <=> \"#{i2[:cidrip]}\" }\n          @item[:ec2_security_groups].sort!{ |i1,i2| \"#{i1[:owner_id]}#{i1[:name]}\" <=> \"#{i2[:owner_id]}#{i2[:name]}\" }\n          @result[:db_security_groups] << @item\n        else\n          case full_tag_name\n          when %r{IPRange/Status$}          then @ip_range[:status]           = @text\n          when %r{EC2SecurityGroup/Status$} then @ec2_security_group[:status] = @text\n          end\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Security Groups\n    # --------------------------------------------\n\n    class DescribeDbParameterGroupsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :db_parameter_groups => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBParameterGroup',\n             'ModifyDBParameterGroupResult' then @item = { }\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                 then @result[:marker]       = @text\n        when 'MaxRecords'             then @result[:max_records]  = @text.to_i\n        when 'DBParameterGroupName'   then @item[:name]           = @text\n        when 'Description'            then @item[:description]    = @text\n        when 'DBParameterGroupFamily' then @item[:db_parameter_group_family] = @text\n        when 'DBParameterGroup',\n             'ModifyDBParameterGroupResult' then @result[:db_parameter_groups] << @item\n        end\n      end\n    end\n\n    class DescribeDbParametersParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :parameters => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'Parameter' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'               then @result[:marker]       = @text\n        when 'MaxRecords'           then @result[:max_records]  = @text.to_i\n        when 'DBParameterGroupName'   then @result[:group_name]                = @text # DescribeDbParametersResponse\n        when 'DBParameterGroupFamily' then @result[:db_parameter_group_family] = @text # DescribeDBEngineDefaultParametersResponse\n        when 'DataType'             then @item[:data_type]      = @text\n        when 'Source'               then @item[:source]         = @text\n        when 'Description'          then @item[:description]    = @text\n        when 'IsModifiable'         then @item[:is_modifiable]  = (@text == 'true')\n        when 'ApplyType'            then @item[:apply_type]     = @text\n        when 'ApplyMethod'          then @item[:apply_method]   = @text\n        when 'MinimumEngineVersion' then @item[:minimum_engine_version] = @text\n        when 'AllowedValues'        then @item[:allowed_values] = @text\n        when 'ParameterName'        then @item[:name]           = @text\n        when 'ParameterValue'       then @item[:value]          = @text\n        when 'Parameter'            then @result[:parameters]  << @item\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Snapshots\n    # --------------------------------------------\n\n    class DescribeDbSnapshotsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :db_snapshots => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBSnapshot' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'               then @result[:marker]             = @text\n        when 'MaxRecords'           then @result[:max_records]        = @text.to_i  # ?\n        when 'Engine'               then @item[:engine]               = @text\n        when 'EngineVersion'        then @item[:engine_version]       = @text\n        when 'InstanceCreateTime'   then @item[:instance_create_time] = @text\n        when 'Port'                 then @item[:endpoint_port]        = @text.to_i\n        when 'Status'               then @item[:status]               = @text\n        when 'AvailabilityZone'     then @item[:availability_zone]    = @text\n        when 'MasterUsername'       then @item[:master_username]      = @text\n        when 'AllocatedStorage'     then @item[:allocated_storage]    = @text.to_i\n        when 'SnapshotCreateTime'   then @item[:create_time]          = @text\n        when 'DBInstanceIdentifier' then @item[:instance_aws_id]      = @text\n        when 'DBSnapshotIdentifier' then @item[:aws_id]               = @text\n        when 'LicenseModel'         then @item[:license_model]        = @text\n        when 'Iops'                 then @item[:iops]                 = @text\n        when 'SnapshotType'         then @item[:snapshot_type]        = @text\n        when 'VpcId'                then @item[:vpc_id]               = @text\n        when 'DBSnapshot'           then @result[:db_snapshots]      << @item\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Events\n    # --------------------------------------------\n\n    class DescribeEventsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :events => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'Event' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'           then @result[:marker]       = @text\n        when 'MaxRecords'       then @result[:max_records]  = @text.to_i  # ?\n        when 'Date'             then @item[:date]           = @text\n        when 'SourceIdentifier' then @item[:aws_id]         = @text\n        when 'SourceType'       then @item[:source_type]    = @text\n        when 'Message'          then @item[:message]        = @text\n        when 'Event'            then @result[:events]      << @item\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Engine Versions\n    # --------------------------------------------\n\n    class DescribeDBEngineVersionsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :db_engine_versions => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBEngineVersion' then @item = {}\n        else\n          case full_tag_name\n          when %r{DefaultCharacterSet$}    then @item[:default_character_set] = {}\n          when %r{SupportedCharacterSets/CharacterSet$} then @set = {}\n          end\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                 then @result[:marker]                  = @text\n        when 'MaxRecords'             then @result[:max_records]             = @text.to_i\n        when 'DBParameterGroupFamily' then @item[:db_parameter_group_family] = @text\n        when 'Engine'                 then @item[:engine]                    = @text\n        when 'EngineVersion'          then @item[:engine_version]            = @text\n        when 'DBEngineDescription'    then @item[:db_engine_description]     = @text\n        when 'DBEngineVersionDescription' then @item[:db_engine_version_description] = @text\n        when 'DBEngineVersion'        then @result[:db_engine_versions]     << @item\n        else\n          case full_tag_name\n          when %r{DefaultCharacterSet/CharacterSetDescription$}    then @item[:default_character_set][:description] = @text\n          when %r{DefaultCharacterSet/CharacterSetName$}           then @item[:default_character_set][:name]        = @text\n          when %r{SupportedCharacterSets/CharacterSet/CharacterSetDescription$} then @set[:description] = @text\n          when %r{SupportedCharacterSets/CharacterSet/CharacterSetName$}        then @set[:name]        = @text\n          when %r{SupportedCharacterSets/CharacterSet$}                         then (@item[:supported_character_sets] ||= []) << @set\n          end\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Reserved Instances\n    # --------------------------------------------\n\n    class DescribeReservedDBInstancesOfferingsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :reserved_db_instances_offerings => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'ReservedDBInstancesOffering' then @item = { :recurring_charges => [] }\n        when 'RecurringCharge'             then @recurring_charge = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                        then @result[:marker]            = @text\n        when 'MaxRecords'                    then @result[:max_records]       = @text.to_i\n        when 'CurrencyCode'                  then @item[:currency_code]       = @text\n        when 'DBInstanceClass'               then @item[:instance_class]      = @text\n        when 'Duration'                      then @item[:duration]            = @text.to_i\n        when 'FixedPrice'                    then @item[:fixed_price]         = @text.to_f\n        when 'UsagePrice'                    then @item[:usage_price]         = @text.to_f\n        when 'MultiAZ'                       then @item[:multi_az]            = (@text == 'true')\n        when 'ProductDescription'            then @item[:product_description] = @text\n        when 'OfferingType'                  then @item[:offering_type]       = @text\n        when 'ReservedDBInstancesOfferingId' then @item[:aws_id]              = @text\n        when 'RecurringCharge'               then @item[:recurring_charges]  << @recurring_charge\n        when 'ReservedDBInstancesOffering'   then @result[:reserved_db_instances_offerings] << @item\n        else\n          case full_tag_name\n          when %r{RecurringCharge/RecurringChargeAmount$}    then @recurring_charge[:amount]    = @text\n          when %r{RecurringCharge/RecurringChargeFrequency$} then @recurring_charge[:frequency] = @text\n          end\n        end\n      end\n    end\n\n    class DescribeReservedDBInstancesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :reserved_db_instances => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'ReservedDBInstance' then @item = { :recurring_charges => [] }\n        when 'RecurringCharge'    then @recurring_charge = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                        then @result[:marker]            = @text\n        when 'MaxRecords'                    then @result[:max_records]       = @text.to_i\n        when 'DBInstanceClass'               then @item[:instance_class]      = @text\n        when 'CurrencyCode'                  then @item[:currency_code]       = @text\n        when 'Duration'                      then @item[:duration]            = @text.to_i\n        when 'FixedPrice'                    then @item[:fixed_price]         = @text.to_f\n        when 'UsagePrice'                    then @item[:usage_price]         = @text.to_f\n        when 'MultiAZ'                       then @item[:multi_az]            = (@text == 'true')\n        when 'ProductDescription'            then @item[:product_description] = @text\n        when 'ReservedDBInstancesOfferingId' then @item[:offering_aws_id]     = @text\n        when 'ReservedDBInstanceId'          then @item[:aws_id]              = @text\n        when 'State'                         then @item[:state]               = @text\n        when 'DBInstanceCount'               then @item[:instance_count]      = @text.to_i\n        when 'StartTime'                     then @item[:start_time]          = @text\n        when 'OfferingType'                  then @item[:offering_type]       = @text\n        when 'RecurringCharge'               then @item[:recurring_charges]  << @recurring_charge\n        when 'ReservedDBInstance'            then @result[:reserved_db_instances] << @item\n        else\n          case full_tag_name\n          when %r{RecurringCharge/RecurringChargeAmount$}    then @recurring_charge[:amount]    = @text\n          when %r{RecurringCharge/RecurringChargeFrequency$} then @recurring_charge[:frequency] = @text\n          end\n        end\n      end\n    end\n\n    # --------------------------------------------\n    #  DB Subnet Groups\n    # --------------------------------------------\n\n    class DescribeDBSubnetGroupsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :subnet_groups => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'DBSubnetGroup' then @item   = { :subnets           => [] }\n        when 'Subnet'        then @subnet = { :availability_zone => {}}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'Marker'                   then @result[:marker]        = @text\n        when 'MaxRecords'               then @result[:max_records]   = @text.to_i\n        when 'DBSubnetGroupName'        then @item[:name]            = @text\n        when 'DBSubnetGroupDescription' then @item[:description]     = @text\n        when 'SubnetGroupStatus'        then @item[:status]          = @text\n        when 'Subnet'                   then @item[:subnets]         << @subnet\n        when 'VpcId'                    then @item[:vpc_id]          = @text\n        when 'DBSubnetGroup'            then @result[:subnet_groups] << @item\n        else\n          case full_tag_name\n          when %r{Subnet/SubnetIdentifier$}                 then @subnet[:subnet_id]                = @text\n          when %r{Subnet/SubnetStatus$}                     then @subnet[:status]                   = @text\n          when %r{AvailabilityZone/Name$}                   then @subnet[:availability_zone][:name] = @text\n          when %r{AvailabilityZone/ProvisionedIopsCapable$} then @subnet[:availability_zone][:provisioned_iops_capable] = @text == 'true'\n          end\n        end\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/right_aws.rb",
    "content": "#\n# Copyright (c) 2007-2011 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nrequire 'benchmark'\nrequire 'net/https'\nrequire 'uri'\nrequire 'time'\nrequire \"cgi\"\nrequire \"base64\"\nrequire \"rexml/document\"\nrequire \"openssl\"\nrequire \"digest/sha1\"\n\nrequire 'rubygems'\nrequire 'right_http_connection'\n\n$:.unshift(File.dirname(__FILE__))\nrequire 'awsbase/version'\nrequire 'awsbase/support'\nrequire 'awsbase/benchmark_fix'\nrequire 'awsbase/right_awsbase'\nrequire 'ec2/right_ec2'\nrequire 'ec2/right_ec2_images'\nrequire 'ec2/right_ec2_instances'\nrequire 'ec2/right_ec2_security_groups'\nrequire 'ec2/right_ec2_spot_instances'\nrequire 'ec2/right_ec2_ebs'\nrequire 'ec2/right_ec2_reserved_instances'\nrequire 'ec2/right_ec2_vpc'\nrequire 'ec2/right_ec2_vpc2'\nrequire 'ec2/right_ec2_monitoring'\nrequire 'ec2/right_ec2_placement_groups'\nrequire 'ec2/right_ec2_windows_mobility'\nrequire 'ec2/right_ec2_tags'\nrequire 'elb/right_elb_interface'\nrequire 'emr/right_emr_interface'\nrequire 'acw/right_acw_interface'\nrequire 'as/right_as_interface'\nrequire 's3/right_s3_interface'\nrequire 's3/right_s3'\nrequire 'sqs/right_sqs_interface'\nrequire 'sqs/right_sqs'\nrequire 'sqs/right_sqs_gen2_interface'\nrequire 'sqs/right_sqs_gen2'\nrequire 'sdb/right_sdb_interface'\nrequire 'acf/right_acf_interface'\nrequire 'acf/right_acf_streaming_interface'\nrequire 'acf/right_acf_origin_access_identities'\nrequire 'acf/right_acf_invalidations'\nrequire 'rds/right_rds_interface'\nrequire 'iam/right_iam_interface'\nrequire 'iam/right_iam_groups'\nrequire 'iam/right_iam_users'\nrequire 'iam/right_iam_access_keys'\nrequire 'iam/right_iam_mfa_devices'\nrequire 'route_53/right_route_53_interface'\nrequire 'sns/right_sns_interface'\n\n#-\n\n# We also want everything available in the Rightscale namespace for backward\n# compatibility reasons.\nmodule Rightscale #:nodoc:\n  include RightAws\n  extend RightAws\nend\n"
  },
  {
    "path": "lib/route_53/right_route_53_interface.rb",
    "content": "# Copyright (c) 2007-2011 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAws::Route53Interface -- Amazon Route 53 web service interface.\n  #\n  # The RightAws::Route53Interface class provides a complete interface to Amazon Route 53: a web\n  # service that enables you to manage your DNS service.\n  #\n  # For explanations of the semantics of each call, please refer to Amazon's documentation at\n  # http://aws.amazon.com/documentation/route53/\n  #\n  # Examples:\n  #\n  #  # Create Route53 handler\n  #  r53 = RightAws::Route53Interface.new(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)\n  #\n  #  #------------------------\n  #  # Create Hosted Zone\n  #  #------------------------\n  #  \n  #  hosted_zone_config = {\n  #    :name   => 'my-awesome-site.com.',\n  #    :config => {\n  #      :comment => 'My test site!'\n  #     }\n  #  }\n  #  r53.create_hosted_zone(hosted_zone_config) #=>\n  #    {:name_servers=>\n  #      [\"ns-1115.awsdns-11.org\",\n  #       \"ns-696.awsdns-23.net\",\n  #       \"ns-1963.awsdns-53.co.uk\",\n  #       \"ns-362.awsdns-45.com\"],\n  #     :aws_id=>\"/hostedzone/Z1K6NCF0EB26FB\",\n  #     :caller_reference=>\"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9\",\n  #     :config=>{:comment=>\"My test site!\"},\n  #     :change_info=>\n  #      {:status=>\"PENDING\",\n  #       :aws_id=>\"/change/C23QGMT8XTCAJY\",\n  #       :submitted_at=>\"2011-01-19T08:16:31.046Z\"},\n  #     :name=>\"my-awesome-site.com.\"}\n  #\n  #  # List Hosted Zones\n  #  r53.list_hosted_zones #=> []\n  #    [{:aws_id=>\"/hostedzone/Z1K6NCF0EB26FB\",\n  #      :caller_reference=>\"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9\",\n  #      :config=>{:comment=>\"My test site!\"},\n  #      :name=>\"my-awesome-site.com.\"}]\n  #\n  #  #--------------------------------\n  #  # Manage DNS Records and Changes\n  #  #--------------------------------\n  #\n  #  # Create DNS Records\n  #  resource_record_sets = [ { :name => 'www1.my-awesome-site.com.',\n  #                             :type => 'NS',\n  #                             :ttl => 600,\n  #                             :resource_records => 'www.mysite.com' },\n  #                           { :name => 'www2.my-awesome-site.com.',\n  #                             :type => 'A',\n  #                             :ttl => 600,\n  #                             :resource_records => ['10.0.0.1'] } ]\n  #  r53.create_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'my first set of records') #=>\n  #    { :status=>\"PENDING\",\n  #      :aws_id=>\"/change/C2C6IGNRTKA0AY\",\n  #      :submitted_at=>\"2011-01-19T08:29:26.160Z\" }\n  #\n  #  # Delete DNS records\n  #  r53.delete_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'I dont need them any more') #=>\n  #    { :status=>\"PENDING\",\n  #      :aws_id=>\"/change/C1CYJ10EZBFLO7\",\n  #      :submitted_at=>\"2011-01-19T08:26:41.220Z\" }\n  #\n  #  # Create or delete DNS records (:action key must be provided):\n  #  resource_record_sets = [ { :action => :create,\n  #                             :name => 'www1.my-awesome-site.com.',\n  #                             :type => 'NS',\n  #                             :ttl => 600,\n  #                             :resource_records => 'www.mysite.com' },\n  #                           { :action => :delete,\n  #                             :name => 'www2.my-awesome-site.com.',\n  #                             :type => 'A',\n  #                             :ttl => 600,\n  #                             :resource_records => ['10.0.0.1'] } ]\n  #  r53.change_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'do change records')\n  #    { :status=>\"PENDING\",\n  #      :aws_id=>\"/change/C2PWXVECN794LK\",\n  #      :submitted_at=>\"2011-01-19T08:31:33.301Z\" }\n  #\n  #  # List DNS Records\n  #  r53.list_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\") #=>\n  #    [{:type=>\"NS\",\n  #      :ttl=>172800,\n  #      :resource_records=>\n  #       [\"ns-1115.awsdns-11.org.\",\n  #        \"ns-696.awsdns-23.net.\",\n  #        \"ns-1963.awsdns-53.co.uk.\",\n  #        \"ns-362.awsdns-45.com.\"],\n  #      :name=>\"my-awesome-site.com.\"},\n  #     {:type=>\"SOA\",\n  #      :ttl=>900,\n  #      :resource_records=>\n  #       [\"ns-1115.awsdns-11.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400\"],\n  #      :name=>\"my-awesome-site.com.\"},\n  #     {:type=>\"NS\",\n  #      :ttl=>600,\n  #      :resource_records=>[\"www.mysite.com\"],\n  #      :name=>\"www1.my-awesome-site.com.\"}]\n  #\n  #  # Get Change info\n  #  r53.get_change(\"/change/C2C6IGNRTKA0AY\")\n  #    {:status=>\"INSYNC\",\n  #     :aws_id=>\"/change/C2C6IGNRTKA0AY\",\n  #     :submitted_at=>\"2011-01-19T08:29:26.160Z\"}\n  #\n  #  #------------------------\n  #  # Delete Hosted Zone\n  #  #------------------------\n  #\n  #  # Get a list of DNS records I have created (the first 2 records were added by Amazon and cannot be deleted)\n  #  resource_record_sets = r53.list_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\")\n  #  resource_record_sets.shift\n  #  resource_record_sets.shift\n  #\n  #  # Delete them all\n  #  delete_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'kill all records I have created') #=>\n  #    { :status=>\"PENDING\",\n  #      :aws_id=>\"/change/C6NCO8Z50MHXV\",\n  #      :submitted_at=>\"2011-01-19T08:46:37.307Z\" }\n  #\n  #  # Delete Hosted Zone\n  #  r53.delete_hosted_zone(\"/hostedzone/Z1K6NCF0EB26FB\") #=> \n  #    { :status=>\"PENDING\",\n  #      :aws_id=>\"/change/C3OJ31D4V5P2LU\",\n  #      :submitted_at=>\"2011-01-19T08:46:37.530Z\" }\n  #\n  class Route53Interface < RightAwsBase\n    \n    include RightAwsBaseInterface\n\n    API_VERSION       = \"2011-05-05\"\n    DEFAULT_HOST      = \"route53.amazonaws.com\"\n    DEFAULT_PATH      = '/'\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PORT      = 443\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    # Create a new handle to an Route53 account. All handles share the same per process or per thread\n    # HTTP connection to Amazon Route53. Each handle is for a specific account. The params have the\n    # following options:\n    # * <tt>:endpoint_url</tt> a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol).\n    # * <tt>:server</tt>: Route53 service host, default: DEFAULT_HOST\n    # * <tt>:port</tt>: Route53 service port, default: DEFAULT_PORT\n    # * <tt>:protocol</tt>: 'http' or 'https', default: DEFAULT_PROTOCOL\n    # * <tt>:logger</tt>: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT\n    # * <tt>:signature_version</tt>:  The signature version : '0','1' or '2'(default)\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name                => 'ROUTE_53',\n             :default_host        => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['ROUTE_53_URL'] ? URI.parse(ENV['ROUTE_53_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['ROUTE_53_API_VERSION'] || API_VERSION },\n           aws_access_key_id    || ENV['AWS_ACCESS_KEY_ID'] ,\n           aws_secret_access_key|| ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    #-----------------------------------------------------------------\n    #      Requests\n    #-----------------------------------------------------------------\n\n    # Generates request hash for REST API.\n    def generate_request(method, path, params={}, body=nil, headers={})  # :nodoc:\n      # Params\n      params.delete_if{ |key, val| val.right_blank? }\n      unless params.right_blank?\n        path += \"?\" + params.to_a.collect{ |key,val| \"#{AwsUtils::amz_escape(key)}=#{AwsUtils::amz_escape(val.to_s)}\" }.join(\"&\")\n      end\n      # Headers\n      headers = AwsUtils::fix_headers(headers)\n      headers['content-type'] ||= 'text/xml' if body\n      headers['date']           = Time.now.httpdate\n      # Auth\n      signature = AwsUtils::sign(@aws_secret_access_key, headers['date'])\n      headers['X-Amzn-Authorization'] = \"AWS3-HTTPS AWSAccessKeyId=#{@aws_access_key_id},Algorithm=HmacSHA1,Signature=#{signature}\"\n      # Request\n      path    = \"#{@params[:service]}#{@params[:api_version]}/#{path}\"\n      request = \"Net::HTTP::#{method.capitalize}\".right_constantize.new(path)\n      request.body = body if body\n      # Set request headers\n      headers.each { |key, value| request[key.to_s] = value }\n      # prepare output hash\n      { :request  => request,\n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n      end\n\n      # Sends request to Amazon and parses the response.\n      # Raises AwsError if any banana happened.\n    def request_info(request, parser, &block) # :nodoc:\n      request_info_impl(:acf_connection, @@bench, request, parser, &block)\n    end\n\n    def incrementally_list_hosted_zones(path, parser, params={}, &block) # :nodoc:\n      opts = {}\n      opts['MaxItems'] = params[:max_items] if params[:max_items]\n      opts['Marker']   = params[:marker]    if params[:marker]\n      last_response = nil\n      loop do\n        link = generate_request('GET', path, opts)\n        last_response = request_info(link,  parser.new(:logger => @logger))\n        opts['Marker'] = last_response[:next_marker]\n        break unless block && block.call(last_response) && !last_response[:next_marker].right_blank?\n      end\n      last_response\n    end\n\n    def incrementally_list_resource_records(path, parser, params={}, &block) # :nodoc:\n      opts = {}\n      opts[:maxitems] = params.delete(:max_items) if params[:max_items]\n      last_response = nil\n      loop do\n        link = generate_request('GET', path, opts)\n        last_response = request_info(link,  parser.new(:logger => @logger))\n        opts[:maxitems] = last_response[:max_items]\n        opts[:name]     = last_response[:next_record_name]\n        opts[:type]     = last_response[:next_record_type]\n        break unless block && block.call(last_response) && last_response[:is_truncated]\n      end\n      last_response\n    end\n\n    def expand_hosted_zone_id(aws_id) # :nodoc:\n      aws_id[%r{^/hostedzone/}] ? aws_id : \"/hostedzone/#{aws_id}\"\n    end\n\n    def expand_change_id(aws_id) # :nodoc:\n      aws_id[%r{^/change/}] ? aws_id : \"/change/#{aws_id}\"\n    end\n\n    def hosted_zone_config_to_xml(config) # :nodoc:\n      config[:caller_reference] ||= AwsUtils::generate_call_reference\n      hosted_zone_config = ''\n      unless config[:config].right_blank?\n        hosted_zone_config = \"  <HostedZoneConfig>\\n\" +\n                             \"    <Comment>#{AwsUtils::xml_escape config[:config][:comment]}</Comment>\\n\" +\n                             \"  </HostedZoneConfig>\\n\"\n      end\n      # XML\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" +\n      \"<CreateHostedZoneRequest xmlns=\\\"https://#{@params[:server]}/doc/#{API_VERSION}/\\\">\\n\" +\n      \"  <Name>#{config[:name]}</Name>\\n\" +\n      \"  <CallerReference>#{config[:caller_reference]}</CallerReference>\\n\" +\n      hosted_zone_config +\n      \"</CreateHostedZoneRequest>\\n\"\n    end\n\n    def resource_record_sets_to_xml(resource_record_changes, comment) # :nodoc:\n      # Comment\n      xml_comment = comment.right_blank? ? '' : \"    <Comment>#{AwsUtils::xml_escape(comment)}</Comment>\\n\"\n      # Changes\n      xml_changes = ''\n      resource_record_changes.each do |change|\n        xml_resource_records = Array(change[:resource_records]).map{|record| \"            <ResourceRecord><Value>#{AwsUtils::xml_escape(record)}</Value></ResourceRecord>\\n\" }.join('')\n        xml_changes += \"      <Change>\\n\"                                                                +\n                       \"        <Action>#{AwsUtils::xml_escape(change[:action].to_s.upcase)}</Action>\\n\" +\n                       \"        <ResourceRecordSet>\\n\"                                                   +\n                       \"          <Name>#{AwsUtils::xml_escape(change[:name])}</Name>\\n\"                 +\n                       \"          <Type>#{AwsUtils::xml_escape(change[:type].to_s.upcase)}</Type>\\n\"\n        if change[:alias_target]\n          alias_target = change[:alias_target]\n          xml_changes +=\n                       \"          <AliasTarget>\\n\"                                                                              +\n                       \"            <HostedZoneId>#{AwsUtils::xml_escape(alias_target[:hosted_zone_id].to_s)}</HostedZoneId>\\n\" +\n                       \"            <DNSName>#{AwsUtils::xml_escape(alias_target[:dns_name].to_s)}</DNSName>\\n\"                 +\n                       \"          </AliasTarget>\\n\"\n        else\n          xml_changes +=\n                       \"          <TTL>#{AwsUtils::xml_escape(change[:ttl].to_s)}</TTL>\\n\"               +\n                       \"          <ResourceRecords>\\n\"                                                   +\n                       xml_resource_records                                                              +\n                       \"          </ResourceRecords>\\n\"\n        end\n        xml_changes +=\n                       \"        </ResourceRecordSet>\\n\"                                                  +\n                       \"      </Change>\\n\"\n      end\n      # XML\n      \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\" +\n      \"<ChangeResourceRecordSetsRequest xmlns=\\\"https://#{@params[:server]}/doc/#{API_VERSION}/\\\">\\n\" +\n      \"  <ChangeBatch>\\n\"  +\n      xml_comment          +\n      \"    <Changes>\\n\"    +\n      xml_changes          +\n      \"    </Changes>\\n\"   +\n      \"  </ChangeBatch>\\n\" +\n      \"</ChangeResourceRecordSetsRequest>\\n\"\n    end\n\n\n    #-----------------------------------------------------------------\n    #      Hosted Zones\n    #-----------------------------------------------------------------\n\n    # List your hosted zones.\n    #\n    #  r53.list_hosted_zones #=>\n    #    [{:config=>{:comment=>\"KD1, description\"},\n    #      :aws_id=>\"/hostedzone/Z2P714ENJN23PN\",\n    #      :caller_reference=>\"1295424990-710392-gqMuw-KcY8F-LFlrB-SQhp9\",\n    #      :name=>\"patch-island.com.\"},\n    #     {:config=>{:comment=>\"My awesome site!\"},\n    #      :aws_id=>\"/hostedzone/ZWEC7PPVACGQ4\",\n    #      :caller_reference=>\"1295422234-657482-hfkeo-JFKid-Ldfle-Sdrty\",\n    #      :name=>\"mysite.patch-island.com.\"}, ...]\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ListHostedZones.html\n    #\n    def list_hosted_zones\n      result = []\n      incrementally_list_hosted_zones('hostedzone', ListHostedZonesParser) do |response|\n        result += response[:items]\n        true\n      end\n      result\n    end\n\n    # Create new hosted zone\n    #\n    #  config = {\n    #    :name => 'mysite.patch-island.com.',\n    #    :config => {\n    #      :comment => 'My awesome site!'\n    #     }\n    #  }\n    #  r53.create_hosted_zone(config) #=>\n    #    {:config=>{:comment=>\"My awesome site!\"},\n    #     :change_info=>\n    #      {:status=>\"PENDING\",\n    #       :aws_id=>\"/change/C2NOTVGL7IOFFF\",\n    #       :submitted_at=>\"2011-01-18T15:34:18.086Z\"},\n    #     :aws_id=>\"/hostedzone/ZWEC7PPVACGQ4\",\n    #     :caller_reference=>\"1295365357-227168-NfZ4P-VGHWi-Yq0p7-nuN6q\",\n    #     :name_servers=>\n    #      [\"ns-794.awsdns-35.net\",\n    #       \"ns-459.awsdns-57.com\",\n    #       \"ns-1537.awsdns-00.co.uk\",\n    #       \"ns-1165.awsdns-17.org\"],\n    #     :name=>\"mysite.patch-island.com.\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/index.html?API_CreateHostedZone.html\n    #\n    def create_hosted_zone(config)\n      config[:caller_reference] ||= AwsUtils::generate_unique_token\n      link = generate_request('POST', 'hostedzone', {}, hosted_zone_config_to_xml(config))\n      request_info(link, GetHostedZoneParser.new(:logger => @logger))\n    end\n\n    # Get your hosted zone.\n    #\n    #  r53.get_hosted_zone(\"ZWEC7PPVACGQ4\") #=>\n    #    {:config=>{:comment=>\"My awesome site!\"},\n    #     :aws_id=>\"/hostedzone/ZWEC7PPVACGQ4\",\n    #     :caller_reference=>\"1295422234-657482-hfkeo-JFKid-Ldfle-Sdrty\",\n    #     :name_servers=>\n    #      [\"ns-794.awsdns-35.net\",\n    #       \"ns-459.awsdns-57.com\",\n    #       \"ns-1537.awsdns-00.co.uk\",\n    #       \"ns-1165.awsdns-17.org\"],\n    #     :name=>\"mysite.patch-island.com.\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_GetHostedZone.html\n    #\n    def get_hosted_zone(hosted_zone_aws_id)\n      link = generate_request('GET', expand_hosted_zone_id(hosted_zone_aws_id))\n      request_info(link, GetHostedZoneParser.new(:logger => @logger))\n    end\n\n    # Delete hosted zone.\n    #\n    #  r53.delete_hosted_zone(\"/hostedzone/Z2P714ENJN23PN\") #=>\n    #    {:status=>\"PENDING\",\n    #     :submitted_at=>\"2011-01-18T15:45:45.060Z\",\n    #     :aws_id=>\"/change/C1PN1SDWZKPTAC\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_DeleteHostedZone.html\n    #\n    def delete_hosted_zone(hosted_zone_aws_id)\n      link = generate_request('DELETE', expand_hosted_zone_id(hosted_zone_aws_id))\n      request_info(link, GetChangeParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Resource Records Set\n    #-----------------------------------------------------------------\n\n    # List your resource record sets.\n    # Options: :type, :name, :max_items\n    #\n    #  r53.list_resource_record_sets(\"/hostedzone/ZWEC7PPVACGQ4\") #=>\n    #      [{:type=>\"NS\",\n    #        :ttl=>172800,\n    #        :name=>\"mysite.patch-island.com.\",\n    #        :resource_records=>\n    #         [\"ns-459.awsdns-57.com.\",\n    #          \"ns-794.awsdns-35.net.\",\n    #          \"ns-1165.awsdns-17.org.\",\n    #          \"ns-1537.awsdns-00.co.uk.\"]},\n    #       {:type=>\"SOA\",\n    #        :ttl=>900,\n    #        :name=>\"mysite.patch-island.com.\",\n    #        :resource_records=>\n    #         [\"ns-794.awsdns-35.net. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400\"]},\n    #       {:type=>\"NS\",\n    #        :ttl=>600,\n    #        :resource_records=>[\"xxx.mysite.com\"],\n    #        :name=>\"m1.mysite.patch-island.com.\"}]\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ListResourceRecordSets.html\n    #\n    def list_resource_record_sets(hosted_zone_aws_id, options={})\n      options = options.dup\n      result = []\n      incrementally_list_resource_records(\"#{expand_hosted_zone_id(hosted_zone_aws_id)}/rrset\", ListResourceRecordSetsParser, options) do |response|\n        result += response[:items]\n        true\n      end\n      result\n    end\n\n    # Create or delete DNS records.\n    #\n    #  resource_record_sets = [{ :action => :create,\n    #                            :name => 'm3.mysite.patch-island.com',\n    #                            :type => 'NS',\n    #                            :ttl => 600,\n    #                            :resource_records => 'xxx.mysite.com' },\n    #                          { :action => :delete,\n    #                            :name => 'm2.mysite.patch-island.com',\n    #                            :type => 'A',\n    #                            :ttl => 600,\n    #                            :resource_records => ['10.0.0.1'] }]\n    #  r53.change_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'KD: Comment#1') #=>\n    #    {:status=>\"PENDING\",\n    #     :submitted_at=>\"2011-01-18T20:21:56.828Z\",\n    #     :aws_id=>\"/change/C394PNLM1B2P08\"}\n    #\n    # PS: resource_record_sets must have an :action key set (== :create or :delete)\n    # PPS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html\n    #\n    def change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')\n      link = generate_request('POST', \"#{expand_hosted_zone_id(hosted_zone_aws_id)}/rrset\", {}, resource_record_sets_to_xml(resource_record_sets, comment))\n      request_info(link, GetChangeParser.new(:logger => @logger))\n    end\n\n    # Create DNS records.\n    #\n    #  resource_record_sets = [{ :name => 'm3.mysite.patch-island.com',\n    #                            :type => 'NS',\n    #                            :ttl => 600,\n    #                            :resource_records => 'xxx.mysite.com' },\n    #                          { :name => 'm2.mysite.patch-island.com',\n    #                            :type => 'A',\n    #                            :ttl => 600,\n    #                            :resource_records => ['10.0.0.1'] }]\n    #  r53.create_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'KD: Comment#1') #=>\n    #    {:status=>\"PENDING\",\n    #     :submitted_at=>\"2011-01-18T20:21:56.828Z\",\n    #     :aws_id=>\"/change/C394PNLM1B2P08\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html\n    #\n    def create_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')\n      resource_record_sets.each{|rrs| rrs[:action] = :create}\n      change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment)\n    end\n\n    # Delete DNS records.\n    #\n    #  resource_record_sets = [{ :name => 'm3.mysite.patch-island.com',\n    #                            :type => 'NS',\n    #                            :ttl => 600,\n    #                            :resource_records => 'xxx.mysite.com' },\n    #                          { :name => 'm2.mysite.patch-island.com',\n    #                            :type => 'A',\n    #                            :ttl => 600,\n    #                            :resource_records => ['10.0.0.1'] }]\n    #  r53.create_resource_record_sets(\"/hostedzone/Z1K6NCF0EB26FB\", resource_record_sets, 'KD: Comment#1') #=>\n    #    {:status=>\"PENDING\",\n    #     :submitted_at=>\"2011-01-18T20:21:56.828Z\",\n    #     :aws_id=>\"/change/C394PNLM1B2P08\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html\n    #\n    def delete_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment = '')\n      resource_record_sets.each{|rrs| rrs[:action] = :delete}\n      change_resource_record_sets(hosted_zone_aws_id, resource_record_sets, comment)\n    end\n\n\n    # Get the current state of a change request.\n    #\n    #  r53.get_change(\"/change/C1PN1SDWZKPTAC\") #=>\n    #    {:status=>\"INSYNC\",\n    #     :aws_id=>\"/change/C1PN1SDWZKPTAC\",\n    #     :submitted_at=>\"2011-01-18T15:45:45.060Z\"}\n    #\n    # PS: http://docs.amazonwebservices.com/Route53/latest/APIReference/API_GetChange.html\n    #\n    def get_change(change_aws_id)\n      link = generate_request('GET', expand_change_id(change_aws_id))\n      request_info(link, GetChangeParser.new(:logger => @logger))\n    end\n\n    #-----------------------------------------------------------------\n    #      Hosted Zones\n    #-----------------------------------------------------------------\n\n    class ListHostedZonesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'HostedZone' then @item = { :config => {} }\n        end\n      end\n      def tagend(name)\n        case name\n        when 'IsTruncated'     then @result[:is_truncated]   = @text == 'true'\n        when 'NextMarker'      then @result[:next_marker]    = @text\n        when 'MaxItems'        then @result[:max_items]      = @text.to_i\n        when 'Id'              then @item[:aws_id]           = @text\n        when 'Name'            then @item[:name]             = @text\n        when 'CallerReference' then @item[:caller_reference] = @text\n        when 'HostedZone'      then @result[:items]         << @item\n        else\n          case full_tag_name\n          when %r{/Config/Comment$} then @item[:config][:comment] = @text\n          end\n        end\n      end\n    end\n\n    class GetHostedZoneParser < RightAWSParser # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/HostedZone/Id}                         then  @result[:aws_id]                             = @text\n        when %r{/HostedZone/Name}                       then  @result[:name]                               = @text\n        when %r{/HostedZone/CallerReference}            then  @result[:caller_reference]                   = @text\n        when %r{/Config/Comment$}                       then (@result[:config] ||= {})[:comment]           = AwsUtils::xml_unescape(@text)\n        when %r{/ChangeInfo/Id$}                        then (@result[:change_info] ||= {})[:aws_id]       = @text\n        when %r{/ChangeInfo/Status$}                    then (@result[:change_info] ||= {})[:status]       = @text\n        when %r{/ChangeInfo/SubmittedAt$}               then (@result[:change_info] ||= {})[:submitted_at] = @text\n        when %r{/DelegationSet/NameServers/NameServer$} then (@result[:name_servers] ||= [])              << @text\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      Resource Records Set\n    #-----------------------------------------------------------------\n\n    class ListResourceRecordSetsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'ResourceRecordSet' then @item = {}\n        end\n      end\n      def tagend(name)\n        case name\n        when 'IsTruncated'       then @result[:is_truncated]     = @text == 'true'\n        when 'NextRecordName'    then @result[:next_record_name] = @text\n        when 'NextRecordType'    then @result[:next_record_type] = @text\n        when 'MaxItems'          then @result[:max_items]        = @text.to_i\n        when 'Type'              then @item[:type]     = @text\n        when 'Name'              then @item[:name]     = @text\n        when 'TTL'               then @item[:ttl]      = @text.to_i\n        when 'ResourceRecordSet' then @result[:items] << @item\n        else\n          case full_tag_name\n          when %r{/ResourceRecord/Value}     then (@item[:resource_records] ||= []) << @text\n          when %r{/AliasTarget/DNSName}      then (@item[:alias_target] ||= {})[:dns_name] = @text\n          when %r{/AliasTarget/HostedZoneId} then (@item[:alias_target] ||= {})[:hosted_zone_id] = @text\n          end\n        end\n      end\n    end\n\n    class GetChangeParser < RightAWSParser # :nodoc:\n      def reset\n        @result = { }\n      end\n      def tagend(name)\n        case name\n        when 'Id'          then @result[:aws_id]       = @text\n        when 'Status'      then @result[:status]       = @text\n        when 'SubmittedAt' then @result[:submitted_at] = @text\n        end\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "lib/s3/right_s3.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n  \n  # = RightAws::S3 -- RightScale's Amazon S3 interface\n  # The RightAws::S3 class provides a complete interface to Amazon's Simple\n  # Storage Service.\n  # For explanations of the semantics\n  # of each call, please refer to Amazon's documentation at\n  # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=48\n  #\n  # See examples below for the bucket and buckets methods.\n  #\n  # Error handling: all operations raise an RightAws::AwsError in case\n  # of problems. Note that transient errors are automatically retried.\n  #\n  # It is a good way to use domain naming style getting a name for the buckets. \n  # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingBucket.html\n  # about the naming convention for the buckets. This case they can be accessed using a virtual domains.\n  # \n  # Let assume you have 3 buckets: 'awesome-bucket', 'awesome_bucket' and 'AWEsomE-bucket'.\n  # The first ones objects can be accessed as: http:// awesome-bucket.s3.amazonaws.com/key/object\n  # \n  # But the rest have to be accessed as:\n  # http:// s3.amazonaws.com/awesome_bucket/key/object and  http:// s3.amazonaws.com/AWEsomE-bucket/key/object\n  #  \n  # See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html for better explanation.\n  #\n  class S3\n    attr_reader :interface\n    \n    # Create a new handle to an S3 account. All handles share the same per process or per thread\n    # HTTP connection to Amazon S3. Each handle is for a specific account.\n    # The +params+ are passed through as-is to RightAws::S3Interface.new\n    # \n    # Params is a hash:\n    #\n    #    {:server       => 's3.amazonaws.com'   # Amazon service host: 's3.amazonaws.com'(default)\n    #     :port         => 443                  # Amazon service port: 80 or 443(default)\n    #     :protocol     => 'https'              # Amazon service protocol: 'http' or 'https'(default)\n    #     :logger       => Logger Object}       # Logger instance: logs to STDOUT if omitted }\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)\n    end\n    \n    # Retrieve a list of buckets.\n    # Returns an array of RightAws::S3::Bucket instances.\n    #  # Create handle to S3 account\n    #  s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)\n    #  my_buckets_names = s3.buckets.map{|b| b.name}\n    #  puts \"Buckets on S3: #{my_bucket_names.join(', ')}\"\n    def buckets\n      @interface.list_all_my_buckets.map! do |entry|\n        owner = Owner.new(entry[:owner_id], entry[:owner_display_name])\n        Bucket.new(self, entry[:name], entry[:creation_date], owner)\n      end\n    end\n    \n    # Retrieve an individual bucket.\n    # If the bucket does not exist and +create+ is set, a new bucket\n    # is created on S3. Launching this method with +create+=+true+ may\n    # affect on the bucket's ACL if the bucket already exists.\n    # Returns a RightAws::S3::Bucket instance or +nil+ if the bucket does not exist \n    # and +create+ is not set.\n    #\n    #  s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)\n    #  bucket1 = s3.bucket('my_awesome_bucket_1')\n    #  bucket1.keys  #=> exception here if the bucket does not exists\n    #   ...\n    #  bucket2 = s3.bucket('my_awesome_bucket_2', true)\n    #  bucket2.keys  #=> list of keys\n    #  # create a bucket at the European location with public read access\n    #  bucket3 = s3.bucket('my-awesome-bucket-3', true, 'public-read', :location => :eu)\n    #  \n    #  see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html\n    #  (section: Canned Access Policies)\n    #\n    def bucket(name, create=false, perms=nil, headers={})\n      result = nil\n      if create\n        headers['x-amz-acl'] = perms if perms\n        @interface.create_bucket(name, headers) \n      end\n      begin\n        buckets.each do |bucket| \n          if bucket.name == name \n            result = bucket\n            break\n          end\n        end\n      rescue RightAws::AwsError => e\n        # With non root creds one can use bucket(s) but can't list them\n        raise e unless e.message['AccessDenied']\n        result = Bucket::new(self, name)\n      end\n      result\n    end\n    \n\n    class Bucket\n      attr_reader :s3, :name, :owner, :creation_date\n      \n      # Create a Bucket instance.\n      # If the bucket does not exist and +create+ is set, a new bucket\n      # is created on S3. Launching this method with +create+=+true+ may\n      # affect on the bucket's ACL if the bucket already exists.\n      # Returns Bucket instance or +nil+ if the bucket does not exist \n      # and +create+ is not set.\n      #\n      #  s3 = RightAws::S3.new(aws_access_key_id, aws_secret_access_key)\n      #   ...\n      #  bucket1 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_1')\n      #  bucket1.keys  #=> exception here if the bucket does not exists\n      #   ...\n      #  bucket2 = RightAws::S3::Bucket.create(s3, 'my_awesome_bucket_2', true)\n      #  bucket2.keys  #=> list of keys\n      #  # create a bucket at the European location with public read access\n      #  bucket3 = RightAws::S3::Bucket.create(s3,'my-awesome-bucket-3', true, 'public-read', :location => :eu)\n      #  \n      #  see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html\n      #  (section: Canned Access Policies)\n      #\n      def self.create(s3, name, create=false, perms=nil, headers={}) \n        s3.bucket(name, create, perms, headers)\n      end\n\n\n        # Create a bucket instance. In normal use this method should\n        # not be called directly.\n        # Use RightAws::S3::Bucket.create or RightAws::S3.bucket instead. \n      def initialize(s3, name, creation_date=nil, owner=nil)\n        @s3    = s3\n        @name  = name\n        @owner = owner\n        @creation_date = creation_date\n        if @creation_date && !@creation_date.is_a?(Time)\n          @creation_date = Time.parse(@creation_date)\n        end\n      end\n      \n        # Return bucket name as a String.\n        #\n        #  bucket = RightAws::S3.bucket('my_awesome_bucket') \n        #  puts bucket #=> 'my_awesome_bucket'\n        #\n      def to_s\n        @name.to_s\n      end\n      alias_method :full_name, :to_s\n      \n        # Return a public link to bucket.\n        # \n        #  bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'\n        #\n      def public_link\n        params = @s3.interface.params\n        \"#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}\"\n      end\n      \n        # Returns the bucket location\n      def location\n        @location ||= @s3.interface.bucket_location(@name)\n      end\n      \n      # Retrieves the logging configuration for a bucket. \n      # Returns a hash of {:enabled, :targetbucket, :targetprefix}\n      # \n      #   bucket.logging_info()\n      #   => {:enabled=>true, :targetbucket=>\"mylogbucket\", :targetprefix=>\"loggylogs/\"}\n      def logging_info\n        @s3.interface.get_logging_parse(:bucket => @name)\n      end\n      \n      # Enables S3 server access logging on a bucket.  The target bucket must have been properly configured to receive server\n      # access logs.\n      #  Params:\n      #   :targetbucket - either the target bucket object or the name of the target bucket\n      #   :targetprefix - the prefix under which all logs should be stored\n      #\n      #  bucket.enable_logging(:targetbucket=>\"mylogbucket\", :targetprefix=>\"loggylogs/\")\n      #    => true\n      def enable_logging(params)\n        AwsUtils.mandatory_arguments([:targetbucket, :targetprefix], params)\n        AwsUtils.allow_only([:targetbucket, :targetprefix], params)\n        xmldoc = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><BucketLoggingStatus xmlns=\\\"http://doc.s3.amazonaws.com/2006-03-01\\\"><LoggingEnabled><TargetBucket>#{params[:targetbucket]}</TargetBucket><TargetPrefix>#{params[:targetprefix]}</TargetPrefix></LoggingEnabled></BucketLoggingStatus>\"\n        @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)\n      end\n      \n      # Disables S3 server access logging on a bucket.  Takes no arguments.\n      def disable_logging\n        xmldoc = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?><BucketLoggingStatus xmlns=\\\"http://doc.s3.amazonaws.com/2006-03-01\\\"></BucketLoggingStatus>\"\n        @s3.interface.put_logging(:bucket => @name, :xmldoc => xmldoc)\n      end\n\n        # Retrieve a group of keys from Amazon. \n        # +options+ is a hash: { 'prefix'=>'', 'marker'=>'', 'max-keys'=>5, 'delimiter'=>'' }). \n        # Retrieves meta-headers information if +head+ it +true+. \n        # Returns an array of Key instances. \n        #\n        #  bucket.keys                     #=> # returns all keys from bucket\n        #  bucket.keys('prefix' => 'logs') #=> # returns all keys that starts with 'logs'\n        #\n      def keys(options={}, head=false)\n        keys_and_service(options, head)[0]\n      end\n\n        # Same as +keys+ method but return an array of [keys, service_data]. \n        # where +service_data+ is a hash with additional output information.\n        #\n        #  keys, service = bucket.keys_and_service({'max-keys'=> 2, 'prefix' => 'logs'})\n        #  p keys    #=> # 2 keys array\n        #  p service #=> {\"max-keys\"=>\"2\", \"prefix\"=>\"logs\", \"name\"=>\"my_awesome_bucket\", \"marker\"=>\"\", \"is_truncated\"=>true, :common_prefixes=>[]}\n        #\n      def keys_and_service(options={}, head=false)\n        opt = {}; options.each{ |key, value| opt[key.to_s] = value }\n        service = {}\n        keys = []\n        @s3.interface.incrementally_list_bucket(@name, opt) do |_service|\n          service = _service\n          service[:contents].each do |entry|\n            owner = Owner.new(entry[:owner_id], entry[:owner_display_name])\n            key = Key.new(self, entry[:key], nil, {}, {}, entry[:last_modified], entry[:e_tag], entry[:size], entry[:storage_class], owner)\n            key.head if head\n            keys << key\n          end\n        end\n        service.delete(:contents)\n        [keys, service]\n      end\n\n        # Retrieve key information from Amazon. \n        # The +key_name+ is a +String+ or Key instance. \n        # Retrieves meta-header information if +head+ is +true+. \n        # Returns new Key instance. \n        #\n        #  key = bucket.key('logs/today/1.log', true) #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #   # is the same as:\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')\n        #  key.head\n        #\n      def key(key_name, head=false, &blck)\n        raise 'Key name can not be empty.' if key_name.right_blank?\n        key_instance = nil\n          # if this key exists - find it ....\n        keys({'prefix'=>key_name}, head).each do |key|\n          blck.call if block_given?\n          if key.name == key_name.to_s\n            key_instance = key\n            break\n          end\n        end\n          # .... else this key is unknown\n        unless key_instance\n          key_instance = Key.create(self, key_name.to_s)\n        end\n        key_instance\n      end\n      \n        # Store object data. \n        # The +key+ is a +String+ or Key instance. \n        # Returns +true+.\n        #\n        #  bucket.put('logs/today/1.log', 'Olala!') #=> true\n        #\n      def put(key, data=nil, meta_headers={}, perms=nil, headers={}, &blck)\n        key = Key.create(self, key.to_s, data, meta_headers) unless key.is_a?(Key) \n        key.put(data, perms, headers, &blck)\n      end\n\n        # Retrieve data object from Amazon. \n        # The +key+ is a +String+ or Key. \n        # Returns String instance. \n        #\n        #  data = bucket.get('logs/today/1.log') #=> \n        #  puts data #=> 'sasfasfasdf'\n        #\n      def get(key, headers={})\n        key = Key.create(self, key.to_s) unless key.is_a?(Key)\n        key.get(headers)\n      end\n\n        # Rename object. Returns RightAws::S3::Key instance.\n        # \n        #  new_key = bucket.rename_key('logs/today/1.log','logs/today/2.log')   #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  puts key.name   #=> 'logs/today/2.log'\n        #  key.exists?     #=> true\n        #\n      def rename_key(old_key_or_name, new_name)\n        old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)\n        old_key_or_name.rename(new_name)\n        old_key_or_name\n      end\n\n        # Create an object copy. Returns a destination RightAws::S3::Key instance.\n        # \n        #  new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log')   #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  puts key.name   #=> 'logs/today/2.log'\n        #  key.exists?     #=> true\n        #\n      def copy_key(old_key_or_name, new_key_or_name)\n        old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)\n        old_key_or_name.copy(new_key_or_name)\n      end\n      \n        # Move an object to other location. Returns a destination RightAws::S3::Key instance.\n        # \n        #  new_key = bucket.copy_key('logs/today/1.log','logs/today/2.log')   #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  puts key.name   #=> 'logs/today/2.log'\n        #  key.exists?     #=> true\n        #\n      def move_key(old_key_or_name, new_key_or_name)\n        old_key_or_name = Key.create(self, old_key_or_name.to_s) unless old_key_or_name.is_a?(Key)\n        old_key_or_name.move(new_key_or_name)\n      end\n      \n        # Remove all keys from a bucket. \n        # Returns +true+. \n        #\n        #  bucket.clear #=> true\n        #\n      def clear\n        @s3.interface.clear_bucket(@name)  \n      end\n\n        # Delete all keys where the 'folder_key' can be interpreted\n        # as a 'folder' name. \n        # Returns an array of string keys that have been deleted.\n        #\n        #  bucket.keys.map{|key| key.name}.join(', ') #=> 'test, test/2/34, test/3, test1, test1/logs'\n        #  bucket.delete_folder('test')               #=> ['test','test/2/34','test/3']\n        #\n      def delete_folder(folder, separator='/')\n        @s3.interface.delete_folder(@name, folder, separator)\n      end\n      \n        # Delete a bucket. Bucket must be empty.\n        # If +force+ is set, clears and deletes the bucket. \n        # Returns +true+. \n        #\n        #  bucket.delete(:force => true) #=> true\n        #\n      def delete(options={})\n        force = options.is_a?(Hash) && options[:force]==true\n        force ? @s3.interface.force_delete_bucket(@name) : @s3.interface.delete_bucket(@name)\n      end\n\n        # Return a list of grantees. \n        #\n      def grantees\n        Grantee::grantees(self)\n      end\n\n    end\n\n\n    class Key\n      attr_reader   :bucket,  :name, :last_modified, :e_tag, :size, :storage_class, :owner\n      attr_accessor :headers, :meta_headers\n      attr_writer   :data\n\n        # Separate Amazon meta headers from other headers\n      def self.split_meta(headers) #:nodoc:\n        hash = headers.dup\n        meta = {}\n        hash.each do |key, value|\n          if key[/^#{S3Interface::AMAZON_METADATA_PREFIX}/]\n            meta[key.gsub(S3Interface::AMAZON_METADATA_PREFIX,'')] = value\n            hash.delete(key)\n          end\n        end\n        [hash, meta]\n      end\n      \n      def self.add_meta_prefix(meta_headers, prefix=S3Interface::AMAZON_METADATA_PREFIX)\n        meta = {}\n        meta_headers.each do |meta_header, value|\n          if meta_header[/#{prefix}/]\n            meta[meta_header] = value\n          else\n            meta[\"#{S3Interface::AMAZON_METADATA_PREFIX}#{meta_header}\"] = value\n          end\n        end\n        meta\n      end\n\n\n        # Create a new Key instance, but do not create the actual key. \n        # The +name+ is a +String+.\n        # Returns a new Key instance. \n        #\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key.exists?                                                  #=> true | false\n        #  key.put('Woohoo!')                                           #=> true\n        #  key.exists?                                                  #=> true\n        #\n      def self.create(bucket, name, data=nil, meta_headers={})\n        new(bucket, name, data, {}, meta_headers)\n      end\n      \n        # Create a new Key instance, but do not create the actual key.\n        # In normal use this method should not be called directly.\n        # Use RightAws::S3::Key.create or bucket.key() instead. \n        #\n      def initialize(bucket, name, data=nil, headers={}, meta_headers={}, \n                     last_modified=nil, e_tag=nil, size=nil, storage_class=nil, owner=nil)\n        raise 'Bucket must be a Bucket instance.' unless bucket.is_a?(Bucket)\n        @bucket        = bucket\n        @name          = name\n        @data          = data\n        @e_tag         = e_tag\n        @size          = size.to_i\n        @storage_class = storage_class\n        @owner         = owner\n        @last_modified = last_modified\n        if @last_modified && !@last_modified.is_a?(Time) \n          @last_modified = Time.parse(@last_modified)\n        end\n        @headers, @meta_headers = self.class.split_meta(headers)\n        @meta_headers.merge!(meta_headers)\n      end\n      \n        # Return key name as a String.\n        #\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  puts key                                                   #=> 'logs/today/1.log'\n        #\n      def to_s\n        @name.to_s\n      end\n      \n        # Return the full S3 path to this key (bucket/key).\n        # \n        #  key.full_name #=> 'my_awesome_bucket/cool_key'\n        #\n      def full_name(separator='/')\n        \"#{@bucket.to_s}#{separator}#{@name}\"\n      end\n        \n        # Return a public link to a key.\n        # \n        #  key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'\n        #\n      def public_link\n        params = @bucket.s3.interface.params\n        \"#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}\"\n      end\n         \n        # Return Key data. Retrieve this data from Amazon if it is the first time call.\n        # TODO TRB 6/19/07 What does the above mean? Clarify.\n        #\n      def data\n        get if !@data and exists?\n        @data\n      end\n        \n        # Getter for the 'content-type' metadata\n      def content_type\n        @headers['content-type'] if @headers\n      end\n      \n        # Helper to get and URI-decode a header metadata.\n        # Metadata have to be HTTP encoded (rfc2616) as we use the Amazon S3 REST api\n        # see http://docs.amazonwebservices.com/AmazonS3/latest/index.html?UsingMetadata.html\n      def decoded_meta_headers(key = nil)\n        if key\n          # Get one metadata value by its key\n          URI.decode(@meta_headers[key.to_s])\n        else\n          # Get a hash of all metadata with a decoded value\n          @decoded_meta_headers ||= begin\n            metadata = {}\n            @meta_headers.each do |key, value|\n              metadata[key.to_sym] = URI.decode(value)\n            end\n            metadata\n          end\n        end\n      end\n      \n        # Retrieve object data and attributes from Amazon. \n        # Returns a +String+. \n        #\n      def get(headers={})\n        response = @bucket.s3.interface.get(@bucket.name, @name, headers)\n        @data    = response[:object]\n        @headers, @meta_headers = self.class.split_meta(response[:headers])\n        refresh(false)\n        @data\n      end\n      \n        # Store object data on S3. \n        # Parameter +data+ is a +String+ or S3Object instance. \n        # Returns +true+.\n        #\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')\n        #  key.data = 'Qwerty'\n        #  key.put             #=> true\n        #   ...\n        #  key.put('Olala!')   #=> true\n        #\n      def put(data=nil, perms=nil, headers={}, &blck)\n        headers['x-amz-acl'] = perms if perms\n        @data = data || @data\n        meta  = self.class.add_meta_prefix(@meta_headers)\n        @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers), &blck)\n      end\n\n        # Store object data on S3 using the Multipart Upload API. This is useful if you do not know the file size\n        # upfront (for example reading from pipe or socket) or if you are transmitting data over an unreliable network.\n        #\n        # Parameter +data+ is an object which responds to :read or an object which can be converted to a String prior to upload.\n        # Parameter +part_size+ determines the size of each part sent (must be > 5MB per Amazon's API requirements)\n        #\n        # If data is a stream the caller is responsible for calling close() on the stream after this methods returns\n        #\n        # Returns +true+.\n        #\n        #  upload_data = StringIO.new('My sample data')\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')\n        #  key.data = upload_data\n        #  key.put_multipart(:part_size => 5*1024*1024)             #=> true\n        #\n      def put_multipart(data=nil, perms=nil, headers={}, part_size=nil)\n        headers['x-amz-acl'] = perms if perms\n        @data = data || @data\n        meta  = self.class.add_meta_prefix(@meta_headers)\n        @bucket.s3.interface.store_object_multipart({:bucket => @bucket.name, :key => @name, :data => @data, :headers => meta.merge(headers), :part_size => part_size})\n      end\n      \n        # Rename an object. Returns new object name.\n        # \n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key.rename('logs/today/2.log')   #=> 'logs/today/2.log'\n        #  puts key.name                    #=> 'logs/today/2.log'\n        #  key.exists?                      #=> true\n        #\n      def rename(new_name)\n        @bucket.s3.interface.rename(@bucket.name, @name, new_name)\n        @name = new_name\n      end\n      \n        # Create an object copy. Returns a destination RightAws::S3::Key instance.\n        #\n        #  # Key instance as destination\n        #  key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key1.put('Olala!')   #=> true\n        #  key1.copy(key2)      #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key1.exists?         #=> true\n        #  key2.exists?         #=> true\n        #  puts key2.data       #=> 'Olala!'\n        #\n        #  # String as destination\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key.put('Olala!')                          #=> true\n        #  new_key = key.copy('logs/today/888.log')   #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key.exists?                                #=> true\n        #  new_key.exists?                            #=> true\n        #\n      def copy(new_key_or_name)\n        new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)\n        @bucket.s3.interface.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)\n        new_key_or_name\n      end\n\n        # Move an object to other location. Returns a destination RightAws::S3::Key instance.\n        #\n        #  # Key instance as destination\n        #  key1 = RightAws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key2 = RightAws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key1.put('Olala!')   #=> true\n        #  key1.move(key2)      #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key1.exists?         #=> false\n        #  key2.exists?         #=> true\n        #  puts key2.data       #=> 'Olala!'\n        #  \n        #  # String as destination\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<RightAws::S3::Key:0xb7b1e240 ... >\n        #  key.put('Olala!')                          #=> true\n        #  new_key = key.move('logs/today/888.log')   #=> #<RightAws::S3::Key:0xb7b5e240 ... >\n        #  key.exists?                                #=> false\n        #  new_key.exists?                            #=> true\n        #\n      def move(new_key_or_name)\n        new_key_or_name = Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(Key)\n        @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)\n        new_key_or_name\n      end\n      \n        # Retrieve key info from bucket and update attributes.\n        # Refresh meta-headers (by calling +head+ method) if +head+ is set. \n        # Returns +true+ if the key exists in bucket and +false+ otherwise. \n        #\n        #  key = RightAws::S3::Key.create(bucket, 'logs/today/1.log')\n        #  key.e_tag        #=> nil\n        #  key.meta_headers #=> {}\n        #  key.refresh      #=> true\n        #  key.e_tag        #=> '12345678901234567890bf11094484b6'\n        #  key.meta_headers #=> {\"family\"=>\"qwerty\", \"name\"=>\"asdfg\"}\n        #\n      def refresh(head=true)\n        new_key        = @bucket.key(self)\n        @last_modified = new_key.last_modified\n        @e_tag         = new_key.e_tag\n        @size          = new_key.size\n        @storage_class = new_key.storage_class\n        @owner         = new_key.owner\n        if @last_modified\n          self.head\n          true\n        else\n          @headers = @meta_headers = {}\n          false\n        end\n      end\n\n        # Updates headers and meta-headers from S3.\n        # Returns +true+. \n        #\n        #  key.meta_headers #=> {\"family\"=>\"qwerty\"}\n        #  key.head         #=> true\n        #  key.meta_headers #=> {\"family\"=>\"qwerty\", \"name\"=>\"asdfg\"}\n        #\n      def head\n        @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))\n        true\n      end\n      \n        # Reload meta-headers only. Returns meta-headers hash.\n        #\n        #  key.reload_meta   #=> {\"family\"=>\"qwerty\", \"name\"=>\"asdfg\"}\n        #\n      def reload_meta\n        @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last\n      end\n      \n        # Replace meta-headers by new hash at S3. Returns new meta-headers hash.\n        #\n        #  key.reload_meta   #=> {\"family\"=>\"qwerty\", \"name\"=>\"asdfg\"}\n        #  key.save_meta     #=> {\"family\"=>\"oops\", \"race\" => \"troll\"}\n        #  key.reload_meta   #=> {\"family\"=>\"oops\", \"race\" => \"troll\"}\n        #\n      def save_meta(meta_headers)\n        meta = self.class.add_meta_prefix(meta_headers)\n        @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)\n        @meta_headers = self.class.split_meta(meta)[1]\n      end\n \n        # Check for existence of the key in the given bucket. \n        # Returns +true+ or +false+. \n        #\n        #  key = RightAws::S3::Key.create(bucket,'logs/today/1.log')\n        #  key.exists?        #=> false\n        #  key.put('Woohoo!') #=> true\n        #  key.exists?        #=> true\n        #\n      def exists?\n        @bucket.key(self).last_modified ? true : false\n      end\n      \n        # Remove key from bucket. \n        # Returns +true+. \n        #\n        #  key.delete #=> true\n        #\n      def delete\n        raise 'Key name must be specified.' if @name.right_blank?\n        @bucket.s3.interface.delete(@bucket, @name) \n      end\n      \n        # Return a list of grantees. \n        #\n      def grantees\n        Grantee::grantees(self)\n      end\n      \n    end\n    \n\n    class Owner\n      attr_reader :id, :name\n      \n      def initialize(id, name)\n        @id   = id\n        @name = name\n      end\n      \n        # Return Owner name as a +String+.\n      def to_s\n        @name\n      end\n    end\n    \n    \n      # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):\n      #\n      # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>, \n      # <tt>bucket.put</tt> and <tt>key.put</tt> methods. \n      # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and\n      # 'authenticated-read'. \n      # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).\n      #\n      #  bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')\n      #  key.put('Woohoo!','public-read-write' )\n      #\n      # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE', \n      # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):\n      #\n      #  bucket  = s3.bucket('my_awesome_bucket', true)\n      #  grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)\n      #  grantee2 = RightAws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)\n      #\n      # There is only one way to get and to remove permission (via Grantee instances):\n      #\n      #  grantees = bucket.grantees # a list of Grantees that have any access for this bucket\n      #  grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c')\n      #  grantee1.perms #=> returns a list of perms for this grantee to that bucket\n      #    ...\n      #  grantee1.drop             # remove all perms for this grantee\n      #  grantee2.revoke('WRITE')  # revoke write access only\n      #\n    class Grantee\n        # A bucket or a key the grantee has an access to.\n      attr_reader :thing\n        # Grantee Amazon id.\n      attr_reader :id\n        # Grantee display name.\n      attr_reader :name\n        # Array of permissions.\n      attr_accessor :perms\n        \n        # Retrieve Owner information and a list of Grantee instances that have\n        # a access to this thing (bucket or key). \n        #\n        #  bucket = s3.bucket('my_awesome_bucket', true, 'public-read')\n        #   ...\n        #  RightAws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]\n        #\n      def self.owner_and_grantees(thing)\n        if thing.is_a?(Bucket)\n          bucket, key = thing, ''\n        else\n          bucket, key = thing.bucket, thing\n        end\n        hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)\n        owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])\n        \n        grantees = []\n        hash[:grantees].each do |id, params|\n          grantees << new(thing, id, params[:permissions], nil, params[:display_name])\n        end\n        [owner, grantees]\n      end\n\n        # Retrieves a list of Grantees instances that have an access to this thing(bucket or key). \n        #\n        #  bucket = s3.bucket('my_awesome_bucket', true, 'public-read')\n        #   ...\n        #  RightAws::S3::Grantee.grantees(bucket) #=> grantees\n        #\n      def self.grantees(thing)\n        owner_and_grantees(thing)[1]\n      end\n\n      def self.put_acl(thing, owner, grantees) #:nodoc:\n        if thing.is_a?(Bucket)\n          bucket, key = thing, ''\n        else\n          bucket, key = thing.bucket, thing\n        end\n        body = \"<AccessControlPolicy>\" +\n               \"<Owner>\" +\n               \"<ID>#{owner.id}</ID>\" +\n               \"<DisplayName>#{owner.name}</DisplayName>\" +\n               \"</Owner>\" +\n               \"<AccessControlList>\" +\n               grantees.map{|grantee| grantee.to_xml}.join +\n               \"</AccessControlList>\" +\n               \"</AccessControlPolicy>\"\n        bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)\n      end\n\n        # Create a new Grantee instance. \n        # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve\n        # permissions from S3 and update @perms. If +action+ == :apply, then apply\n        # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.\n        # both the actions. This is used for the new grantees that had no perms to \n        # this thing before. The default action is :refresh.\n        #\n        #  bucket = s3.bucket('my_awesome_bucket', true, 'public-read')\n        #  grantee1 = RightAws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)\n        #    ...\n        #  grantee2 = RightAws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)\n        #  grantee3 = RightAws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)  \n        #\n      def initialize(thing, id, perms=[], action=:refresh, name=nil)\n        @thing = thing\n        @id    = id\n        @name  = name\n        @perms = Array(perms)\n        case action\n          when :apply             then apply\n          when :refresh           then refresh\n          when :apply_and_refresh then apply; refresh\n        end\n      end\n      \n        # Return +true+ if the grantee has any permissions to the thing.\n      def exists?\n        self.class.grantees(@thing).each do |grantee|\n          return true if @id == grantee.id\n        end\n        false\n      end\n      \n        # Return Grantee type (+String+): \"Group\", \"AmazonCustomerByEmail\" or \"CanonicalUser\".\n      def type\n        case @id\n        when /^http:/ then \"Group\"\n        when /@/      then \"AmazonCustomerByEmail\"\n        else               \"CanonicalUser\"\n        end\n      end\n \n        # Return a name or an id.\n      def to_s\n        @name || @id\n      end\n      \n        # Add permissions for grantee. \n        # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'. \n        # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .\n        # Returns +true+. \n        #\n        #  grantee.grant('FULL_CONTROL')                  #=> true\n        #  grantee.grant('FULL_CONTROL','WRITE','READ')   #=> true\n        #  grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true\n        #  \n      def grant(*permissions)\n        permissions.flatten!\n        old_perms = @perms.dup\n        @perms   += permissions\n        @perms.uniq!\n        return true if @perms == old_perms\n        apply\n      end\n      \n        # Revoke permissions for grantee. \n        # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'\n        # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .\n        # Default value is 'FULL_CONTROL'. \n        # Returns +true+.\n        #\n        #  grantee.revoke('READ')                   #=> true\n        #  grantee.revoke('FULL_CONTROL','WRITE')   #=> true\n        #  grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true\n        #\n      def revoke(*permissions)\n        permissions.flatten!\n        old_perms = @perms.dup\n        @perms   -= permissions\n        @perms.uniq!\n        return true if @perms == old_perms\n        apply\n      end\n     \n        # Revoke all permissions for this grantee. \n        # Returns +true+.\n        #\n        #  grantee.drop #=> true\n        #\n      def drop\n        @perms = []\n        apply\n      end\n         \n        # Refresh grantee perms for its +thing+.\n        # Returns +true+ if the grantee has perms for this +thing+ or\n        # +false+ otherwise, and updates @perms value as a side-effect.\n        #\n        #  grantee.grant('FULL_CONTROL') #=> true\n        #  grantee.refresh               #=> true\n        #  grantee.drop                  #=> true\n        #  grantee.refresh               #=> false\n        #\n      def refresh\n        @perms = []\n        self.class.grantees(@thing).each do |grantee|\n          if @id == grantee.id\n            @name  = grantee.name\n            @perms = grantee.perms\n            return true\n          end\n        end\n        false\n      end\n\n        # Apply current grantee @perms to +thing+. This method is called internally by the +grant+\n        # and +revoke+ methods. In normal use this method should not\n        # be called directly.\n        # \n        #  grantee.perms = ['FULL_CONTROL']\n        #  grantee.apply #=> true\n        #\n      def apply\n        @perms.uniq!\n        owner, grantees = self.class.owner_and_grantees(@thing)\n        # walk through all the grantees and replace the data for the current one and ...\n        grantees.map! { |grantee| grantee.id == @id ? self : grantee }\n        # ... if this grantee is not known - add this bad boy to a list\n        grantees << self unless grantees.include?(self)\n        # set permissions\n        self.class.put_acl(@thing, owner, grantees)\n      end\n\n      def to_xml   # :nodoc:\n        id_str = case @id\n                 when /^http/ then \"<URI>#{@id}</URI>\"\n                 when /@/     then \"<EmailAddress>#{@id}</EmailAddress>\"\n                 else              \"<ID>#{@id}</ID>\"\n                 end\n        grants = ''\n        @perms.each do |perm|\n          grants << \"<Grant>\"    +\n                    \"<Grantee xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" \" +\n                      \"xsi:type=\\\"#{type}\\\">#{id_str}</Grantee>\" +\n                    \"<Permission>#{perm}</Permission>\" +\n                    \"</Grant>\"\n        end\n        grants\n      end\n\n    end\n    \n  end\n\n    # RightAws::S3Generator and RightAws::S3Generator::Bucket methods:\n    #\n    #  s3g = RightAws::S3Generator.new('1...2', 'nx...Y6') #=> #<RightAws::S3Generator:0xb7b5cc94>\n    #\n    #    # List all buckets(method 'GET'):\n    #  buckets_list = s3g.buckets #=> 'https://s3.amazonaws.com:443/?Signature=Y...D&Expires=1180941864&AWSAccessKeyId=1...2'\n    #    # Create bucket link (method 'PUT'):\n    #  bucket = s3g.bucket('my_awesome_bucket')     #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>\n    #  link_to_create = bucket.create_link(1.hour)  #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2\n    #    # ... or:\n    #  bucket = RightAws::S3Generator::Bucket.create(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>\n    #  link_to_create = bucket.create_link(1.hour)                                 #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2\n    #    # ... or:\n    #  bucket = RightAws::S3Generator::Bucket.new(s3g, 'my_awesome_bucket') #=> #<RightAws::S3Generator::Bucket:0xb7bcbda8>\n    #  link_to_create = bucket.create_link(1.hour)                              #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=4...D&Expires=1180942132&AWSAccessKeyId=1...2\n    #    # List bucket(method 'GET'):\n    #  bucket.keys(1.day) #=> https://s3.amazonaws.com:443/my_awesome_bucket?Signature=i...D&Expires=1180942620&AWSAccessKeyId=1...2\n    #    # Create/put key (method 'PUT'):\n    #  bucket.put('my_cool_key') #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=q...D&Expires=1180943094&AWSAccessKeyId=1...2\n    #    # Get key data (method 'GET'):\n    #  bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2\n    #    # Delete bucket (method 'DELETE'): \n    #  bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2\n    #  \n    # RightAws::S3Generator::Key methods:\n    #\n    #    # Create Key instance:  \n    #  key = RightAws::S3Generator::Key.new(bicket, 'my_cool_key') #=> #<RightAws::S3Generator::Key:0xb7b7394c>\n    #    # Put key data (method 'PUT'):\n    #  key.put    #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=2...D&Expires=1180943302&AWSAccessKeyId=1...2\n    #    # Get key data (method 'GET'):\n    #  key.get    #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=a...D&Expires=1180820032&AWSAccessKeyId=1...2\n    #    # Head key (method 'HEAD'):\n    #  key.head   #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=b...D&Expires=1180820032&AWSAccessKeyId=1...2\n    #    # Delete key (method 'DELETE'):\n    #  key.delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=x...D&Expires=1180820032&AWSAccessKeyId=1...2\n    #\n  class S3Generator\n    attr_reader :interface\n    \n    def initialize(aws_access_key_id, aws_secret_access_key, params={})\n      @interface = S3Interface.new(aws_access_key_id, aws_secret_access_key, params)\n    end\n    \n      # Generate link to list all buckets\n      #\n      #  s3.buckets(1.hour)\n      #\n    def buckets(expires=nil, headers={})\n      @interface.list_all_my_buckets_link(expires, headers)\n    end\n\n      # Create new S3LinkBucket instance and generate link to create it at S3.\n      #\n      #  bucket= s3.bucket('my_owesome_bucket')\n      #\n    def bucket(name, expires=nil, headers={})\n      Bucket.create(self, name.to_s)\n    end\n  \n    class Bucket\n      attr_reader :s3, :name\n      \n      def to_s\n        @name\n      end\n      alias_method :full_name, :to_s\n        \n        # Return a public link to bucket.\n        # \n        #  bucket.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket'\n        #\n      def public_link\n        params = @s3.interface.params\n        \"#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name}\"\n      end\n      \n        #  Create new S3LinkBucket instance and generate creation link for it. \n      def self.create(s3, name, expires=nil, headers={})\n        new(s3, name.to_s)\n      end\n        \n        #  Create new S3LinkBucket instance. \n      def initialize(s3, name)\n        @s3, @name = s3, name.to_s\n      end\n        \n        # Return a link to create this bucket. \n        #\n      def create_link(expires=nil, headers={})\n        @s3.interface.create_bucket_link(@name, expires, headers)\n      end\n\n        # Generate link to list keys. \n        #\n        #  bucket.keys\n        #  bucket.keys('prefix'=>'logs')\n        #\n      def keys(options=nil, expires=nil, headers={})\n        @s3.interface.list_bucket_link(@name, options, expires, headers)\n      end\n\n        # Return a S3Generator::Key instance.\n        #\n        #  bucket.key('my_cool_key').get    #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2\n        #  bucket.key('my_cool_key').delete #=> https://s3.amazonaws.com:443/my_awesome_bucket/my_cool_key?Signature=B...D&Expires=1180820098&AWSAccessKeyId=1...2\n        #\n      def key(name)\n        Key.new(self, name)\n      end\n\n        # Generates link to PUT key data. \n        #\n        #  puts bucket.put('logs/today/1.log', 2.hour)\n        #\n      def put(key, meta_headers={}, expires=nil, headers={})\n        meta = RightAws::S3::Key.add_meta_prefix(meta_headers)\n        @s3.interface.put_link(@name, key.to_s, nil, expires, meta.merge(headers))\n      end\n        \n        # Generate link to GET key data. \n        #\n        #  bucket.get('logs/today/1.log', 1.hour)\n        #\n      def get(key, expires=nil, headers={}, response_params={})\n        @s3.interface.get_link(@name, key.to_s, expires, headers, response_params)\n      end\n       \n        # Generate link to delete bucket. \n        #\n        #  bucket.delete(2.hour)\n        #\n      def delete(expires=nil,  headers={})\n        @s3.interface.delete_bucket_link(@name, expires,  headers)\n      end\n    end\n\n\n    class Key\n      attr_reader :bucket, :name\n      \n      def to_s\n        @name\n      end\n      \n        # Return a full S# name (bucket/key).\n        # \n        #  key.full_name #=> 'my_awesome_bucket/cool_key'\n        #\n      def full_name(separator='/')\n        \"#{@bucket.to_s}#{separator}#{@name}\"\n      end\n        \n        # Return a public link to key.\n        # \n        #  key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'\n        #\n      def public_link\n        params = @bucket.s3.interface.params\n        \"#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}\"\n      end\n      \n      def initialize(bucket, name, meta_headers={})\n        @bucket       = bucket\n        @name         = name.to_s\n        @meta_headers = meta_headers\n        raise 'Key name can not be empty.' if @name.right_blank?\n      end\n      \n        # Generate link to PUT key data. \n        #\n        #  puts bucket.put('logs/today/1.log', '123', 2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=B...D&Expires=1180820032&AWSAccessKeyId=1...2\n        #\n      def put(expires=nil, headers={})\n        @bucket.put(@name.to_s, @meta_headers, expires, headers)\n      end\n        \n        # Generate link to GET key data. \n        #\n        #  bucket.get('logs/today/1.log', 1.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=h...M%3D&Expires=1180820032&AWSAccessKeyId=1...2\n        #\n      def get(expires=nil, headers={}, response_params={})\n        @bucket.s3.interface.get_link(@bucket.to_s, @name, expires, headers, response_params)\n      end\n       \n        # Generate link to delete key. \n        #\n        #  bucket.delete(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2\n        #\n      def delete(expires=nil,  headers={})\n        @bucket.s3.interface.delete_link(@bucket.to_s, @name, expires,  headers)\n      end\n      \n        # Generate link to head key. \n        #\n        #  bucket.head(2.hour) #=> https://s3.amazonaws.com:443/my_awesome_bucket/logs%2Ftoday%2F1.log?Signature=4...D&Expires=1180820032&AWSAccessKeyId=1...2\n        #\n      def head(expires=nil,  headers={})\n        @bucket.s3.interface.head_link(@bucket.to_s, @name, expires,  headers)\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "lib/s3/right_s3_interface.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class S3Interface < RightAwsBase\n    \n    USE_100_CONTINUE_PUT_SIZE = 1_000_000\n    MINIMUM_PART_SIZE         = 5 * 1024 * 1024\n    DEFAULT_RETRY_COUNT       = 5\n    \n    include RightAwsBaseInterface\n    \n    DEFAULT_HOST           = 's3.amazonaws.com'\n    DEFAULT_PORT           = 443\n    DEFAULT_PROTOCOL       = 'https'\n    DEFAULT_SERVICE        = '/'\n    REQUEST_TTL            = 30\n    DEFAULT_EXPIRES_AFTER  =   1 * 24 * 60 * 60 # One day's worth of seconds\n    ONE_YEAR_IN_SECONDS    = 365 * 24 * 60 * 60\n    AMAZON_HEADER_PREFIX   = 'x-amz-'\n    AMAZON_METADATA_PREFIX = 'x-amz-meta-'\n    S3_REQUEST_PARAMETERS = [ 'acl',\n        'location',\n        'logging', # this one is beta, no support for now\n        'partNumber',\n        'response-content-type',\n        'response-content-language',\n        'response-expires',\n        'response-cache-control',\n        'response-content-disposition',\n        'response-content-encoding',\n        'torrent',\n        'uploadId',\n        'uploads',\n        'delete'].sort\n    MULTI_OBJECT_DELETE_MAX_KEYS = 1000\n\n    \n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_s3\n      @@bench.service\n    end\n\n      # Params supported:\n      #  :no_subdomains => true # do not use bucket as a part of domain name but as a part of path\n    @@params = {}\n    def self.params\n      @@params\n    end\n\n      # get custom option\n    def param(name)\n      # - check explicitly defined param (@params)\n      # - otherwise check implicitly defined one (@@params)\n      @params.has_key?(name) ? @params[name] : @@params[name]\n    end\n\n      # Creates new RightS3 instance.\n      #\n      #  s3 = RightAws::S3Interface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #<RightAws::S3Interface:0xb7b3c27c>\n      #  \n      # Params is a hash:\n      #\n      #    {:server        => 's3.amazonaws.com'   # Amazon service host: 's3.amazonaws.com'(default)\n      #     :port          => 443                  # Amazon service port: 80 or 443(default)\n      #     :protocol      => 'https'              # Amazon service protocol: 'http' or 'https'(default)\n      #     :logger        => Logger Object        # Logger instance: logs to STDOUT if omitted\n      #     :no_subdomains => true}                # Force placing bucket name into path instead of domain name\n      #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name             => 'S3', \n             :default_host     => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).host   : DEFAULT_HOST, \n             :default_port     => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).port   : DEFAULT_PORT,\n             :default_service  => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).path   : DEFAULT_SERVICE,\n             :default_protocol => ENV['S3_URL'] ? URI.parse(ENV['S3_URL']).scheme : DEFAULT_PROTOCOL }, \n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n\n\n  #-----------------------------------------------------------------\n  #      Requests\n  #-----------------------------------------------------------------\n      # Produces canonical string for signing.\n    def canonical_string(method, path, headers={}, expires=nil) # :nodoc:\n      s3_headers = {}\n      headers.each do |key, value|\n        key = key.downcase\n        value = case\n                when value.is_a?(Array) then value.join('')\n                else                         value.to_s\n                end\n        s3_headers[key] = value.strip if key[/^#{AMAZON_HEADER_PREFIX}|^content-md5$|^content-type$|^date$/o]\n      end\n      s3_headers['content-type'] ||= ''\n      s3_headers['content-md5']  ||= ''\n      s3_headers['date']           = ''      if s3_headers.has_key? 'x-amz-date'\n      s3_headers['date']           = expires if expires\n        # prepare output string\n      out_string = \"#{method}\\n\"\n      s3_headers.sort { |a, b| a[0] <=> b[0] }.each do |key, value|\n        out_string << (key[/^#{AMAZON_HEADER_PREFIX}/o] ? \"#{key}:#{value}\\n\" : \"#{value}\\n\")\n      end\n      # ignore everything after the question mark by default...\n      out_string << path.gsub(/\\?.*$/, '')\n      # ... unless there is a parameter that we care about.\n      S3_REQUEST_PARAMETERS.each do |parameter|\n        if path[/[&?]#{parameter}(=[^&]*)?($|&)/]\n          if $1\n            value = CGI::unescape($1)\n          else\n            value = ''\n          end\n          out_string << (out_string[/[?]/] ? \"&#{parameter}#{value}\" : \"?#{parameter}#{value}\")\n        end\n      end\n\n      out_string\n    end\n\n    # http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?BucketRestrictions.html\n    def is_dns_bucket?(bucket_name)\n      bucket_name = bucket_name.to_s\n      return nil unless (3..63) === bucket_name.size\n      bucket_name.split('.').each do |component|\n        return nil unless component[/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/]\n      end\n      true\n    end\n\n    def fetch_request_params(headers) #:nodoc:\n      # default server to use\n      server  = @params[:server]\n      service = @params[:service].to_s\n      service.chop! if service[%r{/$}] # remove trailing '/' from service\n      # extract bucket name and check it's dns compartibility\n      headers[:url].to_s[%r{^([a-z0-9._-]*)(/[^?]*)?(\\?.+)?}i]\n      bucket_name, key_path, params_list = $1, $2, $3\n      key_path = key_path.gsub( '%2F', '/' ) if key_path\n      # select request model\n      if !param(:no_subdomains) && is_dns_bucket?(bucket_name)\n        # fix a path\n        server = \"#{bucket_name}.#{server}\"\n        key_path ||= '/'\n        path = \"#{service}#{key_path}#{params_list}\"\n      else\n        path = \"#{service}/#{bucket_name}#{key_path}#{params_list}\"\n      end\n      path_to_sign = \"#{service}/#{bucket_name}#{key_path}#{params_list}\"\n#      path_to_sign = \"/#{bucket_name}#{key_path}#{params_list}\"\n      [ server, path, path_to_sign ]\n    end\n\n      # Generates request hash for REST API.\n      # Assumes that headers[:url] is URL encoded (use CGI::escape)\n    def generate_rest_request(method, headers)  # :nodoc:\n        # calculate request data\n      server, path, path_to_sign = fetch_request_params(headers)\n      data = headers[:data]\n        # make sure headers are downcased strings\n      headers = AwsUtils::fix_headers(headers)\n        #\n      headers['content-type'] ||= ''\n      headers['date']           = Time.now.httpdate\n        # create request\n      request      = \"Net::HTTP::#{method.capitalize}\".right_constantize.new(path)\n      request.body = data if data\n        # set request headers and meta headers\n      headers.each      { |key, value| request[key.to_s] = value }\n        #generate auth strings\n      auth_string = canonical_string(request.method, path_to_sign, request.to_hash)\n      signature   = AwsUtils::sign(@aws_secret_access_key, auth_string)\n        # set other headers\n      request['Authorization'] = \"AWS #{@aws_access_key_id}:#{signature}\"\n        # prepare output hash\n      { :request  => request, \n        :server   => server,\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n      end\n      \n      # Sends request to Amazon and parses the response.\n      # Raises AwsError if any banana happened.\n    def request_info(request, parser, &block) # :nodoc:\n      request_info_impl(:s3_connection, @@bench, request, parser, &block)\n    end\n\n      # Returns an array of customer's buckets. Each item is a +hash+.\n      #\n      #  s3.list_all_my_buckets #=> \n      #    [{:owner_id           => \"00000000009314cc309ffe736daa2b264357476c7fea6efb2c3347ac3ab2792a\",\n      #      :owner_display_name => \"root\",\n      #      :name               => \"bucket_name\",\n      #      :creation_date      => \"2007-04-19T18:47:43.000Z\"}, ..., {...}]\n      #\n    def list_all_my_buckets(headers={})\n      req_hash = generate_rest_request('GET', headers.merge(:url=>''))\n      request_info(req_hash, S3ListAllMyBucketsParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Creates new bucket. Returns +true+ or an exception.\n      #\n      #  # create a bucket at American server\n      #  s3.create_bucket('my-awesome-bucket-us') #=> true\n      #  # create a bucket at European server\n      #  s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true\n      #\n    def create_bucket(bucket, headers={})\n      data = nil\n      location = case headers[:location].to_s\n                 when 'us','US' then ''\n                 when 'eu'      then 'EU'\n                 else                headers[:location].to_s\n                 end\n      \n      unless location.right_blank?\n        data = \"<CreateBucketConfiguration><LocationConstraint>#{location}</LocationConstraint></CreateBucketConfiguration>\"\n      end\n      req_hash = generate_rest_request('PUT', headers.merge(:url=>bucket, :data => data))\n      request_info(req_hash, RightHttp2xxParser.new)\n    rescue Exception => e\n        # if the bucket exists AWS returns an error for the location constraint interface. Drop it\n      e.is_a?(RightAws::AwsError) && e.message.include?('BucketAlreadyOwnedByYou') ? true  : on_exception\n    end\n    \n      # Retrieve bucket location\n      # \n      #  s3.create_bucket('my-awesome-bucket-us')        #=> true\n      #  puts s3.bucket_location('my-awesome-bucket-us') #=> '' (Amazon's default value assumed)\n      #\n      #  s3.create_bucket('my-awesome-bucket-eu', :location => :eu) #=> true\n      #  puts s3.bucket_location('my-awesome-bucket-eu')            #=> 'EU'\n      #\n    def bucket_location(bucket, headers={})\n      req_hash = generate_rest_request('GET', headers.merge(:url=>\"#{bucket}?location\"))\n      request_info(req_hash, S3BucketLocationParser.new)\n    rescue\n      on_exception\n    end\n    \n    # Retrieves the logging configuration for a bucket. \n      # Returns a hash of {:enabled, :targetbucket, :targetprefix}\n      # \n      # s3.interface.get_logging_parse(:bucket => \"asset_bucket\")\n      #   => {:enabled=>true, :targetbucket=>\"mylogbucket\", :targetprefix=>\"loggylogs/\"}\n      #\n      #  \n    def get_logging_parse(params)\n      AwsUtils.mandatory_arguments([:bucket], params)\n      AwsUtils.allow_only([:bucket, :headers], params)\n      params[:headers] = {} unless params[:headers]\n      req_hash = generate_rest_request('GET', params[:headers].merge(:url=>\"#{params[:bucket]}?logging\"))\n      request_info(req_hash, S3LoggingParser.new)\n    rescue\n      on_exception\n    end\n    \n    # Sets logging configuration for a bucket from the XML configuration document.\n    #   params:\n    #    :bucket\n    #    :xmldoc\n    def put_logging(params)  \n      AwsUtils.mandatory_arguments([:bucket,:xmldoc], params)\n      AwsUtils.allow_only([:bucket,:xmldoc, :headers], params)\n      params[:headers] = {} unless params[:headers]\n      req_hash = generate_rest_request('PUT', params[:headers].merge(:url=>\"#{params[:bucket]}?logging\", :data => params[:xmldoc]))\n      request_info(req_hash, RightHttp2xxParser.new)\n    rescue\n      on_exception\n    end\n\n      # Deletes new bucket. Bucket must be empty! Returns +true+ or an exception.\n      #\n      #  s3.delete_bucket('my_awesome_bucket')  #=> true\n      # \n      # See also: force_delete_bucket method\n      #\n    def delete_bucket(bucket, headers={})\n      req_hash = generate_rest_request('DELETE', headers.merge(:url=>bucket))\n      request_info(req_hash, RightHttp2xxParser.new)\n    rescue\n      on_exception\n    end\n    \n      # Returns an array of bucket's keys. Each array item (key data) is a +hash+.\n      #\n      #  s3.list_bucket('my_awesome_bucket', { 'prefix'=>'t', 'marker'=>'', 'max-keys'=>5, delimiter=>'' }) #=>\n      #    [{:key                => \"test1\",\n      #      :last_modified      => \"2007-05-18T07:00:59.000Z\",\n      #      :owner_id           => \"00000000009314cc309ffe736daa2b264357476c7fea6efb2c3347ac3ab2792a\",\n      #      :owner_display_name => \"root\",\n      #      :e_tag              => \"000000000059075b964b07152d234b70\",\n      #      :storage_class      => \"STANDARD\",\n      #      :size               => 3,\n      #      :service=> {'is_truncated' => false,\n      #                  'prefix'       => \"t\",\n      #                  'marker'       => \"\",\n      #                  'name'         => \"my_awesome_bucket\",\n      #                  'max-keys'     => \"5\"}, ..., {...}]\n      #\n    def list_bucket(bucket, options={}, headers={})\n      bucket  += '?'+options.map{|k, v| \"#{k.to_s}=#{CGI::escape v.to_s}\"}.join('&') unless options.right_blank?\n      req_hash = generate_rest_request('GET', headers.merge(:url=>bucket))\n      request_info(req_hash, S3ListBucketParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Incrementally list the contents of a bucket. Yields the following hash to a block:\n    #  s3.incrementally_list_bucket('my_awesome_bucket', { 'prefix'=>'t', 'marker'=>'', 'max-keys'=>5, delimiter=>'' }) yields  \n    #   {\n    #     :name => 'bucketname',\n    #     :prefix => 'subfolder/',\n    #     :marker => 'fileN.jpg',\n    #     :max_keys => 234,\n    #     :delimiter => '/',\n    #     :is_truncated => true,\n    #     :next_marker => 'fileX.jpg',\n    #     :contents => [\n    #       { :key => \"file1\",\n    #         :last_modified => \"2007-05-18T07:00:59.000Z\",\n    #         :e_tag => \"000000000059075b964b07152d234b70\",\n    #         :size => 3,\n    #         :storage_class => \"STANDARD\",\n    #         :owner_id => \"00000000009314cc309ffe736daa2b264357476c7fea6efb2c3347ac3ab2792a\",\n    #         :owner_display_name => \"root\"\n    #       }, { :key, ...}, ... {:key, ...}\n    #     ]\n    #     :common_prefixes => [\n    #       \"prefix1\",\n    #       \"prefix2\",\n    #       ...,\n    #       \"prefixN\"\n    #     ]\n    #   }\n    def incrementally_list_bucket(bucket, options={}, headers={}, &block)\n      internal_options = options.right_symbolize_keys\n      begin \n        internal_bucket = bucket.dup\n        internal_bucket  += '?'+internal_options.map{|k, v| \"#{k.to_s}=#{CGI::escape v.to_s}\"}.join('&') unless internal_options.right_blank?\n        req_hash = generate_rest_request('GET', headers.merge(:url=>internal_bucket))\n        response = request_info(req_hash, S3ImprovedListBucketParser.new(:logger => @logger))\n        there_are_more_keys = response[:is_truncated]\n        if(there_are_more_keys)\n          internal_options[:marker] = decide_marker(response)\n          total_results = response[:contents].length + response[:common_prefixes].length\n          internal_options[:'max-keys'] ? (internal_options[:'max-keys'] -= total_results) : nil \n        end\n        yield response\n      end while there_are_more_keys && under_max_keys(internal_options)\n      true\n    rescue\n      on_exception\n    end\n    \n    \n    private\n    def decide_marker(response)\n      return response[:next_marker].dup if response[:next_marker]\n      last_key = response[:contents].last[:key]\n      last_prefix = response[:common_prefixes].last\n      if(!last_key)\n        return nil if(!last_prefix)\n        last_prefix.dup\n      elsif(!last_prefix)\n        last_key.dup\n      else\n        last_key > last_prefix ? last_key.dup : last_prefix.dup\n      end\n    end\n    \n    def under_max_keys(internal_options)\n      internal_options[:'max-keys'] ? internal_options[:'max-keys'] > 0 : true\n    end\n    \n    public\n      # Saves object to Amazon. Returns +true+  or an exception.\n      # Any header starting with AMAZON_METADATA_PREFIX is considered\n      # user metadata. It will be stored with the object and returned\n      # when you retrieve the object. The total size of the HTTP\n      # request, not including the body, must be less than 4 KB.\n      #\n      #  s3.put('my_awesome_bucket', 'log/current/1.log', 'Ola-la!', 'x-amz-meta-family'=>'Woho556!') #=> true\n      #\n      # This method is capable of 'streaming' uploads; that is, it can upload\n      # data from a file or other IO object without first reading all the data\n      # into memory.  This is most useful for large PUTs - it is difficult to read\n      # a 2 GB file entirely into memory before sending it to S3.\n      # To stream an upload, pass an object that responds to 'read' (like the read\n      # method of IO) and to either 'lstat' or 'size'.  For files, this means\n      # streaming is enabled by simply making the call:\n      #\n      #  s3.put(bucket_name, 'S3keyname.forthisfile',  File.open('localfilename.dat'))\n      #\n      # If the IO object you wish to stream from responds to the read method but\n      # doesn't implement lstat or size, you can extend the object dynamically\n      # to implement these methods, or define your own class which defines these\n      # methods.  Be sure that your class returns 'nil' from read() after having\n      # read 'size' bytes. Otherwise S3 will drop the socket after\n      # 'Content-Length' bytes have been uploaded, and HttpConnection will\n      # interpret this as an error. \n      #    \n      # This method now supports very large PUTs, where very large\n      # is > 2 GB. \n      # \n      # For Win32 users: Files and IO objects should be opened in binary mode.  If\n      # a text mode IO object is passed to PUT, it will be converted to binary\n      # mode.\n      #\n      \n    def put(bucket, key, data=nil, headers={}, &blck)\n      # On Windows, if someone opens a file in text mode, we must reset it so\n      # to binary mode for streaming to work properly\n      if(data.respond_to?(:binmode))\n        data.binmode\n      end\n      if (data.respond_to?(:lstat) && data.lstat.size >= USE_100_CONTINUE_PUT_SIZE) ||\n         (data.respond_to?(:size)  && data.size       >= USE_100_CONTINUE_PUT_SIZE)\n        headers['expect'] = '100-continue'\n      end\n      req_hash = generate_rest_request('PUT', headers.merge(:url=>\"#{bucket}/#{CGI::escape key}\", :data=>data))\n      request_info(req_hash, RightHttp2xxParser.new, &blck)\n    rescue\n      on_exception\n    end\n   \n   \n    \n    # New experimental API for uploading objects, introduced in RightAws 1.8.1.\n    # store_object is similar in function to the older function put, but returns the full response metadata.  It also allows for optional verification\n    # of object md5 checksums on upload.  Parameters are passed as hash entries and are checked for completeness as well as for spurious arguments.\n    # The hash of the response headers contains useful information like the Amazon request ID and the object ETag (MD5 checksum).\n    #\n    # If the optional :md5 argument is provided, store_object verifies that the given md5 matches the md5 returned by S3.  The :verified_md5 field in the response hash is\n    # set true or false depending on the outcome of this check.  If no :md5 argument is given, :verified_md5 will be false in the response.\n    #\n    # The optional argument of :headers allows the caller to specify arbitrary request header values.\n    #\n    # s3.store_object(:bucket => \"foobucket\", :key => \"foo\", :md5 => \"a507841b1bc8115094b00bbe8c1b2954\", :data => \"polemonium\" )\n    #   => {\"x-amz-id-2\"=>\"SVsnS2nfDaR+ixyJUlRKM8GndRyEMS16+oZRieamuL61pPxPaTuWrWtlYaEhYrI/\", \n    #       \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n    #       \"date\"=>\"Mon, 29 Sep 2008 18:57:46 GMT\", \n    #       :verified_md5=>true, \n    #       \"x-amz-request-id\"=>\"63916465939995BA\", \n    #       \"server\"=>\"AmazonS3\", \n    #       \"content-length\"=>\"0\"}\n    #\n    # s3.store_object(:bucket => \"foobucket\", :key => \"foo\", :data => \"polemonium\" )\n    #   => {\"x-amz-id-2\"=>\"MAt9PLjgLX9UYJ5tV2fI/5dBZdpFjlzRVpWgBDpvZpl+V+gJFcBMW2L+LBstYpbR\", \n    #       \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n    #       \"date\"=>\"Mon, 29 Sep 2008 18:58:56 GMT\", \n    #       :verified_md5=>false, \n    #       \"x-amz-request-id\"=>\"3B25A996BC2CDD3B\", \n    #       \"server\"=>\"AmazonS3\", \n    #       \"content-length\"=>\"0\"}\n    \n    def store_object(params)\n      AwsUtils.allow_only([:bucket, :key, :data, :headers, :md5], params)\n      AwsUtils.mandatory_arguments([:bucket, :key, :data], params)\n      params[:headers] = {} unless params[:headers]\n          \n      params[:data].binmode if(params[:data].respond_to?(:binmode)) # On Windows, if someone opens a file in text mode, we must reset it to binary mode for streaming to work properly\n      if (params[:data].respond_to?(:lstat) && params[:data].lstat.size >= USE_100_CONTINUE_PUT_SIZE) ||\n         (params[:data].respond_to?(:size)  && params[:data].size       >= USE_100_CONTINUE_PUT_SIZE)\n        params[:headers]['expect'] = '100-continue'\n      end\n      \n      req_hash = generate_rest_request('PUT', params[:headers].merge(:url=>\"#{params[:bucket]}/#{CGI::escape params[:key]}\", :data=>params[:data]))\n      resp = request_info(req_hash, S3HttpResponseHeadParser.new)\n      if(params[:md5])\n        resp[:verified_md5] = (resp['etag'].gsub(/\\\"/, '') == params[:md5]) ? true : false\n      else\n        resp[:verified_md5] = false\n      end\n      resp\n    rescue\n      on_exception\n    end\n    \n      # Identical in function to store_object, but requires verification that the returned ETag is identical to the checksum passed in by the user as the 'md5' argument.\n      # If the check passes, returns the response metadata with the \"verified_md5\" field set true.  Raises an exception if the checksums conflict.\n      # This call is implemented as a wrapper around store_object and the user may gain different semantics by creating a custom wrapper.\n      # \n      # s3.store_object_and_verify(:bucket => \"foobucket\", :key => \"foo\", :md5 => \"a507841b1bc8115094b00bbe8c1b2954\", :data => \"polemonium\" )\n      #   => {\"x-amz-id-2\"=>\"IZN3XsH4FlBU0+XYkFTfHwaiF1tNzrm6dIW2EM/cthKvl71nldfVC0oVQyydzWpb\", \n      #       \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n      #       \"date\"=>\"Mon, 29 Sep 2008 18:38:32 GMT\", \n      #       :verified_md5=>true, \n      #       \"x-amz-request-id\"=>\"E8D7EA4FE00F5DF7\", \n      #       \"server\"=>\"AmazonS3\", \n      #       \"content-length\"=>\"0\"}\n      #\n      # s3.store_object_and_verify(:bucket => \"foobucket\", :key => \"foo\", :md5 => \"a507841b1bc8115094b00bbe8c1b2953\", :data => \"polemonium\" )\n      #   RightAws::AwsError: Uploaded object failed MD5 checksum verification: {\"x-amz-id-2\"=>\"HTxVtd2bf7UHHDn+WzEH43MkEjFZ26xuYvUzbstkV6nrWvECRWQWFSx91z/bl03n\", \n      #                                                                          \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n      #                                                                          \"date\"=>\"Mon, 29 Sep 2008 18:38:41 GMT\", \n      #                                                                          :verified_md5=>false, \n      #                                                                          \"x-amz-request-id\"=>\"0D7ADE09F42606F2\", \n      #                                                                          \"server\"=>\"AmazonS3\", \n      #                                                                          \"content-length\"=>\"0\"}\n    def store_object_and_verify(params)\n      AwsUtils.mandatory_arguments([:md5], params)\n      r = store_object(params)\n      r[:verified_md5] ? (return r) : (raise AwsError.new(\"Uploaded object failed MD5 checksum verification: #{r.inspect}\"))\n    end\n    \n    # New experimental API for uploading objects using the multipart upload API.\n    # store_object_multipart is similar in function to the store_object method, but breaks the input into parts and transmits each\n    # part separately.  The multipart upload API has the benefit of being be able to retransmit a part in isolation without needing to\n    # restart the entire upload.  This makes it ideal for uploading large files over unreliable networks.  It also does not\n    # require the file size to be known before starting the upload, making it useful for stream data as it is created (say via reading a pipe or socket).\n    # The hash of the response headers contains useful information like the location (the URI for the newly created object), bucket, key, and etag).\n    #\n    # The optional argument of :headers allows the caller to specify arbitrary request header values.\n    #\n    # s3.store_object_multipart(:bucket => \"foobucket\", :key => \"foo\", :data => \"polemonium\" )\n    #   => {:location=>\"https://s3.amazonaws.com/right_s3_awesome_test_bucket_000B1_officedrop/test%2Flarge_multipart_file\",\n    #       :e_tag=>\"\\\"72b81ac08aed4d4d1055c11f56c2a258-1\\\"\",\n    #       :key=>\"test/large_multipart_file\",\n    #       :bucket=>\"right_s3_awesome_test_bucket_000B1_officedrop\"}\n    #\n    # f = File.new(\"some_file\", \"r\")\n    # s3.store_object_multipart(:bucket => \"foobucket\", :key => \"foo\", :data => f )\n    #   => {:location=>\"https://s3.amazonaws.com/right_s3_awesome_test_bucket_000B1_officedrop/test%2Flarge_multipart_file\",\n    #       :e_tag=>\"\\\"72b81ac08aed4d4d1055c11f56c2a258-1\\\"\",\n    #       :key=>\"test/large_multipart_file\",\n    #       :bucket=>\"right_s3_awesome_test_bucket_000B1_officedrop\"}\n    def store_object_multipart(params)\n      AwsUtils.allow_only([:bucket, :key, :data, :headers, :part_size, :retry_count], params)\n      AwsUtils.mandatory_arguments([:bucket, :key, :data], params)\n      params[:headers] = {} unless params[:headers]\n\n      params[:data].binmode if(params[:data].respond_to?(:binmode)) # On Windows, if someone opens a file in text mode, we must reset it to binary mode for streaming to work properly\n\n      # detect whether we are using straight read or converting to string first\n      unless(params[:data].respond_to?(:read))\n        params[:data] = StringIO.new(params[:data].to_s)\n      end\n\n      # make sure part size is > 5 MB minimum\n      params[:part_size] ||= MINIMUM_PART_SIZE\n      if params[:part_size] < MINIMUM_PART_SIZE\n        raise AwsError.new(\"Part size for a multipart upload must be greater than or equal to #{5 * 1024 * 1024} bytes.  #{params[:part_size]} bytes was provided.\")\n      end\n\n      # make sure retry_count is positive\n      params[:retry_count] ||= DEFAULT_RETRY_COUNT\n      if params[:retry_count] < 0\n        raise AwsError.new(\"Retry count must be positive.  #{params[:retry_count]} bytes was provided.\")\n      end\n\n      # Set 100-continue for large part sizes\n      if (params[:part_size] >= USE_100_CONTINUE_PUT_SIZE)\n        params[:headers]['expect'] = '100-continue'\n      end\n\n      # initiate upload\n      initiate_hash = generate_rest_request('POST', params[:headers].merge(:url=>\"#{params[:bucket]}/#{CGI::escape params[:key]}?uploads\"))\n      initiate_resp = request_info(initiate_hash, S3MultipartUploadInitiateResponseParser.new)\n      upload_id = initiate_resp[:upload_id]\n\n      # split into parts and upload each one, re-trying if necessary\n      #   upload occurs serially at this time.\n      part_etags = []\n      part_data = \"\"\n      index = 1\n      until params[:data].eof?\n        part_data = params[:data].read(params[:part_size])\n        unless part_data.size == 0\n          retry_attempts = 1\n          while true\n            begin\n              send_part_hash = generate_rest_request('PUT', params[:headers].merge({ :url=>\"#{params[:bucket]}/#{CGI::escape params[:key]}?partNumber=#{index}&uploadId=#{upload_id}\", :data=>part_data } ))\n              send_part_resp = request_info(send_part_hash, S3HttpResponseHeadParser.new)\n              part_etags << {:part_num => index, :etag => send_part_resp['etag']}\n              index += 1\n              break # successful, can move to next part\n            rescue AwsError => e\n              if retry_attempts >= params[:retry_count]\n                raise e\n              else\n                #Hit an error attempting to transmit part, retry until retry_attemts have been exhausted\n                retry_attempts += 1\n              end\n            end\n          end\n        end\n      end\n\n      # assemble complete upload message\n      complete_body = \"<CompleteMultipartUpload>\"\n      part_etags.each do |part_hash|\n        complete_body << \"<Part><PartNumber>#{part_hash[:part_num]}</PartNumber><ETag>#{part_hash[:etag]}</ETag></Part>\"\n      end\n      complete_body << \"</CompleteMultipartUpload>\"\n      complete_req_hash = generate_rest_request('POST', {:url=>\"#{params[:bucket]}/#{CGI::escape params[:key]}?uploadId=#{upload_id}\", :data => complete_body})\n      return request_info(complete_req_hash, S3CompleteMultipartParser.new)\n    rescue\n      on_exception\n    end\n\n    class S3MultipartUploadInitiateResponseParser < RightAWSParser\n      def reset\n        @result = {}\n      end\n      def headers_to_string(headers)\n        result = {}\n        headers.each do |key, value|\n          value       = value.first if value.is_a?(Array) && value.size<2\n          result[key] = value\n        end\n        result\n      end\n      def tagend(name)\n        case name\n          when 'UploadId'          then @result[:upload_id] = @text\n        end\n      end\n    end\n\n    class S3CompleteMultipartParser < RightAWSParser  # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name\n          when 'Location' then @result[:location] = @text\n          when 'Bucket'   then @result[:bucket]   = @text\n          when 'Key'      then @result[:key]      = @text\n          when 'ETag'     then @result[:e_tag]    = @text\n        end\n      end\n    end\n\n      # Retrieves object data from Amazon. Returns a +hash+  or an exception.\n      #\n      #  s3.get('my_awesome_bucket', 'log/curent/1.log') #=>\n      #\n      #      {:object  => \"Ola-la!\", \n      #       :headers => {\"last-modified\"     => \"Wed, 23 May 2007 09:08:04 GMT\", \n      #                    \"content-type\"      => \"\", \n      #                    \"etag\"              => \"\\\"000000000096f4ee74bc4596443ef2a4\\\"\", \n      #                    \"date\"              => \"Wed, 23 May 2007 09:08:03 GMT\", \n      #                    \"x-amz-id-2\"        => \"ZZZZZZZZZZZZZZZZZZZZ1HJXZoehfrS4QxcxTdNGldR7w/FVqblP50fU8cuIMLiu\", \n      #                    \"x-amz-meta-family\" => \"Woho556!\",\n      #                    \"x-amz-request-id\"  => \"0000000C246D770C\", \n      #                    \"server\"            => \"AmazonS3\", \n      #                    \"content-length\"    => \"7\"}}\n      #\n      # If a block is provided, yields incrementally to the block as\n      # the response is read.  For large responses, this function is ideal as\n      # the response can be 'streamed'.  The hash containing header fields is\n      # still returned.\n      # Example:\n      # foo = File.new('./chunder.txt', File::CREAT|File::RDWR)\n      # rhdr = s3.get('aws-test', 'Cent5V1_7_1.img.part.00') do |chunk|\n      #   foo.write(chunk)\n      # end\n      # foo.close\n      # \n\n    def get(bucket, key, headers={}, &block)\n      req_hash = generate_rest_request('GET', headers.merge(:url=>\"#{bucket}/#{CGI::escape key}\"))\n      request_info(req_hash, S3HttpResponseBodyParser.new, &block)\n    rescue\n      on_exception\n    end\n    \n    # New experimental API for retrieving objects, introduced in RightAws 1.8.1.\n    # retrieve_object is similar in function to the older function get.  It allows for optional verification\n    # of object md5 checksums on retrieval.  Parameters are passed as hash entries and are checked for completeness as well as for spurious arguments.\n    #\n    # If the optional :md5 argument is provided, retrieve_object verifies that the given md5 matches the md5 returned by S3.  The :verified_md5 field in the response hash is\n    # set true or false depending on the outcome of this check.  If no :md5 argument is given, :verified_md5 will be false in the response.\n    #\n    # The optional argument of :headers allows the caller to specify arbitrary request header values.\n    # Mandatory arguments:\n    #   :bucket - the bucket in which the object is stored\n    #   :key    - the object address (or path) within the bucket\n    # Optional arguments:\n    #   :headers - hash of additional HTTP headers to include with the request\n    #   :md5     - MD5 checksum against which to verify the retrieved object\n    #\n    #  s3.retrieve_object(:bucket => \"foobucket\", :key => \"foo\") \n    #    => {:verified_md5=>false, \n    #        :headers=>{\"last-modified\"=>\"Mon, 29 Sep 2008 18:58:56 GMT\", \n    #                   \"x-amz-id-2\"=>\"2Aj3TDz6HP5109qly//18uHZ2a1TNHGLns9hyAtq2ved7wmzEXDOPGRHOYEa3Qnp\", \n    #                   \"content-type\"=>\"\", \n    #                   \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n    #                   \"date\"=>\"Tue, 30 Sep 2008 00:52:44 GMT\", \n    #                   \"x-amz-request-id\"=>\"EE4855DE27A2688C\", \n    #                   \"server\"=>\"AmazonS3\", \n    #                   \"content-length\"=>\"10\"}, \n    #        :object=>\"polemonium\"}\n    #\n    #  s3.retrieve_object(:bucket => \"foobucket\", :key => \"foo\", :md5=>'a507841b1bc8115094b00bbe8c1b2954') \n    #    => {:verified_md5=>true, \n    #        :headers=>{\"last-modified\"=>\"Mon, 29 Sep 2008 18:58:56 GMT\", \n    #                   \"x-amz-id-2\"=>\"mLWQcI+VuKVIdpTaPXEo84g0cz+vzmRLbj79TS8eFPfw19cGFOPxuLy4uGYVCvdH\", \n    #                   \"content-type\"=>\"\", \"etag\"=>\"\\\"a507841b1bc8115094b00bbe8c1b2954\\\"\", \n    #                   \"date\"=>\"Tue, 30 Sep 2008 00:53:08 GMT\", \n    #                   \"x-amz-request-id\"=>\"6E7F317356580599\", \n    #                   \"server\"=>\"AmazonS3\", \n    #                   \"content-length\"=>\"10\"}, \n    #        :object=>\"polemonium\"}\n    # If a block is provided, yields incrementally to the block as\n    # the response is read.  For large responses, this function is ideal as\n    # the response can be 'streamed'.  The hash containing header fields is\n    # still returned.\n    def retrieve_object(params, &block)\n      AwsUtils.mandatory_arguments([:bucket, :key], params)\n      AwsUtils.allow_only([:bucket, :key, :headers, :md5], params)\n      params[:headers] = {} unless params[:headers]\n      req_hash = generate_rest_request('GET', params[:headers].merge(:url=>\"#{params[:bucket]}/#{CGI::escape params[:key]}\"))\n      resp = request_info(req_hash, S3HttpResponseBodyParser.new, &block)\n      resp[:verified_md5] = false\n      if(params[:md5] && (resp[:headers]['etag'].gsub(/\\\"/,'') == params[:md5]))\n        resp[:verified_md5] = true\n      end\n      resp\n    rescue\n      on_exception\n    end\n    \n      # Identical in function to retrieve_object, but requires verification that the returned ETag is identical to the checksum passed in by the user as the 'md5' argument.\n      # If the check passes, returns the response metadata with the \"verified_md5\" field set true.  Raises an exception if the checksums conflict.\n      # This call is implemented as a wrapper around retrieve_object and the user may gain different semantics by creating a custom wrapper.\n    def retrieve_object_and_verify(params, &block)\n      AwsUtils.mandatory_arguments([:md5], params)\n      resp = retrieve_object(params, &block)\n      return resp if resp[:verified_md5]\n      raise AwsError.new(\"Retrieved object failed MD5 checksum verification: #{resp.inspect}\")\n    end\n\n      # Retrieves object metadata. Returns a +hash+ of http_response_headers.\n      #\n      #  s3.head('my_awesome_bucket', 'log/curent/1.log') #=>\n      #    {\"last-modified\"     => \"Wed, 23 May 2007 09:08:04 GMT\", \n      #     \"content-type\"      => \"\", \n      #     \"etag\"              => \"\\\"000000000096f4ee74bc4596443ef2a4\\\"\", \n      #     \"date\"              => \"Wed, 23 May 2007 09:08:03 GMT\", \n      #     \"x-amz-id-2\"        => \"ZZZZZZZZZZZZZZZZZZZZ1HJXZoehfrS4QxcxTdNGldR7w/FVqblP50fU8cuIMLiu\", \n      #     \"x-amz-meta-family\" => \"Woho556!\",\n      #     \"x-amz-request-id\"  => \"0000000C246D770C\", \n      #     \"server\"            => \"AmazonS3\", \n      #     \"content-length\"    => \"7\"}\n      #\n    def head(bucket, key, headers={})\n      req_hash = generate_rest_request('HEAD', headers.merge(:url=>\"#{bucket}/#{CGI::escape key}\"))\n      request_info(req_hash, S3HttpResponseHeadParser.new)\n    rescue\n      on_exception\n    end\n\n      # Deletes key. Returns +true+ or an exception.\n      #\n      #  s3.delete('my_awesome_bucket', 'log/curent/1.log') #=> true\n      #\n    def delete(bucket, key='', headers={})\n      req_hash = generate_rest_request('DELETE', headers.merge(:url=>\"#{bucket}/#{CGI::escape key}\"))\n      request_info(req_hash, RightHttp2xxParser.new)\n    rescue\n      on_exception\n    end\n\n    # Deletes multiple keys. Returns an array with errors, if any.\n    #\n    #  s3.delete_multiple('my_awesome_bucket', ['key1', 'key2', ...)\n    #    #=> [ { :key => 'key2', :code => 'AccessDenied', :message => \"Access Denied\" } ]\n    #\n    def delete_multiple(bucket, keys=[], headers={})\n      errors = []\n      keys = Array.new(keys)\n      while keys.length > 0\n        data = \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n\"\n        data += \"<Delete>\\n<Quiet>true</Quiet>\\n\"\n        keys.take(MULTI_OBJECT_DELETE_MAX_KEYS).each do |key|\n          data += \"<Object><Key>#{AwsUtils::xml_escape(key)}</Key></Object>\\n\"\n        end\n        data += \"</Delete>\"\n        req_hash = generate_rest_request('POST', headers.merge(\n          :url  => \"#{bucket}?delete\",\n          :data => data,\n          'content-md5' => AwsUtils::content_md5(data)\n        ))\n        errors += request_info(req_hash, S3DeleteMultipleParser.new)\n        keys = keys.drop(MULTI_OBJECT_DELETE_MAX_KEYS)\n      end\n      errors\n    rescue\n      on_exception\n    end\n\n      # Copy an object. \n      #  directive: :copy    - copy meta-headers from source (default value)\n      #             :replace - replace meta-headers by passed ones\n      #\n      #  # copy a key with meta-headers\n      #  s3.copy('b1', 'key1', 'b1', 'key1_copy') #=> {:e_tag=>\"\\\"e8b...8d\\\"\", :last_modified=>\"2008-05-11T10:25:22.000Z\"}\n      #  \n      #  # copy a key, overwrite meta-headers\n      #  s3.copy('b1', 'key2', 'b1', 'key2_copy', :replace, 'x-amz-meta-family'=>'Woho555!') #=> {:e_tag=>\"\\\"e8b...8d\\\"\", :last_modified=>\"2008-05-11T10:26:22.000Z\"}\n      #\n      # see: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingCopyingObjects.html\n      #      http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectCOPY.html\n      #\n    def copy(src_bucket, src_key, dest_bucket, dest_key=nil, directive=:copy, headers={})\n      dest_key ||= src_key\n      headers['x-amz-metadata-directive'] = directive.to_s.upcase\n      headers['x-amz-copy-source']        = \"#{src_bucket}/#{CGI::escape src_key}\"\n      req_hash = generate_rest_request('PUT', headers.merge(:url=>\"#{dest_bucket}/#{CGI::escape dest_key}\"))\n      request_info(req_hash, S3CopyParser.new)\n    rescue\n      on_exception\n    end\n    \n      # Move an object. \n      #  directive: :copy    - copy meta-headers from source (default value)\n      #             :replace - replace meta-headers by passed ones\n      #\n      #  # move bucket1/key1 to bucket1/key2\n      #  s3.move('bucket1', 'key1', 'bucket1', 'key2') #=> {:e_tag=>\"\\\"e8b...8d\\\"\", :last_modified=>\"2008-05-11T10:27:22.000Z\"}\n      #  \n      #  # move bucket1/key1 to bucket2/key2 with new meta-headers assignment\n      #  s3.copy('bucket1', 'key1', 'bucket2', 'key2', :replace, 'x-amz-meta-family'=>'Woho555!') #=> {:e_tag=>\"\\\"e8b...8d\\\"\", :last_modified=>\"2008-05-11T10:28:22.000Z\"}\n      #  \n    def move(src_bucket, src_key, dest_bucket, dest_key=nil, directive=:copy, headers={})\n      copy_result = copy(src_bucket, src_key, dest_bucket, dest_key, directive, headers)\n      # delete an original key if it differs from a destination one\n      delete(src_bucket, src_key) unless src_bucket == dest_bucket && src_key == dest_key\n      copy_result\n    end\n\n      # Rename an object. \n      # \n      #  # rename bucket1/key1 to bucket1/key2\n      #  s3.rename('bucket1', 'key1', 'key2') #=> {:e_tag=>\"\\\"e8b...8d\\\"\", :last_modified=>\"2008-05-11T10:29:22.000Z\"}\n      #  \n    def rename(src_bucket, src_key, dest_key, headers={})\n      move(src_bucket, src_key, src_bucket, dest_key, :copy, headers)\n    end\n      \n      # Retieves the ACL (access control policy) for a bucket or object. Returns a hash of headers and xml doc with ACL data. See: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html.\n      #\n      #  s3.get_acl('my_awesome_bucket', 'log/curent/1.log') #=>\n      #    {:headers => {\"x-amz-id-2\"=>\"B3BdDMDUz+phFF2mGBH04E46ZD4Qb9HF5PoPHqDRWBv+NVGeA3TOQ3BkVvPBjgxX\",\n      #                  \"content-type\"=>\"application/xml;charset=ISO-8859-1\",\n      #                  \"date\"=>\"Wed, 23 May 2007 09:40:16 GMT\",\n      #                  \"x-amz-request-id\"=>\"B183FA7AB5FBB4DD\",\n      #                  \"server\"=>\"AmazonS3\",\n      #                  \"transfer-encoding\"=>\"chunked\"},\n      #     :object  => \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<AccessControlPolicy xmlns=\\\"http://s3.amazonaws.com/doc/2006-03-01/\\\"><Owner>\n      #                  <ID>16144ab2929314cc309ffe736daa2b264357476c7fea6efb2c3347ac3ab2792a</ID><DisplayName>root</DisplayName></Owner>\n      #                  <AccessControlList><Grant><Grantee xmlns:xsi=\\\"http://www.w3.org/2001/XMLSchema-instance\\\" xsi:type=\\\"CanonicalUser\\\"><ID>\n      #                  16144ab2929314cc309ffe736daa2b264357476c7fea6efb2c3347ac3ab2792a</ID><DisplayName>root</DisplayName></Grantee>\n      #                  <Permission>FULL_CONTROL</Permission></Grant></AccessControlList></AccessControlPolicy>\" }\n      #\n    def get_acl(bucket, key='', headers={})\n      key = key.right_blank? ? '' : \"/#{CGI::escape key}\"\n      req_hash = generate_rest_request('GET', headers.merge(:url=>\"#{bucket}#{key}?acl\"))\n      request_info(req_hash, S3HttpResponseBodyParser.new) \n    rescue\n      on_exception\n    end\n      \n      # Retieves the ACL (access control policy) for a bucket or object. \n      # Returns a hash of {:owner, :grantees}\n      #\n      #  s3.get_acl_parse('my_awesome_bucket', 'log/curent/1.log') #=>\n      #\n      #  { :grantees=>\n      #    { \"16...2a\"=>\n      #      { :display_name=>\"root\",\n      #        :permissions=>[\"FULL_CONTROL\"],\n      #        :attributes=>\n      #         { \"xsi:type\"=>\"CanonicalUser\",\n      #           \"xmlns:xsi\"=>\"http://www.w3.org/2001/XMLSchema-instance\"}},\n      #     \"http://acs.amazonaws.com/groups/global/AllUsers\"=>\n      #       { :display_name=>\"AllUsers\",\n      #         :permissions=>[\"READ\"],\n      #         :attributes=>\n      #          { \"xsi:type\"=>\"Group\",\n      #            \"xmlns:xsi\"=>\"http://www.w3.org/2001/XMLSchema-instance\"}}},\n      #   :owner=>\n      #     { :id=>\"16..2a\",\n      #       :display_name=>\"root\"}}\n      #\n    def get_acl_parse(bucket, key='', headers={})\n      key = key.right_blank? ? '' : \"/#{CGI::escape key}\"\n      req_hash = generate_rest_request('GET', headers.merge(:url=>\"#{bucket}#{key}?acl\"))\n      acl = request_info(req_hash, S3AclParser.new(:logger => @logger))\n      result = {}\n      result[:owner]    = acl[:owner]\n      result[:grantees] = {}\n      acl[:grantees].each do |grantee|\n        key = grantee[:id] || grantee[:uri]\n        if result[:grantees].key?(key)\n          result[:grantees][key][:permissions] << grantee[:permissions]\n        else\n          result[:grantees][key] = \n            { :display_name => grantee[:display_name] || grantee[:uri].to_s[/[^\\/]*$/],\n              :permissions  => Array(grantee[:permissions]),\n              :attributes   => grantee[:attributes] }\n        end\n      end\n      result\n    rescue\n      on_exception\n    end\n    \n      # Sets the ACL on a bucket or object.\n    def put_acl(bucket, key, acl_xml_doc, headers={})\n      key = key.right_blank? ? '' : \"/#{CGI::escape key}\"\n      req_hash = generate_rest_request('PUT', headers.merge(:url=>\"#{bucket}#{key}?acl\", :data=>acl_xml_doc))\n      request_info(req_hash, S3HttpResponseBodyParser.new)\n    rescue\n      on_exception\n    end\n    \n      # Retieves the ACL (access control policy) for a bucket. Returns a hash of headers and xml doc with ACL data.\n    def get_bucket_acl(bucket, headers={})\n      return get_acl(bucket, '', headers)\n    rescue\n      on_exception\n    end\n    \n      # Sets the ACL on a bucket only.\n    def put_bucket_acl(bucket, acl_xml_doc, headers={})\n      return put_acl(bucket, '', acl_xml_doc, headers)\n    rescue\n      on_exception\n    end\n\n\n      # Removes all keys from bucket. Returns +true+ or an exception.\n      #\n      #  s3.clear_bucket('my_awesome_bucket') #=> true\n      #\n    def clear_bucket(bucket)\n      incrementally_list_bucket(bucket) do |results|\n        results[:contents].each { |key| delete(bucket, key[:key]) }\n      end\n      true\n    rescue\n      on_exception\n    end\n    \n      # Deletes all keys in bucket then deletes bucket. Returns +true+ or an exception.\n      #\n      #  s3.force_delete_bucket('my_awesome_bucket')\n      #\n    def force_delete_bucket(bucket)\n      clear_bucket(bucket)\n      delete_bucket(bucket)\n    rescue\n      on_exception\n    end\n    \n      # Deletes all keys where the 'folder_key' may be assumed as 'folder' name. Returns an array of string keys that have been deleted.\n      #\n      #  s3.list_bucket('my_awesome_bucket').map{|key_data| key_data[:key]} #=> ['test','test/2/34','test/3','test1','test1/logs']\n      #  s3.delete_folder('my_awesome_bucket','test')                       #=> ['test','test/2/34','test/3']\n      #\n    def delete_folder(bucket, folder_key, separator='/')\n      folder_key.chomp!(separator)\n      allkeys = []\n      incrementally_list_bucket(bucket, { 'prefix' => folder_key }) do |results|\n        keys = results[:contents].map{ |s3_key| s3_key[:key][/^#{folder_key}($|#{separator}.*)/] ? s3_key[:key] : nil}.compact\n        keys.each{ |key| delete(bucket, key) }\n        allkeys << keys\n      end\n      allkeys\n    rescue\n      on_exception\n    end\n    \n      # Retrieves object data only (headers are omitted). Returns +string+ or an exception.\n      #\n      #  s3.get('my_awesome_bucket', 'log/curent/1.log') #=> 'Ola-la!'\n      #\n    def get_object(bucket, key, headers={})\n      get(bucket, key, headers)[:object]\n    rescue\n      on_exception\n    end\n    \n    #-----------------------------------------------------------------\n    #      Query API: Links\n    #-----------------------------------------------------------------\n\n    def s3_link_escape(text)\n      #CGI::escape(text.to_s).gsub(/[+]/, '%20')\n      AwsUtils::amz_escape(text.to_s)\n    end\n    \n      # Generates link for QUERY API\n    def generate_link(method, headers={}, expires=nil) #:nodoc:\n        # calculate request data\n      server, path, path_to_sign = fetch_request_params(headers)\n\n        # expiration time\n      expires ||= DEFAULT_EXPIRES_AFTER\n      expires   = Time.now.utc + expires if expires.is_a?(Fixnum) && (expires < ONE_YEAR_IN_SECONDS)\n      expires   = expires.to_i\n        # make sure headers are downcased strings\n      headers = AwsUtils::fix_headers(headers)\n        #generate auth strings\n      auth_string = canonical_string(method, path_to_sign, headers, expires)\n      signature   = CGI::escape(AwsUtils::sign( @aws_secret_access_key, auth_string))\n        # path building\n      addon = \"Signature=#{signature}&Expires=#{expires}&AWSAccessKeyId=#{@aws_access_key_id}\"\n      path += path[/\\?/] ? \"&#{addon}\" : \"?#{addon}\"\n      \"#{@params[:protocol]}://#{server}:#{@params[:port]}#{path}\"\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'ListAllMyBuckets'.\n      #\n      #  s3.list_all_my_buckets_link #=> url string\n      #\n    def list_all_my_buckets_link(expires=nil, headers={})\n      generate_link('GET', headers.merge(:url=>''), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'CreateBucket'.\n      #\n      #  s3.create_bucket_link('my_awesome_bucket') #=> url string\n      #\n    def create_bucket_link(bucket, expires=nil, headers={})\n      generate_link('PUT', headers.merge(:url=>bucket), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'DeleteBucket'.\n      #\n      #  s3.delete_bucket_link('my_awesome_bucket') #=> url string\n      #\n    def delete_bucket_link(bucket, expires=nil,  headers={})\n      generate_link('DELETE', headers.merge(:url=>bucket), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'ListBucket'.\n      #\n      #  s3.list_bucket_link('my_awesome_bucket') #=> url string\n      #\n    def list_bucket_link(bucket, options=nil, expires=nil, headers={})\n      bucket += '?' + options.map{|k, v| \"#{k.to_s}=#{s3_link_escape(v)}\"}.join('&') unless options.right_blank?\n      generate_link('GET', headers.merge(:url=>bucket), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'PutObject'.\n      #\n      #  s3.put_link('my_awesome_bucket',key, object) #=> url string\n      #\n    def put_link(bucket, key, data=nil, expires=nil, headers={})\n      generate_link('PUT', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}\", :data=>data), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'GetObject'.\n      #\n      # if a bucket comply with virtual hosting naming then retuns a link with the \n      # bucket as a part of host name:\n      # \n      #  s3.get_link('my-awesome-bucket',key) #=> https://my-awesome-bucket.s3.amazonaws.com:443/asia%2Fcustomers?Signature=nh7...\n      #  \n      # otherwise returns an old style link (the bucket is a part of path):\n      # \n      #  s3.get_link('my_awesome_bucket',key) #=> https://s3.amazonaws.com:443/my_awesome_bucket/asia%2Fcustomers?Signature=QAO...\n      #\n      # see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html\n      #\n      # To specify +response+-* parameters, define them in the response_params hash:\n      #\n      #  s3.get_link('my_awesome_bucket',key,nil,{},{ \"response-content-disposition\" => \"attachment; filename=caf�.png\", \"response-content-type\" => \"image/png\"})\n      #\n      #    #=> https://s3.amazonaws.com:443/my_awesome_bucket/asia%2Fcustomers?response-content-disposition=attachment%3B%20filename%3Dcaf%25C3%25A9.png&response-content-type=image%2Fpng&Signature=wio...\n      #\n    def get_link(bucket, key, expires=nil, headers={}, response_params={})\n      if response_params.size > 0\n        response_params = '?' + response_params.map { |k, v| \"#{k}=#{s3_link_escape(v)}\" }.join('&')\n      else\n        response_params = ''\n      end\n      generate_link('GET', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}#{response_params}\"), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'HeadObject'.\n      #\n      #  s3.head_link('my_awesome_bucket',key) #=> url string\n      #\n    def head_link(bucket, key, expires=nil,  headers={})\n      generate_link('HEAD', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}\"), expires)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'DeleteObject'.\n      #\n      #  s3.delete_link('my_awesome_bucket',key) #=> url string\n      #\n    def delete_link(bucket, key, expires=nil, headers={})\n      generate_link('DELETE', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}\"), expires)\n    rescue\n      on_exception\n    end\n    \n    \n      # Generates link for 'GetACL'.\n      #\n      #  s3.get_acl_link('my_awesome_bucket',key) #=> url string\n      #\n    def get_acl_link(bucket, key='', headers={})\n      return generate_link('GET', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}?acl\"))\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'PutACL'.\n      #\n      #  s3.put_acl_link('my_awesome_bucket',key) #=> url string\n      #\n    def put_acl_link(bucket, key='', headers={})\n      return generate_link('PUT', headers.merge(:url=>\"#{bucket}/#{s3_link_escape(key)}?acl\"))\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'GetBucketACL'.\n      #\n      #  s3.get_acl_link('my_awesome_bucket',key) #=> url string\n      #\n    def get_bucket_acl_link(bucket, headers={})\n      return get_acl_link(bucket, '', headers)\n    rescue\n      on_exception\n    end\n    \n      # Generates link for 'PutBucketACL'.\n      #\n      #  s3.put_acl_link('my_awesome_bucket',key) #=> url string\n      #\n    def put_bucket_acl_link(bucket, acl_xml_doc, headers={})\n      return put_acl_link(bucket, '', acl_xml_doc, headers)\n    rescue\n      on_exception\n    end\n\n    class S3DeleteMultipleParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagstart(name, attributes)\n        @error = {} if name == 'Error'\n      end\n      def tagend(name)\n        case name\n          when 'Key'     then @error[:key]     = @text\n          when 'Code'    then @error[:code]    = @text\n          when 'Message' then @error[:message] = @text\n          when 'Error'   then @result << @error\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n\n    class S3ListAllMyBucketsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n        @owner  = {}\n      end\n      def tagstart(name, attributes)\n        @current_bucket = {} if name == 'Bucket'\n      end\n      def tagend(name)\n        case name\n          when 'ID'          then @owner[:owner_id]               = @text\n          when 'DisplayName' then @owner[:owner_display_name]     = @text\n          when 'Name'        then @current_bucket[:name]          = @text\n          when 'CreationDate'then @current_bucket[:creation_date] = @text\n          when 'Bucket'      then @result << @current_bucket.merge(@owner)\n        end\n      end\n    end\n\n    class S3ListBucketParser < RightAWSParser  # :nodoc:\n      def reset\n        @result      = []\n        @service     = {}\n        @current_key = {}\n      end\n      def tagstart(name, attributes)\n        @current_key = {} if name == 'Contents'\n      end\n      def tagend(name)\n        case name\n            # service info\n          when 'Name'        then @service['name']         = @text\n          when 'Prefix'      then @service['prefix']       = @text\n          when 'Marker'      then @service['marker']       = @text\n          when 'MaxKeys'     then @service['max-keys']     = @text\n          when 'Delimiter'   then @service['delimiter']    = @text\n          when 'IsTruncated' then @service['is_truncated'] = (@text =~ /false/ ? false : true)\n            # key data\n          when 'Key'         then @current_key[:key]                = @text\n          when 'LastModified'then @current_key[:last_modified]      = @text\n          when 'ETag'        then @current_key[:e_tag]              = @text\n          when 'Size'        then @current_key[:size]               = @text.to_i\n          when 'StorageClass'then @current_key[:storage_class]      = @text\n          when 'ID'          then @current_key[:owner_id]           = @text\n          when 'DisplayName' then @current_key[:owner_display_name] = @text\n          when 'Contents'\n            @current_key[:service] = @service\n            @result << @current_key\n        end\n      end\n    end\n\n    class S3ImprovedListBucketParser < RightAWSParser  # :nodoc:\n      def reset\n        @result      = {}\n        @result[:contents] = []\n        @result[:common_prefixes] = []\n        @contents    = []\n        @current_key = {}\n        @common_prefixes = []\n        @in_common_prefixes = false\n      end\n      def tagstart(name, attributes)\n        @current_key = {} if name == 'Contents'\n        @in_common_prefixes = true if name == 'CommonPrefixes'\n      end\n      def tagend(name)\n        case name\n            # service info\n          when 'Name'        then @result[:name]         = @text\n          # Amazon uses the same tag for the search prefix and for the entries\n            # in common prefix...so use our simple flag to see which element\n            # we are parsing\n          when 'Prefix'      then @in_common_prefixes ? @common_prefixes << @text : @result[:prefix] = @text\n          when 'Marker'      then @result[:marker]       = @text\n          when 'MaxKeys'     then @result[:max_keys]     = @text\n          when 'Delimiter'   then @result[:delimiter]    = @text\n          when 'IsTruncated' then @result[:is_truncated] = (@text =~ /false/ ? false : true)\n          when 'NextMarker'  then @result[:next_marker]  = @text\n            # key data\n          when 'Key'         then @current_key[:key]                = @text\n          when 'LastModified'then @current_key[:last_modified]      = @text\n          when 'ETag'        then @current_key[:e_tag]              = @text\n          when 'Size'        then @current_key[:size]               = @text.to_i\n          when 'StorageClass'then @current_key[:storage_class]      = @text\n          when 'ID'          then @current_key[:owner_id]           = @text\n          when 'DisplayName' then @current_key[:owner_display_name] = @text\n          when 'Contents'    then @result[:contents] << @current_key\n            # Common Prefix stuff\n          when 'CommonPrefixes' \n            @result[:common_prefixes] = @common_prefixes\n            @in_common_prefixes = false\n        end\n      end\n    end\n\n    class S3BucketLocationParser < RightAWSParser # :nodoc:\n      def reset\n        @result = ''\n      end\n      def tagend(name)\n        @result = @text if name == 'LocationConstraint'\n      end\n    end\n\n    class S3AclParser < RightAWSParser  # :nodoc:\n      def reset\n        @result          = {:grantees=>[], :owner=>{}}\n        @current_grantee = {}\n      end\n      def tagstart(name, attributes)\n        @current_grantee = { :attributes => attributes } if name=='Grantee'\n      end\n      def tagend(name)\n        case name\n            # service info\n          when 'ID'\n            if @xmlpath == 'AccessControlPolicy/Owner'\n              @result[:owner][:id] = @text\n            else\n              @current_grantee[:id] = @text\n            end\n          when 'DisplayName'\n            if @xmlpath == 'AccessControlPolicy/Owner'\n              @result[:owner][:display_name] = @text\n            else\n              @current_grantee[:display_name] = @text\n            end\n          when 'URI'\n            @current_grantee[:uri] = @text\n          when 'Permission'\n            @current_grantee[:permissions] = @text\n          when 'Grant'\n            @result[:grantees] << @current_grantee\n        end\n      end\n    end\n    \n    class S3LoggingParser < RightAWSParser  # :nodoc:\n      def reset\n        @result          = {:enabled => false, :targetbucket => '', :targetprefix => ''}\n        @current_grantee = {}\n      end\n      def tagend(name)\n        case name\n            # service info\n          when 'TargetBucket'\n            if @xmlpath == 'BucketLoggingStatus/LoggingEnabled'\n              @result[:targetbucket] = @text\n              @result[:enabled] = true\n            end\n          when 'TargetPrefix'\n            if @xmlpath == 'BucketLoggingStatus/LoggingEnabled'\n              @result[:targetprefix] = @text\n              @result[:enabled] = true\n            end\n        end\n      end\n    end\n\n    class S3CopyParser < RightAWSParser  # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name\n        when 'LastModified' then @result[:last_modified] = @text\n        when 'ETag'         then @result[:e_tag]         = @text\n        end\n      end\n    end\n    \n    #-----------------------------------------------------------------\n    #      PARSERS: Non XML\n    #-----------------------------------------------------------------\n\n    class S3HttpResponseParser   # :nodoc:\n      attr_reader :result\n      def parse(response)\n        @result = response\n      end\n      def headers_to_string(headers)\n        result = {}\n        headers.each do |key, value|\n          value       = value.first if value.is_a?(Array) && value.size<2\n          result[key] = value\n        end\n        result\n      end\n    end\n\n    class S3HttpResponseBodyParser < S3HttpResponseParser  # :nodoc:\n      def parse(response)\n        @result = { \n          :object  => response.body, \n          :headers => headers_to_string(response.to_hash)\n        }\n      end\n    end\n\n    class S3HttpResponseHeadParser < S3HttpResponseParser  # :nodoc:\n      def parse(response)\n        @result = headers_to_string(response.to_hash)\n      end\n    end\n    \n  end\n\nend\n"
  },
  {
    "path": "lib/sdb/active_sdb.rb",
    "content": "#\n# Copyright (c) 2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  # = RightAws::ActiveSdb -- RightScale SDB interface (alpha release)\n  # The RightAws::ActiveSdb class provides a complete interface to Amazon's Simple\n  # Database Service.\n  # \n  # ActiveSdb is in alpha and does not load by default with the rest of RightAws.  You must use an additional require statement to load the ActiveSdb class.  For example:\n  # \n  #   require 'right_aws'\n  #   require 'sdb/active_sdb'\n  #   \n  # Simple ActiveSdb usage example:\n  #\n  #  class Client < RightAws::ActiveSdb::Base \n  #  end\n  #  \n  #  # connect to SDB\n  #  RightAws::ActiveSdb.establish_connection\n  #  \n  #  # create domain\n  #  Client.create_domain\n  #  \n  #  # create initial DB\n  #  Client.create 'name' => 'Bush',     'country' => 'USA',    'gender' => 'male',   'expiration' => '2009', 'post' => 'president'\n  #  Client.create 'name' => 'Putin',    'country' => 'Russia', 'gender' => 'male',   'expiration' => '2008', 'post' => 'president' \n  #  Client.create 'name' => 'Medvedev', 'country' => 'Russia', 'gender' => 'male',   'expiration' => '2012', 'post' => 'president'\n  #  Client.create 'name' => 'Mary',     'country' => 'USA',    'gender' => 'female', 'hobby' => ['patchwork', 'bundle jumping']\n  #  Client.create 'name' => 'Mary',     'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking']\n  #  sandy_id = Client.create('name' => 'Sandy', 'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking']).id\n  #  \n  #  # find all Bushes in USA\n  #  Client.find(:all, :conditions => [\"['name'=?] intersection ['country'=?]\",'Bush','USA']).each do |client|\n  #    client.reload\n  #    puts client.attributes.inspect\n  #  end\n  #  \n  #  # find all Maries through the world\n  #  Client.find_all_by_name_and_gender('Mary','female').each do |mary|\n  #    mary.reload\n  #    puts \"#{mary[:name]}, gender: #{mary[:gender]}, hobbies: #{mary[:hobby].join(',')}\"\n  #  end\n  #  \n  #  # find new russian president\n  #  medvedev = Client.find_by_post_and_country_and_expiration('president','Russia','2012')\n  #  if medvedev\n  #    medvedev.reload\n  #    medvedev.save_attributes('age' => '42', 'hobby' => 'Gazprom')\n  #  end\n  #  \n  #  # retire old president\n  #  Client.find_by_name('Putin').delete\n  #  \n  #  # Sandy disappointed in 'cooking' and decided to hide her 'gender' and 'country' ()\n  #  sandy = Client.find(sandy_id)\n  #  sandy.reload\n  #  sandy.delete_values('hobby' => ['cooking'] )\n  #  sandy.delete_attributes('country', 'gender')\n  #\n  #  # remove domain\n  #  Client.delete_domain\n  #\n  #  # Dynamic attribute accessors\n  #\n  #  class KdClient < RightAws::ActiveSdb::Base\n  #  end\n  #\n  #  client = KdClient.select(:all, :order => 'expiration').first\n  #    pp client.attributes #=>\n  #      {\"name\"=>[\"Putin\"],\n  #       \"post\"=>[\"president\"],\n  #       \"country\"=>[\"Russia\"],\n  #       \"expiration\"=>[\"2008\"],\n  #       \"id\"=>\"376d2e00-75b0-11dd-9557-001bfc466dd7\",\n  #       \"gender\"=>[\"male\"]}\n  #\n  #    pp client.name    #=> [\"Putin\"]\n  #    pp client.country #=> [\"Russia\"]\n  #    pp client.post    #=> [\"president\"]\n  #\n  # # Columns and simple typecasting\n  #\n  #  class Person < RightAws::ActiveSdb::Base\n  #    columns do\n  #      name\n  #      email\n  #      score         :Integer\n  #      is_active     :Boolean\n  #      registered_at :DateTime\n  #      created_at    :DateTime, :default => lambda{ Time.now }\n  #    end\n  #  end\n  #  Person::create( :name => 'Yetta E. Andrews', :email => 'nulla.facilisis@metus.com', :score => 100, :is_active => true, :registered_at => Time.local(2000, 1, 1) )\n  #\n  #  person = Person.find_by_email 'nulla.facilisis@metus.com'\n  #  person.reload\n  #\n  #  pp person.attributes #=>\n  #    {\"name\"=>[\"Yetta E. Andrews\"],\n  #     \"created_at\"=>[\"2010-04-02T20:51:58+0400\"],\n  #     \"id\"=>\"0ee24946-3e60-11df-9d4c-0025b37efad0\",\n  #     \"registered_at\"=>[\"2000-01-01T00:00:00+0300\"],\n  #     \"is_active\"=>[\"T\"],\n  #     \"score\"=>[\"100\"],\n  #     \"email\"=>[\"nulla.facilisis@metus.com\"]}\n  #  pp person.name                #=> \"Yetta E. Andrews\"\n  #  pp person.name.class          #=> String\n  #  pp person.registered_at.to_s  #=> \"2000-01-01T00:00:00+03:00\"\n  #  pp person.registered_at.class #=> DateTime\n  #  pp person.is_active           #=> true\n  #  pp person.is_active.class     #=> TrueClass\n  #  pp person.score               #=> 100\n  #  pp person.score.class         #=> Fixnum\n  #  pp person.created_at.to_s     #=> \"2010-04-02T20:51:58+04:00\"\n  #\n  class ActiveSdb\n    \n    module ActiveSdbConnect\n      def connection\n        @connection || raise(ActiveSdbError.new('Connection to SDB is not established'))\n      end\n      # Create a new handle to an Sdb account. All handles share the same per process or per thread\n      # HTTP connection to Amazon Sdb. Each handle is for a specific account.\n      # The +params+ are passed through as-is to RightAws::SdbInterface.new\n      # Params:\n      #    { :server       => 'sdb.amazonaws.com'  # Amazon service host: 'sdb.amazonaws.com'(default)\n      #      :port         => 443                  # Amazon service port: 80 or 443(default)\n      #      :protocol     => 'https'              # Amazon service protocol: 'http' or 'https'(default)\n      #      :signature_version => '0'             # The signature version : '0' or '1'(default)\n      #      :logger       => Logger Object        # Logger instance: logs to STDOUT if omitted \n      #      :nil_representation => 'mynil'}       # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')\n\n      def establish_connection(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n        @connection = RightAws::SdbInterface.new(aws_access_key_id, aws_secret_access_key, params)\n      end\n    end\n    \n    class ActiveSdbError < RuntimeError ; end\n    \n    class << self\n      include ActiveSdbConnect\n\n      # Retreive a list of domains.\n      #\n      #  put RightAws::ActiveSdb.domains #=> ['co-workers','family','friends','clients']\n      #\n      def domains\n        connection.list_domains[:domains]\n      end\n\n      # Create new domain. \n      # Raises no errors if the domain already exists.\n      # \n      #  RightAws::ActiveSdb.create_domain('alpha')  #=> {:request_id=>\"6fc652a0-0000-41d5-91f4-3ed390a3d3b2\", :box_usage=>\"0.0055590278\"}\n      #\n      def create_domain(domain_name)\n        connection.create_domain(domain_name)\n      end\n\n      # Remove domain from SDB. \n      # Raises no errors if the domain does not exist.\n      # \n      #  RightAws::ActiveSdb.create_domain('alpha')  #=> {:request_id=>\"6fc652a0-0000-41c6-91f4-3ed390a3d3b2\", :box_usage=>\"0.0055590001\"}\n      #\n      def delete_domain(domain_name)\n        connection.delete_domain(domain_name)\n      end\n    end\n    \n    class Base\n      \n      class << self\n        include ActiveSdbConnect\n        \n        # next_token value returned by last find: is useful to continue finding\n        attr_accessor :next_token\n      \n        # Returns a RightAws::SdbInterface object\n        #\n        #  class A < RightAws::ActiveSdb::Base\n        #  end\n        #  \n        #  class B < RightAws::ActiveSdb::Base\n        #  end\n        #  \n        #  class C < RightAws::ActiveSdb::Base\n        #  end\n        #\n        #  RightAws::ActiveSdb.establish_connection 'key_id_1', 'secret_key_1'\n        #  \n        #  C.establish_connection 'key_id_2', 'secret_key_2'\n        #\n        #  # A and B uses the default connection, C - uses its own \n        #  puts A.connection  #=> #<RightAws::SdbInterface:0xb76d6d7c>\n        #  puts B.connection  #=> #<RightAws::SdbInterface:0xb76d6d7c>\n        #  puts C.connection  #=> #<RightAws::SdbInterface:0xb76d6ca0>\n        #\n        def connection\n          @connection || ActiveSdb::connection\n        end\n\n        @domain = nil\n\n        # Current domain name.\n        #\n        #  # if 'ActiveSupport' is not loaded then class name converted to downcase\n        #  class Client < RightAws::ActiveSdb::Base\n        #  end\n        #  puts Client.domain  #=> 'client'\n        #  \n        #  # if 'ActiveSupport' is loaded then class name being tableized\n        #  require 'activesupport'\n        #  class Client < RightAws::ActiveSdb::Base\n        #  end\n        #  puts Client.domain  #=> 'clients'\n        #\n        #  # Explicit domain name definition\n        #  class Client < RightAws::ActiveSdb::Base\n        #    set_domain_name :foreign_clients\n        #  end\n        #  puts Client.domain  #=> 'foreign_clients'\n        #\n        def domain\n          unless @domain\n            if defined? ActiveSupport::CoreExtensions::String::Inflections\n              @domain = name.tableize\n            else\n              @domain = name.downcase\n            end\n          end\n          @domain\n        end\n\n        # Change the default domain name to user defined.\n        # \n        #  class Client < RightAws::ActiveSdb::Base\n        #    set_domain_name :foreign_clients\n        #  end\n        #\n        def set_domain_name(domain)\n          @domain = domain.to_s\n        end\n\n        # Create domain at SDB.\n        # Raises no errors if the domain already exists.\n        # \n        #  class Client < RightAws::ActiveSdb::Base\n        #  end\n        #  Client.create_domain  #=> {:request_id=>\"6fc652a0-0000-41d5-91f4-3ed390a3d3b2\", :box_usage=>\"0.0055590278\"}\n        #\n        def create_domain\n          connection.create_domain(domain)\n        end\n\n        # Remove domain from SDB.\n        # Raises no errors if the domain does not exist.\n        # \n        #  class Client < RightAws::ActiveSdb::Base\n        #  end\n        #  Client.delete_domain  #=> {:request_id=>\"e14d90d3-0000-4898-9995-0de28cdda270\", :box_usage=>\"0.0055590278\"}\n        #\n        def delete_domain\n          connection.delete_domain(domain)\n        end\n\n        def columns(&block)\n          @columns ||= ColumnSet.new\n          @columns.instance_eval(&block) if block\n          @columns\n        end\n\n        def column?(col_name)\n          columns.include?(col_name)\n        end\n\n        def type_of(col_name)\n          columns.type_of(col_name)\n        end\n\n        def serialize(attribute, value)\n          s = serialization_for_type(type_of(attribute))\n          s ? s.serialize(value) : value.to_s\n        end\n\n        def deserialize(attribute, value)\n          s = serialization_for_type(type_of(attribute))\n          s ? s.deserialize(value) : value\n        end\n\n        # Perform a find request.\n        #  \n        # Single record: \n        # \n        #  Client.find(:first)\n        #  Client.find(:first, :conditions=> [ \"['name'=?] intersection ['wife'=?]\", \"Jon\", \"Sandy\"])\n        #  \n        # Bunch of records: \n        # \n        #  Client.find(:all)\n        #  Client.find(:all, :limit => 10)\n        #  Client.find(:all, :conditions=> [ \"['name'=?] intersection ['girlfriend'=?]\", \"Jon\", \"Judy\"])\n        #  Client.find(:all, :conditions=> [ \"['name'=?]\", \"Sandy\"], :limit => 3)\n        #  \n        # Records by ids:\n        # \n        #  Client.find('1')\n        #  Client.find('1234987b4583475347523948')\n        #  Client.find('1','2','3','4', :conditions=> [ \"['toys'=?]\", \"beer\"])\n        #\n        # Find helpers: RightAws::ActiveSdb::Base.find_by_... and RightAws::ActiveSdb::Base.find_all_by_...\n        # \n        #  Client.find_by_name('Matias Rust')\n        #  Client.find_by_name_and_city('Putin','Moscow')\n        #  Client.find_by_name_and_city_and_post('Medvedev','Moscow','president')\n        #\n        #  Client.find_all_by_author('G.Bush jr')\n        #  Client.find_all_by_age_and_gender_and_ethnicity('34','male','russian')\n        #  Client.find_all_by_gender_and_country('male', 'Russia', :auto_load => true, :order => 'name desc')\n        #\n        # Returned records have to be +reloaded+ to access their attributes.\n        # \n        #  item = Client.find_by_name('Cat')  #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\"}, @new_record=false>\n        #  item.reload                        #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"name\"=>[\"Cat\"], \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}, @new_record=false>\n        #\n        # Continue listing:\n        #  # initial listing\n        #  Client.find(:all, :limit => 10)\n        #  # continue listing\n        #  begin\n        #    Client.find(:all, :limit => 10, :next_token => Client.next_token)\n        #  end while Client.next_token\n        #\n        #  Sort oder:\n        #    Client.find(:all, :order => 'gender')\n        #    Client.find(:all, :order => 'name desc')\n        #\n        #  Attributes auto load (be carefull - this may take lot of time for a huge bunch of records):\n        #    Client.find(:first)                      #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\"}, @new_record=false>\n        #    Client.find(:first, :auto_load => true)  #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"name\"=>[\"Cat\"], \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}, @new_record=false>\n        #\n        # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingQuery.html\n        #\n        def find(*args)\n          options = args.last.is_a?(Hash) ? args.pop : {}\n          case args.first\n            when :all   then find_every    options\n            when :first then find_initial  options\n            else             find_from_ids args, options\n          end\n        end\n\n        # Perform a SQL-like select request.\n        #\n        # Single record:\n        #\n        #  Client.select(:first)\n        #  Client.select(:first, :conditions=> [ \"name=? AND wife=?\", \"Jon\", \"Sandy\"])\n        #  Client.select(:first, :conditions=> { :name=>\"Jon\", :wife=>\"Sandy\" }, :select => :girfriends)\n        #\n        # Bunch of records:\n        #\n        #  Client.select(:all)\n        #  Client.select(:all, :limit => 10)\n        #  Client.select(:all, :conditions=> [ \"name=? AND 'girlfriend'=?\", \"Jon\", \"Judy\"])\n        #  Client.select(:all, :conditions=> { :name=>\"Sandy\" }, :limit => 3)\n        #\n        # Records by ids:\n        #\n        #  Client.select('1')\n        #  Client.select('1234987b4583475347523948')\n        #  Client.select('1','2','3','4', :conditions=> [\"toys=?\", \"beer\"])\n        #\n        # Find helpers: RightAws::ActiveSdb::Base.select_by_... and RightAws::ActiveSdb::Base.select_all_by_...\n        #\n        #  Client.select_by_name('Matias Rust')\n        #  Client.select_by_name_and_city('Putin','Moscow')\n        #  Client.select_by_name_and_city_and_post('Medvedev','Moscow','president')\n        #\n        #  Client.select_all_by_author('G.Bush jr')\n        #  Client.select_all_by_age_and_gender_and_ethnicity('34','male','russian')\n        #  Client.select_all_by_gender_and_country('male', 'Russia', :order => 'name')\n        #\n        # Continue listing:\n        #\n        #  # initial listing\n        #  Client.select(:all, :limit => 10)\n        #  # continue listing\n        #  begin\n        #    Client.select(:all, :limit => 10, :next_token => Client.next_token)\n        #  end while Client.next_token\n        #\n        #  Sort oder:\n        #  If :order=>'attribute' option is specified then result response (ordered by 'attribute') will contain only items where attribute is defined (is not null).\n        #  \n        #    Client.select(:all)                         # returns all records\n        #    Client.select(:all, :order => 'gender')     # returns all records ordered by gender where gender attribute exists\n        #    Client.select(:all, :order => 'name desc')  # returns all records ordered by name in desc order where name attribute exists\n        #\n        # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html\n        #\n        def select(*args)\n          options = args.last.is_a?(Hash) ? args.pop : {}\n          case args.first\n            when :all   then sql_select(options)\n            when :first then sql_select(options.merge(:limit => 1)).first\n            else             select_from_ids args, options\n          end\n        end\n\n        def generate_id # :nodoc:\n          AwsUtils::generate_unique_token\n        end\n\n      protected\n\n        # Select\n\n        def select_from_ids(args, options) # :nodoc:\n          cond = []\n          # detect amount of records requested\n          bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)\n          # flatten ids\n          args = Array(args).flatten\n          args.each { |id| cond << \"id=#{self.connection.escape(id)}\" }\n          ids_cond = \"(#{cond.join(' OR ')})\"\n          # user defined :conditions to string (if it was defined)\n          options[:conditions] = build_conditions(options[:conditions])\n          # join ids condition and user defined conditions\n          options[:conditions] = options[:conditions].right_blank? ? ids_cond : \"(#{options[:conditions]}) AND #{ids_cond}\"\n          result = sql_select(options)\n          # if one record was requested then return it\n          unless bunch_of_records_requested\n            record = result.first\n            # railse if nothing was found\n            raise ActiveSdbError.new(\"Couldn't find #{name} with ID #{args}\") unless record\n            record\n          else\n            # if a bunch of records was requested then return check that we found all of them\n            # and return as an array\n            unless args.size == result.size\n              id_list = args.map{|i| \"'#{i}'\"}.join(',')\n              raise ActiveSdbError.new(\"Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})\")\n            else\n              result\n            end\n          end\n        end\n\n        def sql_select(options) # :nodoc:\n          @next_token = options[:next_token]\n          select_expression = build_select(options)\n          # request items\n          query_result = self.connection.select(select_expression, @next_token)\n          @next_token = query_result[:next_token]\n          items = query_result[:items].map do |hash|\n            id, attributes = hash.shift\n            new_item = self.new( attributes.merge({ 'id' => id }))\n            new_item.mark_as_old\n            new_item\n          end\n          items\n        end\n\n        # select_by helpers\n        def select_all_by_(format_str, args, options) # :nodoc:\n          fields = format_str.to_s.sub(/^select_(all_)?by_/,'').split('_and_')\n          conditions = fields.map { |field| \"#{field}=?\" }.join(' AND ')\n          options[:conditions] = [conditions, *args]\n          select(:all, options)\n        end\n\n        def select_by_(format_str, args, options) # :nodoc:\n          options[:limit] = 1\n          select_all_by_(format_str, args, options).first\n        end\n\n        # Query\n\n        # Returns an array of query attributes.\n        # Query_expression must be a well formated SDB query string:\n        # query_attributes(\"['title' starts-with 'O\\\\'Reily'] intersection ['year' = '2007']\") #=> [\"title\", \"year\"]\n        def query_attributes(query_expression) # :nodoc:\n          attrs = []\n          array = query_expression.scan(/['\"](.*?[^\\\\])['\"]/).flatten\n          until array.empty? do\n            attrs << array.shift # skip it's value\n            array.shift #\n          end\n          attrs\n        end\n\n        # Returns an array of [attribute_name, 'asc'|'desc']\n        def sort_options(sort_string)\n          sort_string[/['\"]?(\\w+)['\"]? *(asc|desc)?/i]\n          [$1, ($2 || 'asc')]\n        end\n\n        # Perform a query request.\n        #\n        # Options\n        #  :query_expression     nil | string | array\n        #  :max_number_of_items  nil | integer\n        #  :next_token           nil | string\n        #  :sort_option          nil | string    \"name desc|asc\"\n        #\n        def query(options) # :nodoc:\n          @next_token = options[:next_token]\n          query_expression = build_conditions(options[:query_expression])\n          # add sort_options to the query_expression\n          if options[:sort_option]\n            sort_by, sort_order = sort_options(options[:sort_option])\n            sort_query_expression = \"['#{sort_by}' starts-with '']\"\n            sort_by_expression    = \" sort '#{sort_by}' #{sort_order}\"\n            # make query_expression to be a string (it may be null)\n            query_expression = query_expression.to_s\n            # quote from Amazon:\n            # The sort attribute must be present in at least one of the predicates of the query expression.\n            if query_expression.right_blank?\n              query_expression = sort_query_expression\n            elsif !query_attributes(query_expression).include?(sort_by)\n              query_expression += \" intersection #{sort_query_expression}\"\n            end\n            query_expression += sort_by_expression\n          end\n          # request items\n          query_result = self.connection.query(domain, query_expression, options[:max_number_of_items], @next_token)\n          @next_token = query_result[:next_token]\n          items = query_result[:items].map do |name| \n            new_item = self.new('id' => name)\n            new_item.mark_as_old\n            reload_if_exists(record) if options[:auto_load]\n            new_item\n          end\n          items\n        end\n\n        # reload a record unless it is nil\n        def reload_if_exists(record) # :nodoc:\n          record && record.reload\n        end\n\n        def reload_all_records(*list) # :nodoc:\n          list.flatten.each { |record| reload_if_exists(record) }\n        end\n\n        def find_every(options) # :nodoc:\n          records = query( :query_expression    => options[:conditions],\n                           :max_number_of_items => options[:limit],\n                           :next_token          => options[:next_token],\n                           :sort_option         => options[:sort] || options[:order] )\n          options[:auto_load] ? reload_all_records(records) : records\n        end\n\n        def find_initial(options) # :nodoc:\n          options[:limit] = 1\n          record = find_every(options).first\n          options[:auto_load] ? reload_all_records(record).first : record\n        end\n\n        def find_from_ids(args, options) # :nodoc:\n          cond = []\n          # detect amount of records requested\n          bunch_of_records_requested = args.size > 1 || args.first.is_a?(Array)\n          # flatten ids\n          args = Array(args).flatten\n          args.each { |id| cond << \"'id'=#{self.connection.escape(id)}\" }\n          ids_cond = \"[#{cond.join(' OR ')}]\"\n          # user defined :conditions to string (if it was defined)\n          options[:conditions] = build_conditions(options[:conditions])\n          # join ids condition and user defined conditions\n          options[:conditions] = options[:conditions].right_blank? ? ids_cond : \"#{options[:conditions]} intersection #{ids_cond}\"\n          result = find_every(options)\n          # if one record was requested then return it\n          unless bunch_of_records_requested\n            record = result.first\n            # railse if nothing was found\n            raise ActiveSdbError.new(\"Couldn't find #{name} with ID #{args}\") unless record\n            options[:auto_load] ? reload_all_records(record).first : record\n          else\n            # if a bunch of records was requested then return check that we found all of them\n            # and return as an array\n            unless args.size == result.size\n              id_list = args.map{|i| \"'#{i}'\"}.join(',')\n              raise ActiveSdbError.new(\"Couldn't find all #{name} with IDs (#{id_list}) (found #{result.size} results, but was looking for #{args.size})\")\n            else\n              options[:auto_load] ? reload_all_records(result) : result\n            end\n          end\n        end\n\n        # find_by helpers \n        def find_all_by_(format_str, args, options) # :nodoc:\n          fields = format_str.to_s.sub(/^find_(all_)?by_/,'').split('_and_')\n          conditions = fields.map { |field| \"['#{field}'=?]\" }.join(' intersection ')\n          options[:conditions] = [conditions, *args]\n          find(:all, options)\n        end\n\n        def find_by_(format_str, args, options) # :nodoc:\n          options[:limit] = 1\n          find_all_by_(format_str, args, options).first\n        end\n\n        # Misc\n\n        def method_missing(method, *args) # :nodoc:\n          if method.to_s[/^(find_all_by_|find_by_|select_all_by_|select_by_)/]\n            options = args.last.is_a?(Hash) ? args.pop : {}\n            __send__($1, method, args, options)\n          else\n            super(method, *args)\n          end\n        end\n\n        def build_select(options) # :nodoc:\n          select     = options[:select]    || '*'\n          from       = options[:from]      || domain\n          conditions = options[:conditions] ? \" WHERE #{build_conditions(options[:conditions])}\" : ''\n          order      = options[:order]      ? \" ORDER BY #{options[:order]}\"                     : ''\n          limit      = options[:limit]      ? \" LIMIT #{options[:limit]}\"                        : ''\n          # mix sort by argument (it must present in response)\n          unless order.right_blank?\n            sort_by, sort_order = sort_options(options[:order])\n            conditions << (conditions.right_blank? ? \" WHERE \" : \" AND \") << \"(#{sort_by} IS NOT NULL)\"\n          end\n          \"SELECT #{select} FROM #{from}#{conditions}#{order}#{limit}\"\n        end\n\n        def build_conditions(conditions) # :nodoc:\n          case\n          when conditions.is_a?(Array) then connection.query_expression_from_array(conditions)\n          when conditions.is_a?(Hash)  then connection.query_expression_from_hash(conditions)\n          else                              conditions\n          end\n        end\n\n        def serialization_for_type(type)\n          @serializations ||= {}\n          unless @serializations.has_key? type\n            @serializations[type] = ::RightAws::ActiveSdb.const_get(\"#{type}Serialization\") rescue false\n          end\n          @serializations[type]\n        end\n      end\n      \n      public\n\n      # instance attributes\n      attr_accessor :attributes \n      \n      # item name\n      attr_accessor :id\n\n      # Create new Item instance.\n      # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.\n      #\n      #  item = Client.new('name' => 'Jon', 'toys' => ['girls', 'beer', 'pub'])\n      #  puts item.inspect   #=> #<Client:0xb77a2698 @new_record=true, @attributes={\"name\"=>[\"Jon\"], \"toys\"=>[\"girls\", \"beer\", \"pub\"]}>\n      #  item.save           #=> {\"name\"=>[\"Jon\"], \"id\"=>\"c03edb7e-e45c-11dc-bede-001bfc466dd7\", \"toys\"=>[\"girls\", \"beer\", \"pub\"]}\n      #  puts item.inspect   #=> #<Client:0xb77a2698 @new_record=false, @attributes={\"name\"=>[\"Jon\"], \"id\"=>\"c03edb7e-e45c-11dc-bede-001bfc466dd7\", \"toys\"=>[\"girls\", \"beer\", \"pub\"]}>\n      #  \n      def initialize(attrs={})\n        @attributes = uniq_values(attrs)\n        @new_record = true\n      end\n\n      # Create and save new Item instance.\n      # +Attributes+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.\n      #\n      #  item = Client.create('name' => 'Cat', 'toys' => ['Jons socks', 'mice', 'clew']) \n      #  puts item.inspect   #=> #<Client:0xb77a0a78 @new_record=false, @attributes={\"name\"=>[\"Cat\"], \"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"toys\"=>[\"Jons socks\", \"mice\", \"clew\"]}>\n      #  \n      def self.create(attributes={})\n        item = self.new(attributes)\n        item.save\n        item\n      end\n      \n      # Returns an item id. Same as: item['id'] or item.attributes['id']\n      def id\n        @attributes['id']\n      end\n      \n      # Sets an item id.\n      def id=(id)\n        @attributes['id'] = id.to_s\n      end\n\n      # Returns a hash of all the attributes.\n      #\n      #  puts item.attributes.inspect #=> {\"name\"=>[\"Cat\"], \"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}\n      #\n      def attributes\n        @attributes.dup\n      end\n      \n      # Allows one to set all the attributes at once by passing in a hash with keys matching the attribute names.\n      # if '+id+' attribute is not set in new attributes has then it being derived from old attributes.\n      #\n      #  puts item.attributes.inspect   #=> {\"name\"=>[\"Cat\"], \"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}\n      #  # set new attributes ('id' is missed)\n      #  item.attributes = { 'name'=>'Dog', 'toys'=>['bones','cats'] }\n      #  puts item.attributes.inspect   #=> {\"name\"=>[\"Dog\"], \"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"toys\"=>[\"bones\", \"cats\"]}\n      #  # set new attributes ('id' is set)\n      #  item.attributes = { 'id' => 'blah-blah', 'name'=>'Birds', 'toys'=>['seeds','dogs tail'] }\n      #  puts item.attributes.inspect   #=> {\"name\"=>[\"Birds\"], \"id\"=>\"blah-blah\", \"toys\"=>[\"seeds\", \"dogs tail\"]}\n      #\n      def attributes=(attrs)\n        old_id = @attributes['id']\n        @attributes = uniq_values(attrs)\n        @attributes['id'] = old_id if @attributes['id'].right_blank? && !old_id.right_blank?\n        self.attributes\n      end\n\n      def columns\n        self.class.columns\n      end\n\n      def connection\n        self.class.connection\n      end\n\n      # Item domain name.\n      def domain\n        self.class.domain\n      end\n      \n      # Returns the values of the attribute identified by +attribute+.\n      # \n      #  puts item['Cat'].inspect  #=> [\"Jons socks\", \"clew\", \"mice\"]\n      #\n      def [](attribute)\n        raw = @attributes[attribute.to_s]\n        self.class.column?(attribute) && raw ? self.class.deserialize(attribute, raw.first) : raw\n      end\n\n      # Updates the attribute identified by +attribute+ with the specified +values+.\n      # \n      #  puts item['Cat'].inspect  #=> [\"Jons socks\", \"clew\", \"mice\"]\n      #  item['Cat'] = [\"Whiskas\", \"chicken\"]\n      #  puts item['Cat'].inspect  #=> [\"Whiskas\", \"chicken\"]\n      #\n      def []=(attribute, values)\n        attribute = attribute.to_s\n        @attributes[attribute] = case\n        when attribute == 'id'\n          values.to_s\n        when self.class.column?(attribute)\n          self.class.serialize(attribute, values)\n        else\n          Array(values).uniq\n        end\n      end\n\n      # Reload attributes from SDB. Replaces in-memory attributes.\n      # \n      #  item = Client.find_by_name('Cat')  #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\"}, @new_record=false>\n      #  item.reload                        #=> #<Client:0xb77d0d40 @attributes={\"id\"=>\"2937601a-e45d-11dc-a75f-001bfc466dd7\", \"name\"=>[\"Cat\"], \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}, @new_record=false>\n      #  \n      def reload\n        raise_on_id_absence\n        old_id = id\n        attrs = connection.get_attributes(domain, id)[:attributes]\n        @attributes = {}\n        unless attrs.right_blank?\n          attrs.each { |attribute, values| @attributes[attribute] = values }\n          @attributes['id'] = old_id\n        end\n        mark_as_old\n        @attributes\n      end\n\n      # Reload a set of attributes from SDB. Adds the loaded list to in-memory data.\n      # +attrs_list+ is an array or comma separated list of attributes names.\n      # Returns a hash of loaded attributes.\n      # \n      # This is not the best method to get a bunch of attributes because\n      # a web service call is being performed for every attribute.\n      # \n      #  item = Client.find_by_name('Cat')\n      #  item.reload_attributes('toys', 'name')   #=> {\"name\"=>[\"Cat\"], \"toys\"=>[\"Jons socks\", \"clew\", \"mice\"]}\n      #\n      def reload_attributes(*attrs_list)\n        raise_on_id_absence\n        attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }\n        attrs_list.delete('id')\n        result = {}\n        attrs_list.flatten.uniq.each do |attribute|\n          attribute = attribute.to_s\n          values = connection.get_attributes(domain, id, attribute)[:attributes][attribute]\n          unless values.right_blank?\n            @attributes[attribute] = result[attribute] = values\n          else\n            @attributes.delete(attribute)\n          end\n        end\n        mark_as_old\n        result\n      end\n\n      # Stores in-memory attributes to SDB.\n      # Adds the attributes values to already stored at SDB.\n      # Returns a hash of stored attributes. \n      #\n      #  sandy = Client.new(:name => 'Sandy') #=> #<Client:0xb775a7a8 @attributes={\"name\"=>[\"Sandy\"]}, @new_record=true>\n      #  sandy['toys'] = 'boys'\n      #  sandy.put\n      #  sandy['toys'] = 'patchwork'\n      #  sandy.put\n      #  sandy['toys'] = 'kids'\n      #  sandy.put\n      #  puts sandy.attributes.inspect        #=> {\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"kids\"]}\n      #  sandy.reload                         #=> {\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"boys\", \"kids\", \"patchwork\"]}\n      #\n      # compare to +save+ method\n      def put\n        @attributes = uniq_values(@attributes)\n        prepare_for_update\n        attrs = @attributes.dup\n        attrs.delete('id')\n        connection.put_attributes(domain, id, attrs) unless attrs.right_blank?\n        connection.put_attributes(domain, id, { 'id' => id }, :replace)\n        mark_as_old\n        @attributes\n      end\n\n      # Stores specified attributes.\n      # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.\n      # Returns a hash of saved attributes.\n      #\n      # see to +put+ method\n      def put_attributes(attrs)\n        attrs = uniq_values(attrs)\n        prepare_for_update\n        # if 'id' is present in attrs hash:\n        # replace internal 'id' attribute and remove it from the attributes to be sent\n        @attributes['id'] = attrs['id'] unless attrs['id'].right_blank?\n        attrs.delete('id')\n        # add new values to all attributes from list\n        connection.put_attributes(domain, id, attrs) unless attrs.right_blank?\n        connection.put_attributes(domain, id, { 'id' => id }, :replace)\n        attrs.each do |attribute, values|\n          @attributes[attribute] ||= []\n          @attributes[attribute] += values\n          @attributes[attribute].uniq!\n        end\n        mark_as_old\n        attributes\n      end\n\n      # Store in-memory attributes to SDB.\n      # Replaces the attributes values already stored at SDB by in-memory data.\n      # Returns a hash of stored attributes. \n      # \n      #  sandy = Client.new(:name => 'Sandy')  #=> #<Client:0xb775a7a8 @attributes={\"name\"=>[\"Sandy\"]}, @new_record=true>\n      #  sandy['toys'] = 'boys'\n      #  sandy.put\n      #  sandy['toys'] = 'patchwork'\n      #  sandy.put\n      #  sandy['toys'] = 'kids'\n      #  sandy.put\n      #  puts sandy.attributes.inspect         #=> {\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"kids\"]}\n      #  sandy.reload                          #=> {\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"kids\"]}\n      #\n      # compare to +put+ method\n      def save\n        @attributes = uniq_values(@attributes)\n        prepare_for_update\n        connection.put_attributes(domain, id, @attributes, :replace)\n        mark_as_old\n        @attributes\n      end\n\n      # Replaces the attributes at SDB by the given values.\n      # +Attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.\n      # The other in-memory attributes are not being saved.\n      # Returns a hash of stored attributes.\n      #\n      # see +save+ method\n      def save_attributes(attrs)\n        prepare_for_update\n        attrs = uniq_values(attrs)\n        # if 'id' is present in attrs hash then replace internal 'id' attribute\n        unless attrs['id'].right_blank?\n          @attributes['id'] = attrs['id']\n        else\n          attrs['id'] = id\n        end\n        connection.put_attributes(domain, id, attrs, :replace) unless attrs.right_blank?\n        attrs.each { |attribute, values| attrs[attribute] = values }\n        mark_as_old\n        attrs\n      end\n\n      # Remove specified values from corresponding attributes.\n      # +attrs+ is a hash: { attribute1 => values1, ..., attributeN => valuesN }.\n      #\n      #  sandy = Client.find_by_name 'Sandy' \n      #  sandy.reload\n      #  puts sandy.inspect                                #=> #<Client:0xb77b48fc @new_record=false, @attributes={\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"boys\", \"kids\", \"patchwork\"]}>\n      #  puts sandy.delete_values('toys' => 'patchwork')   #=> { 'toys' => ['patchwork'] }\n      #  puts sandy.inspect                                #=> #<Client:0xb77b48fc @new_record=false, @attributes={\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"boys\", \"kids\"]}>\n      #\n      def delete_values(attrs)\n        raise_on_id_absence\n        attrs = uniq_values(attrs)\n        attrs.delete('id')\n        unless attrs.right_blank?\n          connection.delete_attributes(domain, id, attrs)\n          attrs.each do |attribute, values|\n            # remove the values from the attribute\n            if @attributes[attribute]\n              @attributes[attribute] -= values\n            else\n              # if the attribute is unknown remove it from a resulting list of fixed attributes\n              attrs.delete(attribute)\n            end\n          end\n        end\n        attrs\n      end\n\n      # Removes specified attributes from the item.\n      # +attrs_list+ is an array or comma separated list of attributes names.\n      # Returns the list of deleted attributes.\n      # \n      #  sandy = Client.find_by_name 'Sandy' \n      #  sandy.reload\n      #  puts sandy.inspect                   #=> #<Client:0xb7761d28 @new_record=false, @attributes={\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"boys\", \"kids\", \"patchwork\"}>\n      #  puts sandy.delete_attributes('toys') #=> ['toys']\n      #  puts sandy.inspect                   #=> #<Client:0xb7761d28 @new_record=false, @attributes={\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\"}>\n      #\n      def delete_attributes(*attrs_list)\n        raise_on_id_absence\n        attrs_list = attrs_list.flatten.map{ |attribute| attribute.to_s }\n        attrs_list.delete('id')\n        unless attrs_list.right_blank?\n          connection.delete_attributes(domain, id, attrs_list)\n          attrs_list.each { |attribute| @attributes.delete(attribute) }\n        end\n        attrs_list\n      end\n\n      # Delete the Item entirely from SDB.\n      # \n      #  sandy = Client.find_by_name 'Sandy' \n      #  sandy.reload\n      #  sandy.inspect       #=> #<Client:0xb7761d28 @new_record=false, @attributes={\"name\"=>[\"Sandy\"], \"id\"=>\"b2832ce2-e461-11dc-b13c-001bfc466dd7\", \"toys\"=>[\"boys\", \"kids\", \"patchwork\"}>\n      #  puts sandy.delete\n      #  sandy.reload      \n      #  puts sandy.inspect  #=> #<Client:0xb7761d28 @attributes={}, @new_record=false>\n      # \n      def delete\n        raise_on_id_absence\n        connection.delete_attributes(domain, id)\n      end\n\n      # Item ID\n      def to_s\n        @id\n      end\n\n      # Returns true if this object hasn‘t been saved yet.      \n      def new_record?\n        @new_record\n      end\n\n      def mark_as_old  # :nodoc:\n        @new_record = false\n      end\n\n      # support accessing attribute values via method call\n      def method_missing(method_sym, *args)\n        method_name = method_sym.to_s\n        setter = method_name[-1,1] == '='\n        method_name.chop! if setter\n\n        if @attributes.has_key?(method_name) || self.class.column?(method_name)\n          setter ? self[method_name] = args.first : self[method_name]\n        else\n          super\n        end\n      end\n\n    private\n\n      def raise_on_id_absence\n        raise ActiveSdbError.new('Unknown record id') unless id\n      end\n      \n      def prepare_for_update\n        @attributes['id'] = self.class.generate_id if @attributes['id'].right_blank?\n        columns.all.each do |col_name|\n          self[col_name] ||= columns.default(col_name)\n        end\n      end\n      \n      def uniq_values(attributes=nil) # :nodoc:\n        attrs = {}\n        attributes.each do |attribute, values|\n          attribute = attribute.to_s\n          attrs[attribute] = case\n          when attribute == 'id'\n            values.to_s\n          when self.class.column?(attribute)\n            values.is_a?(String) ? values : self.class.serialize(attribute, values)\n          else\n            Array(values).uniq\n          end\n          attrs.delete(attribute) if values.right_blank?\n        end\n        attrs\n      end\n    end\n\n    class ColumnSet\n      attr_accessor :columns\n      def initialize\n        @columns = {}\n      end\n\n      def all\n        @columns.keys\n      end\n\n      def column(col_name)\n        @columns[col_name.to_s]\n      end\n      alias_method :include?, :column\n\n      def type_of(col_name)\n        column(col_name) && column(col_name)[:type]\n      end\n\n      def default(col_name)\n        return nil unless include?(col_name)\n        default = column(col_name)[:default]\n        default.respond_to?(:call) ? default.call : default\n      end\n\n      def method_missing(method_sym, *args)\n        data_type = args.shift || :String\n        options = args.shift || {}\n        @columns[method_sym.to_s] = options.merge( :type => data_type )\n      end\n    end\n\n    class DateTimeSerialization\n      class << self\n        def serialize(date)\n          date.strftime('%Y-%m-%dT%H:%M:%S%z')\n        end\n\n        def deserialize(string)\n          r = DateTime.parse(string) rescue nil\n        end\n      end\n    end\n\n    class BooleanSerialization\n      class << self\n        def serialize(boolean)\n          boolean ? 'T' : 'F'\n        end\n\n        def deserialize(string)\n          string == 'T'\n        end\n      end\n    end\n\n    class IntegerSerialization\n      class << self\n        def serialize(int)\n          int.to_s\n        end\n\n        def deserialize(string)\n          string.to_i\n        end\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/sdb/right_sdb_interface.rb",
    "content": "#\n# Copyright (c) 2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nrequire \"right_aws\"\n\nmodule RightAws\n\n  class SdbInterface < RightAwsBase\n    \n    include RightAwsBaseInterface\n\n    DEFAULT_HOST      = 'sdb.amazonaws.com'\n    DEFAULT_PORT      = 443\n    DEFAULT_PROTOCOL  = 'https'\n    DEFAULT_PATH      = '/'\n    API_VERSION       = '2009-04-15'\n    DEFAULT_NIL_REPRESENTATION = 'nil'\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml; @@bench.xml;     end\n    def self.bench_sdb; @@bench.service; end\n\n    attr_reader :last_query_expression\n\n    # Creates new RightSdb instance.\n    #\n    # Params:\n    #    { :server       => 'sdb.amazonaws.com'  # Amazon service host: 'sdb.amazonaws.com'(default)\n    #      :port         => 443                  # Amazon service port: 80 or 443(default)\n    #      :protocol     => 'https'              # Amazon service protocol: 'http' or 'https'(default)\n    #      :signature_version => '0'             # The signature version : '0','1 or '2'(default)\n    #      :logger       => Logger Object        # Logger instance: logs to STDOUT if omitted \n    #      :nil_representation => 'mynil'}       # interpret Ruby nil as this string value; i.e. use this string in SDB to represent Ruby nils (default is the string 'nil')\n    #      \n    # Example:\n    # \n    #  sdb = RightAws::SdbInterface.new('1E3GDYEOGFJPIT7XXXXXX','hgTHt68JY07JKUY08ftHYtERkjgtfERn57XXXXXX', {:logger => Logger.new('/tmp/x.log')}) #=> #<RightSdb:0xa6b8c27c>\n    #  \n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      @nil_rep = params[:nil_representation] ? params[:nil_representation] : DEFAULT_NIL_REPRESENTATION\n      params.delete(:nil_representation)\n      init({ :name                => 'SDB',\n             :default_host        => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).host   : DEFAULT_HOST,\n             :default_port        => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).port   : DEFAULT_PORT,\n             :default_service     => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).path   : DEFAULT_PATH,\n             :default_protocol    => ENV['SDB_URL'] ? URI.parse(ENV['SDB_URL']).scheme : DEFAULT_PROTOCOL,\n             :default_api_version => ENV['SDB_API_VERSION'] || API_VERSION },\n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n    \n    #-----------------------------------------------------------------\n    #      Requests\n    #-----------------------------------------------------------------\n    \n    def generate_request(action, params={}) #:nodoc:\n      generate_request_impl(:get, action, params )\n    end\n\n    # Sends request to Amazon and parses the response\n    # Raises AwsError if any banana happened\n    def request_info(request, parser)  #:nodoc:\n      request_info_impl(:sdb_connection, @@bench, request, parser)\n    end\n\n    # Prepare attributes for putting.\n    # (used by put_attributes)\n    def pack_attributes(items_or_attributes, replace = false, batch = false) #:nodoc:\n      if batch\n        index = 0\n        items_or_attributes.inject({}){|result, (item_name, attributes)|\n          item_prefix = \"Item.#{index}.\"\n          result[\"#{item_prefix}ItemName\"] = item_name.to_s\n          result.merge!(\n            pack_single_item_attributes(attributes, replace, item_prefix))\n          index += 1\n          result\n        }\n      else\n        pack_single_item_attributes(items_or_attributes, replace)\n      end\n    end\n\n    def pack_single_item_attributes(attributes, replace, prefix = \"\")\n      result = {}\n      if attributes\n        idx = 0\n        skip_values = attributes.is_a?(Array)\n        attributes.each do |attribute, values|\n          # set replacement attribute\n          result[\"#{prefix}Attribute.#{idx}.Replace\"] = 'true' if replace\n          # pack Name/Value\n          unless values.nil?\n            # Array(values) does not work here:\n            #  - Array('') => [] but we wanna get here ['']\n            [values].flatten.each do |value|\n              result[\"#{prefix}Attribute.#{idx}.Name\"]  = attribute\n              result[\"#{prefix}Attribute.#{idx}.Value\"] = ruby_to_sdb(value) unless skip_values\n              idx += 1\n            end\n          else\n            result[\"#{prefix}Attribute.#{idx}.Name\"] = attribute\n            result[\"#{prefix}Attribute.#{idx}.Value\"] = ruby_to_sdb(nil) unless skip_values\n            idx += 1\n          end\n        end\n      end\n      result\n    end\n    \n    # Use this helper to manually escape the fields in the query expressions. \n    # To escape the single quotes and backslashes and to wrap the string into the single quotes.\n    # \n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API.html\n    #\n    def escape(value)\n      %Q{'#{value.to_s.gsub(/(['\\\\])/){ \"\\\\#{$1}\" }}'} if value\n    end\n    \n    # Convert a Ruby language value to a SDB value by replacing Ruby nil with the user's chosen string representation of nil.\n    # Non-nil values are unaffected by this filter.\n    def ruby_to_sdb(value)\n      value.nil? ? @nil_rep : value\n    end\n    \n    # Convert a SDB value to a Ruby language value by replacing the user's chosen string representation of nil with Ruby nil.\n    # Values are unaffected by this filter unless they match the nil representation exactly.\n    def sdb_to_ruby(value)\n      value.eql?(@nil_rep) ? nil : value\n    end\n\n    # Convert select and query_with_attributes responses to a Ruby language values by replacing the user's chosen string representation of nil with Ruby nil.\n    # (This method affects on a passed response value)\n    def select_response_to_ruby(response) #:nodoc:\n      response[:items].each_with_index do |item, idx|\n        item.each do |key, attributes|\n          attributes.each do |name, values|\n            values.collect! { |value| sdb_to_ruby(value) }\n          end\n        end\n      end\n      response\n    end\n\n    # Create query expression from an array.\n    # (similar to ActiveRecord::Base#find using :conditions => ['query', param1, .., paramN])\n    #\n    def query_expression_from_array(params) #:nodoc:\n      return '' if params.right_blank?\n      query = params.shift.to_s\n      query.gsub(/(\\\\)?(\\?)/) do\n        if $1 # if escaped '\\?' is found - replace it by '?' without backslash\n          \"?\"\n        else  # well, if no backslash precedes '?' then replace it by next param from the list\n          escape(params.shift)\n        end\n      end\n    end\n\n    def query_expression_from_hash(hash)\n      return '' if hash.right_blank?\n      expression = []\n      hash.each do |key, value|\n        expression << \"#{key}=#{escape(value)}\"\n      end\n      expression.join(' AND ')\n    end\n\n    # Retrieve a list of SDB domains from Amazon.\n    # \n    # Returns a hash:\n    #   { :domains     => [domain1, ..., domainN],\n    #     :next_token => string || nil,\n    #     :box_usage   => string,\n    #     :request_id  => string }\n    #     \n    # Example:\n    # \n    #  sdb = RightAws::SdbInterface.new\n    #  sdb.list_domains  #=> { :box_usage  => \"0.0000071759\",\n    #                          :request_id => \"976709f9-0111-2345-92cb-9ce90acd0982\",\n    #                          :domains    => [\"toys\", \"dolls\"]}\n    # \n    # If a block is given, this method yields to it.  If the block returns true, list_domains will continue looping the request.  If the block returns false,\n    # list_domains will end.\n    # \n    #   sdb.list_domains(10) do |result|   # list by 10 domains per iteration\n    #     puts result.inspect\n    #     true\n    #   end\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_ListDomains.html\n    #\n    def list_domains(max_number_of_domains = nil, next_token = nil )\n      request_params = { 'MaxNumberOfDomains' => max_number_of_domains,\n                         'NextToken'          => next_token }\n      link   = generate_request(\"ListDomains\", request_params)\n      result = request_info(link, QSdbListDomainParser.new)\n      # return result if no block given\n      return result unless block_given?\n      # loop if block if given\n      begin\n        # the block must return true if it wanna continue \n        break unless yield(result) && result[:next_token]\n        # make new request\n        request_params['NextToken'] = result[:next_token]\n        link   = generate_request(\"ListDomains\", request_params)\n        result = request_info(link, QSdbListDomainParser.new)\n      end while true\n    rescue Exception\n      on_exception\n    end\n    \n    # Create new SDB domain at Amazon.\n    # \n    # Returns a hash: { :box_usage, :request_id } on success or an exception on error.\n    # (Amazon raises no errors if the domain already exists).\n    # \n    # Example:\n    # \n    #  sdb = RightAws::SdbInterface.new\n    #  sdb.create_domain('toys') # => { :box_usage  => \"0.0000071759\",\n    #                                   :request_id => \"976709f9-0111-2345-92cb-9ce90acd0982\" }\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_CreateDomain.html\n    def create_domain(domain_name)\n      link = generate_request(\"CreateDomain\",\n                              'DomainName' => domain_name)\n      request_info(link, QSdbSimpleParser.new)\n    rescue Exception\n      on_exception\n    end\n\n    # Query Metadata for Domain\n    #\n    # Returns a hash on success or an exception on error.\n    #\n    # example: \n    # sdb = RightAWS:::SdbInterface.new\n    # sdb.domain_metadata('toys') # => {:attribute_values_size_bytes=>\"2754\",\n    #                                   :item_count=>\"25\",\n    #                                   :item_names_size_bytes=>\"900\",\n    #                                   :timestamp=>\"1291890409\",\n    #                                   :attribute_name_count=>\"7\",\n    #                                   :box_usage=>\"0.0000071759\",\n    #                                   :attribute_names_size_bytes=>\"48\",\n    #                                   :attribute_value_count=>\"154\",\n    #                                   :request_id=>\"79bbfe8f-f0c9-59a2-0963-16d5fc6c3c52\"}\n    # see http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?SDB_API_DomainMetadata.html\n    def domain_metadata(domain)\n      link = generate_request(\"DomainMetadata\",\"DomainName\"=>domain)\n      request_info(link,QSdbGenericParser.new)\n    end\n\n    # Delete SDB domain at Amazon.\n    # \n    # Returns a hash: { :box_usage, :request_id } on success or an exception on error.\n    # (Amazon raises no errors if the domain does not exist).\n    # \n    # Example:\n    # \n    #  sdb = RightAws::SdbInterface.new\n    #  sdb.delete_domain('toys') # => { :box_usage  => \"0.0000071759\",\n    #                                   :request_id => \"976709f9-0111-2345-92cb-9ce90acd0982\" }\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteDomain.html\n    #\n    def delete_domain(domain_name)\n      link = generate_request(\"DeleteDomain\",\n                              'DomainName' => domain_name)\n      request_info(link, QSdbSimpleParser.new)\n    rescue Exception\n      on_exception\n    end\n    \n    # Add/Replace item attributes.\n    # \n    # Params:\n    #  domain_name = DomainName\n    #  item_name   = ItemName\n    #  attributes  = {\n    #    'nameA' => [valueA1,..., valueAN],\n    #    ...\n    #    'nameZ' => [valueZ1,..., valueZN]\n    #  }\n    #  replace = :replace | any other value to skip replacement\n    #  \n    # Returns a hash: { :box_usage, :request_id } on success or an exception on error. \n    # (Amazon raises no errors if the attribute was not overridden, as when the :replace param is unset).\n    # \n    # Example:\n    # \n    #  sdb = RightAws::SdbInterface.new\n    #  sdb.create_domain 'family'\n    #  \n    #  attributes = {}\n    #  # create attributes for Jon and Silvia\n    #  attributes['Jon']    = %w{ car beer }\n    #  attributes['Silvia'] = %w{ beetle rolling_pin kids } \n    #  sdb.put_attributes 'family', 'toys', attributes   #=> ok\n    #  # now: Jon=>[car, beer], Silvia=>[beetle, rolling_pin, kids]\n    #  \n    #  # add attributes to Jon\n    #  attributes.delete('Silvia')\n    #  attributes['Jon'] = %w{ girls pub }\n    #  sdb.put_attributes 'family', 'toys', attributes   #=> ok\n    #  # now: Jon=>[car, beer, girls, pub], Silvia=>[beetle, rolling_pin, kids]\n    #  \n    #  # replace attributes for Jon and add to a cat (the cat had no attributes before)\n    #  attributes['Jon'] = %w{ vacuum_cleaner hammer spade }\n    #  attributes['cat'] = %w{ mouse clew Jons_socks }\n    #  sdb.put_attributes 'family', 'toys', attributes, :replace #=> ok\n    #  # now: Jon=>[vacuum_cleaner, hammer, spade], Silvia=>[beetle, rolling_pin, kids], cat=>[mouse, clew, Jons_socks]\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_PutAttributes.html\n    #\n    def put_attributes(domain_name, item_name, attributes, replace = false)\n      params = { 'DomainName' => domain_name,\n                 'ItemName'   => item_name }.merge(pack_attributes(attributes, replace))\n      link = generate_request(\"PutAttributes\", params)\n      request_info( link, QSdbSimpleParser.new )\n    rescue Exception\n      on_exception\n    end\n\n    # Add/Replace attributes for multiple items at a time.\n    #\n    # Params:\n    #   domain_name = DomainName\n    #   items       = {\n    #     'Item1' => {\n    #       'nameA'  => [valueA1, valueA2,..., valueAN],\n    #       ...\n    #       'nameB'  => [valueB1, valueB2,..., valueBN]\n    #     },\n    #     'Item2' => {\n    #       'nameC'  => [valueC1, valueC2,..., valueCN],\n    #       ...\n    #       'nameD'  => [valueD1, valueD2,..., valueDN]\n    #     }\n    #   }\n    #   replace = :replace | any other value to skip replacement\n    #\n    # Usage of batch_put_attributes is similar to put_attributes except that\n    # instead of supplying an item_name and a hash of attributes, you supply a\n    # hash of item names to attributes.\n    #\n    # See: http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?SDB_API_BatchPutAttributes.html\n    def batch_put_attributes(domain_name, items, replace = false)\n      params = { 'DomainName' => domain_name }.merge(pack_attributes(items, replace, true))\n      link = generate_request(\"BatchPutAttributes\", params)\n      request_info( link, QSdbSimpleParser.new)\n    rescue Exception\n      on_exception\n    end\n    \n    # Retrieve SDB item's attribute(s).\n    # \n    # Returns a hash:\n    #  { :box_usage  => string,\n    #    :request_id => string,\n    #    :attributes => { 'nameA' => [valueA1,..., valueAN],\n    #                     ... ,\n    #                     'nameZ' => [valueZ1,..., valueZN] } }\n    # \n    # Example:\n    #  # request all attributes\n    #  sdb.get_attributes('family', 'toys') # => { :attributes => {\"cat\"    => [\"clew\", \"Jons_socks\", \"mouse\"] },\n    #                                                              \"Silvia\" => [\"beetle\", \"rolling_pin\", \"kids\"],\n    #                                                              \"Jon\"    => [\"vacuum_cleaner\", \"hammer\", \"spade\"]},\n    #                                              :box_usage  => \"0.0000093222\",\n    #                                              :request_id => \"81273d21-000-1111-b3f9-512d91d29ac8\" }\n    #  \n    #  # request cat's attributes only\n    #  sdb.get_attributes('family', 'toys', 'cat') # => { :attributes => {\"cat\" => [\"clew\", \"Jons_socks\", \"mouse\"] },\n    #                                                     :box_usage  => \"0.0000093222\",\n    #                                                     :request_id => \"81273d21-001-1111-b3f9-512d91d29ac8\" }\n    #\n    #  # request all attributes using a consistent read\n    #  # see:  http://docs.amazonwebservices.com/AmazonSimpleDB/latest/DeveloperGuide/index.html?ConsistencySummary.html\n    #  sdb.get_attributes('family', 'toys', nil, true) # => { :attributes => {\"cat\"    => [\"clew\", \"Jons_socks\", \"mouse\"] },\n    #                                                              \"Silvia\" => [\"beetle\", \"rolling_pin\", \"kids\"],\n    #                                                              \"Jon\"    => [\"vacuum_cleaner\", \"hammer\", \"spade\"]},\n    #                                              :box_usage  => \"0.0000093222\",\n    #                                              :request_id => \"81273d21-000-1111-b3f9-512d91d29ac8\" }\n    # \n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_GetAttributes.html\n    #\n    def get_attributes(domain_name, item_name, attribute_name=nil, consistent_read=nil)\n      link = generate_request(\"GetAttributes\", 'DomainName'     => domain_name,\n                                               'ItemName'       => item_name,\n                                               'AttributeName'  => attribute_name,\n                                               'ConsistentRead' => consistent_read )\n      res = request_info(link, QSdbGetAttributesParser.new)\n      res[:attributes].each_value do |values|\n        values.collect! { |e| sdb_to_ruby(e) }\n      end\n      res\n    rescue Exception\n      on_exception\n    end\n\n    # Delete value, attribute or item.\n    #\n    # Example:\n    #  # delete 'vodka' and 'girls' from 'Jon' and 'mice' from 'cat'.\n    #  sdb.delete_attributes 'family', 'toys', { 'Jon' => ['vodka', 'girls'], 'cat' => ['mice'] }\n    #\n    #  # delete the all the values from attributes (i.e. delete the attributes)\n    #  sdb.delete_attributes 'family', 'toys', { 'Jon' => [], 'cat' => [] }\n    #  # or \n    #  sdb.delete_attributes 'family', 'toys', [ 'Jon', 'cat' ]\n    #\n    #  # delete all the attributes from item 'toys' (i.e. delete the item)\n    #  sdb.delete_attributes 'family', 'toys'\n    #  \n    # see http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_DeleteAttributes.html \n    #\n    def delete_attributes(domain_name, item_name, attributes = nil)\n      params = { 'DomainName' => domain_name,\n                 'ItemName'   => item_name }.merge(pack_attributes(attributes))\n      link = generate_request(\"DeleteAttributes\", params)\n      request_info( link, QSdbSimpleParser.new )\n    rescue Exception\n      on_exception\n    end\n    \n    \n    # QUERY:\n \n    # Perform a query on SDB.\n    # \n    # Returns a hash:\n    #   { :box_usage  => string,\n    #     :request_id => string,\n    #     :next_token => string,\n    #     :items      => [ItemName1,..., ItemNameN] }\n    #     \n    # Example:\n    # \n    #   query = \"['cat' = 'clew']\"\n    #   sdb.query('family', query)     #=> hash of data\n    #   sdb.query('family', query, 10) #=> hash of data with max of 10 items\n    # \n    # If a block is given, query will iteratively yield results to it as long as the block continues to return true.\n    # \n    #   # List 10 items per iteration. Don't \n    #   # forget to escape single quotes and backslashes and wrap all the items in single quotes.\n    #   query = \"['cat'='clew'] union ['dog'='Jon\\\\'s boot']\"\n    #   sdb.query('family', query, 10) do |result|\n    #     puts result.inspect\n    #     true\n    #   end\n    #  \n    #   # Same query using automatic escaping...to use the auto escape, pass the query and its params as an array:\n    #   query = [ \"['cat'=?] union ['dog'=?]\", \"clew\", \"Jon's boot\" ]\n    #   sdb.query('family', query)\n    #\n    #   query = [ \"['cat'=?] union ['dog'=?] sort 'cat' desc\", \"clew\", \"Jon's boot\" ]\n    #   sdb.query('family', query)\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/SDB_API_Query.html\n    #      http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SortingData.html\n    #\n    def query(domain_name, query_expression = nil, max_number_of_items = nil, next_token = nil)\n      query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)\n      @last_query_expression = query_expression\n      #\n      request_params = { 'DomainName'       => domain_name,\n                         'QueryExpression'  => query_expression,\n                         'MaxNumberOfItems' => max_number_of_items,\n                         'NextToken'        => next_token }\n      link   = generate_request(\"Query\", request_params)\n      result = request_info( link, QSdbQueryParser.new )\n      # return result if no block given\n      return result unless block_given?\n      # loop if block if given\n      begin\n        # the block must return true if it wanna continue \n        break unless yield(result) && result[:next_token]\n        # make new request\n        request_params['NextToken'] = result[:next_token]\n        link   = generate_request(\"Query\", request_params)\n        result = request_info( link, QSdbQueryParser.new )\n      end while true\n    rescue Exception\n      on_exception\n    end\n    \n    # Perform a query and fetch specified attributes.\n    # If attributes are not specified then fetches the whole list of attributes.\n    #\n    #\n    # Returns a hash:\n    #   { :box_usage  => string,\n    #     :request_id => string,\n    #     :next_token => string,\n    #     :items      => [ { ItemName1 => { attribute1 => value1, ...  attributeM => valueM } },\n    #                      { ItemName2 => {...}}, ... ]\n    #\n    # Example:\n    #\n    #   sdb.query_with_attributes(domain, ['hobby', 'country'], \"['gender'='female'] intersection ['name' starts-with ''] sort 'name'\") #=>\n    #     { :request_id => \"06057228-70d0-4487-89fb-fd9c028580d3\",\n    #       :items =>\n    #         [ { \"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #             { \"hobby\"   => [\"cooking\", \"flowers\", \"cats\"],\n    #               \"country\" => [\"Russia\"]}},\n    #           { \"0327614a-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #             { \"hobby\"   => [\"patchwork\", \"bundle jumping\"],\n    #               \"country\" => [\"USA\"]}}, ... ],\n    #        :box_usage=>\"0.0000504786\"}\n    #\n    #   sdb.query_with_attributes(domain, [], \"['gender'='female'] intersection ['name' starts-with ''] sort 'name'\") #=>\n    #     { :request_id => \"75bb19db-a529-4f69-b86f-5e3800f79a45\",\n    #       :items =>\n    #       [ { \"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #           { \"hobby\"   => [\"cooking\", \"flowers\", \"cats\"],\n    #             \"name\"    => [\"Mary\"],\n    #             \"country\" => [\"Russia\"],\n    #             \"gender\"  => [\"female\"],\n    #             \"id\"      => [\"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"]}},\n    #         { \"0327614a-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #           { \"hobby\"   => [\"patchwork\", \"bundle jumping\"],\n    #             \"name\"    => [\"Mary\"],\n    #             \"country\" => [\"USA\"],\n    #             \"gender\"  => [\"female\"],\n    #             \"id\"      => [\"0327614a-dbd8-11dd-80bd-001bfc466dd7\"]}}, ... ],\n    #      :box_usage=>\"0.0000506668\"}\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_QueryWithAttributes.html\n    #\n    def query_with_attributes(domain_name, attributes=[], query_expression = nil, max_number_of_items = nil, next_token = nil)\n      attributes = Array(attributes)\n      query_expression = query_expression_from_array(query_expression) if query_expression.is_a?(Array)\n      @last_query_expression = query_expression\n      #\n      request_params = { 'DomainName'       => domain_name,\n                         'QueryExpression'  => query_expression,\n                         'MaxNumberOfItems' => max_number_of_items,\n                         'NextToken'        => next_token }\n      attributes.each_with_index do |attribute, idx|\n        request_params[\"AttributeName.#{idx+1}\"] = attribute\n      end\n      link   = generate_request(\"QueryWithAttributes\", request_params)\n      result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))\n      # return result if no block given\n      return result unless block_given?\n      # loop if block if given\n      begin\n        # the block must return true if it wanna continue\n        break unless yield(result) && result[:next_token]\n        # make new request\n        request_params['NextToken'] = result[:next_token]\n        link   = generate_request(\"QueryWithAttributes\", request_params)\n        result = select_response_to_ruby(request_info( link, QSdbQueryWithAttributesParser.new ))\n      end while true\n    rescue Exception\n      on_exception\n    end\n\n    # Perform SQL-like select and fetch attributes.\n    # Attribute values must be quoted with a single or double quote. If a quote appears within the attribute value, it must be escaped with the same quote symbol as shown in the following example.\n    # (Use array to pass select_expression params to avoid manual escaping).\n    #\n    #  sdb.select([\"select * from my_domain where gender=?\", 'female']) #=>\n    #    {:request_id =>\"8241b843-0fb9-4d66-9100-effae12249ec\",\n    #     :items =>\n    #      [ { \"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #          {\"hobby\"   => [\"cooking\", \"flowers\", \"cats\"],\n    #           \"name\"    => [\"Mary\"],\n    #           \"country\" => [\"Russia\"],\n    #           \"gender\"  => [\"female\"],\n    #           \"id\"      => [\"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"]}},\n    #        { \"0327614a-dbd8-11dd-80bd-001bfc466dd7\"=>\n    #          {\"hobby\"   => [\"patchwork\", \"bundle jumping\"],\n    #           \"name\"    => [\"Mary\"],\n    #           \"country\" => [\"USA\"],\n    #           \"gender\"  => [\"female\"],\n    #           \"id\"      => [\"0327614a-dbd8-11dd-80bd-001bfc466dd7\"]}}, ... ]\n    #     :box_usage =>\"0.0000506197\"}\n    #\n    #   sdb.select('select country, name from my_domain') #=>\n    #    {:request_id=>\"b1600198-c317-413f-a8dc-4e7f864a940a\",\n    #     :items=>\n    #      [ { \"035f1ba8-dbd8-11dd-80bd-001bfc466dd7\"=> {\"name\"=>[\"Mary\"],     \"country\"=>[\"Russia\"]} },\n    #        { \"376d2e00-75b0-11dd-9557-001bfc466dd7\"=> {\"name\"=>[\"Putin\"],    \"country\"=>[\"Russia\"]} },\n    #        { \"0327614a-dbd8-11dd-80bd-001bfc466dd7\"=> {\"name\"=>[\"Mary\"],     \"country\"=>[\"USA\"]}    },\n    #        { \"372ebbd4-75b0-11dd-9557-001bfc466dd7\"=> {\"name\"=>[\"Bush\"],     \"country\"=>[\"USA\"]}    },\n    #        { \"37a4e552-75b0-11dd-9557-001bfc466dd7\"=> {\"name\"=>[\"Medvedev\"], \"country\"=>[\"Russia\"]} },\n    #        { \"38278dfe-75b0-11dd-9557-001bfc466dd7\"=> {\"name\"=>[\"Mary\"],     \"country\"=>[\"Russia\"]} },\n    #        { \"37df6c36-75b0-11dd-9557-001bfc466dd7\"=> {\"name\"=>[\"Mary\"],     \"country\"=>[\"USA\"]}    } ],\n    #     :box_usage=>\"0.0000777663\"}\n    #\n    # see: http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDB_API_Select.html\n    #      http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?UsingSelect.html\n    #      http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/index.html?SDBLimits.html\n    #\n    def select(select_expression, next_token = nil)\n      select_expression      = query_expression_from_array(select_expression) if select_expression.is_a?(Array)\n      @last_query_expression = select_expression\n      #\n      request_params = { 'SelectExpression' => select_expression,\n                         'NextToken'        => next_token }\n      link   = generate_request(\"Select\", request_params)\n      result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))\n      return result unless block_given?\n      # loop if block if given\n      begin\n        # the block must return true if it wanna continue\n        break unless yield(result) && result[:next_token]\n        # make new request\n        request_params['NextToken'] = result[:next_token]\n        link   = generate_request(\"Select\", request_params)\n        result = select_response_to_ruby(request_info( link, QSdbSelectParser.new ))\n      end while true\n    rescue Exception\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS:\n    #-----------------------------------------------------------------\n    class QSdbGenericParser < RightAWSParser #:nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case full_tag_name\n        when %r{/(DomainMetadataResult|ResponseMetadata)/}\n          @result[name.right_underscore.to_sym] = @text\n        end\n      end\n    end\n      \n    class QSdbListDomainParser < RightAWSParser #:nodoc:\n      def reset\n        @result = { :domains => [] }\n      end\n      def tagend(name)\n        case name\n        when 'NextToken'  then @result[:next_token] =  @text\n        when 'DomainName' then @result[:domains]    << @text\n        when 'BoxUsage'   then @result[:box_usage]  =  @text\n        when 'RequestId'  then @result[:request_id] =  @text\n        end\n      end\n    end\n\n    class QSdbSimpleParser < RightAWSParser #:nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name\n        when 'BoxUsage'  then @result[:box_usage]  =  @text\n        when 'RequestId' then @result[:request_id] =  @text\n        end\n      end\n    end\n\n    class QSdbGetAttributesParser < RightAWSParser #:nodoc:\n      def reset\n        @last_attribute_name = nil\n        @result = { :attributes => {} }\n      end\n      def tagend(name)\n        case name\n        when 'Name'      then @last_attribute_name = @text\n        when 'Value'     then (@result[:attributes][@last_attribute_name] ||= []) << @text\n        when 'BoxUsage'  then @result[:box_usage]  =  @text\n        when 'RequestId' then @result[:request_id] =  @text\n        end\n      end\n    end\n\n    class QSdbQueryParser < RightAWSParser #:nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagend(name)\n        case name\n        when 'ItemName'  then @result[:items]      << @text\n        when 'BoxUsage'  then @result[:box_usage]  =  @text\n        when 'RequestId' then @result[:request_id] =  @text\n        when 'NextToken' then @result[:next_token] =  @text\n        end\n      end\n    end\n\n    class QSdbQueryWithAttributesParser < RightAWSParser #:nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagend(name)\n        case name\n        when 'Name'\n          case @xmlpath\n          when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item'\n            @item = @text\n            @result[:items] << { @item => {} }\n          when 'QueryWithAttributesResponse/QueryWithAttributesResult/Item/Attribute'\n            @attribute = @text\n            @result[:items].last[@item][@attribute] ||= []\n          end\n        when 'RequestId' then @result[:request_id] = @text\n        when 'BoxUsage'  then @result[:box_usage]  = @text\n        when 'NextToken' then @result[:next_token] = @text\n        when 'Value'     then @result[:items].last[@item][@attribute] << @text\n        end\n      end\n    end\n\n    class QSdbSelectParser < RightAWSParser #:nodoc:\n      def reset\n        @result = { :items => [] }\n      end\n      def tagend(name)\n        case name\n        when 'Name'\n          case @xmlpath\n          when 'SelectResponse/SelectResult/Item'\n            @item = @text\n            @result[:items] << { @item => {} }\n          when 'SelectResponse/SelectResult/Item/Attribute'\n            @attribute = @text\n            @result[:items].last[@item][@attribute] ||= []\n          end\n        when 'RequestId' then @result[:request_id] = @text\n        when 'BoxUsage'  then @result[:box_usage]  = @text\n        when 'NextToken' then @result[:next_token] = @text\n        when 'Value'     then @result[:items].last[@item][@attribute] << @text\n        end\n      end\n    end\n\n  end\n  \nend\n"
  },
  {
    "path": "lib/sns/right_sns_interface.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n  class SnsInterface < RightAwsBase\n    include RightAwsBaseInterface\n\n    DEFAULT_HOST        = 'sns.us-east-1.amazonaws.com'\n    DEFAULT_PORT        = 443\n    DEFAULT_PROTOCOL    = 'https'\n    DEFAULT_SERVICE     = '/'\n    REQUEST_TTL         = 30\n\n    # Apparently boilerplate stuff\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_service\n      @@bench.service\n    end\n\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      if params[:region]\n        server = \"sns.#{params[:region]}.amazonaws.com\"\n        params.delete(:region)\n      else\n        server = DEFAULT_HOST\n      end\n      init({  :name               => 'SNS',\n              :default_host       => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).host    : server,\n              :default_port       => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).port    : DEFAULT_PORT,\n              :default_service    => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).path    : DEFAULT_SERVICE,\n              :default_protocol   => ENV['SNS_URL'] ? URI.parse(ENV['SNS_URL']).scheme  : DEFAULT_PROTOCOL},\n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'],\n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'],\n           params)\n    end\n\n    # TODO: RJG - Seems like generate_request and generate_rest_request could be in a sub class?\n    # Generates a request hash for the sns API\n    def generate_request(action, params={})  # :nodoc:\n        # Sometimes we need to use queue uri (delete queue etc)\n        # In that case we will use Symbol key: 'param[:queue_url]'\n      service = params[:sns_url] ? URI(params[:sns_url]).path : '/'\n        # remove unset(=optional) and symbolyc keys\n      params.each{ |key, value| params.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n        # prepare output hash\n      service_hash = { \"Action\"           => action,\n                       \"Expires\"          => (Time.now + REQUEST_TTL).utc.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                       \"AWSAccessKeyId\"   => @aws_access_key_id }\n                       #\"Version\"          => API_VERSION }\n      service_hash.update(params)\n      service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], service)\n      request        = Net::HTTP::Get.new(\"#{AwsUtils::URLencode(service)}?#{service_params}\")\n        # prepare output hash\n      { :request  => request,\n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n    # Generates a request hash for the REST API\n    def generate_rest_request(method, param) # :nodoc:\n      sns_uri = param[:sns_url] ? URI(param[:sns_url]).path : '/'\n      message   = param[:message]                # extract message body if nesessary\n        # remove unset(=optional) and symbolyc keys\n      param.each{ |key, value| param.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n        # created request\n      param_to_str = param.to_a.collect{|key,val| key.to_s + \"=\" + CGI::escape(val.to_s) }.join(\"&\")\n      param_to_str = \"?#{param_to_str}\" unless param_to_str.right_blank?\n      request = \"Net::HTTP::#{method.capitalize}\".right_constantize.new(\"#{sns_uri}#{param_to_str}\")\n      request.body = message if message\n        # set main headers\n      request['content-md5']  = ''\n      request['Content-Type'] = 'text/plain'\n      request['Date']         = Time.now.httpdate\n        # generate authorization string\n      auth_string = \"#{method.upcase}\\n#{request['content-md5']}\\n#{request['Content-Type']}\\n#{request['Date']}\\n#{CGI::unescape(sns_uri)}\"\n      signature   = AwsUtils::sign(@aws_secret_access_key, auth_string)\n        # set other headers\n      request['Authorization'] = \"AWS #{@aws_access_key_id}:#{signature}\"\n      #request['AWS-Version']   = API_VERSION\n        # prepare output hash\n      { :request  => request,\n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n    # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser) # :nodoc:\n      request_info_impl(:sns_connection, @@bench, request, parser)\n    end\n\n    def create_topic(topic_name)\n      req_hash = generate_request('CreateTopic', 'Name' => topic_name)\n      request_info(req_hash, SnsCreateTopicParser.new)\n    end\n\n    def list_topics()\n      req_hash = generate_request('ListTopics')\n      request_info(req_hash, SnsListTopicsParser.new)\n    end\n\n    def delete_topic(topic_arn)\n      req_hash = generate_request('DeleteTopic', 'TopicArn' => topic_arn)\n      request_info(req_hash, RightHttp2xxParser.new)\n    end\n\n    def subscribe(topic_arn, protocol, endpoint)\n      req_hash = generate_request('Subscribe', 'TopicArn' => topic_arn, 'Protocol' => protocol, 'Endpoint' => endpoint)\n      request_info(req_hash, SnsSubscribeParser.new)\n    end\n\n    def unsubscribe(subscription_arn)\n      req_hash = generate_request('Unsubscribe', 'SubscriptionArn' => subscription_arn)\n      request_info(req_hash, RightHttp2xxParser.new)\n    end\n\n    def publish(topic_arn, message, subject)\n      req_hash = generate_request('Publish', 'TopicArn' => topic_arn, 'Message' => message, 'Subject' => subject)\n      request_info(req_hash, SnsPublishParser.new)\n    end\n\n    def set_topic_attribute(topic_arn, attribute_name, attribute_value)\n      if attribute_name != 'Policy' && attribute_name != 'DisplayName'\n        raise(ArgumentError, \"The only values accepted for the attribute_name parameter are (Policy, DisplayName)\")\n      end\n      req_hash = generate_request('SetTopicAttributes', 'TopicArn' => topic_arn, 'AttributeName' => attribute_name, 'AttributeValue' => attribute_value)\n      request_info(req_hash, RightHttp2xxParser.new)\n    end\n\n    def get_topic_attributes(topic_arn)\n      req_hash = generate_request('GetTopicAttributes', 'TopicArn' => topic_arn)\n      request_info(req_hash, SnsGetTopicAttributesParser.new)\n    end\n\n    # Calls either the ListSubscriptions or ListSubscriptionsByTopic depending on whether or not the topic_arn parameter is provided.\n    def list_subscriptions(topic_arn = nil)\n      req_hash = topic_arn ? generate_request('ListSubscriptionsByTopic', 'TopicArn' => topic_arn) : generate_request('ListSubscriptions')\n      request_info(req_hash, SnsListSubscriptionsParser.new)\n    end\n\n    def confirm_subscription(topic_arn, token, authenticate_on_unsubscribe=false)\n      req_hash = generate_request('ConfirmSubscription', 'AuthenticateOnUnsubscribe' => authenticate_on_unsubscribe.to_s, 'Token' => token, 'TopicArn' => topic_arn)\n      request_info(req_hash, SnsConfirmSubscriptionParser.new)\n    end\n\n    def add_permission(topic_arn, label, acct_action_hash_ary)\n      n_hash = {\n        'TopicArn'  => topic_arn,\n        'Label'     => label\n      }\n\n      acct_action_hash_ary.each_with_index do |hash_val, idx|\n        n_hash[\"AWSAccountId.member.#{idx+1}\"]  = hash_val[:aws_account_id]\n        n_hash[\"ActionName.member.#{idx+1}\"]    = hash_val[:action]\n      end\n\n      req_hash = generate_request('AddPermission', n_hash)\n      request_info(req_hash, RightHttp2xxParser.new)\n    end\n\n    def remove_permission(topic_arn, label)\n      req_hash = generate_request('RemovePermission', 'TopicArn' => topic_arn, 'Label' => label)\n      request_info(req_hash, RightHttp2xxParser.new)\n    end\n\n    class SnsCreateTopicParser < RightAWSParser # :nodoc:\n      def reset\n        @result  = ''\n        @request_id = ''\n      end\n      def tagend(name)\n        case name\n          when 'RequestId'  then @result_id = @text\n          when 'TopicArn'   then @result = @text\n        end\n      end\n    end\n\n    class SnsListTopicsParser < RightAWSParser # :nodoc:\n      def reset\n        @result     = []\n        @request_id = ''\n      end\n      def tagstart(name, attributes)\n        @current_key = {} if name == 'member'\n      end\n      def tagend(name)\n        case name\n          when 'RequestId'  then @result_id         = @text\n          when 'TopicArn'   then @current_key[:arn] = @text\n          when 'member'     then @result << @current_key\n        end\n      end\n    end\n\n    class SnsSubscribeParser < RightAWSParser # :nodoc:\n      def reset\n        @result = ''\n      end\n      def tagend(name)\n        case name\n          when 'SubscriptionArn' then @result = @text\n        end\n      end\n    end\n\n    class SnsPublishParser < RightAWSParser # :nodoc:\n      def reset\n        @result = ''\n      end\n      def tagend(name)\n        case name\n          when 'MessageId' then @result = @text\n        end\n      end\n    end\n\n    class SnsGetTopicAttributesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name\n          when 'key' then @current_attr = @text\n          when 'value' then @result[@current_attr] = @text\n        end\n      end\n    end\n\n    class SnsListSubscriptionsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagstart(name, attributes)\n        @current_key = {} if name == 'member'\n      end\n      def tagend(name)\n        case name\n          when 'TopicArn'         then @current_key[:topic_arn]         = @text\n          when 'Protocol'         then @current_key[:protocol]          = @text\n          when 'SubscriptionArn'  then @current_key[:subscription_arn]  = @text\n          when 'Owner'            then @current_key[:owner]             = @text\n          when 'Endpoint'         then @current_key[:endpoint]          = @text\n          when 'member'     then @result << @current_key\n        end\n      end\n    end\n\n    class SnsConfirmSubscriptionParser < RightAWSParser # :nodoc:\n      def reset\n        @result = ''\n      end\n      def tagend(name)\n        case name\n          when 'SubscriptionArn' then @result = @text\n        end\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/sqs/right_sqs.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n    #\n    # = RightAws::Sqs -- RightScale's Amazon SQS interface\n    # The RightAws::Sqs class provides a complete interface to Amazon's Simple\n    # Queue Service.\n    # For explanations of the semantics\n    # of each call, please refer to Amazon's documentation at\n    # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=31\n    #\n    # Error handling: all operations raise an RightAws::AwsError in case\n    # of problems. Note that transient errors are automatically retried.\n    #\n    #  sqs    = RightAws::Sqs.new(aws_access_key_id, aws_secret_access_key)\n    #  queue1 = sqs.queue('my_awesome_queue')\n    #   ...\n    #  queue2 = RightAws::Sqs::Queue.create(sqs, 'my_cool_queue', true)\n    #  puts queue2.size\n    #   ...\n    #  message1 = queue2.receive\n    #  message1.visibility = 0\n    #  puts message1\n    #   ...\n    #  queue2.clear(true)\n    #  queue2.send_message('Ola-la!')\n    #  message2 = queue2.pop\n    #   ...\n    #  grantee1 = RightAws::Sqs::Grantee.create(queue2,'one_cool_guy@email.address')\n    #  grantee1.grant('FULLCONTROL')\n    #  grantee1.drop\n    #   ...\n    #  grantee2 = queue.grantees('another_cool_guy@email.address')\n    #  grantee2.revoke('SENDMESSAGE')\n    #       \n    # Params is a hash:\n    #\n    #    {:server       => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)\n    #     :port         => 443                   # Amazon service port: 80 or 443 (default)\n    #     :signature_version => '0'              # The signature version : '0' or '1'(default)\n    #     :logger       => Logger Object}        # Logger instance: logs to STDOUT if omitted }\n    #\n  class Sqs\n    attr_reader :interface\n    \n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      @interface = SqsInterface.new(aws_access_key_id, aws_secret_access_key, params)\n    end\n    \n      # Retrieves a list of queues. \n      # Returns an +array+ of +Queue+ instances.\n      #\n      #  RightAws::Sqs.queues #=> array of queues\n      #\n    def queues(prefix=nil)\n      @interface.list_queues(prefix).map do |url|\n        Queue.new(self, url)\n      end\n    end\n    \n      # Returns Queue instance by queue name. \n      # If the queue does not exist at Amazon SQS and +create+ is true, the method creates it.\n      #\n      #  RightAws::Sqs.queue('my_awesome_queue') #=> #<RightAws::Sqs::Queue:0xb7b626e4 ... >\n      #\n    def queue(queue_name, create=true, visibility=nil)\n      url = @interface.queue_url_by_name(queue_name)\n      url = (create ? @interface.create_queue(queue_name, visibility) : nil) unless url\n      url ? Queue.new(self, url) : nil\n    end\n  \n  \n    class Queue\n      attr_reader :name, :url, :sqs\n      \n        # Returns Queue instance by queue name. \n        # If the queue does not exist at Amazon SQS and +create+ is true, the method creates it.\n        #\n        #  RightAws::Sqs::Queue.create(sqs, 'my_awesome_queue') #=> #<RightAws::Sqs::Queue:0xb7b626e4 ... >\n        #\n      def self.create(sqs, url_or_name, create=true, visibility=nil)\n        sqs.queue(url_or_name, create, visibility)\n      end\n      \n        # Creates new Queue instance. \n        # Does not create a queue at Amazon.\n        #\n        #  queue = RightAws::Sqs::Queue.new(sqs, 'my_awesome_queue')\n        #\n      def initialize(sqs, url_or_name)\n        @sqs  = sqs\n        @url  = @sqs.interface.queue_url_by_name(url_or_name)\n        @name = @sqs.interface.queue_name_by_url(@url)\n      end\n      \n        # Retrieves queue size.\n        #\n        #  queue.size #=> 1\n        #\n      def size\n        @sqs.interface.get_queue_length(@url)\n      end\n      \n        # Clears queue. \n        # Deletes only the visible messages unless +force+ is +true+.\n        #\n        #  queue.clear(true) #=> true\n        #\n        # P.S. when <tt>force==true</tt> the queue deletes then creates again. This is \n        # the quickest method to clear a big queue or a queue with 'locked' messages. All queue\n        # attributes are restored. But there is no way to restore grantees' permissions to \n        # this queue. If you have no grantees except 'root' then you have no problems.\n        # Otherwise, it's better to use <tt>queue.clear(false)</tt>.\n        #\n        # PS This function is no longer supported.  Amazon has changed the SQS semantics to require at least 60 seconds between \n        # queue deletion and creation. Hence this method will fail with an exception.\n        #\n      def clear(force=false)\n##        if force\n##          @sqs.interface.force_clear_queue(@url)\n##        else\n          @sqs.interface.clear_queue(@url)\n##        end\n      end\n      \n        # Deletes queue. \n        # Queue must be empty or +force+ must be set to +true+. \n        # Returns +true+. \n        #\n        #  queue.delete(true) #=> true\n        #\n      def delete(force=false)\n        @sqs.interface.delete_queue(@url, force)\n      end\n\n        # Sends new message to queue. \n        # Returns new Message instance that has been sent to queue.\n      def send_message(message)\n        message = message.to_s\n        msg     = Message.new(self, @sqs.interface.send_message(@url, message), message)\n        msg.sent_at = Time.now\n        msg\n      end\n      alias_method :push, :send_message\n\n        # Retrieves several messages from queue. \n        # Returns an array of Message instances. \n        #\n        #  queue.receive_messages(2,10) #=> array of messages\n        #\n      def receive_messages(number_of_messages=1, visibility=nil)\n        list = @sqs.interface.receive_messages(@url, number_of_messages, visibility)\n        list.map! do |entry|\n          msg             = Message.new(self, entry[:id], entry[:body], visibility)\n          msg.received_at = Time.now \n          msg\n        end\n      end\n      \n        # Retrieves first accessible message from queue. \n        # Returns Message instance or +nil+ it the queue is empty.\n        #\n        #  queue.receive #=> #<RightAws::Sqs::Message:0xb7bf0884 ... >\n        #\n      def receive(visibility=nil)\n        list = receive_messages(1, visibility)\n        list.empty? ? nil : list[0]\n      end\n\n        # Peeks message body.\n        #\n        #  queue.peek #=> #<RightAws::Sqs::Message:0xb7bf0884 ... >\n        #\n      def peek(message_id)\n        entry = @sqs.interface.peek_message(@url, message_id)\n        msg   = Message.new(self, entry[:id], entry[:body])\n        msg.received_at = Time.now \n        msg\n      end\n\n        # Pops (and deletes) first accessible message from queue. \n        # Returns Message instance or +nil+ it the queue is empty.\n        #\n        #  queue.pop #=> #<RightAws::Sqs::Message:0xb7bf0884 ... >\n        #\n      def pop\n        msg = receive\n        msg.delete if msg\n        msg\n      end\n\n        # Retrieves +VisibilityTimeout+ value for the queue. \n        # Returns new timeout value.\n        #\n        #  queue.visibility #=> 30\n        #\n      def visibility\n        @sqs.interface.get_visibility_timeout(@url)\n      end\n        \n        # Sets new +VisibilityTimeout+ for the queue. \n        # Returns new timeout value. \n        #\n        #  queue.visibility #=> 30\n        #  queue.visibility = 33\n        #  queue.visibility #=> 33\n        #\n      def visibility=(visibility_timeout)\n        @sqs.interface.set_visibility_timeout(@url, visibility_timeout)\n        visibility_timeout\n      end\n        \n        # Sets new queue attribute value. \n        # Not all attributes may be changed: +ApproximateNumberOfMessages+ (for example) is a read only attribute. \n        # Returns a value to be assigned to attribute. \n        #\n        # queue.set_attribute('VisibilityTimeout', '100')  #=> '100'\n        # queue.get_attribute('VisibilityTimeout')         #=> '100'\n        #\n      def set_attribute(attribute, value)\n        @sqs.interface.set_queue_attributes(@url, attribute, value)\n        value\n      end\n        \n        # Retrieves queue attributes. \n        # At this moment Amazon supports +VisibilityTimeout+ and +ApproximateNumberOfMessages+ only. \n        # If the name of attribute is set, returns its value. Otherwise, returns a hash of attributes.\n        #\n        # queue.get_attribute('VisibilityTimeout')  #=> '100'\n        #\n      def get_attribute(attribute='All')\n        attributes = @sqs.interface.get_queue_attributes(@url, attribute)\n        attribute=='All' ? attributes : attributes[attribute]\n      end\n        \n        # Retrieves a list of grantees. \n        # Returns an +array+ of Grantee instances if the +grantee_email_address+ is unset. \n        # Otherwise returns a Grantee instance that points to +grantee_email_address+ or +nil+.\n        #\n        #  grantees = queue.grantees #=> [#<RightAws::Sqs::Grantee:0xb7bf0888 ... >, ...]\n        #   ...\n        #  grantee  = queue.grantees('cool_guy@email.address') #=> nil | #<RightAws::Sqs::Grantee:0xb7bf0888 ... >\n        #\n      def grantees(grantee_email_address=nil, permission = nil)\n        hash = @sqs.interface.list_grants(@url, grantee_email_address, permission)\n        grantees = []\n        hash.each do |key, value|\n          grantees << Grantee.new(self, grantee_email_address, key, value[:name], value[:perms])\n        end\n        if grantee_email_address\n          grantees.right_blank? ? nil : grantees.shift\n        else\n          grantees\n        end\n      end\n    end\n    \n    \n    class Message\n      attr_reader   :queue, :id, :body, :visibility\n      attr_accessor :sent_at, :received_at\n      \n      def initialize(queue, id=nil, body=nil, visibility=nil)\n        @queue       = queue\n        @id          = id\n        @body        = body\n        @visibility  = visibility\n        @sent_at     = nil\n        @received_at = nil\n      end\n      \n        # Returns +Message+ instance body.\n      def to_s\n        @body\n      end\n      \n        # Changes +VisibilityTimeout+ for current message. \n        # Returns new +VisibilityTimeout+ value.\n      def visibility=(visibility_timeout)\n        @queue.sqs.interface.change_message_visibility(@queue.url, @id, visibility_timeout)\n        @visibility = visibility_timeout\n      end\n      \n        # Removes message from queue. \n        # Returns +true+.\n      def delete\n        @queue.sqs.interface.delete_message(@queue.url, @id)\n      end\n    end\n\n\n    class Grantee\n      attr_accessor :queue, :id, :name, :perms, :email\n      \n        # Creates new Grantee instance. \n        # To create new grantee for queue use:\n        #\n        #   grantee = Grantee.new(queue, grantee@email.address)\n        #   grantee.grant('FULLCONTROL')\n        #\n      def initialize(queue, email=nil, id=nil, name=nil, perms=[])\n        @queue = queue\n        @id    = id\n        @name  = name\n        @perms = perms\n        @email = email\n        retrieve unless id\n      end\n\n        # Retrieves security information for grantee identified by email. \n        # Returns +nil+ if the named user has no privileges on this queue, or \n        # +true+ if perms updated successfully. \n      def retrieve # :nodoc:\n        @id    = nil\n        @name  = nil\n        @perms = []\n        \n        hash = @queue.sqs.interface.list_grants(@queue.url, @email)\n        return nil if hash.empty?\n        \n        grantee = hash.shift\n        @id     = grantee[0]\n        @name   = grantee[1][:name]\n        @perms  = grantee[1][:perms]\n        true\n      end\n        \n        # Adds permissions for grantee. \n        # Permission: 'FULLCONTROL' | 'RECEIVEMESSAGE' | 'SENDMESSAGE'. \n        # The caller must have set the email instance variable. \n      def grant(permission=nil)\n        raise \"You can't grant permission without defining a grantee email address!\" unless @email\n        @queue.sqs.interface.add_grant(@queue.url, @email, permission)\n        retrieve\n      end\n      \n        # Revokes permissions for grantee. \n        # Permission: 'FULLCONTROL' | 'RECEIVEMESSAGE' | 'SENDMESSAGE'. \n        # Default value is 'FULLCONTROL'. \n        # User must have +@email+ or +@id+ set. \n        # Returns +true+.\n      def revoke(permission='FULLCONTROL')\n        @queue.sqs.interface.remove_grant(@queue.url, @email || @id, permission)\n        unless @email   # if email is unknown - just remove permission from local perms list...\n          @perms.delete(permission)\n        else            # ... else retrieve updated information from Amazon\n          retrieve\n        end\n        true\n      end\n      \n        # Revokes all permissions for this grantee.\n        # Returns +true+\n      def drop\n        @perms.each do |permission|\n          @queue.sqs.interface.remove_grant(@queue.url, @email || @id, permission)\n        end\n        retrieve\n        true\n      end\n      \n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/sqs/right_sqs_gen2.rb",
    "content": "#\n# Copyright (c) 2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n    #\n    # RightAws::SqsGen2 -- RightScale's Amazon SQS interface, API version\n    # 2008-01-01 and later.\n    # The RightAws::SqsGen2 class provides a complete interface to the second generation of Amazon's Simple\n    # Queue Service.\n    # For explanations of the semantics\n    # of each call, please refer to Amazon's documentation at\n    # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=31\n    #\n    #\n    # RightAws::SqsGen2 is built atop RightAws::SqsGen2Interface, a lower-level\n    # procedural API that may be appropriate for certain programs.\n    #\n    # Error handling: all operations raise an RightAws::AwsError in case\n    # of problems. Note that transient errors are automatically retried.\n    #\n    #  sqs    = RightAws::SqsGen2.new(aws_access_key_id, aws_secret_access_key)\n    #  queue1 = sqs.queue('my_awesome_queue')\n    #   ...\n    #  queue2 = RightAws::SqsGen2::Queue.create(sqs, 'my_cool_queue', true)\n    #  puts queue2.size\n    #   ...\n    #  message1 = queue2.receive\n    #  message1.visibility = 0\n    #  puts message1\n    #   ...\n    #  queue2.clear(true)\n    #  queue2.send_message('Ola-la!')\n    #  message2 = queue2.pop\n    #   ...\n    #\n    # NB: Second-generation SQS has eliminated the entire access grant mechanism present in Gen 1.\n    #\n    # Params is a hash:\n    #\n    #    {:server       => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)\n    #     :port         => 443                   # Amazon service port: 80 or 443 (default)\n    #     :signature_version => '0'              # The signature version : '0' or '1'(default)\n    #     :logger       => Logger Object}        # Logger instance: logs to STDOUT if omitted }\n  class SqsGen2\n    attr_reader :interface\n    \n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      @interface = SqsGen2Interface.new(aws_access_key_id, aws_secret_access_key, params)\n    end\n    \n      # Retrieves a list of queues. \n      # Returns an +array+ of +Queue+ instances.\n      #\n      #  RightAws::Sqs.queues #=> array of queues\n      #\n    def queues(prefix=nil)\n      @interface.list_queues(prefix).map do |url|\n        Queue.new(self, url)\n      end\n    end\n    \n      # Returns Queue instance by queue name. \n      # If the queue does not exist at Amazon SQS and +create+ is true, the method creates it.\n      #\n      #  RightAws::SqsGen2.queue('my_awesome_queue') #=> #<RightAws::SqsGen2::Queue:0xb7b626e4 ... >\n      #\n    def queue(queue_name, create=true, visibility=nil)\n      url = @interface.queue_url_by_name(queue_name)\n      url = (create ? @interface.create_queue(queue_name, visibility) : nil) unless url\n      url ? Queue.new(self, url) : nil\n    end\n  \n  \n    class Queue\n      attr_reader :name, :url, :sqs\n      \n        # Returns Queue instance by queue name. \n        # If the queue does not exist at Amazon SQS and +create+ is true, the method creates it.\n        #\n        #  RightAws::SqsGen2::Queue.create(sqs, 'my_awesome_queue') #=> #<RightAws::SqsGen2::Queue:0xb7b626e4 ... >\n        #\n      def self.create(sqs, url_or_name, create=true, visibility=nil)\n        sqs.queue(url_or_name, create, visibility)\n      end\n      \n        # Creates new Queue instance. \n        # Does not create a queue at Amazon.\n        #\n        #  queue = RightAws::SqsGen2::Queue.new(sqs, 'my_awesome_queue')\n        #\n      def initialize(sqs, url_or_name)\n        @sqs  = sqs\n        @url  = @sqs.interface.queue_url_by_name(url_or_name)\n        @name = @sqs.interface.queue_name_by_url(@url)\n      end\n      \n        # Retrieves queue size.\n        #\n        #  queue.size #=> 1\n        #\n      def size\n        @sqs.interface.get_queue_length(@url)\n      end\n      \n        # Clears queue, deleting only the visible messages.  Any message within its visibility\n        # timeout will not be deleted, and will re-appear in the queue in the\n        # future when the timeout expires.\n        #\n        # To delete all messages in a queue and eliminate the chance of any\n        # messages re-appearing in the future, it's best to delete the queue and\n        # re-create it as a new queue.  Note that doing this will take at least 60\n        # s since SQS does not allow re-creation of a queue within this interval.\n        #\n        #  queue.clear() #=> true\n        #\n      def clear()\n        @sqs.interface.clear_queue(@url)\n      end\n      \n        # Deletes queue.  Any messages in the queue will be permanently lost. \n        # Returns +true+. \n        #\n        # NB: Use with caution; severe data loss is possible!\n        #\n        # queue.delete(true) #=> true\n        #\n      def delete(force=false)\n        @sqs.interface.delete_queue(@url)\n      end\n\n        # Sends new message to queue. \n        # Returns new Message instance that has been sent to queue.\n      def send_message(message)\n        message = message.to_s\n        res = @sqs.interface.send_message(@url, message)\n        msg = Message.new(self, res['MessageId'], nil, message)\n        msg.send_checksum = res['MD5OfMessageBody']\n        msg.sent_at = Time.now\n        msg\n      end\n      alias_method :push, :send_message\n\n        # Retrieves several messages from queue. \n        # Returns an array of Message instances. \n        #\n        #  queue.receive_messages(2,10) #=> array of messages\n        #\n      def receive_messages(number_of_messages=1, visibility=nil, attributes=nil)\n        list = @sqs.interface.receive_message(@url, number_of_messages, visibility, attributes)\n        list.map! do |entry|\n          msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'], entry['Body'], visibility, entry['Attributes'])\n          msg.received_at = Time.now \n          msg.receive_checksum = entry['MD5OfBody']\n          msg\n        end\n      end\n      \n        # Retrieves first accessible message from queue. \n        # Returns Message instance or +nil+ it the queue is empty.\n        #\n        #  queue.receive #=> #<RightAws::SqsGen2::Message:0xb7bf0884 ... >\n        #\n      def receive(visibility=nil, attributes=nil)\n        list = receive_messages(1, visibility, attributes)\n        list.empty? ? nil : list[0]\n      end\n\n        # Pops (and deletes) first accessible message from queue. \n        # Returns Message instance or +nil+ if the queue is empty.\n        #\n        #  queue.pop #=> #<RightAws::SqsGen2::Message:0xb7bf0884 ... >\n        #\n        #  # pop a message with custom attributes\n        #  m = queue.pop(['SenderId', 'SentTimestamp']) #=> #<RightAws::SqsGen2::Message:0xb7bf1884 ... >\n        #  m.attributes #=> {\"SentTimestamp\"=>\"1240991906937\", \"SenderId\"=>\"800000000005\"}\n        #\n      def pop(attributes=nil)\n        list = @sqs.interface.pop_messages(@url, 1, attributes)\n        return nil if list.empty?\n        entry = list[0]\n        msg = Message.new(self, entry['MessageId'], entry['ReceiptHandle'], entry['Body'], visibility, entry['Attributes'])\n        msg.received_at = Time.now \n        msg.receive_checksum = entry['MD5OfBody']\n        msg\n      end\n\n        # Retrieves +VisibilityTimeout+ value for the queue. \n        # Returns new timeout value.\n        #\n        #  queue.visibility #=> 30\n        #\n      def visibility\n        @sqs.interface.get_queue_attributes(@url, 'VisibilityTimeout')['VisibilityTimeout']\n      end\n        \n        # Sets new +VisibilityTimeout+ for the queue. \n        # Returns new timeout value. \n        #\n        #  queue.visibility #=> 30\n        #  queue.visibility = 33\n        #  queue.visibility #=> 33\n        #\n      def visibility=(visibility_timeout)\n        @sqs.interface.set_queue_attributes(@url, 'VisibilityTimeout', visibility_timeout)\n        visibility_timeout\n      end\n        \n        # Sets new queue attribute value. \n        # Not all attributes may be changed: +ApproximateNumberOfMessages+ (for example) is a read only attribute. \n        # Returns a value to be assigned to attribute. \n        # Currently, 'VisibilityTimeout' is the only settable queue attribute.\n        # Attempting to set non-existent attributes generates an indignant\n        # exception. \n        #\n        # queue.set_attribute('VisibilityTimeout', '100')  #=> '100'\n        # queue.get_attribute('VisibilityTimeout')         #=> '100'\n        #\n      def set_attribute(attribute, value)\n        @sqs.interface.set_queue_attributes(@url, attribute, value)\n        value\n      end\n        \n        # Retrieves queue attributes. \n        # If the name of attribute is set, returns its value. Otherwise, returns a hash of attributes.\n        #\n        # queue.get_attribute('VisibilityTimeout')  #=> {\"VisibilityTimeout\"=>\"45\"} \n        #\n        # P.S. This guy is deprecated. Use +get_attributes+ instead.\n      def get_attribute(attribute='All')\n        attributes = get_attributes(attribute)\n        attribute=='All' ? attributes : attributes[attribute]\n      end\n\n      # Retrieves queue attributes.\n      #\n      #  queue.get_attributes #=>\n      #    {\"ApproximateNumberOfMessages\" => \"0\",\n      #     \"LastModifiedTimestamp\"       => \"1240946032\",\n      #     \"CreatedTimestamp\"            => \"1240816887\",\n      #     \"VisibilityTimeout\"           => \"30\",\n      #     \"Policy\"                      => \"{\"Version\":\"2008-10-17\",\"Id\":...}\"}\n      #\n      #  queue.get_attributes(\"LastModifiedTimestamp\", \"VisibilityTimeout\") #=>\n      #    {\"LastModifiedTimestamp\" => \"1240946032\",\n      #     \"VisibilityTimeout\"     => \"30\"}\n      #\n      def get_attributes(*attributes)\n        @sqs.interface.get_queue_attributes(@url, attributes)\n      end\n\n      # Add permission to the queue.\n      #\n      #  queue.add_permissions('testLabel',['125074342641', '125074342642'],\n      #                        ['SendMessage','SendMessage','ReceiveMessage']) #=> true\n      #\n      def add_permissions(label, grantees, actions)\n        @sqs.interface.add_permissions(@url, label, grantees, actions)\n      end\n\n      # Revoke any permissions in the queue policy that matches the +label+ parameter.\n      #\n      #  sqs.remove_permissions('testLabel') # => true\n      #\n      def remove_permissions(label)\n        @sqs.interface.remove_permissions(@url, label)\n      end\n\n      # Get current permissions set. The set is JSON packed.\n      # \n      #  sqs.get_permissions #=>\n      #    '{\"Version\":\"2008-10-17\",\"Id\":\"/826693181925/kd-test-gen-2_5/SQSDefaultPolicy\",\n      #      \"Statement\":[{\"Sid\":\"kd-perm-04\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"100000000001\",\n      #      \"AWS\":\"100000000001\",\"AWS\":\"100000000002\"},\"Action\":[\"SQS:SendMessage\",\"SQS:DeleteMessage\",\n      #      \"SQS:ReceiveMessage\"],\"Resource\":\"/826693181925/kd-test-gen-2_5\"},{\"Sid\":\"kd-perm-03\",\n      #      \"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"648772224137\"},\"Action\":\"SQS:SendMessage\",\n      #      \"Resource\":\"/826693181925/kd-test-gen-2_5\"}]}'\n      #\n      def get_permissions\n        get_attributes('Policy')['Policy']\n      end\n      \n    end\n        \n    class Message\n      attr_reader   :queue, :id, :body, :visibility, :receipt_handle, :attributes\n      attr_accessor :sent_at, :received_at, :send_checksum, :receive_checksum\n      \n      def initialize(queue, id=nil, rh = nil, body=nil, visibility=nil, attributes=nil)\n        @queue       = queue\n        @id          = id\n        @receipt_handle = rh \n        @body        = body\n        @visibility  = visibility\n        @attributes  = attributes\n        @sent_at     = nil\n        @received_at = nil\n        @send_checksum = nil\n        @receive_checksum = nil\n      end\n      \n        # Returns +Message+ instance body.\n      def to_s\n        @body\n      end\n\n      # Set message visibility timeout.\n      def visibility=(visibility_timeout)\n        @queue.sqs.interface.change_message_visibility(@queue.url, @receipt_handle, visibility_timeout)\n        @visibility = visibility_timeout\n      end\n\n        # Removes message from queue. \n        # Returns +true+.\n      def delete\n        @queue.sqs.interface.delete_message(@queue.url, @receipt_handle) if @receipt_handle\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/sqs/right_sqs_gen2_interface.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  #\n  # Right::Aws::SqsGen2Interface - RightScale's low-level Amazon SQS interface\n  # for API version 2008-01-01 and later.\n  # For explanations of the semantics\n  # of each call, please refer to Amazon's documentation at\n  # http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=31\n  #\n  # This class provides a procedural interface to SQS.  Conceptually it is\n  # mostly a pass-through interface to SQS and its API is very similar to the\n  # bare SQS API.  For a somewhat higher-level and object-oriented interface, see\n  # RightAws::SqsGen2. \n\n  class SqsGen2Interface < RightAwsBase\n    include RightAwsBaseInterface\n    \n    API_VERSION       = \"2009-02-01\"\n    DEFAULT_HOST      = \"queue.amazonaws.com\"\n    DEFAULT_PORT      = 443\n    DEFAULT_PROTOCOL  = 'https'\n    REQUEST_TTL       = 30\n    DEFAULT_VISIBILITY_TIMEOUT = 30\n\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_sqs\n      @@bench.service\n    end\n\n    @@api = API_VERSION\n    def self.api \n      @@api\n    end\n\n    # Creates a new SqsInterface instance. This instance is limited to\n    # operations on SQS objects created with Amazon's 2008-01-01 API version.  This\n    # interface will not work on objects created with prior API versions.  See\n    # Amazon's article \"Migrating to Amazon SQS API version 2008-01-01\" at:\n    # http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1148\n    #\n    #  sqs = RightAws::SqsGen2Interface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:logger => Logger.new('/tmp/x.log')}) \n    #  \n    # Params is a hash:\n    #\n    #    {:server       => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com' (default)\n    #     :port         => 443                   # Amazon service port: 80 or 443 (default)\n    #     :signature_version => '0'              # The signature version : '0', '1' or '2'(default)\n    #     :logger       => Logger Object}        # Logger instance: logs to STDOUT if omitted }\n    #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name             => 'SQS', \n             :default_host     => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).host   : DEFAULT_HOST, \n             :default_port     => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).port   : DEFAULT_PORT, \n             :default_protocol => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).scheme : DEFAULT_PROTOCOL }, \n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n\n\n  #-----------------------------------------------------------------\n  #      Requests\n  #-----------------------------------------------------------------\n\n    # Generates a request hash for the query API\n    def generate_request(action, param={})  # :nodoc:\n      # For operation requests on a queue, the queue URI will be a parameter,\n      # so we first extract it from the call parameters.  Next we remove any\n      # parameters with no value or with symbolic keys.  We add the header\n      # fields required in all requests, and then the headers passed in as\n      # params.  We sort the header fields alphabetically and then generate the\n      # signature before URL escaping the resulting query and sending it.\n      service = param[:queue_url] ? URI(param[:queue_url]).path : '/'\n      param.each{ |key, value| param.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n      service_hash = { \"Action\"           => action,\n                       \"Expires\"          => (Time.now + REQUEST_TTL).utc.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                       \"AWSAccessKeyId\"   => @aws_access_key_id,\n                       \"Version\"          => API_VERSION }\n      service_hash.update(param)\n      service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], service)\n      request        = Net::HTTP::Get.new(\"#{AwsUtils.URLencode(service)}?#{service_params}\")\n        # prepare output hash\n      { :request  => request, \n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n    def generate_post_request(action, param={})  # :nodoc:\n      service = param[:queue_url] ? URI(param[:queue_url]).path : '/'\n      message   = param[:message]                # extract message body if nesessary\n      param.each{ |key, value| param.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n      service_hash = { \"Action\"           => action,\n                       \"Expires\"          => (Time.now + REQUEST_TTL).utc.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                       \"AWSAccessKeyId\"   => @aws_access_key_id,\n                       \"MessageBody\"      => message,\n                       \"Version\"          => API_VERSION }\n      service_hash.update(param)\n      #\n      service_params = signed_service_params(@aws_secret_access_key, service_hash, :post, @params[:server], service)\n      request        = Net::HTTP::Post.new(AwsUtils::URLencode(service))\n      request['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'\n      request.body = service_params\n        # prepare output hash\n      { :request  => request, \n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser) # :nodoc:\n      request_info_impl(:sqs_connection, @@bench, request, parser)\n    end\n\n      # Creates a new queue, returning its URI.\n      #\n      #  sqs.create_queue('my_awesome_queue') #=> 'https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'\n      #\n    def create_queue(queue_name, default_visibility_timeout=nil)\n      req_hash = generate_request('CreateQueue', 'QueueName' => queue_name,\n                                  'DefaultVisibilityTimeout' => default_visibility_timeout || DEFAULT_VISIBILITY_TIMEOUT )\n      request_info(req_hash, SqsCreateQueueParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n     # Lists all queues owned by this user that have names beginning with +queue_name_prefix+. \n     # If +queue_name_prefix+ is omitted then retrieves a list of all queues.\n     # Queue creation is an eventual operation and created queues may not show up in immediately subsequent list_queues calls.\n     #\n     #  sqs.create_queue('my_awesome_queue')\n     #  sqs.create_queue('my_awesome_queue_2')\n     #  sqs.list_queues('my_awesome') #=> ['https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue','https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2']\n     #\n    def list_queues(queue_name_prefix=nil)\n      req_hash = generate_request('ListQueues', 'QueueNamePrefix' => queue_name_prefix)\n      request_info(req_hash, SqsListQueuesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n      \n      # Deletes queue. Any messages in the queue are permanently lost. \n      # Returns +true+ or an exception.\n      # Queue deletion can take up to 60 s to propagate through SQS.  Thus, after a deletion, subsequent list_queues calls\n      # may still show the deleted queue.  It is not unusual within the 60 s window to see the deleted queue absent from \n      # one list_queues call but present in the subsequent one.  Deletion is eventual.\n      #\n      #  sqs.delete_queue('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2') #=> true\n      # \n    def delete_queue(queue_url)\n      req_hash = generate_request('DeleteQueue', :queue_url => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Retrieves the queue attribute(s). Returns a hash of attribute(s) or an exception.\n      #\n      #  sqs.get_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>\n      #    {\"ApproximateNumberOfMessages\" => \"0\",\n      #     \"LastModifiedTimestamp\"       => \"1240946032\",\n      #     \"CreatedTimestamp\"            => \"1240816887\",\n      #     \"VisibilityTimeout\"           => \"30\",\n      #     \"Policy\"                      => \"{\"Version\":\"2008-10-17\",\"Id\":...}\"}\n      #     \n      #  queue.get_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', \"LastModifiedTimestamp\", \"VisibilityTimeout\") #=>\n      #    {\"LastModifiedTimestamp\" => \"1240946032\",\n      #     \"VisibilityTimeout\"     => \"30\"}\n      #\n      # http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryGetQueueAttributes.html\n    def get_queue_attributes(queue_url, *attributes)\n      attributes.flatten!\n      attributes << 'All' if attributes.right_blank?\n      params = amazonize_list('AttributeName', attributes)\n      params.merge!(:queue_url  => queue_url)\n      req_hash = generate_request('GetQueueAttributes', params)\n      request_info(req_hash, SqsGetQueueAttributesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Sets queue attribute. Returns +true+ or an exception.\n      #\n      #  sqs.set_queue_attributes('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', \"VisibilityTimeout\", 10) #=> true\n      #\n      # From the SQS Dev Guide:\n      # \"When you change a queue's attributes, the change can take up to 60 seconds to propagate \n      # throughout the SQS system.\"\n      #\n      # NB: Attribute values may not be immediately available to other queries\n      # for some time after an update. See the SQS documentation for\n      # semantics, but in general propagation can take up to 60 s.\n      # \n      #  see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QuerySetQueueAttributes.html\n    def set_queue_attributes(queue_url, attribute, value)\n      req_hash = generate_request('SetQueueAttributes', \n                                  'Attribute.Name'  => attribute,\n                                  'Attribute.Value' => value,\n                                  :queue_url        => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Add permissions to a queue.\n    #\n    #  sqs.add_permissions('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',\n    #                     'testLabel', ['125074342641','125074342642'],\n    #                     ['SendMessage','SendMessage','ReceiveMessage']) #=> true\n    #\n    #  +permissions+ is a hash of: AccountId => ActionName\n    #  (valid ActionNames: * | SendMessage | ReceiveMessage | DeleteMessage | ChangeMessageVisibility | GetQueueAttributes )\n    #\n    #  see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryAddPermission.html\n    #      http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?acp-overview.html\n    def add_permissions(queue_url, label, grantees, actions)\n      params      = amazonize_list('AWSAccountId', Array(grantees))\n      params.merge!(amazonize_list('ActionName', Array(actions)))\n      params.merge!('Label'    => label,\n                    :queue_url => queue_url )\n      req_hash = generate_request('AddPermission', params)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Revoke any permissions in the queue policy that matches the +label+ parameter.\n    #\n    #  sqs.remove_permissions('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',\n    #                        'testLabel') # => true\n    #\n    #  see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryRemovePermission.html\n    def remove_permissions(queue_url, label)\n      req_hash = generate_request('RemovePermission',\n                                  'Label'    => label,\n                                  :queue_url => queue_url )\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Retrieves a list of messages from queue. Returns an array of hashes in format: <tt>{:id=>'message_id', :body=>'message_body'}</tt>\n      #\n      #   sqs.receive_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',10, 5) #=>\n      #    [{\"ReceiptHandle\"=>\"Euvo62...kw==\", \"MD5OfBody\"=>\"16af2171b5b83cfa35ce254966ba81e3\", \n      #      \"Body\"=>\"Goodbyte World!\", \"MessageId\"=>\"MUM4WlAyR...pYOTA=\"}, ..., {}]\n      #\n      # Normally this call returns fewer messages than the maximum specified,\n      # even if they are available.\n      #\n    def receive_message(queue_url, max_number_of_messages=1, visibility_timeout=nil, attributes=nil)\n      return [] if max_number_of_messages == 0\n      params = {}\n      params.merge!(amazonize_list('AttributeName', Array(attributes))) unless attributes.right_blank?\n      params.merge!('MaxNumberOfMessages' => max_number_of_messages,\n                    'VisibilityTimeout'   => visibility_timeout,\n                    :queue_url            => queue_url )\n      req_hash = generate_post_request('ReceiveMessage', params)\n      request_info(req_hash, SqsReceiveMessageParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n    # Change the visibility timeout of a specified message in a queue.\n    #\n    #  sqs.change_message_visibility('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'Euvo62...kw==', 33) #=> true\n    #\n    #  see http://docs.amazonwebservices.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/index.html?Query_QueryChangeMessageVisibility.html\n    def change_message_visibility(queue_url, receipt_handle, visibility_timeout)\n      req_hash = generate_request('ChangeMessageVisibility',\n                                  'ReceiptHandle'     => receipt_handle,\n                                  'VisibilityTimeout' => visibility_timeout,\n                                  :queue_url          => queue_url )\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Sends a new message to a queue.  Message size is limited to 8 KB.\n      # If successful, this call returns a hash containing key/value pairs for\n      # \"MessageId\" and \"MD5OfMessageBody\":\n      #\n      #  sqs.send_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'message_1') #=> \n      #    {\"MessageId\"=>\"MEs4M0JKNlRCRTBBSENaMjROTk58QVFRNzNEREhDVFlFOVJDQ1JKNjF8UTdBRllCUlJUMjhKMUI1WDJSWDE=\",\n      #     \"MD5OfMessageBody\"=>\"16af2171b5b83cfa35ce254966ba81e3\"}\n      #\n      # On failure, send_message raises an exception.\n      #\n      #\n    def send_message(queue_url, message)\n      req_hash = generate_post_request('SendMessage', :message  => message, :queue_url => queue_url)\n      request_info(req_hash, SqsSendMessagesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Same as send_message\n    alias_method :push_message, :send_message\n    \n    \n      # Deletes message from queue. Returns +true+ or an exception.  Amazon\n      # returns +true+ on deletion of non-existent messages.  You must use the\n      # receipt handle for a message to delete it, not the message ID.  \n      #\n      # From the SQS Developer Guide:\n      # \"It is possible you will receive a message even after you have deleted it. This might happen \n      # on rare occasions if one of the servers storing a copy of the message is unavailable when \n      # you request to delete the message. The copy remains on the server and might be returned to \n      # you again on a subsequent receive request. You should create your system to be \n      # idempotent so that receiving a particular message more than once is not a problem. \"\n      #\n      #  sqs.delete_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'Euvo62/1nlIet...ao03hd9Sa0w==') #=> true\n      #\n    def delete_message(queue_url, receipt_handle)\n      req_hash = generate_request('DeleteMessage', 'ReceiptHandle' => receipt_handle, :queue_url  => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Given the queue's short name, this call returns the queue URL or +nil+ if queue is not found\n      #  sqs.queue_url_by_name('my_awesome_queue') #=> 'https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'\n      #\n    def queue_url_by_name(queue_name)\n      return queue_name if queue_name.include?('/')\n      queue_urls = list_queues(queue_name)\n      queue_urls.each do |queue_url|\n        return queue_url if queue_name_by_url(queue_url) == queue_name\n      end\n      nil\n    rescue\n      on_exception\n    end\n\n      # Returns short queue name by url.\n      #\n      #  RightSqs.queue_name_by_url('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'\n      #\n    def self.queue_name_by_url(queue_url)\n      queue_url[/[^\\/]*$/]\n    rescue\n      on_exception\n    end\n    \n      # Returns short queue name by url.\n      #\n      #  sqs.queue_name_by_url('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'\n      #\n    def queue_name_by_url(queue_url)\n      self.class.queue_name_by_url(queue_url)\n    rescue\n      on_exception\n    end\n\n      # Returns approximate number of messages in queue.\n      #\n      #  sqs.get_queue_length('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 3\n      #\n    def get_queue_length(queue_url)\n      attrs = get_queue_attributes(queue_url)\n      attrs['ApproximateNumberOfMessages'].to_i +\n      attrs['ApproximateNumberOfMessagesNotVisible'].to_i\n    rescue\n      on_exception\n    end\n\n      # Removes all visible messages from queue. Return +true+ or an exception.\n      #\n      #  sqs.clear_queue('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true\n      #\n    def clear_queue(queue_url)\n      while (pop_messages(queue_url, 10).length > 0) ; end   # delete all messages in queue\n      true\n    rescue\n      on_exception\n    end\n\n      # Pops (retrieves and deletes) up to 'number_of_messages' from queue. Returns an array of retrieved messages in format: <tt>[{:id=>'message_id', :body=>'message_body'}]</tt>.\n      #\n      #   sqs.pop_messages('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 3) #=>\n      #   [{\"ReceiptHandle\"=>\"Euvo62/...+Zw==\", \"MD5OfBody\"=>\"16af2...81e3\", \"Body\"=>\"Goodbyte World!\", \n      #   \"MessageId\"=>\"MEZI...JSWDE=\"}, {...}, ... , {...} ]\n      #\n    def pop_messages(queue_url, number_of_messages=1, attributes=nil)\n      messages = receive_message(queue_url, number_of_messages, nil, attributes)\n      messages.each do |message|\n        delete_message(queue_url, message['ReceiptHandle'])\n      end\n      messages\n    rescue\n      on_exception\n    end\n\n      # Pops (retrieves and  deletes) first accessible message from queue. Returns the message in format <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.\n      #\n      #  sqs.pop_message('https://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>\n      #    {:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}\n      #\n    def pop_message(queue_url, attributes=nil)\n      messages = pop_messages(queue_url, 1, attributes)\n      messages.right_blank? ? nil : messages[0]\n    rescue\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Status Response Parser\n    #-----------------------------------------------------------------\n\n    class SqsStatusParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        if name == 'ResponseMetadata'\n          @result = true\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Queue\n    #-----------------------------------------------------------------\n\n    class SqsCreateQueueParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        @result = @text if name == 'QueueUrl'\n      end\n    end\n\n    class SqsListQueuesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagend(name)\n        @result << @text if name == 'QueueUrl'\n      end\n    end\n\n    class SqsGetQueueAttributesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name \n          when 'Name'  then @current_attribute          = @text\n          when 'Value' then @result[@current_attribute] = @text\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Messages\n    #-----------------------------------------------------------------\n\n    class SqsReceiveMessageParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagstart(name, attributes)\n        case name\n        when 'Message' then @current_message = { }\n        when 'Attribute' then\n          @current_message['Attributes'] ||= {}\n          @current_attribute_name  = ''\n          @current_attribute_value = ''\n        end\n      end\n      def tagend(name)\n        case name\n        when 'MessageId'     then @current_message['MessageId']     = @text\n        when 'ReceiptHandle' then @current_message['ReceiptHandle'] = @text\n        when 'MD5OfBody'     then @current_message['MD5OfBody']     = @text\n        when 'Name'          then @current_attribute_name  = @text\n        when 'Value'         then @current_attribute_value = @text\n        when 'Attribute'     then @current_message['Attributes'][@current_attribute_name] = @current_attribute_value\n        when 'Body'          then @current_message['Body'] = @text; @result << @current_message\n        end\n      end\n    end\n\n    class SqsSendMessagesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name\n          when 'MessageId'        then @result['MessageId']        = @text\n          when 'MD5OfMessageBody' then @result['MD5OfMessageBody'] = @text\n        end\n      end\n    end\n    \n  end\n\nend\n"
  },
  {
    "path": "lib/sqs/right_sqs_interface.rb",
    "content": "#\n# Copyright (c) 2007-2008 RightScale Inc\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# \"Software\"), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#\n\nmodule RightAws\n\n  class SqsInterface < RightAwsBase\n    include RightAwsBaseInterface\n    \n    API_VERSION       = \"2007-05-01\"\n    DEFAULT_HOST      = \"queue.amazonaws.com\"\n    DEFAULT_PORT      = 443\n    DEFAULT_PROTOCOL  = 'https'\n    REQUEST_TTL       = 30\n    DEFAULT_VISIBILITY_TIMEOUT = 30\n\n\n    @@bench = AwsBenchmarkingBlock.new\n    def self.bench_xml\n      @@bench.xml\n    end\n    def self.bench_sqs\n      @@bench.service\n    end\n\n    @@api = API_VERSION\n    def self.api \n      @@api\n    end\n\n      # Creates a new SqsInterface instance.\n      #\n      #  sqs = RightAws::SqsInterface.new('1E3GDYEOGFJPIT75KDT40','hgTHt68JY07JKUY08ftHYtERkjgtfERn57DFE379', {:logger => Logger.new('/tmp/x.log')}) \n      #  \n      # Params is a hash:\n      #\n      #    {:server       => 'queue.amazonaws.com' # Amazon service host: 'queue.amazonaws.com'(default)\n      #     :port         => 443                   # Amazon service port: 80 or 443(default)\n      #     :signature_version => '0'              # The signature version : '0', '1' or '2'(default)\n      #     :logger       => Logger Object}        # Logger instance: logs to STDOUT if omitted }\n      #\n    def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={})\n      init({ :name             => 'SQS', \n             :default_host     => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).host   : DEFAULT_HOST, \n             :default_port     => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).port   : DEFAULT_PORT, \n             :default_protocol => ENV['SQS_URL'] ? URI.parse(ENV['SQS_URL']).scheme : DEFAULT_PROTOCOL }, \n           aws_access_key_id     || ENV['AWS_ACCESS_KEY_ID'], \n           aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], \n           params)\n    end\n\n\n  #-----------------------------------------------------------------\n  #      Requests\n  #-----------------------------------------------------------------\n\n      # Generates a request hash for the query API\n    def generate_request(action, params={})  # :nodoc:\n        # Sometimes we need to use queue uri (delete queue etc)\n        # In that case we will use Symbol key: 'param[:queue_url]'\n      service = params[:queue_url] ? URI(params[:queue_url]).path : '/'\n        # remove unset(=optional) and symbolyc keys\n      params.each{ |key, value| params.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n        # prepare output hash\n      service_hash = { \"Action\"           => action,\n                       \"Expires\"          => (Time.now + REQUEST_TTL).utc.strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n                       \"AWSAccessKeyId\"   => @aws_access_key_id,\n                       \"Version\"          => API_VERSION }\n      service_hash.update(params)\n      service_params = signed_service_params(@aws_secret_access_key, service_hash, :get, @params[:server], service)\n      request        = Net::HTTP::Get.new(\"#{AwsUtils::URLencode(service)}?#{service_params}\")\n        # prepare output hash\n      { :request  => request, \n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n      # Generates a request hash for the REST API\n    def generate_rest_request(method, param) # :nodoc:\n      queue_uri = param[:queue_url] ? URI(param[:queue_url]).path : '/'\n      message   = param[:message]                # extract message body if nesessary\n        # remove unset(=optional) and symbolyc keys\n      param.each{ |key, value| param.delete(key) if (value.nil? || key.is_a?(Symbol)) }\n        # created request\n      param_to_str = param.to_a.collect{|key,val| key.to_s + \"=\" + CGI::escape(val.to_s) }.join(\"&\")\n      param_to_str = \"?#{param_to_str}\" unless param_to_str.right_blank?\n      request = \"Net::HTTP::#{method.capitalize}\".right_constantize.new(\"#{queue_uri}#{param_to_str}\")\n      request.body = message if message\n        # set main headers\n      request['content-md5']  = ''\n      request['Content-Type'] = 'text/plain'\n      request['Date']         = Time.now.httpdate\n        # generate authorization string\n      auth_string = \"#{method.upcase}\\n#{request['content-md5']}\\n#{request['Content-Type']}\\n#{request['Date']}\\n#{CGI::unescape(queue_uri)}\"\n      signature   = AwsUtils::sign(@aws_secret_access_key, auth_string)\n        # set other headers\n      request['Authorization'] = \"AWS #{@aws_access_key_id}:#{signature}\"\n      request['AWS-Version']   = API_VERSION\n        # prepare output hash\n      { :request  => request, \n        :server   => @params[:server],\n        :port     => @params[:port],\n        :protocol => @params[:protocol] }\n    end\n\n\n      # Sends request to Amazon and parses the response\n      # Raises AwsError if any banana happened\n    def request_info(request, parser) # :nodoc:\n      request_info_impl(:sqs_connection, @@bench, request, parser)\n    end\n\n\n      # Creates new queue. Returns new queue link.\n      #\n      #  sqs.create_queue('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'\n      #\n      # PS Some queue based requests may not become available until a couple of minutes after queue creation\n      # (permission grant and removal for example)\n      #\n    def create_queue(queue_name, default_visibility_timeout=nil)\n      req_hash = generate_request('CreateQueue', \n                                  'QueueName'                => queue_name,\n                                  'DefaultVisibilityTimeout' => default_visibility_timeout || DEFAULT_VISIBILITY_TIMEOUT )\n      request_info(req_hash, SqsCreateQueueParser.new(:logger => @logger))\n    end\n\n     # Lists all queues owned by this user that have names beginning with +queue_name_prefix+. If +queue_name_prefix+ is omitted then retrieves a list of all queues.\n     #\n     #  sqs.create_queue('my_awesome_queue')\n     #  sqs.create_queue('my_awesome_queue_2')\n     #  sqs.list_queues('my_awesome') #=> ['http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue','http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2']\n     #\n    def list_queues(queue_name_prefix=nil)\n      req_hash = generate_request('ListQueues', 'QueueNamePrefix' => queue_name_prefix)\n      request_info(req_hash, SqsListQueuesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n      \n      # Deletes queue (queue must be empty or +force_deletion+ must be set to true). Queue is identified by url. Returns +true+ or an exception.\n      #\n      #  sqs.delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue_2') #=> true\n      #\n    def delete_queue(queue_url, force_deletion = false)\n      req_hash = generate_request('DeleteQueue', \n                                  'ForceDeletion' => force_deletion.to_s,\n                                  :queue_url      => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Retrieves the queue attribute(s). Returns a hash of attribute(s) or an exception.\n      #\n      #  sqs.get_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> {\"ApproximateNumberOfMessages\"=>\"0\", \"VisibilityTimeout\"=>\"30\"}\n      #\n    def get_queue_attributes(queue_url, attribute='All')\n      req_hash = generate_request('GetQueueAttributes', \n                                  'Attribute' => attribute,\n                                  :queue_url  => queue_url)\n      request_info(req_hash, SqsGetQueueAttributesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Sets queue attribute. Returns +true+ or an exception.\n      #\n      #  sqs.set_queue_attributes('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', \"VisibilityTimeout\", 10) #=> true\n      #\n      # P.S. Amazon returns success even if the attribute does not exist. Also, attribute values may not be immediately available to other queries\n      # for some time after an update (see the SQS documentation for\n      # semantics).\n    def set_queue_attributes(queue_url, attribute, value)\n      req_hash = generate_request('SetQueueAttributes', \n                                  'Attribute' => attribute,\n                                  'Value'     => value,\n                                  :queue_url  => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n     # Sets visibility timeout. Returns +true+ or an exception.\n     #\n     #  sqs.set_visibility_timeout('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 15) #=> true\n     #\n     # See also: +set_queue_attributes+\n     #\n    def set_visibility_timeout(queue_url, visibility_timeout=nil)\n      req_hash = generate_request('SetVisibilityTimeout', \n                                  'VisibilityTimeout' => visibility_timeout || DEFAULT_VISIBILITY_TIMEOUT,\n                                  :queue_url => queue_url )\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n     # Retrieves visibility timeout.\n     #\n     #  sqs.get_visibility_timeout('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 15\n     #\n     # See also: +get_queue_attributes+\n     #\n    def get_visibility_timeout(queue_url)\n      req_hash = generate_request('GetVisibilityTimeout', :queue_url => queue_url )\n      request_info(req_hash, SqsGetVisibilityTimeoutParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n     # Adds grants for user (identified by email he registered at Amazon). Returns +true+ or an exception. Permission = 'FULLCONTROL' | 'RECEIVEMESSAGE' | 'SENDMESSAGE'.\n     #\n     #  sqs.add_grant('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'my_awesome_friend@gmail.com', 'FULLCONTROL') #=> true\n     #\n    def add_grant(queue_url, grantee_email_address, permission = nil)\n      req_hash = generate_request('AddGrant', \n                                  'Grantee.EmailAddress' => grantee_email_address,\n                                  'Permission'           => permission,\n                                  :queue_url             => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Retrieves hash of +grantee_id+ => +perms+ for this queue:\n      #\n      #  sqs.list_grants('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>\n      #    {\"000000000000000000000001111111111117476c7fea6efb2c3347ac3ab2792a\"=>{:name=>\"root\", :perms=>[\"FULLCONTROL\"]},\n      #     \"00000000000000000000000111111111111e5828344600fc9e4a784a09e97041\"=>{:name=>\"myawesomefriend\", :perms=>[\"FULLCONTROL\"]}  \n      #\n    def list_grants(queue_url, grantee_email_address=nil, permission = nil)\n      req_hash = generate_request('ListGrants', \n                                  'Grantee.EmailAddress' => grantee_email_address,\n                                  'Permission'           => permission,\n                                  :queue_url             => queue_url)\n      response = request_info(req_hash, SqsListGrantsParser.new(:logger => @logger))\n        # One user may have up to 3 permission records for every queue.\n        # We will join these records to one.\n      result = {}    \n      response.each do |perm|\n        id = perm[:id]\n          # create hash for new user if unexisit\n        result[id] = {:perms=>[]} unless result[id]\n          # fill current grantee params\n        result[id][:perms] << perm[:permission]\n        result[id][:name] = perm[:name]\n      end\n      result\n    end\n\n      # Revokes permission from user. Returns +true+ or an exception.\n      #\n      #  sqs.remove_grant('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'my_awesome_friend@gmail.com', 'FULLCONTROL') #=> true\n      #\n    def remove_grant(queue_url, grantee_email_address_or_id, permission = nil)\n      grantee_key = grantee_email_address_or_id.include?('@') ? 'Grantee.EmailAddress' : 'Grantee.ID'\n      req_hash = generate_request('RemoveGrant', \n                                  grantee_key  => grantee_email_address_or_id,\n                                  'Permission' => permission,\n                                  :queue_url   => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n\n      # Retrieves a list of messages from queue. Returns an array of hashes in format: <tt>{:id=>'message_id', body=>'message_body'}</tt>\n      #\n      #   sqs.receive_messages('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue',10, 5) #=>\n      #    [{:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}, ..., {}]\n      #\n      # P.S. Usually returns fewer messages than requested even if they are available.\n      #\n    def receive_messages(queue_url, number_of_messages=1, visibility_timeout=nil)\n      return [] if number_of_messages == 0\n      req_hash = generate_rest_request('GET',\n                                       'NumberOfMessages'  => number_of_messages,\n                                       'VisibilityTimeout' => visibility_timeout,\n                                       :queue_url          => \"#{queue_url}/front\" )\n      request_info(req_hash, SqsReceiveMessagesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Peeks message from queue by message id. Returns message in format of <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.\n      #\n      #  sqs.peek_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '1234567890...0987654321') #=>\n      #    {:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}\n      #\n    def peek_message(queue_url, message_id)\n      req_hash = generate_rest_request('GET', :queue_url => \"#{queue_url}/#{CGI::escape message_id}\" )\n      messages = request_info(req_hash, SqsReceiveMessagesParser.new(:logger => @logger))\n      messages.right_blank? ? nil : messages[0]\n    rescue\n      on_exception\n    end\n\n      # Sends new message to queue.Returns 'message_id' or raises an exception.\n      #\n      #  sqs.send_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 'message_1') #=> \"1234567890...0987654321\"\n      #\n    def send_message(queue_url, message)\n      req_hash = generate_rest_request('PUT',\n                                       :message   => message,\n                                       :queue_url => \"#{queue_url}/back\")\n      request_info(req_hash, SqsSendMessagesParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Deletes message from queue. Returns +true+ or an exception.  Amazon\n      # returns +true+ on deletion of non-existent messages.\n      #\n      #  sqs.delete_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '12345678904...0987654321') #=> true\n      #\n    def delete_message(queue_url, message_id)\n      req_hash = generate_request('DeleteMessage', \n                                  'MessageId' => message_id,\n                                  :queue_url  => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Changes message visibility timeout. Returns +true+ or an exception.\n      #\n      #  sqs.change_message_visibility('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', '1234567890...0987654321', 10) #=> true\n      #\n    def change_message_visibility(queue_url, message_id, visibility_timeout=0)\n      req_hash = generate_request('ChangeMessageVisibility', \n                                  'MessageId'         => message_id,\n                                  'VisibilityTimeout' => visibility_timeout.to_s,\n                                  :queue_url          => queue_url)\n      request_info(req_hash, SqsStatusParser.new(:logger => @logger))\n    rescue\n      on_exception\n    end\n    \n      # Returns queue url by queue short name or +nil+ if queue is not found\n      #\n      #  sqs.queue_url_by_name('my_awesome_queue') #=> 'http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue'\n      #\n    def queue_url_by_name(queue_name)\n      return queue_name if queue_name.include?('/')\n      queue_urls = list_queues(queue_name)\n      queue_urls.each do |queue_url|\n        return queue_url if queue_name_by_url(queue_url) == queue_name\n      end\n      nil\n    rescue\n      on_exception\n    end\n\n      # Returns short queue name by url.\n      #\n      #  RightSqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'\n      #\n    def self.queue_name_by_url(queue_url)\n      queue_url[/[^\\/]*$/]\n    rescue\n      on_exception\n    end\n    \n      # Returns short queue name by url.\n      #\n      #  sqs.queue_name_by_url('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 'my_awesome_queue'\n      #\n    def queue_name_by_url(queue_url)\n      self.class.queue_name_by_url(queue_url)\n    rescue\n      on_exception\n    end\n\n      # Returns approximate number of messages in queue.\n      #\n      #  sqs.get_queue_length('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> 3\n      #\n    def get_queue_length(queue_url)\n      get_queue_attributes(queue_url)['ApproximateNumberOfMessages'].to_i\n    rescue\n      on_exception\n    end\n\n      # Removes all visible messages from queue. Return +true+ or an exception.\n      #\n      #  sqs.clear_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true\n      #\n    def clear_queue(queue_url)\n      while (m = pop_message(queue_url)) ; end   # delete all messages in queue\n      true\n    rescue\n      on_exception\n    end\n\n      # Deletes queue then re-creates it (restores attributes also). The fastest method to clear big queue or queue with invisible messages. Return +true+ or an exception.\n      #\n      #  sqs.force_clear_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true\n      #\n      # PS This function is no longer supported.  Amazon has changed the SQS semantics to require at least 60 seconds between \n      # queue deletion and creation. Hence this method will fail with an exception.\n      #\n    def force_clear_queue(queue_url)\n      queue_name       = queue_name_by_url(queue_url)\n      queue_attributes = get_queue_attributes(queue_url)\n      force_delete_queue(queue_url)\n      create_queue(queue_name)\n        # hmmm... The next line is a trick. Amazon do not want change attributes immediately after queue creation\n        # So we do 'empty' get_queue_attributes. Probably they need some time to allow attributes change.\n      get_queue_attributes(queue_url)  \n      queue_attributes.each{ |attribute, value| set_queue_attributes(queue_url, attribute, value) }\n      true\n    rescue\n      on_exception\n    end\n\n      # Deletes queue even if it has messages. Return +true+ or an exception.\n      #\n      #  force_delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=> true\n      #\n      # P.S. same as <tt>delete_queue('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', true)</tt>\n    def force_delete_queue(queue_url)\n      delete_queue(queue_url, true)\n    rescue\n      on_exception\n    end\n\n      # Reads first accessible message from queue. Returns message as a hash: <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.\n      #\n      #  sqs.receive_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 10) #=>\n      #    {:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}\n      #\n    def receive_message(queue_url, visibility_timeout=nil)\n      result = receive_messages(queue_url, 1, visibility_timeout)\n      result.right_blank? ? nil : result[0]\n    rescue\n      on_exception\n    end\n    \n      # Same as send_message\n    alias_method :push_message, :send_message\n    \n      # Pops (retrieves and deletes) up to 'number_of_messages' from queue. Returns an array of retrieved messages in format: <tt>[{:id=>'message_id', :body=>'message_body'}]</tt>.\n      #\n      #   sqs.pop_messages('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue', 3) #=>\n      #    [{:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}, ..., {}]\n      #\n    def pop_messages(queue_url, number_of_messages=1)\n      messages = receive_messages(queue_url, number_of_messages)\n      messages.each do |message|\n        delete_message(queue_url, message[:id])\n      end\n      messages\n    rescue\n      on_exception\n    end\n\n      # Pops (retrieves and  deletes) first accessible message from queue. Returns the message in format <tt>{:id=>'message_id', :body=>'message_body'}</tt> or +nil+.\n      #\n      #  sqs.pop_message('http://queue.amazonaws.com/ZZ7XXXYYYBINS/my_awesome_queue') #=>\n      #    {:id=>\"12345678904GEZX9746N|0N9ED344VK5Z3SV1DTM0|1RVYH4X3TJ0987654321\", :body=>\"message_1\"}\n      #\n    def pop_message(queue_url)\n      messages = pop_messages(queue_url)\n      messages.right_blank? ? nil : messages[0]\n    rescue\n      on_exception\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Status Response Parser\n    #-----------------------------------------------------------------\n\n    class SqsStatusParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        if name == 'StatusCode'\n          @result = @text=='Success' ? true : false\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Queue\n    #-----------------------------------------------------------------\n\n    class SqsCreateQueueParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        @result = @text if name == 'QueueUrl'\n      end\n    end\n\n    class SqsListQueuesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagend(name)\n        @result << @text if name == 'QueueUrl'\n      end\n    end\n\n    class SqsGetQueueAttributesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = {}\n      end\n      def tagend(name)\n        case name \n          when 'Attribute' ; @current_attribute          = @text\n          when 'Value'     ; @result[@current_attribute] = @text\n  #        when 'StatusCode'; @result['status_code']      = @text\n  #        when 'RequestId' ; @result['request_id']       = @text\n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Timeouts\n    #-----------------------------------------------------------------\n\n    class SqsGetVisibilityTimeoutParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        @result = @text.to_i if name == 'VisibilityTimeout'\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Permissions\n    #-----------------------------------------------------------------\n\n    class SqsListGrantsParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagstart(name, attributes)\n        @current_perms = {} if name == 'GrantList'\n      end\n      def tagend(name)\n        case name\n          when 'ID'         ; @current_perms[:id]         = @text\n          when 'DisplayName'; @current_perms[:name]       = @text\n          when 'Permission' ; @current_perms[:permission] = @text\n          when 'GrantList'  ; @result << @current_perms \n        end\n      end\n    end\n\n    #-----------------------------------------------------------------\n    #      PARSERS: Messages\n    #-----------------------------------------------------------------\n\n    class SqsReceiveMessagesParser < RightAWSParser # :nodoc:\n      def reset\n        @result = []\n      end\n      def tagstart(name, attributes)\n        @current_message = {} if name == 'Message'\n      end\n      def tagend(name)\n        case name\n          when 'MessageId'  ; @current_message[:id]   = @text\n          when 'MessageBody'; @current_message[:body] = @text\n          when 'Message'    ; @result << @current_message\n        end\n      end\n    end\n\n    class SqsSendMessagesParser < RightAWSParser # :nodoc:\n      def tagend(name)\n        @result = @text if name == 'MessageId'\n      end\n    end\n    \n  end\n\nend\n"
  },
  {
    "path": "right_aws.gemspec",
    "content": "#--  -*- mode: ruby; encoding: utf-8 -*-\n# Copyright: Copyright (c) 2010 RightScale, Inc.\n#\n# Permission is hereby granted, free of charge, to any person obtaining\n# a copy of this software and associated documentation files (the\n# 'Software'), to deal in the Software without restriction, including\n# without limitation the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the Software, and to\n# permit persons to whom the Software is furnished to do so, subject to\n# the following conditions:\n#\n# The above copyright notice and this permission notice shall be\n# included in all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\n# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n#++\n\nrequire 'rubygems'\nrequire File.expand_path(File.join(File.dirname(__FILE__), \"lib\", \"awsbase\", \"version\"))\n\nGem::Specification.new do |spec|\n  spec.name = 'right_aws'\n  spec.rubyforge_project = 'rightaws'\n  spec.version = RightAws::VERSION::STRING\n  spec.authors = ['RightScale, Inc.']\n  spec.email = 'support@rightscale.com'\n  spec.summary = 'Interface classes for the Amazon EC2, SQS, and S3 Web Services'\n  spec.rdoc_options = ['--main', 'README.txt', '--title', '']\n  spec.extra_rdoc_files = ['README.txt']\n  spec.required_ruby_version = '>= 1.8.7'\n  spec.require_path = 'lib'\n\n  spec.add_dependency('right_http_connection', '>= 1.2.5')\n  #spec.requirements << \"uuidtools >= 1.0.7 if you want to use ActiveSdb\"\n  spec.requirements << \"libxml-ruby >= 0.5.2.0 is encouraged\"\n\n  spec.add_development_dependency('rake')\n\n  spec.summary = 'The RightScale AWS gems have been designed to provide a robust, fast, and secure interface to Amazon EC2, EBS, S3, SQS, SDB, and CloudFront.'\n  spec.description = <<-EOF\n== DESCRIPTION:\n\nThe RightScale AWS gems have been designed to provide a robust, fast, and secure interface to Amazon EC2, EBS, S3, SQS, SDB, and CloudFront.\nThese gems have been used in production by RightScale since late 2006 and are being maintained to track enhancements made by Amazon.\nThe RightScale AWS gems comprise:\n\n- RightAws::Ec2 -- interface to Amazon EC2 (Elastic Compute Cloud) and the\n  associated EBS (Elastic Block Store)\n- RightAws::S3 and RightAws::S3Interface -- interface to Amazon S3 (Simple Storage Service)\n- RightAws::Sqs and RightAws::SqsInterface -- interface to first-generation Amazon SQS (Simple Queue Service) (API version 2007-05-01)\n- RightAws::SqsGen2 and RightAws::SqsGen2Interface -- interface to second-generation Amazon SQS (Simple Queue Service) (API version 2008-01-01)\n- RightAws::SdbInterface and RightAws::ActiveSdb -- interface to Amazon SDB (SimpleDB)\n- RightAws::AcfInterface -- interface to Amazon CloudFront, a content distribution service\n\n== FEATURES:\n\n- Full programmmatic access to EC2, EBS, S3, SQS, SDB, and CloudFront.\n- Complete error handling: all operations check for errors and report complete\n  error information by raising an AwsError.\n- Persistent HTTP connections with robust network-level retry layer using\n  RightHttpConnection).  This includes socket timeouts and retries.\n- Robust HTTP-level retry layer.  Certain (user-adjustable) HTTP errors returned\n  by Amazon's services are classified as temporary errors.\n  These errors are automaticallly retried using exponentially increasing intervals.\n  The number of retries is user-configurable.\n- Fast REXML-based parsing of responses (as fast as a pure Ruby solution allows).\n- Uses libxml (if available) for faster response parsing.\n- Support for large S3 list operations.  Buckets and key subfolders containing\n  many (> 1000) keys are listed in entirety.  Operations based on list (like\n  bucket clear) work on arbitrary numbers of keys.\n- Support for streaming GETs from S3, and streaming PUTs to S3 if the data source is a file.\n- Support for single-threaded usage, multithreaded usage, as well as usage with multiple\n  AWS accounts.\n- Support for both first- and second-generation SQS (API versions 2007-05-01\n  and 2008-01-01).  These versions of SQS are not compatible.\n- Support for signature versions 0 and 1 on SQS, SDB, and EC2.\n- Interoperability with any cloud running Eucalyptus (http://eucalyptus.cs.ucsb.edu)\n- Test suite (requires AWS account to do \"live\" testing).\nEOF\n\n  candidates = Dir.glob('{lib,test}/**/*') + ['History.txt', 'Manifest.txt', 'README.txt', 'Rakefile', 'right_aws.gemspec']\n  spec.files = candidates.sort\n  spec.test_files = Dir.glob('test/**/*')\nend\n"
  },
  {
    "path": "test/README.mdown",
    "content": "# Notes and tips for developers\n\n## Setting up credentials for testing\n\nBefore you can run any tests, you need to set up credentials (API key and secret) that\nwill be used when talking to AWS.  The credentials are loaded in `test/test_credentials.rb`\nand are expected to be found in `~/.rightscale/testcredentials.rb` and look like this:\n\n    TestCredentials.aws_access_key_id= 'AAAAAAAAAAAAAAAAAAAA'\n    TestCredentials.aws_secret_access_key= 'asdfasdfsadf'\n    TestCredentials.account_number= '???'\n\nIf you prefer to store your secret key in the OS X keychain, you can do this:\n\n    def secret_access_key_from_keychain (key_id)\n      dump = `security -q find-generic-password -a \"#{key_id}\" -g 2>&1`\n      dump[/password: \"(.*)\"/, 1]\n    end\n\n    TestCredentials.aws_access_key_id= 'AAAAAAAAAAAAAAAAAAAA'\n    TestCredentials.aws_secret_access_key= secret_access_key_from_keychain(TestCredentials.aws_access_key_id)\n    TestCredentials.account_number= '???'\n\n## Running tests\n\nThere is no test suite that runs all tests.  Each module is tested separately.  E.g.,\nto run the Load Balancer tests, run `rake testelb`.  Run `rake -T` for a full list.\n\nSome tests need to launch services on AWS to have something to test.  This means two things:\n\n1. Running all the tests will cost you money.\n2. You will need to shut down some services separately once you are done, or things\n    will keep running and cost you money.\n\nAs an example, the ELB and Route 53 tests need a load balancer for testing.  Starting a load balancer\nfor every test would make every test case cost as much as running the LB for one hour, so it makes\nmore sense to leave it running until it's no longer needed.\n\nThe ELB tests contain instructions for shutting down the load balancer.\n"
  },
  {
    "path": "test/acf/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/acf/test_right_acf.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestAcf < Test::Unit::TestCase\n\n  RIGHT_OBJECT_TEXT     = 'Right test message'\n  \n  STDOUT.sync = true\n\n  def setup\n    @acf= Rightscale::AcfInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @s3 = Rightscale::S3.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @bucket_name   = \"right-acf-awesome-test-bucket-xxx1\"\n    @bucket_domain = \"#{@bucket_name}.s3.amazonaws.com\"\n  end\n\n  def test_01_list_distributions_part1\n    distributions = nil\n    assert_nothing_raised(Rightscale::AwsError) do\n      distributions = @acf.list_distributions\n    end\n    assert distributions.is_a?(Array)\n  end\n\n  def test_02_try_to_create_for_bad_bucket\n    # a bucket does not exist\n    assert_raise(Rightscale::AwsError) do\n      @acf.create_distribution(\"right-cloudfront-awesome-test-bucket-not-exist\", \"Mustn't to be born\", true)\n    end\n    # a bucket is not a domain naming complied guy\n    bucket_name = 'right_cloudfront_awesome_test_bucket_BAD_XXX'\n    @s3.bucket(bucket_name, :create)\n    assert_raise(Rightscale::AwsError) do\n      @acf.create_distribution(bucket_name, \"Mustn't to be born\", true)\n    end\n  end\n\n  def test_02_x_delete_bad_bucket\n    bucket_name = 'right_cloudfront_awesome_test_bucket_BAD_XXX'\n    @s3.bucket(bucket_name, false).delete\n  end\n\n  def test_03_create\n    comment = 'WooHoo!!!'\n    # create a test bucket\n    @s3.bucket(@bucket_name, :create)\n    # create a distribution\n    distribution = @acf.create_distribution(@bucket_domain, comment, true)\n    assert_equal comment, distribution[:comment]\n    assert       distribution[:cnames].size == 0\n    assert       distribution[:enabled]\n  end\n\n  def test_04_list_distributions_part2\n    distributions = @acf.list_distributions\n    assert distributions.size > 0\n  end\n\n  def get_test_distribution\n    @acf.list_distributions.select{ |d| d[:origin] == @bucket_domain }.first\n  end\n\n  def test_05_get_distribution\n    old = get_test_distribution\n    assert_nothing_raised do\n      @acf.get_distribution(old[:aws_id])\n    end\n  end\n\n  def test_06_get_and_set_config\n    config = nil\n    old = get_test_distribution\n    assert_nothing_raised do\n      config = @acf.get_distribution_config(old[:aws_id])\n    end\n    # change a config\n    config[:enabled] = false\n    config[:cnames] << 'xxx1.myawesomesite.com'\n    config[:cnames] << 'xxx2.myawesomesite.com'\n    # set config\n    set_config_result = nil\n    assert_nothing_raised do\n      set_config_result = @acf.set_distribution_config(old[:aws_id], config)\n    end\n    assert set_config_result\n    # reget the config and check\n    new_config = nil\n    assert_nothing_raised do\n      new_config = @acf.get_distribution_config(old[:aws_id])\n    end\n    assert           !new_config[:enabled]\n    assert_equal     new_config[:cnames].sort, ['xxx1.myawesomesite.com', 'xxx2.myawesomesite.com']\n    assert_not_equal config[:e_tag], new_config[:e_tag]\n\n    # try to update the old config again (must fail because ETAG has changed)\n    assert_raise(Rightscale::AwsError) do\n      @acf.set_distribution_config(old[:aws_id], config)\n    end\n  end\n\n  def test_08_delete_distribution\n    # we need ETAG so use get_distribution\n    distribution = @acf.get_distribution(get_test_distribution[:aws_id])\n    # try to delete a distribution\n    # should fail because\n    if distribution[:status] == 'InProgress'\n      # should fail because the distribution is not deployed yet\n      assert_raise(Rightscale::AwsError) do\n        @acf.delete_distribution(distribution[:aws_id], distribution[:e_tag])\n      end\n      # wait for a deployed state\n      print \"waiting up to 5 min while the distribution is being deployed: \"\n      100.times do\n        print '.'\n        distribution = @acf.get_distribution(distribution[:aws_id])\n        if distribution[:status] == 'Deployed'\n          print ' done'\n          break\n        end\n        sleep 3\n      end\n      puts\n    end\n\n    # only disabled and deployed distribution can be deleted\n    assert_equal 'Deployed', distribution[:status]\n    assert       !distribution[:enabled]\n\n    # delete the distribution\n    assert_nothing_raised do\n      @acf.delete_distribution(distribution[:aws_id], distribution[:e_tag])\n    end  \n  end\n\n  def test_09_drop_bucket\n    assert @s3.bucket(@bucket_name).delete\n  end\n\nend\n"
  },
  {
    "path": "test/awsbase/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/awsbase/test_right_awsbase.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestAwsbase < Test::Unit::TestCase\n\n  def setup\n  end\n  \n  def test_01_create_describe_key_pairs\n  end\n  \nend\n"
  },
  {
    "path": "test/ec2/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/ec2/test_right_ec2.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestEc2 < Test::Unit::TestCase\n\n    # Some of RightEc2 instance methods concerning instance launching and image registration\n    # are not tested here due to their potentially risk.\n  \n  def setup\n    @ec2   = Rightscale::Ec2.new(TestCredentials.aws_access_key_id,\n                                 TestCredentials.aws_secret_access_key)\n    @key   = 'right_ec2_awesome_test_key'\n    @group = 'right_ec2_awesome_test_security_group'\n  end\n  \n  def test_01_create_describe_key_pairs\n    new_key = @ec2.create_key_pair(@key)\n    assert new_key[:aws_material][/BEGIN RSA PRIVATE KEY/], \"New key material is absent\"\n    keys = @ec2.describe_key_pairs\n    assert keys.map{|key| key[:aws_key_name] }.include?(@key), \"#{@key} must exist\"\n  end\n  \n  def test_02_create_security_group\n    assert @ec2.create_security_group(@group,'My awesone test group'), 'Create_security_group fail'\n    group = @ec2.describe_security_groups([@group])[0]\n    assert_equal @group, group[:aws_group_name], 'Group must be created but does not exist'\n  end\n  \n  def test_03_perms_add\n    assert @ec2.authorize_security_group_named_ingress(@group, TestCredentials.account_number, 'default')\n    assert @ec2.authorize_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')\n  end\n  \n  def test_04_check_new_perms_exist\n    assert_equal 2, @ec2.describe_security_groups([@group])[0][:aws_perms].size\n  end\n\n  def test_05_perms_remove\n    assert @ec2.revoke_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')\n    assert @ec2.revoke_security_group_named_ingress(@group,\n                                                    TestCredentials.account_number, 'default')\n  end\n\n  def test_06_describe_images\n    images = @ec2.describe_images\n    assert images.size>0, 'Amazon must have at least some public images'\n      # unknown image\n    assert_raise(Rightscale::AwsError){ @ec2.describe_images(['ami-ABCDEFGH'])}\n  end\n\n  def test_07_describe_instanses\n    assert @ec2.describe_instances\n      # unknown image\n    assert_raise(Rightscale::AwsError){ @ec2.describe_instances(['i-ABCDEFGH'])}\n  end\n\n  def test_08_delete_security_group\n    assert @ec2.delete_security_group(@group), 'Delete_security_group fail'\n  end\n  \n  def test_09_delete_key_pair\n    assert @ec2.delete_key_pair(@key), 'Delete_key_pair fail'\n##  Hmmm... Amazon does not through the exception any more. It now just returns a 'true' if the key does not exist any more...\n##      # key must be deleted already\n##    assert_raise(Rightscale::AwsError) { @ec2.delete_key_pair(@key) }\n  end\n\n  def test_10_signature_version_0\n    ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0')\n    images = ec2.describe_images\n    assert images.size>0, 'Amazon must have at least some public images'\n    # check that the request has correct signature version\n    assert ec2.last_request.path.include?('SignatureVersion=0')\n  end\n\n  def test_11_regions\n    regions = nil\n    assert_nothing_raised do\n      regions = @ec2.describe_regions\n    end\n    # check we got more that 0 regions\n    assert regions.size > 0\n    # check an access to regions\n    regions.each do |region|\n      regional_ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :region => region)\n      # do we have a correct endpoint server?\n      assert_equal \"#{region}.ec2.amazonaws.com\", regional_ec2.params[:server]\n      # get a list of images from every region\n      images = nil\n      assert_nothing_raised do\n        images = regional_ec2.describe_regions\n      end\n      # every region must have images\n      assert images.size > 0\n    end\n  end\n  \n  def test_12_endpoint_url\n    ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :endpoint_url => 'a://b.c:0/d/', :region => 'z')\n    # :endpoint_url has a priority hence :region should be ommitted\n    assert_equal 'a',   ec2.params[:protocol]\n    assert_equal 'b.c', ec2.params[:server]\n    assert_equal '/d/', ec2.params[:service]\n    assert_equal 0,     ec2.params[:port]\n    assert_nil          ec2.params[:region]\n  end\n\nend\n"
  },
  {
    "path": "test/elb/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/elb/test_right_elb.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestElb < Test::Unit::TestCase\n\n  STDOUT.sync = true\n  BALANCER_NAME = 'right-aws-test-lb'\n\n  def setup\n    @elb = Rightscale::ElbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :logger => Logger.new('/dev/null'))\n\n    unless @elb.describe_load_balancers.detect { |lb| lb[:load_balancer_name] == BALANCER_NAME }\n      @elb.create_load_balancer(BALANCER_NAME, %w[us-east-1b], [])\n    end\n    @lb = @elb.describe_load_balancers.detect { |lb| lb[:load_balancer_name] == BALANCER_NAME }\n  end\n\n  # At the end of the day when you want to shut down the test balancer:\n  # * Uncomment this method.\n  # * Comment out all test except one.\n  # * Run this test file.\n  #\n  # def teardown\n  #   @elb.delete_load_balancer BALANCER_NAME\n  # end\n\n  def test_00_describe_load_balancers\n    items = @elb.describe_load_balancers\n    assert items.is_a?(Array)\n  end\n\n  def test_description\n    assert_match /^#{BALANCER_NAME}-\\d+\\.us-east-1\\.elb\\.amazonaws\\.com$/, @lb[:dns_name]\n  end\n\n  def test_description_has_canonical_hosted_zone_name\n    assert_match /^#{BALANCER_NAME}-\\d+\\.us-east-1\\.elb\\.amazonaws\\.com$/, @lb[:canonical_hosted_zone_name]\n  end\n\n  def test_description_has_canonical_hosted_zone_name_id\n    assert_match /^[A-Z0-9]+$/, @lb[:canonical_hosted_zone_name_id]\n  end\n\nend\n"
  },
  {
    "path": "test/http_connection.rb",
    "content": "=begin\nCopyright (c) 2007 RightScale, Inc. \n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n=end\n\n# Stub extension/redefinition of RightHttpConnection for testing purposes.\nrequire 'net/http'\nrequire 'rubygems'\nrequire 'right_http_connection'\n\nmodule Net\n  class HTTPResponse\n  alias_method :real_body, :body\n    def setmsg(msg)\n      @mymsg = msg\n    end\n\n    def body\n      # defined?() helps us to get rid of a bunch of 'warnings'\n      (defined?(@mymsg) && @mymsg) ? @mymsg : real_body\n    end\n  end\nend\n\nmodule Rightscale\n\n  class HttpConnection\n    @@response_stack = []\n\n    alias_method :real_request, :request\n\n    def request(request_params, &block)\n      if(@@response_stack.length == 0)\n        return real_request(request_params, &block)\n      end\n\n      if(block)\n        # Do something special\n      else\n        next_response = HttpConnection::pop() \n        classname = Net::HTTPResponse::CODE_TO_OBJ[\"#{next_response[:code]}\"]\n        response = classname.new(\"1.1\", next_response[:code], next_response[:msg])\n        if(next_response[:msg])\n          response.setmsg(next_response[:msg])\n        end\n        response\n      end\n    end\n\n    def self.reset\n      @@response_stack = []\n    end\n\n    def self.push(code, msg=nil)\n      response = {:code => code, :msg => msg}\n      @@response_stack << response\n    end\n\n    def self.pop\n      @@response_stack.pop\n    end\n\n    def self.length\n      @@response_stack.length\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "test/rds/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/rds/test_right_rds.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestRds < Test::Unit::TestCase\n\n  STDOUT.sync = true\n\n  TEST_SG_NAME = 'RightRdsSGTest0123456789'\n\n  def setup\n    @rds = Rightscale::RdsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :logger => Logger.new('/dev/null'))\n  end\n\n  def test_00_instances\n    assert_nothing_raised do\n      items = @rds.describe_db_instances\n      assert items.is_a?(Array)\n    end\n    #\n    assert_nothing_raised do\n      @rds.describe_db_instances do |response|\n        assert response.is_a?(Hash)\n        assert response[:db_instances].is_a?(Array)\n      end\n    end\n  end\n\n  def test_10_security_groups\n    assert_nothing_raised do\n      items = @rds.describe_db_security_groups\n      assert items.is_a?(Array)\n    end\n    #\n    assert_nothing_raised do\n      @rds.describe_db_security_groups do |response|\n        assert response.is_a?(Hash)\n        assert response[:db_security_groups].is_a?(Array)\n      end\n    end\n  end\n\n  def test_11_remove_security_group\n    @rds.delete_db_security_group rescue nil\n  end\n\n  def test_12_create_security_group\n    sg = nil\n    assert_nothing_raised do\n      sg = @rds.create_db_security_group(TEST_SG_NAME, 'sg-description')\n    end\n    assert sg.is_a?(Hash)\n  end\n\n  def test_13_authorize\n    assert_nothing_raised do\n      sg = @rds.authorize_db_security_group_ingress(TEST_SG_NAME, :cidrip => '131.0.0.1/8')\n      assert sg.is_a?(Hash)\n      assert_equal 1, sg[:ip_ranges].size\n    end\n    assert_nothing_raised do\n      sg = @rds.authorize_db_security_group_ingress(TEST_SG_NAME, :ec2_security_group_owner => '826693181925',\n                                                                  :ec2_security_group_name  => 'default' )\n      assert sg.is_a?(Hash)\n      assert_equal 1, sg[:ec2_security_groups].size\n    end\n    sleep 30\n  end\n\n  def test_14_revoke\n    assert_nothing_raised do\n      sg = @rds.revoke_db_security_group_ingress(TEST_SG_NAME, :cidrip => '131.0.0.1/8')\n      assert sg.is_a?(Hash)\n    end\n    assert_nothing_raised do\n      sg = @rds.revoke_db_security_group_ingress(TEST_SG_NAME, :ec2_security_group_owner => '826693181925',\n                                                               :ec2_security_group_name  => 'default' )\n      assert sg.is_a?(Hash)\n    end\n    sleep 30\n    #\n    sg = @rds.describe_db_security_groups(TEST_SG_NAME).first\n    assert_equal 0, sg[:ip_ranges].size\n    assert_equal 0, sg[:ec2_security_groups].size\n  end\n\n  def test_15_delete_security_group\n    assert_nothing_raised do\n      @rds.delete_db_security_group(TEST_SG_NAME)\n    end\n  end\n\n\n  def test_20_db_snapshots\n    assert_nothing_raised do\n      items = @rds.describe_db_snapshots\n      assert items.is_a?(Array)\n    end\n    #\n    assert_nothing_raised do\n      @rds.describe_db_snapshots do |response|\n        assert response.is_a?(Hash)\n        assert response[:db_snapshots].is_a?(Array)\n      end\n    end\n  end\n\n  def test_30_events\n    assert_nothing_raised do\n      items = @rds.describe_events\n      assert items.is_a?(Array)\n    end\n    #\n    assert_nothing_raised do\n      @rds.describe_events do |response|\n        assert response.is_a?(Hash)\n        assert response[:events].is_a?(Array)\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "test/route_53/fixtures/a_record.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2011-05-05/\">\n  <ChangeBatch>\n    <Changes>\n      <Change>\n        <Action>CREATE</Action>\n        <ResourceRecordSet>\n          <Name>host.right-aws.example.com.</Name>\n          <Type>A</Type>\n          <TTL>600</TTL>\n          <ResourceRecords>\n            <ResourceRecord><Value>10.0.0.1</Value></ResourceRecord>\n          </ResourceRecords>\n        </ResourceRecordSet>\n      </Change>\n    </Changes>\n  </ChangeBatch>\n</ChangeResourceRecordSetsRequest>\n"
  },
  {
    "path": "test/route_53/fixtures/alias_record.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2011-05-05/\">\n  <ChangeBatch>\n    <Changes>\n      <Change>\n        <Action>CREATE</Action>\n        <ResourceRecordSet>\n          <Name>right-aws.example.com.</Name>\n          <Type>A</Type>\n          <AliasTarget>\n            <HostedZoneId>Z1234567890123</HostedZoneId>\n            <DNSName>example-load-balancer-1111111111.us-east-1.elb.amazonaws.com.</DNSName>\n          </AliasTarget>\n        </ResourceRecordSet>\n      </Change>\n    </Changes>\n  </ChangeBatch>\n</ChangeResourceRecordSetsRequest>\n"
  },
  {
    "path": "test/route_53/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/route_53/test_right_route_53.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestRoute53 < Test::Unit::TestCase\n\n  STDOUT.sync = true\n  BALANCER_NAME = 'right-aws-test-lb'\n\n  def setup\n    @r53 = Rightscale::Route53Interface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :logger => Logger.new('/dev/null'))\n    @zone_config = {:name => \"right-aws.example.com.\", :config => {:comment => 'a comment'}}\n\n    @elb = Rightscale::ElbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :logger => Logger.new('/dev/null'))\n  end\n\n  def teardown\n    @r53.list_hosted_zones.each do |zone|\n      next unless zone[:name] == @zone_config[:name]\n      zone_id = zone[:aws_id]\n      puts zone_id\n      records = @r53.list_resource_record_sets(zone_id)\n      # The NS and SOA records are provided by AWS and must not be deleted\n      records.reject! { |rr| %w[NS SOA].include? rr[:type] }\n      if records.any?\n        response = @r53.delete_resource_record_sets(zone_id, records)\n        puts response.inspect\n      end\n      puts @r53.delete_hosted_zone(zone_id).inspect\n    end\n    # Uncomment to shut down at the end of the day.\n    # @elb.delete_load_balancer BALANCER_NAME\n  end\n\n  def test_00_list_hosted_zones\n    items = @r53.list_hosted_zones\n    assert items.is_a?(Array)\n  end\n\n  def test_create_and_delete_zone\n    response = @r53.create_hosted_zone(@zone_config)\n    assert_equal response[:name], @zone_config[:name]\n    assert response[:aws_id].is_a?(String)\n    assert response[:name_servers].is_a?(Array)\n\n    response2 = @r53.delete_hosted_zone(response[:aws_id])\n    assert_equal response2[:status], 'PENDING'\n  end\n\n  def test_add_and_remove_A_record\n    zone = @r53.create_hosted_zone(@zone_config)\n    zone_id = zone[:aws_id]\n    # add\n    a_record = { :name => 'host.right-aws.example.com.', :type => 'A', :ttl => 600, :resource_records => ['10.0.0.1'] }\n    response = @r53.create_resource_record_sets(zone_id, [a_record.dup]) # .dup since it will get :action => :create\n    assert_equal response[:status], 'PENDING'\n\n    # It should be there now\n    records = @r53.list_resource_record_sets(zone_id)\n    assert records.is_a?(Array)\n    assert records.detect { |rr| rr == a_record }, \"Could not find '#{a_record.inspect}' in '#{records.inspect}'\"\n\n    # remove\n    response = @r53.delete_resource_record_sets(zone_id, [a_record.dup])\n    assert_equal response[:status], 'PENDING'\n\n    # It should not be there anymore\n    records = @r53.list_resource_record_sets(zone_id)\n    assert records.is_a?(Array)\n    assert ! records.detect { |rr| rr == a_record }, \"Record '#{a_record.inspect}' is still in '#{records.inspect}'\"\n\n    @r53.delete_hosted_zone(zone_id)\n  end\n\n  def test_add_and_remove_Alias_record\n    lb = find_or_create_load_balancer\n\n    zone = @r53.create_hosted_zone(@zone_config)\n    zone_id = zone[:aws_id]\n\n    # add\n    alias_target = { :hosted_zone_id => lb[:canonical_hosted_zone_name_id], :dns_name => lb[:dns_name] }\n    alias_record = { :name => 'right-aws.example.com', :type => 'A', :alias_target => alias_target }\n    response = @r53.create_resource_record_sets(zone_id, [alias_record.dup]) # .dup since it will get :action => :create\n    assert_equal response[:status], 'PENDING'\n\n    # It should be there now\n    records = @r53.list_resource_record_sets(zone_id)\n    assert records.is_a?(Array)\n    record = records.detect { |rr| rr[:alias_target] }\n    assert record, \"Could not find '#{alias_record.inspect}' in '#{records.inspect}'\"\n    # AWS adds final dots to names\n    assert_equal \"#{alias_record[:name]}.\", record[:name]\n    assert_equal \"#{alias_target[:dns_name]}.\", record[:alias_target][:dns_name]\n\n    # remove\n    response = @r53.delete_resource_record_sets(zone_id, [alias_record.dup])\n    assert_equal response[:status], 'PENDING'\n\n    # It should not be there anymore\n    records = @r53.list_resource_record_sets(zone_id)\n    assert records.is_a?(Array)\n    record = records.detect { |rr| rr[:alias_target] }\n    assert ! record, \"Record '#{alias_record.inspect}' is still in '#{records.inspect}'\"\n\n    @r53.delete_hosted_zone(zone_id)\n  end\n\n  def find_or_create_load_balancer\n    unless @elb.describe_load_balancers.detect { |lb| lb[:load_balancer_name] == BALANCER_NAME }\n      @elb.create_load_balancer(BALANCER_NAME, %w[us-east-1b], [])\n      puts \"WARNING: Started load balancer.  Remember to shut it down (see teardown).\"\n      puts \"NOTE: Tests might not pass during first few seconds after load balancer is created.\"\n    end\n    @elb.describe_load_balancers.detect { |lb| lb[:load_balancer_name] == BALANCER_NAME }\n  end\n\n  def test_rr_sets_to_xml\n    a_record = { :name => 'host.right-aws.example.com.', :type => 'A', :ttl => 600, :resource_records => ['10.0.0.1'], :action => :create }\n    expected = load_fixture('a_record.xml')\n    assert_equal expected, @r53.resource_record_sets_to_xml([a_record], '')\n\n    # Note final full stop\n    alias_target = { :hosted_zone_id => 'Z1234567890123', :dns_name => 'example-load-balancer-1111111111.us-east-1.elb.amazonaws.com.' }\n    alias_record = { :name => 'right-aws.example.com.', :type => 'A', :alias_target => alias_target, :action => :create }\n    expected = load_fixture('alias_record.xml')\n    assert_same_lines expected, @r53.resource_record_sets_to_xml([alias_record], '')\n  end\n\n  def load_fixture (name)\n    File.read(File.join(File.dirname(__FILE__), 'fixtures', name))\n  end\n\n  def assert_same_lines (expected, actual)\n    expected = expected.split \"\\n\"\n    actual   = actual.split \"\\n\"\n    previous = []\n    while e = expected.shift and a = actual.shift\n      assert_equal e, a, \"After:\\n#{previous.join(\"\\n\")}\"\n      previous << e\n    end\n  end\nend\n"
  },
  {
    "path": "test/s3/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/s3/test_right_s3.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestS3 < Test::Unit::TestCase\n\n  RIGHT_OBJECT_TEXT     = 'Right test message'\n  INTERFACE = Rightscale::S3Interface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  CONNECTION = Rightscale::S3.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n\n  def setup\n    @s3     = INTERFACE\n    @bucket = 'right_s3_awesome_test_bucket_000B1_officedrop'\n    @bucket2 = 'right_s3_awesome_test_bucket_000B2_officedrop'\n    @key1   = 'test/woohoo1/'\n    @key2   = 'test1/key/woohoo2'\n    @key3   = 'test2/A%B@C_D&E?F+G=H\"I'\n    @key4   = 'test/large_multipart_file_string'\n    @key5   = 'test/large_multipart_file_stream'\n    @key1_copy =     'test/woohoo1_2'\n    @key1_new_name = 'test/woohoo1_3'\n    @key2_new_name = 'test1/key/woohoo2_new'\n    @s      = CONNECTION\n  end\n\n  #---------------------------\n  # Rightscale::S3Interface\n  #---------------------------\n\n  def test_01_create_bucket\n    assert @s3.create_bucket(@bucket), 'Create_bucket fail'\n  end\n\n  def test_02_list_all_my_buckets\n    assert @s3.list_all_my_buckets.map{|bucket| bucket[:name]}.include?(@bucket), \"#{@bucket} must exist in bucket list\"\n  end\n\n  def test_03_list_empty_bucket\n    assert_equal 0, @s3.list_bucket(@bucket).size, \"#{@bucket} isn't empty, arrgh!\"\n  end\n\n  def test_04_put\n    assert @s3.put(@bucket, @key1, RIGHT_OBJECT_TEXT, 'x-amz-meta-family'=>'Woohoo1!'), 'Put bucket fail'\n    assert @s3.put(@bucket, @key2, RIGHT_OBJECT_TEXT, 'x-amz-meta-family'=>'Woohoo2!'), 'Put bucket fail'\n    assert @s3.put(@bucket, @key3, RIGHT_OBJECT_TEXT, 'x-amz-meta-family'=>'Woohoo3!'), 'Put bucket fail'\n  end\n\n  def test_04_put_multipart_string\n    test_text = \"\"\n    for i in 1..100000\n      test_text << \"Testing test text #{i}\\n\"\n    end\n    assert @s3.store_object_multipart({:bucket => @bucket, :key => @key4, :data => StringIO.new(test_text)}), 'Put bucket multipart fail'\n  end\n\n  def test_04b_store_object_multipart_stream\n    rd, wr = IO.pipe\n    producer = Thread.new(wr) do |out|\n      for i in 1..100000\n        out.write(\"Testing stream text #{i}\\n\")\n      end\n      out.close\n    end\n    assert @s3.store_object_multipart({:bucket => @bucket, :key => @key5, :data => rd , :part_size => (20*1024*1024)}), 'Put bucket multipart fail'\n    rd.close\n  end\n\n  def test_05_get_and_get_object\n    assert_raise(Rightscale::AwsError) { @s3.get(@bucket, 'undefined/key') }\n    data1 = @s3.get(@bucket, @key1)\n    assert_equal RIGHT_OBJECT_TEXT, data1[:object], \"Object text must be equal to '#{RIGHT_OBJECT_TEXT}'\"\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key1), \"Get_object text must return '#{RIGHT_OBJECT_TEXT}'\"\n    assert_equal 'Woohoo1!', data1[:headers]['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key3), \"Get_object text must return '#{RIGHT_OBJECT_TEXT}'\"\n  end\n\n  def test_06_head\n    assert_equal 'Woohoo1!', @s3.head(@bucket,@key1)['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n  end\n\n\n  def test_07_streaming_get\n    resp = String.new\n    assert_raise(Rightscale::AwsError) do\n      @s3.get(@bucket, 'undefined/key') do |chunk|\n        resp += chunk\n      end\n    end\n\n    resp = String.new\n    data1 = @s3.get(@bucket, @key1) do |chunk|\n      resp += chunk\n    end\n    assert_equal RIGHT_OBJECT_TEXT, resp, \"Object text must be equal to '#{RIGHT_OBJECT_TEXT}'\"\n    assert_equal @s3.get_object(@bucket, @key1), resp, \"Streaming iface must return same as non-streaming\"\n    assert_equal 'Woohoo1!', data1[:headers]['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n  end\n\n  def test_08_keys\n    keys = @s3.list_bucket(@bucket).map{|b| b[:key]}\n    assert_equal keys.size, 5, \"There should be 5 keys\"\n    assert(keys.include?(@key1))\n    assert(keys.include?(@key2))\n    assert(keys.include?(@key3))\n    assert(keys.include?(@key4))\n    assert(keys.include?(@key5))\n  end\n\n  def test_09_copy_key\n    #--- test COPY\n    # copy a key\n    assert @s3.copy(@bucket, @key1, @bucket, @key1_copy)\n    # check it was copied well\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key1_copy), \"copied object must have the same data\"\n    # check meta-headers were copied\n    headers = @s3.head(@bucket, @key1_copy)\n    assert_equal 'Woohoo1!', headers['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n    #--- test REPLACE\n    assert @s3.copy(@bucket, @key1, @bucket, @key1_copy, :replace, 'x-amz-meta-family' => 'oooops!')\n    # check it was copied well\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key1_copy), \"copied object must have the same data\"\n    # check meta-headers were overwrittenn\n    headers = @s3.head(@bucket, @key1_copy)\n    assert_equal 'oooops!', headers['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'oooops!'\"\n  end\n\n  def test_10_move_key\n    # move a key\n    assert @s3.move(@bucket, @key1, @bucket, @key1_new_name)\n    # check it's data was moved correctly\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key1_new_name), \"moved object must have the same data\"\n    # check meta-headers were moved\n    headers = @s3.head(@bucket, @key1_new_name)\n    assert_equal 'Woohoo1!', headers['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n    # check the original key is not exists any more\n    keys = @s3.list_bucket(@bucket).map{|b| b[:key]}\n    assert(!keys.include?(@key1))\n  end\n\n  def test_11_rename_key\n    # rename a key\n    assert @s3.rename(@bucket, @key2, @key2_new_name)\n    # check the new key data\n    assert_equal RIGHT_OBJECT_TEXT, @s3.get_object(@bucket, @key2_new_name), \"moved object must have the same data\"\n    # check meta-headers\n    headers = @s3.head(@bucket, @key2_new_name)\n    assert_equal 'Woohoo2!', headers['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo2!'\"\n    # check the original key is not exists any more\n    keys = @s3.list_bucket(@bucket).map{|b| b[:key]}\n    assert(!keys.include?(@key2))\n  end\n  def test_12_retrieve_object\n    assert_raise(Rightscale::AwsError) { @s3.retrieve_object(:bucket => @bucket, :key => 'undefined/key') }\n    data1 = @s3.retrieve_object(:bucket => @bucket, :key => @key1_new_name)\n    assert_equal RIGHT_OBJECT_TEXT, data1[:object], \"Object text must be equal to '#{RIGHT_OBJECT_TEXT}'\"\n    assert_equal 'Woohoo1!', data1[:headers]['x-amz-meta-family'], \"x-amz-meta-family header must be equal to 'Woohoo1!'\"\n  end\n  def test_13_delete_folder\n    assert_equal 1, @s3.delete_folder(@bucket, 'test').size, \"Only one key(#{@key1}) must be deleted!\"\n  end\n\n  def test_14_delete_bucket\n    assert_raise(Rightscale::AwsError) { @s3.delete_bucket(@bucket) }\n    assert @s3.clear_bucket(@bucket), 'Clear_bucket fail'\n    assert_equal 0, @s3.list_bucket(@bucket).size, 'Bucket must be empty'\n    assert @s3.delete_bucket(@bucket)\n    assert !@s3.list_all_my_buckets.map{|bucket| bucket[:name]}.include?(@bucket), \"#{@bucket} must not exist\"\n  end\n\n\n\n\n\n  #---------------------------\n  # Rightscale::S3 classes\n  #---------------------------\n\n  def test_20_s3\n      # create bucket\n    bucket = @s.bucket(@bucket, true)\n    assert bucket\n      # check that the bucket exists\n    assert @s.buckets.map{|b| b.name}.include?(@bucket)\n      # delete bucket\n    assert bucket.clear\n    assert bucket.delete\n  end\n\n  def test_21_bucket_create_put_get_key\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, true)\n      # check that the bucket exists\n    assert @s.buckets.map{|b| b.name}.include?(@bucket)\n    assert bucket.keys.empty?\n      # put data\n    assert bucket.put(@key3, RIGHT_OBJECT_TEXT, {'family'=>'123456'})\n      # get data and compare\n    assert_equal RIGHT_OBJECT_TEXT, bucket.get(@key3)\n      # get key object\n    key = bucket.key(@key3, true)\n    assert_equal Rightscale::S3::Key, key.class\n    assert       key.exists?\n    assert_equal '123456', key.meta_headers['family']\n  end\n\n  def test_22_keys\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n      # create first key\n    key3 = Rightscale::S3::Key.create(bucket, @key3)\n    key3.refresh\n    assert key3.exists?\n    assert_equal '123456', key3.meta_headers['family']\n      # create second key\n    key2 = Rightscale::S3::Key.create(bucket, @key2)\n    assert !key2.refresh\n    assert !key2.exists?\n    assert_raise(Rightscale::AwsError) { key2.head }\n      # store key\n    key2.meta_headers = {'family'=>'111222333'}\n    assert key2.put(RIGHT_OBJECT_TEXT)\n      # make sure that the key exists\n    assert key2.refresh\n    assert key2.exists?\n    assert key2.head\n      # get its data\n    assert_equal RIGHT_OBJECT_TEXT, key2.get\n      # drop key\n    assert key2.delete\n    assert !key2.exists?\n  end\n\n  def test_23_rename_key\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n    # -- 1 -- (key based rename)\n    # create a key\n    key = bucket.key('test/copy/1')\n    key.put(RIGHT_OBJECT_TEXT)\n    original_key = key.clone\n    assert key.exists?, \"'test/copy/1' should exist\"\n    # rename it\n    key.rename('test/copy/2')\n    assert_equal 'test/copy/2', key.name\n    assert key.exists?, \"'test/copy/2' should exist\"\n    # the original key should not exist\n    assert !original_key.exists?, \"'test/copy/1' should not exist\"\n    # -- 2 -- (bucket based rename)\n    bucket.rename_key('test/copy/2', 'test/copy/3')\n    assert  bucket.key('test/copy/3').exists?, \"'test/copy/3' should exist\"\n    assert !bucket.key('test/copy/2').exists?, \"'test/copy/2' should not exist\"\n  end\n\n  def test_24_copy_key\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n    # -- 1 -- (key based copy)\n    # create a key\n    key = bucket.key('test/copy/10')\n    key.put(RIGHT_OBJECT_TEXT)\n    # make copy\n    new_key = key.copy('test/copy/11')\n    # make sure both the keys exist and have a correct data\n    assert key.exists?,     \"'test/copy/10' should exist\"\n    assert new_key.exists?, \"'test/copy/11' should exist\"\n    assert_equal RIGHT_OBJECT_TEXT, key.get\n    assert_equal RIGHT_OBJECT_TEXT, new_key.get\n    # -- 2 -- (bucket based copy)\n    bucket.copy_key('test/copy/11', 'test/copy/12')\n    assert bucket.key('test/copy/11').exists?, \"'test/copy/11' should exist\"\n    assert bucket.key('test/copy/12').exists?, \"'test/copy/12' should exist\"\n    assert_equal RIGHT_OBJECT_TEXT, bucket.key('test/copy/11').get\n    assert_equal RIGHT_OBJECT_TEXT, bucket.key('test/copy/12').get\n  end\n\n  def test_25_move_key\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n    # -- 1 -- (key based copy)\n    # create a key\n    key = bucket.key('test/copy/20')\n    key.put(RIGHT_OBJECT_TEXT)\n    # move\n    new_key = key.move('test/copy/21')\n    # make sure both the keys exist and have a correct data\n    assert !key.exists?,    \"'test/copy/20' should not exist\"\n    assert new_key.exists?, \"'test/copy/21' should exist\"\n    assert_equal RIGHT_OBJECT_TEXT, new_key.get\n    # -- 2 -- (bucket based copy)\n    bucket.copy_key('test/copy/21', 'test/copy/22')\n    assert bucket.key('test/copy/21').exists?, \"'test/copy/21' should not exist\"\n    assert bucket.key('test/copy/22').exists?, \"'test/copy/22' should exist\"\n    assert_equal RIGHT_OBJECT_TEXT, bucket.key('test/copy/22').get\n  end\n\n  def test_26_save_meta\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n    # create a key\n    key = bucket.key('test/copy/30')\n    key.put(RIGHT_OBJECT_TEXT)\n    assert key.meta_headers.right_blank?\n    # store some meta keys\n    meta = {'family' => 'oops','race' => 'troll'}\n    assert_equal meta, key.save_meta(meta)\n    # reload meta\n    assert_equal meta, key.reload_meta\n  end\n\n  def test_27_clear_delete\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n      # add another key\n    bucket.put(@key2, RIGHT_OBJECT_TEXT)\n      # delete 'folder'\n    assert_equal 1, bucket.delete_folder(@key1).size\n      # delete\n    assert_raise(Rightscale::AwsError) { bucket.delete }\n    bucket.delete(:force => true)\n  end\n\n    # Grantees\n\n  def test_30_create_bucket\n    bucket = @s.bucket(@bucket, true, 'public-read')\n    assert bucket\n  end\n\n  def test_31_list_grantees\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n      # get grantees list\n    grantees = bucket.grantees\n      # check that the grantees count equal to 2 (root, AllUsers)\n    assert_equal 2, grantees.size\n  end\n\n  def test_32_grant_revoke_drop\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n      # Take 'AllUsers' grantee\n    grantee = Rightscale::S3::Grantee.new(bucket,'http://acs.amazonaws.com/groups/global/AllUsers')\n      # Check exists?\n    assert grantee.exists?\n      # Add grant as String\n    assert grantee.grant('WRITE')\n      # Add grants as Array\n    assert grantee.grant(['READ_ACP', 'WRITE_ACP'])\n      # Check perms count\n    assert_equal 4, grantee.perms.size\n      # revoke 'WRITE_ACP'\n    assert grantee.revoke('WRITE_ACP')\n      # Check manual perm removal method\n    grantee.perms -= ['READ_ACP']\n    grantee.apply\n    assert_equal 2, grantee.perms.size\n      # Check grantee removal if it has no permissions\n    assert grantee.perms = []\n    assert grantee.apply\n    assert !grantee.exists?\n      # Check multiple perms assignment\n    assert grantee.grant('FULL_CONTROL', 'READ', 'WRITE')\n    assert_equal ['FULL_CONTROL','READ','WRITE'].sort, grantee.perms.sort\n      # Check multiple perms removal\n    assert grantee.revoke('FULL_CONTROL', 'WRITE')\n    assert_equal ['READ'], grantee.perms\n      # check 'Drop' method\n    assert grantee.drop\n    assert !grantee.exists?\n    assert_equal 1, bucket.grantees.size\n      # Delete bucket\n    bucket.delete(:force => true)\n  end\n\n  def test_33_key_grantees\n      # Create bucket\n    bucket = @s.bucket(@bucket, true)\n      # Create key\n    key = bucket.key(@key1)\n    assert key.put(RIGHT_OBJECT_TEXT, 'public-read')\n      # Get grantees list (must be == 2)\n    grantees = key.grantees\n    assert grantees\n    assert_equal 2, grantees.size\n      # Take one of grantees and give him 'Write' perms\n    grantee = grantees[0]\n    assert grantee.grant('WRITE')\n      # Drop grantee\n    assert grantee.drop\n      # Drop bucket\n    bucket.delete(:force => true)\n  end\n\n  def test_34_bucket_create_put_with_perms\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, true)\n      # check that the bucket exists\n    assert @s.buckets.map{|b| b.name}.include?(@bucket)\n    assert bucket.keys.empty?\n      # put data (with canned ACL)\n    assert bucket.put(@key1, RIGHT_OBJECT_TEXT, {'family'=>'123456'}, \"public-read\")\n      # get data and compare\n    assert_equal RIGHT_OBJECT_TEXT, bucket.get(@key1)\n      # get key object\n    key = bucket.key(@key1, true)\n    assert_equal Rightscale::S3::Key, key.class\n    assert       key.exists?\n    assert_equal '123456', key.meta_headers['family']\n  end\n\n  def test_35_key_put_with_perms\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n      # create first key\n    key1 = Rightscale::S3::Key.create(bucket, @key1)\n    key1.refresh\n    assert key1.exists?\n    assert key1.put(RIGHT_OBJECT_TEXT, \"public-read\")\n      # get its data\n    assert_equal RIGHT_OBJECT_TEXT, key1.get\n      # drop key\n    assert key1.delete\n    assert !key1.exists?\n  end\n\n  def test_36_set_amazon_problems\n    original_problems = RightAws::S3Interface.amazon_problems\n    assert(original_problems.length > 0)\n    RightAws::S3Interface.amazon_problems= original_problems << \"A New Problem\"\n    new_problems = RightAws::S3Interface.amazon_problems\n    assert_equal(new_problems, original_problems)\n\n    RightAws::S3Interface.amazon_problems= nil\n    assert_nil(RightAws::S3Interface.amazon_problems)\n  end\n\n  def test_37_access_logging\n    bucket = Rightscale::S3::Bucket.create(@s, @bucket, false)\n    targetbucket = Rightscale::S3::Bucket.create(@s, @bucket2, true)\n      # Take 'AllUsers' grantee\n    grantee = Rightscale::S3::Grantee.new(targetbucket,'http://acs.amazonaws.com/groups/s3/LogDelivery')\n\n    assert grantee.grant(['READ_ACP', 'WRITE'])\n\n    assert bucket.enable_logging(:targetbucket => targetbucket, :targetprefix => \"loggylogs/\")\n    sleep 10\n\n    assert_equal({:enabled => true, :targetbucket => @bucket2, :targetprefix => \"loggylogs/\"}, bucket.logging_info)\n    \n    assert bucket.disable_logging\n\n      # check 'Drop' method\n    assert grantee.drop\n  end\n\n  def test_40_delete_buckets\n    Rightscale::S3::Bucket.create(@s, @bucket,  false).delete(:force => true)\n    Rightscale::S3::Bucket.create(@s, @bucket2, false).delete(:force => true)\n  end\n\n  def test_41_add_cache_control_response_parameter\n    @cache_control = 'max-age=3600'\n    perform_request( 'response-cache-control' => @cache_control ) do |response|\n       assert_equal response['Cache-Control'], @cache_control\n    end\n  end\n\n  def test_42_add_cache_control_and_content_type_and_content_disposition\n    @cache_control = 'max-age=3600'\n    @content_type = 'text/plain'\n    @content_disposition = 'attachment; filename=sample.txt'\n    perform_request(\n        'response-cache-control' => @cache_control,\n        'response-content-type' => @content_type,\n        'response-content-disposition' => @content_disposition\n    ) do |response|\n      assert_equal response['Cache-Control'], @cache_control\n      assert_equal response['Content-Type'], @content_type\n      assert_equal response['Content-Disposition'], @content_disposition\n    end\n  end\n\n  def test_43_add_expires_and_content_type_and_content_disposition\n    @expires = 'Sun, 26 Jun 2011 02:58:26 GMT'\n    @content_type = 'text/plain'\n    @content_disposition = 'attachment; filename=sample.txt'\n    perform_request(\n        'response-expires' => @expires,\n        'response-content-type' => @content_type,\n        'response-content-disposition' => @content_disposition\n    ) do |response|\n      assert_equal response['Expires'], @expires\n      assert_equal response['Content-Type'], @content_type\n      assert_equal response['Content-Disposition'], @content_disposition\n    end\n  end\n\n  def test_44_delete_multiple\n    bucket = RightAws::S3::Bucket.create(@s, @bucket, true)\n\n    key1 = Rightscale::S3::Key.create(bucket, @key1)\n    key2 = Rightscale::S3::Key.create(bucket, @key2)\n    key3 = Rightscale::S3::Key.create(bucket, @key3)\n\n    assert @s3.put(@bucket, @key1, RIGHT_OBJECT_TEXT), 'Put bucket fail'\n    assert @s3.put(@bucket, @key2, RIGHT_OBJECT_TEXT), 'Put bucket fail'\n    assert @s3.put(@bucket, @key3, RIGHT_OBJECT_TEXT), 'Put bucket fail'\n\n    key1.refresh\n    key2.refresh\n    key3.refresh\n\n    assert key1.exists?\n    assert key2.exists?\n    assert key3.exists?\n\n    result = @s3.delete_multiple(@bucket, [@key1, @key2, @key3])\n    assert result.empty?\n\n    key1.refresh\n    key2.refresh\n    key3.refresh\n\n    assert !key1.exists?\n    assert !key2.exists?\n    assert !key3.exists?\n  end\n\n  def test_45_delete_multiple_more_than_1000_objects\n    n = 1200\n    keys = (1..n).map { |i| \"key-#{i}\"}\n\n    keys.each do |key|\n      assert @s3.put(@bucket, key, RIGHT_OBJECT_TEXT), 'Put bucket fail'\n    end\n\n    result = @s3.delete_multiple(@bucket, keys)\n    assert result.empty?\n\n    keys_after = @s3.list_bucket(@bucket).map { |obj| obj[:key] }\n\n    keys.each do |key|\n      assert !keys_after.include?(key)\n    end\n  end\n\n  private\n\n  def request( uri )\n    url = URI.parse( uri )\n\n    http = Net::HTTP.new(url.host, 80)\n#    http.use_ssl = true\n#    http.verify_mode = OpenSSL::SSL::VERIFY_PEER\n    http.request(Net::HTTP::Get.new( url.request_uri ))\n  end\n\n  def perform_request( headers, &block )\n    @s3.create_bucket( @bucket )\n    @s3.put( @bucket, @key2, RIGHT_OBJECT_TEXT )\n    response = request( @s3.get_link( @bucket, @key2, nil, {}, headers ) )\n    block.call( response )\n    @s3.force_delete_bucket(@bucket)\n  end\n\nend\n"
  },
  {
    "path": "test/s3/test_right_s3_stubbed.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestS3Stubbed < Test::Unit::TestCase\n\n  RIGHT_OBJECT_TEXT     = 'Right test message'\n  \n  def setup\n    @s3     = Rightscale::S3Interface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @bucket = 'right_s3_awesome_test_bucket'\n    @key1   = 'test/woohoo1'\n    @key2   = 'test1/key/woohoo2'\n    @s      = Rightscale::S3.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    Rightscale::HttpConnection.reset\n  end\n\n  # Non-remote tests: these use the stub version of Rightscale::HTTPConnection\n  def test_101_create_bucket\n    Rightscale::HttpConnection.push(409, 'The named bucket you tried to create already exists')\n    Rightscale::HttpConnection.push(500, 'We encountered an internal error.  Please try again.')\n    Rightscale::HttpConnection.push(500, 'We encountered an internal error.  Please try again.')\n    assert_raise RightAws::AwsError do\n      @s3.create_bucket(@bucket)\n    end\n  end\n\n  def test_102_list_all_my_buckets_failure\n    Rightscale::HttpConnection.push(401, 'Unauthorized') \n    assert_raise RightAws::AwsError do\n      @s3.list_all_my_buckets\n    end\n  end\n\n  def test_103_list_empty_bucket\n    Rightscale::HttpConnection.push(403, 'Access Denied') \n    assert_raise RightAws::AwsError do\n      @s3.list_bucket(@bucket)\n    end\n  end\n  \n  def test_104_put\n    Rightscale::HttpConnection.push(400, 'Your proposed upload exceeds the maximum allowed object size.') \n    Rightscale::HttpConnection.push(400, 'The Content-MD5 you specified was an invalid.') \n    Rightscale::HttpConnection.push(409, 'Please try again') \n    assert_raise RightAws::AwsError do\n      assert @s3.put(@bucket, @key1, RIGHT_OBJECT_TEXT, 'x-amz-meta-family'=>'Woohoo1!'), 'Put bucket fail'\n    end\n    assert_raise RightAws::AwsError do\n      assert @s3.put(@bucket, @key2, RIGHT_OBJECT_TEXT, 'x-amz-meta-family'=>'Woohoo2!'), 'Put bucket fail'\n    end\n  end\n  \n  def test_105_get_and_get_object\n    Rightscale::HttpConnection.push(404, 'not found') \n    assert_raise(Rightscale::AwsError) { @s3.get(@bucket, 'undefined/key') }\n  end\n  \n  def test_106_head\n    Rightscale::HttpConnection.push(404, 'Good Luck!') \n    assert_raise RightAws::AwsError do\n      @s3.head(@bucket,@key1)\n    end\n  end\n\n\n  def test_109_delete_bucket\n    Rightscale::HttpConnection.push(403, 'Good Luck!') \n    assert_raise(Rightscale::AwsError) { @s3.delete_bucket(@bucket) }\n  end\n  \n  def test_115_copy_key\n    \n    Rightscale::HttpConnection.push(500, 'not found') \n    #--- test COPY\n    # copy a key\n    assert_raise RightAws::AwsError do\n      @s3.copy(@bucket, @key1, @bucket, @key1_copy)\n    end\n    \n  end\n\n  def test_116_move_key\n    # move a key\n    Rightscale::HttpConnection.push(413, 'not found') \n    assert_raise RightAws::AwsError do\n      @s3.move(@bucket, @key1, @bucket, @key1_new_name)\n    end\n  end\n\n  def test_117_rename_key\n    # rename a key\n    Rightscale::HttpConnection.push(500, 'not found') \n    assert_raise RightAws::AwsError do\n      @s3.rename(@bucket, @key2, @key2_new_name)\n    end\n  end\n\nend\n"
  },
  {
    "path": "test/sdb/test_active_sdb.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSdb < Test::Unit::TestCase\n  DOMAIN_PREFIX = 'right_sdb_awesome_test'\n  CLIENT_DOMAIN = \"#{DOMAIN_PREFIX}_client\"\n  PERSON_DOMAIN = \"#{DOMAIN_PREFIX}_person\"\n\n  class Client < RightAws::ActiveSdb::Base\n    set_domain_name CLIENT_DOMAIN\n  end\n\n  class Person < RightAws::ActiveSdb::Base\n    set_domain_name PERSON_DOMAIN\n\n    columns do\n      name\n      email\n      score         :Integer\n      is_active     :Boolean\n      registered_at :DateTime\n      created_at    :DateTime, :default => lambda{ Time.now }\n    end\n  end\n\n  def setup\n    STDOUT.sync  = true\n    @clients = [ \n      { 'name' => 'Bush',     'country' => 'USA',    'gender' => 'male',   'expiration' => '2009', 'post' => 'president' },\n      { 'name' => 'Putin',    'country' => 'Russia', 'gender' => 'male',   'expiration' => '2008', 'post' => 'president' },\n      { 'name' => 'Medvedev', 'country' => 'Russia', 'gender' => 'male',   'expiration' => '2012', 'post' => 'president' },\n      { 'name' => 'Mary',     'country' => 'USA',    'gender' => 'female', 'hobby' => ['patchwork', 'bundle jumping'] },\n      { 'name' => 'Sandy',    'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking'] },\n      { 'name' => 'Mary',     'country' => 'Russia', 'gender' => 'female', 'hobby' => ['flowers', 'cats', 'cooking'] } ]\n    @people = [\n      { :name => 'Yetta E. Andrews',    :email => 'nulla.facilisis@metus.com', :score => 100, :is_active => true,  :registered_at => Time.local(2000, 1, 1) },\n      { :name => 'Sybill O. Olson',     :email => 'nisi.Aenean.eget@urna.com', :score =>  87, :is_active => true,  :registered_at => Time.local(2008, 7, 6) },\n      { :name => 'Isabelle K. Flynn',   :email => 'velit@amet.com',            :score =>  98, :is_active => false, :registered_at => Time.local(2003, 5, 20) },\n      { :name => 'Juliet H. Witt',      :email => 'egestas@pretiumaliquet.ca', :score =>  72, :is_active => true,  :registered_at => Time.local(2007, 2, 28) },\n      { :name => 'Lucy N. Christensen', :email => 'lacus.v12@stu.edu',         :score =>  94, :is_active => false, :registered_at => Time.local(2005, 10, 26) }\n    ]\n    RightAws::ActiveSdb.establish_connection(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  end\n\n  SDB_DELAY = 3\n  \n  def wait(delay, msg='')\n    print \"     waiting #{delay} seconds: #{msg}\"\n    while delay>0 do\n      delay -= 1\n      print '.'\n      sleep 1\n    end\n    puts\n  end\n\n  #---------------------------\n  # Rightscale::SdbInterface\n  #---------------------------\n\n  def test_00_delete_domain\n    assert RightAws::ActiveSdb.delete_domain(CLIENT_DOMAIN)\n    assert RightAws::ActiveSdb.delete_domain(PERSON_DOMAIN)\n    wait SDB_DELAY, 'test 00: after domain deletion'\n  end\n  \n  def test_01_create_domain\n    # check that domain does not exist\n    assert !RightAws::ActiveSdb.domains.include?(CLIENT_DOMAIN)\n    # create domain\n    assert Client.create_domain\n    assert Person.create_domain\n    wait SDB_DELAY, 'test 01: after domain creation'\n    # check that we have received new domain from Amazin\n    assert RightAws::ActiveSdb.domains.include?(CLIENT_DOMAIN)\n  end\n\n  def test_02_create_items\n    # check that DB is empty\n    clients = Client.find(:all)\n    assert clients.right_blank?\n    # put some clients there\n    @clients.each do |client|\n      Client.create client\n    end\n    wait SDB_DELAY, 'test 02: after clients creation'\n    # check that DB has all the clients we just putted\n    clients = Client.find(:all)\n    assert_equal @clients.size, clients.size\n  end\n  \n  def test_03_create_and_save_new_item\n    # get the db\n    old_clients = Client.find(:all)\n    # create new client\n    new_client = Client.new('country' => 'unknown', 'dummy' => 'yes')\n    wait SDB_DELAY, 'test 03: after in-memory client creation'\n    # get the db and ensure we created the client in-memory only\n    assert_equal old_clients.size, Client.find(:all).size\n    # put the client to DB\n    new_client.save\n    wait SDB_DELAY, 'test 03: after in-memory client saving'\n    # get all db again and compare to original list\n    assert_equal old_clients.size+1, Client.find(:all).size\n  end\n\n  def test_04_find_all\n    # retrieve all the DB, make sure all are in place\n    clients = Client.find(:all)\n    ids = clients.map{|client| client.id }[0..1]\n    assert_equal @clients.size + 1, clients.size\n    # retrieve all presidents (must find: Bush, Putin, Medvedev)\n    assert_equal 3, Client.find(:all, :conditions => [\"[?=?]\",'post','president']).size\n    # retrieve all russian presidents (must find: Putin, Medvedev)\n    assert_equal 2, Client.find(:all, :conditions => [\"['post'=?] intersection ['country'=?]\",'president', 'Russia']).size\n    # retrieve all russian presidents and all women (must find: Putin, Medvedev, 2 Maries and Sandy)\n    assert_equal 5, Client.find(:all, :conditions => [\"['post'=?] intersection ['country'=?] union ['gender'=?]\",'president', 'Russia','female']).size\n    # find all rissian presidents Bushes\n    assert_equal 0, Client.find(:all, :conditions => [\"['post'=?] intersection ['country'=?] intersection ['name'=?]\",'president', 'Russia','Bush']).size\n    # --- find by ids\n    # must find 1 rec (by rec id) and return it\n    assert_equal ids.first, Client.find(ids.first).id\n    # must find 1 rec (by one item array) and return an array\n    assert_equal ids.first, Client.find([ids.first]).first.id\n    # must find 2 recs (by a list of comma separated ids) and return an array\n    assert_equal ids.size, Client.find(*ids).size\n    # must find 2 recs (by an array of ids) and return an array\n    assert_equal ids.size, Client.find(ids).size\n    ids << 'dummy_id'\n    # must raise an error when getting unexistent record\n    assert_raise(RightAws::ActiveSdb::ActiveSdbError) do \n      Client.find(ids)\n    end\n    # find one record by unknown id\n    assert_raise(RightAws::ActiveSdb::ActiveSdbError) do\n      Client.find('dummy_id')\n    end\n  end\n\n  def test_05_find_first\n    # find any record\n    assert Client.find(:first)\n    # find any president\n    assert Client.find(:first, :conditions => [\"[?=?]\",'post','president'])\n    # find any rissian president\n    assert Client.find(:first, :conditions => [\"['post'=?] intersection ['country'=?]\",'president','Russia'])\n    # find any unexistent record\n    assert_nil Client.find(:first, :conditions => [\"['post'=?] intersection ['country'=?]\",'president','Rwanda'])\n  end\n\n  def test_06_find_all_by_helpers\n    # find all Bushes\n    assert_equal 1, Client.find_all_by_name('Bush').size\n    # find all russian presidents\n    assert_equal 2, Client.find_all_by_post_and_country('president','Russia').size\n    # find all women in USA that love flowers\n    assert_equal 2, Client.find_all_by_gender_and_country_and_hobby('female','Russia','flowers').size\n    # order and auto_load:\n    clients = Client.find_all_by_post('president', :order => 'name', :auto_load => true)\n    assert_equal [['Bush'], ['Medvedev'], ['Putin']], clients.map{|c| c['name']}\n    clients = Client.find_all_by_post('president', :order => 'name desc', :auto_load => true)\n    assert_equal [['Putin'], ['Medvedev'], ['Bush']], clients.map{|c| c['name']}\n  end\n  \n  def test_07_find_by_helpers\n    # find mr Bush\n    assert Client.find_by_name('Bush')\n    # find any russian president\n    assert Client.find_by_post_and_country('president','Russia')\n    # find Mary in Russia that loves flowers\n    # order and auto_load:\n    assert_equal ['Bush'],  Client.find_by_post('president', :order => 'name',      :auto_load => true)['name']\n    assert_equal ['Putin'], Client.find_by_post('president', :order => 'name desc', :auto_load => true)['name']\n  end\n\n  def test_08_reload\n    putin = Client.find_by_name('Putin')\n    # attributes must be empty until reload (except 'id' field)\n    assert_nil putin['name']\n    assert_nil putin['country']\n    assert_nil putin['gender']\n    assert_nil putin['expiration']\n    assert_nil putin['post']\n    # reloaded attributes must have 5 items + id\n    putin.reload\n    assert_equal 6, putin.attributes.size\n    # check all attributes\n    assert_equal ['Putin'],     putin['name']\n    assert_equal ['Russia'],    putin['country']\n    assert_equal ['male'],      putin['gender']\n    assert_equal ['2008'],      putin['expiration']\n    assert_equal ['president'], putin['post']\n  end\n\n  def test_09_select\n    # select all records\n    assert_equal 7, Client.select(:all).size\n    # LIMIT\n    # 1 record\n    assert Client.select(:first).is_a?(Client)\n    # select 2 recs\n    assert_equal 2, Client.select(:all, :limit => 2).size\n    # ORDER\n    # select all recs ordered by 'expration' (must find only recs where 'expration' attribute presents)\n    result = Client.select(:all, :order => 'expiration')\n    assert_equal 3, result.size\n    assert_equal ['2008', '2009', '2012'], result.map{ |c| c['expiration'] }.flatten\n    # desc order\n    result = Client.select(:all, :order => 'expiration desc')\n    assert_equal ['2012', '2009', '2008'], result.map{ |c| c['expiration'] }.flatten\n    # CONDITIONS\n    result = Client.select(:all, :conditions => [\"expiration >= ?\", 2009], :order => 'name')\n    assert_equal ['Bush', 'Medvedev'], result.map{ |c| c['name'] }.flatten\n    result = Client.select(:all, :conditions => \"hobby='flowers' AND gender='female'\", :order => 'name')\n    assert_equal ['Mary', 'Sandy'], result.map{ |c| c['name'] }.flatten\n    # SELECT\n    result = Client.select(:all, :select => 'hobby', :conditions => \"gender IS NOT NULL\", :order => 'name')\n    hobbies = result.map{|c| c['hobby']}\n    # must return all recs\n    assert_equal 6, result.size\n    # but anly 3 of them have this field set\n    assert_equal 3, hobbies.compact.size\n  end\n\n  def test_10_select_by\n    assert_equal 2, Client.select_all_by_hobby('flowers').size\n    assert_equal 2, Client.select_all_by_hobby_and_country('flowers', 'Russia').size\n    assert_equal ['Putin'], Client.select_by_post_and_expiration('president','2008')['name']\n  end\n  \n  def test_11_save_and_put\n    putin = Client.find_by_name('Putin')\n    putin.reload\n    putin['hobby'] = 'ski'\n    # SAVE method (replace values)\n    putin.save\n    wait SDB_DELAY, 'test 09: after saving'\n    # check that DB was updated with 'ski'\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['ski'], new_putin['hobby']\n    # replace hobby\n    putin['hobby'] = 'dogs'\n    putin.save\n    wait SDB_DELAY, 'test 09: after saving'\n    # check that 'ski' in DB was replaced by 'dogs'\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['dogs'], new_putin['hobby']\n    # PUT method (add values)\n    putin['hobby'] = 'ski'\n    putin.put\n    wait SDB_DELAY, 'test 09: after putting'\n    # check that 'ski' was added to 'dogs'\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['dogs', 'ski'], new_putin['hobby'].sort\n  end\n\n  def test_12_save_and_put_attributes\n    putin = Client.find_by_name('Putin')\n    putin.reload\n    # SAVE method (replace values)\n    putin.save_attributes('language' => 'russian')\n    wait SDB_DELAY, 'test 10: after save_attributes'\n    # check that DB was updated with 'ski'\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['russian'], new_putin['language']\n    # replace 'russian' by 'german'\n    putin.save_attributes('language' => 'german')\n    wait SDB_DELAY, 'test 10: after save_attributes'\n    # check that 'russian' in DB was replaced by 'german'\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['german'], new_putin['language']\n    # PUT method (add values)\n    putin.put_attributes('language' => ['russian', 'english'])\n    wait SDB_DELAY, 'test 10: after put_attributes'\n    # now Putin must know all the languages\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['english', 'german', 'russian'], new_putin['language'].sort\n  end\n  \n  def test_13_delete\n    putin = Client.find_by_name('Putin')\n    putin.reload\n    # --- delete_values\n    # remove an unknown attribute\n    # should return an empty hash\n    assert_equal( {}, putin.delete_values('undefined_attribute' => 'ohoho'))\n    # remove 2 languages\n    lang_hash = {'language' => ['english', 'german']}\n    assert_equal lang_hash, putin.delete_values(lang_hash)\n    wait SDB_DELAY, 'test 11: after put_attributes'\n    # now Putin must know only russian lang\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert ['russian'], new_putin['language'].sort\n    # --- delete_attributes\n    putin.delete_attributes('language', 'hobby')\n    wait SDB_DELAY, 'test 11: after delete_attributes'\n    # trash hoddy and langs\n    new_putin = Client.find_by_name('Putin')\n    new_putin.reload\n    assert_nil new_putin['language']\n    assert_nil new_putin['hobby']\n    # --- delete item\n    putin.delete\n    wait SDB_DELAY, 'test 11: after delete item'\n    assert_nil Client.find_by_name('Putin')\n  end\n\n  def test_14_dynamic_attribute_accessors\n    bush = Client.find_by_name('Bush')\n    bush.reload\n    assert_nothing_raised {\n      assert_equal ['male'], bush.gender\n      bush.name = 'George'\n      assert_equal ['George'], bush.name\n    }\n    assert_raise(NoMethodError) { bush.flarble }\n  end\n\n  def test_15_column_emulation\n    @people.each do |person|\n      Person.create person\n    end\n    wait SDB_DELAY, 'test 15: after people creation'\n    person = Person.find_by_email 'nulla.facilisis@metus.com'\n    person.reload\n\n    assert_equal 'Yetta E. Andrews', person.name\n    assert_equal DateTime, person.registered_at.class\n    assert person['registered_at'].is_a?(DateTime)\n    assert person[:registered_at].is_a?(DateTime)\n\n    assert ! person[:created_at].nil?\n    assert_equal DateTime, person.created_at.class\n    assert person['created_at'].is_a?(DateTime)\n    assert person[:created_at].is_a?(DateTime)\n\n    assert person.is_active\n\n    assert_equal 100, person.score\n  end\n\n  def test_999_delete_domain\n    assert Client.delete_domain\n    assert Person.delete_domain\n    wait SDB_DELAY, 'test 999: after delete domain'\n    assert_raise(Rightscale::AwsError) do\n      Client.find :all\n    end\n  end\n    \nend"
  },
  {
    "path": "test/sdb/test_batch_put_attributes.rb",
    "content": "# -*- coding: utf-8 -*-\nrequire File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSdb < Test::Unit::TestCase\n\n  def setup\n    STDOUT.sync  = true\n    @domain = 'right_sdb_awesome_test_domain'\n    @attributes = {\n      'a' => { 'foo' => '123' },\n      'b' => { 'bar' => '456' }\n    }\n    # Interface instance\n    @sdb = Rightscale::SdbInterface.new\n    @sdb.delete_domain(@domain)\n    wait(SDB_DELAY, \"after removing domain\")\n    @sdb.create_domain(@domain)\n    wait(SDB_DELAY, \"after recreating domain\")\n  end\n\n  SDB_DELAY = 2\n  \n  def wait(delay, msg='')\n    print \"waiting #{delay} seconds #{msg}\"\n    while delay>0 do\n      delay -= 1\n      print '.'\n      sleep 1\n    end\n    puts\n  end\n\n  def test_batch_put_attributes\n    @sdb.batch_put_attributes(@domain, @attributes)\n    wait(SDB_DELAY, \"after putting attributes\")\n    a = @sdb.get_attributes(@domain, 'a')[:attributes]\n    b = @sdb.get_attributes(@domain, 'b')[:attributes]\n    assert_equal( {'foo' => ['123']}, a)\n    assert_equal( {'bar' => ['456']}, b)\n    \n    # Replace = false\n    @sdb.batch_put_attributes(@domain, { 'a' => {'foo' => ['789']}})\n    wait(SDB_DELAY, \"after putting attributes\") \n    a = @sdb.get_attributes(@domain, 'a')[:attributes]\n    assert_equal ['123', '789'], a['foo'].sort\n\n    # Replace = true\n    @sdb.batch_put_attributes(@domain, {'b' => {'bar' => ['789']}}, true)\n    wait(SDB_DELAY, \"after putting attributes\") \n    b = @sdb.get_attributes(@domain, 'b')[:attributes]\n    assert_equal ['789'], b['bar'].sort\n\n  end\nend\n"
  },
  {
    "path": "test/sdb/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\nrequire 'sdb/active_sdb'\n"
  },
  {
    "path": "test/sdb/test_right_sdb.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSdb < Test::Unit::TestCase\n\n  def setup\n    STDOUT.sync  = true\n    @domain = 'right_sdb_awesome_test_domain'\n    @item   = 'toys'\n    @attr   = { 'Jon' => %w{beer car} }\n    # Interface instance\n    @sdb    = Rightscale::SdbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  end\n\n  SDB_DELAY = 7\n  \n  def wait(delay, msg='')\n    print \"waiting #{delay} seconds #{msg}\"\n    while delay>0 do\n      delay -= 1\n      print '.'\n      sleep 1\n    end\n    puts\n  end\n\n  #---------------------------\n  # Rightscale::SdbInterface\n  #---------------------------\n\n  def test_00_delete_domain\n    # delete the domain to reset all the things\n    assert @sdb.delete_domain(@domain), 'delete_domain fail'\n    wait SDB_DELAY, 'after domain deletion'\n  end\n  \n  def test_01_create_domain\n    # check that domain does not exist\n    assert !@sdb.list_domains[:domains].include?(@domain)\n    # create domain\n    assert @sdb.create_domain(@domain), 'create_domain fail'\n    wait SDB_DELAY, 'after domain creation'\n    # check that we have received new domain from Amazin\n    assert @sdb.list_domains[:domains].include?(@domain)\n  end\n\n  def test_02_put_attributes\n    # put attributes\n    assert @sdb.put_attributes(@domain, @item, @attr)\n    wait SDB_DELAY, 'after putting attributes'\n  end\n  \n  def test_03_get_attributes\n    # get attributes\n    values = Array(@sdb.get_attributes(@domain, @item)[:attributes]['Jon']).sort\n    # compare to original list\n    assert_equal values, @attr['Jon'].sort\n  end\n\n  def test_04_add_attributes\n    # add new attribute\n    new_value = 'girls'\n    @sdb.put_attributes @domain, @item, {'Jon' => new_value}\n    wait SDB_DELAY, 'after putting attributes'\n    # get attributes ('girls' must be added to already existent attributes)\n    values = Array(@sdb.get_attributes(@domain, @item)[:attributes]['Jon']).sort\n    assert_equal values, (@attr['Jon'] << new_value).sort\n  end\n  \n  def test_05_replace_attributes\n    # replace attributes\n    @sdb.put_attributes @domain, @item, {'Jon' => 'pub'}, :replace\n    wait SDB_DELAY, 'after replacing attributes'\n    # get attributes (all must be removed except of 'pub')\n    values = @sdb.get_attributes(@domain, @item)[:attributes]['Jon']\n    assert_equal values, ['pub']\n  end\n  \n  def test_06_delete_attribute\n    # add value 'girls' and 'vodka' to 'Jon'\n    @sdb.put_attributes @domain, @item, {'Jon' => ['girls','vodka']}\n    wait SDB_DELAY, 'after adding attributes'\n    # get attributes ('girls' and 'vodka' must be added 'pub')\n    values = Array(@sdb.get_attributes(@domain, @item)[:attributes]['Jon']).sort\n    assert_equal values, ['girls', 'pub', 'vodka']\n    # delete a single value 'girls' from attribute 'Jon'\n    @sdb.delete_attributes @domain, @item, 'Jon' => ['girls']\n    wait SDB_DELAY, 'after the deletion of attribute'\n    # get attributes ('girls' must be removed)\n    values = @sdb.get_attributes(@domain, @item)[:attributes]['Jon']\n    assert_equal values, ['pub', 'vodka']\n    # delete all values from attribute 'Jon'\n    @sdb.delete_attributes @domain, @item, ['Jon']\n    wait SDB_DELAY, 'after the deletion of attributes'\n    # get attributes (values must be empty)\n    values = @sdb.get_attributes(@domain, @item)[:attributes]['Jon']\n    assert_equal values, nil\n  end\n\n  def test_07_delete_item\n    @sdb.put_attributes @domain, @item, {'Volodya' => ['girls','vodka']}\n    wait SDB_DELAY, 'after adding attributes'\n    # get attributes ('girls' and 'vodka' must be there)\n    values = Array(@sdb.get_attributes(@domain, @item)[:attributes]['Volodya']).sort\n    assert_equal values, ['girls', 'vodka']\n    # delete an item\n    @sdb.delete_attributes @domain, @item\n    wait SDB_DELAY, 'after deleting attributes'\n    # get attributes (values must be empty)\n    values = @sdb.get_attributes(@domain, @item)[:attributes]['Volodya']\n    assert_equal values, nil\n  end  \n  \n  def test_08_query\n    # add some values for query\n    @sdb.put_attributes @domain, @item, {'Jon' => ['girls','vodka']}\n    wait SDB_DELAY, 'after adding attributes'\n    items = @sdb.query(@domain, ['[?=?]', 'Jon','vodka'])[:items]\n    assert_equal items.size, 1\n    assert_equal items.first, @item\n  end\n  \n  def test_09_signature_version_0 \n    sdb    = Rightscale::SdbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0') \n    item   = 'toys' \n    # TODO: need to change the below test.  I think Juergen's intention was to include some umlauts in the values\n    # put attributes \n    # mhhh... Not sure how to translate this: hÃ¶lzchehn klÃ¶tzchen grÃŒnspan buÃe... Lets assume this is:\n    attributes = { 'Jurgen' => %w{kitten puppy chickabiddy piglet} } \n    assert sdb.put_attributes(@domain, item, attributes) \n    wait SDB_DELAY, 'after putting attributes' \n    # get attributes \n    values = Array(sdb.get_attributes(@domain, item)[:attributes]['Jurgen']).sort\n    # compare to original list \n    assert_equal values, attributes['Jurgen'].sort \n    # check that the request has correct signature version\n    assert sdb.last_request.path.include?('SignatureVersion=0')\n  end \n\n  def test_10_signature_version_1\n    sdb = Rightscale::SdbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '1')\n    domains = nil\n    assert_nothing_thrown \"Failed to use signature V1\" do\n      domains = sdb.list_domains\n    end\n    assert domains\n  end\n\n  def test_11_signature_version_1\n    sdb = Rightscale::SdbInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '2')\n    domains = nil\n    assert_nothing_thrown \"Failed to use signature V2\" do\n      domains = sdb.list_domains\n    end\n    assert domains\n  end\n\n  def test_12_array_of_attrs\n    item = 'multiples'\n    assert_nothing_thrown \"Failed to put multiple attrs\" do\n      @sdb.put_attributes(@domain, item, {:one=>1, :two=>2, :three=>3})\n    end\n  end\n  \n  def test_13_zero_len_attrs\n    item = 'zeroes'\n    assert_nothing_thrown \"Failed to put zero-length attributes\" do\n      @sdb.put_attributes(@domain, item, {:one=>\"\", :two=>\"\", :three=>\"\"})\n    end\n  end\n  \n  def test_14_nil_attrs\n    item = 'nils'\n    res = nil\n    assert_nothing_thrown do\n      @sdb.put_attributes(@domain, item, {:one=>nil, :two=>nil, :three=>'chunder'})\n    end\n    wait SDB_DELAY, 'after putting attributes'\n    assert_nothing_thrown do\n      res = @sdb.get_attributes(@domain, item)\n    end\n    assert_nil(res[:attributes]['one'][0])\n    assert_nil(res[:attributes]['two'][0])\n    assert_not_nil(res[:attributes]['three'][0])\n  end\n  \n  def test_15_url_escape\n    item = 'urlescapes'\n    content = {:a=>\"one & two & three\",\n               :b=>\"one ? two / three\"}\n    @sdb.put_attributes(@domain, item, content)\n    wait SDB_DELAY, 'after putting attributes'\n\n    res = @sdb.get_attributes(@domain, item)\n    assert_equal(content[:a], res[:attributes]['a'][0])\n    assert_equal(content[:b], res[:attributes]['b'][0])\n  end\n  \n  def test_16_put_attrs_by_post\n    item = 'reqgirth'\n    i = 0\n    sa = \"\"\n    while(i < 64) do\n      sa += \"aaaaaaaa\"\n      i += 1\n    end\n    @sdb.put_attributes(@domain, item, {:a => sa, :b => sa, :c => sa, :d => sa, :e => sa})\n    wait SDB_DELAY, 'after putting attributes'\n  end\n\n  def test_20_query_with_atributes\n    response = @sdb.query_with_attributes(@domain)\n    # convers response to a hash representation\n    items = {};\n    response[:items].each{ |item| items.merge!(item) }\n    # check we have receied all 5 items each full of attributes\n    assert_equal 6, items.keys.size\n    assert items['toys'].size > 0\n    assert items['nils'].size > 0\n    assert items['urlescapes'].size > 0\n    assert items['multiples'].size > 0\n    assert items['reqgirth'].size > 0\n    assert items['zeroes'].size > 0\n    # fetch only Jon's attributes from all items\n    response = @sdb.query_with_attributes(@domain,['Jon'])\n    items = {};\n    response[:items].each{ |item| items.merge!(item) }\n    # check we have receied all 5 items\n    # check we have receied all 5 items, but only 'toys' has attributes\n    puts items.inspect\n    assert_equal 2, items['toys']['Jon'].size\n    assert_equal 0, items['nils'].size\n    assert_equal 0, items['urlescapes'].size\n    assert_equal 0, items['multiples'].size\n    assert_equal 0, items['reqgirth'].size\n    assert_equal 0, items['zeroes'].size\n    # kust Jurgen's attriburs\n    response = @sdb.query_with_attributes(@domain,['Jurgen'], \"['Jurgen'='piglet']\")\n    items = {};\n    response[:items].each{ |item| items.merge!(item) }\n    # check we have receied an only item\n    assert_equal 1, items.keys.size\n    assert_equal [\"chickabiddy\", \"kitten\", \"piglet\", \"puppy\"], items['toys']['Jurgen'].sort\n  end\n\n  # Keep this test last, because it deletes the domain...\n  def test_21_delete_domain\n    assert @sdb.delete_domain(@domain), 'delete_domain fail'\n    wait SDB_DELAY, 'after domain deletion'\n    # check that domain does not exist\n    assert !@sdb.list_domains[:domains].include?(@domain)\n  end\n  \nend"
  },
  {
    "path": "test/sns/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/sns/test_right_sns.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSns < Test::Unit::TestCase\n\n  # You can change these things to whatever you like\n  @@subscriber_email    = 'foo@bar.baz'\n  @@topic_name          = 'RightTestTopic'\n  @@topic_display_name  = 'right_aws test notification topic'\n  @@queue_name          = \"sns_subscribe_queue_#{Time.now.utc.to_i}\"\n\n  # These are placeholders for values that get set, and consumed during the course of testing.\n  @@topic_arn           = ''\n  @@subscription_arn    = ''\n  @@queue_url           = ''\n  @@queue_arn           = ''\n\n  @@policy_template     = <<-EOF\n{\n  \"Id\": \"Policy1300753700208\",\n  \"Statement\": [\n    {\n      \"Sid\": \"Stmt1300753696680\",\n      \"Action\": [\n        \"SNS:Publish\",\n        \"SNS:RemovePermission\",\n        \"SNS:SetTopicAttributes\",\n        \"SNS:DeleteTopic\",\n        \"SNS:ListSubscriptionsByTopic\",\n        \"SNS:GetTopicAttributes\",\n        \"SNS:Receive\",\n        \"SNS:AddPermission\",\n        \"SNS:Subscribe\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"@@topic_arn@@\",\n      \"Principal\": {\n        \"AWS\": [\n          \"*\"\n        ]\n      }\n    }\n  ]\n}\n    EOF\n\n  def policy_text\n    @@policy_template.gsub('@@topic_arn@@', @@topic_arn)\n  end\n\n  def setup\n    @sns = Rightscale::SnsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @sqs = Rightscale::SqsGen2Interface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  end\n\n  def test_01_create_topic\n    response = @sns.create_topic(@@topic_name)\n    assert_not_nil(response)\n    @@topic_arn = response\n  end\n\n  def test_02_set_topic_attributes\n    response = @sns.set_topic_attribute(@@topic_arn, 'Policy', policy_text())\n    assert_not_nil(response)\n\n    response = @sns.set_topic_attribute(@@topic_arn, 'DisplayName', @@topic_display_name)\n    assert_not_nil(response)\n\n    assert_raise ArgumentError do\n      @sns.set_topic_attribute(@@topic_arn, 'Foo', 'val')\n    end\n  end\n\n  def test_03_get_topic_attributes\n    response = @sns.get_topic_attributes(@@topic_arn)\n    assert_not_nil(response)\n    assert(response['DisplayName'] == @@topic_display_name)\n    assert(response['Policy'] =~ /Policy1300753700208/)\n  end\n\n  def test_04_list_topics\n    sleep(1)\n    assert(@sns.list_topics.collect{|topic| topic[:arn] }.include?(@@topic_arn))\n  end\n\n  def test_05_subscribe_email\n    response = @sns.subscribe(@@topic_arn, 'email', @@subscriber_email)\n    assert_not_nil(response)\n    @@subscription_arn = response\n  end\n\n  def test_06_list_subscriptions\n    sleep(1)\n\n    response = @sns.list_subscriptions()\n    assert_not_nil(response)\n    assert(response.count == 1)\n    assert(response[0][:endpoint] == @@subscriber_email)\n    assert(response[0][:protocol] == 'email')\n    assert(response[0][:subscription_arn] == 'PendingConfirmation')\n    assert(response[0][:topic_arn] == @@topic_arn)\n  end\n\n  def test_07_list_subscriptions_by_topic\n    response = @sns.list_subscriptions(@@topic_arn)\n    assert_not_nil(response)\n    assert(response.count == 1)\n    assert(response[0][:endpoint] == @@subscriber_email)\n    assert(response[0][:protocol] == 'email')\n    assert(response[0][:subscription_arn] == 'PendingConfirmation')\n    assert(response[0][:topic_arn] == @@topic_arn)\n  end\n\n  def test_08_unsubscribe\n    @@queue_url = @sqs.create_queue(@@queue_name)\n    @@queue_arn = \"arn:aws:sqs:us-east-1:#{TestCredentials.account_number.gsub('-','')}:#{@@queue_name}\"\n    sub_response = @sns.subscribe(@@topic_arn, 'sqs', @@queue_arn)\n    assert_not_nil(sub_response)\n    unsub_response = @sns.unsubscribe(sub_response)\n    @sqs.delete_queue(@@queue_url)\n  end\n\n  def test_09_publish\n    response = @sns.publish(@@topic_arn, 'Message to publish', 'Message Subject')\n    assert_not_nil(response)\n  end\n\n  def test_10_add_and_remove_permission\n    acct_num = TestCredentials.account_number.gsub('-','')\n\n    add_response = @sns.add_permission(@@topic_arn, 'PermissionLbl', [\n      {:aws_account_id => acct_num, :action => \"GetTopicAttributes\"},\n      {:aws_account_id => acct_num, :action => \"Publish\"}\n    ])\n    assert_not_nil(add_response)\n\n    remove_response = @sns.remove_permission(@@topic_arn, 'PermissionLbl')\n    assert_not_nil(remove_response)\n  end\n\n# TODO: Cannot easily test confirming subscription because it's only valid for http(s) and email subscriptions.\n# Since we don't want to setup an email box or HTTP server to recive the token, we can't really simulate this\n#  def test_10_confirm_subscription\n#    response = @sns.confirm_subscription(@@topic_arn, 'SomeToken')\n#    assert_not_null(response)\n#  end\n\n  def test_30_delete_topic\n    response = @sns.delete_topic(@@topic_arn)\n    assert_not_nil(response)\n    sleep(1)\n    assert(!@sns.list_topics.collect{|topic| topic[:arn] }.include?(@@topic_arn))\n  end\nend"
  },
  {
    "path": "test/sqs/test_helper.rb",
    "content": "require 'test/unit'\nrequire File.dirname(__FILE__) + '/../../lib/right_aws'\n"
  },
  {
    "path": "test/sqs/test_right_sqs.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSqs < Test::Unit::TestCase\n\n  GRANTEE_EMAIL_ADDRESS = 'madhur@amazon.com'\n  RIGHT_MESSAGE_TEXT    = 'Right test message'\n\n  \n  def setup\n    @sqs = Rightscale::SqsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @queue_name = 'right_sqs_test_awesome_queue'\n      # for classes\n    @s = Rightscale::Sqs.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  end\n  \n  # Wait for the queue to appears in the queues list.\n  # Amazon needs some time to after the queue creation to place\n  # it to the accessible queues list. If we dont want to get\n  # the additional faults then wait a bit...\n  def wait_for_queue_url(queue_name)\n    queue_url = nil\n    until queue_url\n      queue_url = @sqs.queue_url_by_name(queue_name)\n      unless queue_url\n        print '-' \n        STDOUT.flush\n        sleep 1\n      end\n    end\n    queue_url\n  end\n\n  \n\n  \n  def assert_eventually_equal(value, timeout=30, failmsg=\"\", &block)\n    start_time = Time.now.to_i\n    tries = 0\n    while(yield != value) do\n      tries += 1\n      print '-' \n      STDOUT.flush\n      s = Time.now.to_i - start_time\n      flunk(\"Timeout: #{failmsg}: did not equal \\\"#{value}\\\" after #{tries} tries in #{s}s.\") if s > timeout\n      sleep(1)\n      setup if (tries % 10) == 0 \n    end\n  end\n  \n  \n  #---------------------------\n  # Rightscale::SqsInterface\n  #---------------------------\n\n  def test_01_create_queue\n    queue_url = @sqs.create_queue @queue_name\n    assert queue_url[/http.*#{@queue_name}/], 'New queue creation fail'\n  end\n\n  def test_02_list_queues\n    wait_for_queue_url(@queue_name)\n    queues = @sqs.list_queues('right_')\n    assert queues.size>0, 'Must more that 0 queues in list'\n  end\n\n  def test_03_set_and_get_queue_attributes\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert queue_url[/http.*#{@queue_name}/], \"#{@queue_name} must exist!\"\n    assert @sqs.set_queue_attributes(queue_url, 'VisibilityTimeout', 111), 'Set_queue_attributes fail'\n    sleep 20 # Amazon needs some time to change attribute\n    assert_equal '111', @sqs.get_queue_attributes(queue_url)['VisibilityTimeout'], 'New VisibilityTimeout must be equal to 111'\n  end\n  \n  def test_04_set_and_get_visibility_timeout\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert @sqs.set_visibility_timeout(queue_url, 222), 'Set_visibility_timeout fail'\n    sleep 20 # Amazon needs some time to change attribute\n    #assert_equal 222, @sqs.get_visibility_timeout(queue_url), 'Get_visibility_timeout must return to 222'\n    assert_eventually_equal(222, 60, 'Get_visibility_timeout must return to 222') do\n      @sqs.get_visibility_timeout(queue_url)\n    end\n  end\n  \n  def test_05_add_test_remove_grant\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert @sqs.add_grant(queue_url, GRANTEE_EMAIL_ADDRESS, 'FULLCONTROL'), 'Add grant fail'\n    grants_list = @sqs.list_grants(queue_url, GRANTEE_EMAIL_ADDRESS)\n    assert grants_list.size>0, 'List_grants must return at least 1 record for user #{GRANTEE_EMAIL_ADDRESS}'\n    assert @sqs.remove_grant(queue_url, GRANTEE_EMAIL_ADDRESS, 'FULLCONTROL'), 'Remove_grant fail'\n  end\n  \n  def test_06_send_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n      # send 5 messages for the tests below\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n  end\n  \n  def test_07_get_queue_length\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert_equal 5, @sqs.get_queue_length(queue_url), 'Queue must have 5 messages'\n  end\n\n  def test_08_receive_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    r_message = @sqs.receive_message(queue_url, 1)\n    assert_equal RIGHT_MESSAGE_TEXT, r_message[:body], 'Receive message get wron message text'\n    p_message = @sqs.peek_message(queue_url, r_message[:id])\n    assert_equal r_message[:body], p_message[:body], 'Received and Peeked messages must be equal'\n    assert @sqs.change_message_visibility(queue_url, r_message[:id], 0), 'Change_message_visibility fail'\n  end\n  \n  def test_09_delete_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    message = @sqs.receive_message(queue_url)\n    assert @sqs.delete_message(queue_url, message[:id]), 'Delete_message fail'\n    assert @sqs.pop_message(queue_url), 'Pop_message fail'\n  end\n  \n  def test_10_clear_and_delete_queue\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert_raise(Rightscale::AwsError) { @sqs.delete_queue(queue_url) }\n## oops, force_clear_queue does not work any more - amazon expects for 60 secs timeout between \n## queue deletion and recreation...\n##    assert @sqs.force_clear_queue(queue_url), 'Force_clear_queue fail'\n    assert @sqs.clear_queue(queue_url), 'Clear_queue fail'\n    assert @sqs.delete_queue(queue_url), 'Delete_queue fail'\n  end\n  \n  #---------------------------\n  # Rightscale::Sqs classes\n  #---------------------------\n\n  def test_20_sqs_create_delete_queue\n    assert @s, 'Rightscale::Sqs must exist'\n      # get queues list\n    queues_size = @s.queues.size\n      # create new queue\n    queue  = @s.queue(\"#{@queue_name}_20\", true)\n      # check that it is created\n    assert queue.is_a?(Rightscale::Sqs::Queue)\n    wait_for_queue_url(@queue_name)\n      # check that amount of queues has increased\n    assert_eventually_equal(queues_size + 1, 60, \"The number of queues did not increase by one\") do\n      @s.queues.size\n    end\n      # delete queue\n    assert queue.delete\n  end\n  \n  def test_21_queue_create\n      # create new queue\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_21\", true)\n      # check that it is created\n    assert queue.is_a?(Rightscale::Sqs::Queue)\n    wait_for_queue_url(@queue_name)\n  end\n  \n  def test_22_queue_attributes\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_21\", false)\n      # get a list of attrinutes\n    attributes = queue.get_attribute\n    assert attributes.is_a?(Hash) && attributes.size>0\n      # get attribute value and increase it by 10\n    v = (queue.get_attribute('VisibilityTimeout').to_i + 10).to_s\n      # set attribute\n    assert queue.set_attribute('VisibilityTimeout', v)\n      # wait a bit\n    sleep 20\n      # check that attribute has changed\n    assert_equal v, queue.get_attribute('VisibilityTimeout')\n      # get queue visibility timeout\n    assert v.to_i, queue.visibility\n      # change it \n    queue.visibility += 10\n      # make sure that it is changed\n    assert v.to_i + 10, queue.visibility\n  end\n  \n  def test_23_grantees\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_21\", false)\n      # get a list of grantees\n    grantees = queue.grantees\n      # well, queue must exist at least some seconds before we could add grantees to it....\n      # otherwise we get \"Queue does not exists\" message. Hence we use the queue\n      # has been created at previous step.\n      #\n      # create new grantee\n    grantee = Rightscale::Sqs::Grantee.new(queue, GRANTEE_EMAIL_ADDRESS)\n    assert grantee.perms.empty?\n      # grant perms\n    assert grantee.grant('FULLCONTROL')\n    assert grantee.grant('RECEIVEMESSAGE')\n    assert_equal 2, grantee.perms.size\n      # make sure that amount of grantees has increased\n    assert grantees.size < queue.grantees.size\n      # revoke perms\n    assert grantee.revoke('RECEIVEMESSAGE')\n    assert_equal 1, grantee.perms.size\n      # remove grantee\n    assert grantee.drop\n    # Don't test this - just for cleanup purposes\n    queue.delete\n  end\n  \n  def test_24_send_size\n    queue_url = @sqs.queue_url_by_name(\"#{@queue_name}_24\")\n    @sqs.delete_queue(queue_url)\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_24\", true)\n      # send 5 messages\n    assert queue.push('a1')\n    assert queue.push('a2')\n    assert queue.push('a3')\n    assert queue.push('a4')\n    assert queue.push('a5')\n      # check queue size\n    assert_equal 5, queue.size\n      # send one more\n    assert queue.push('a6')\n      # check queue size again\n    assert_equal 6, queue.size\n  end\n  \n  def test_25_message_receive_pop_peek_delete\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_24\", false)\n      # get queue size\n    size = queue.size\n      # get first message\n    m1 = queue.receive(10)\n    assert m1.is_a?(Rightscale::Sqs::Message)\n      # pop second message\n    m2 = queue.pop\n    assert m2.is_a?(Rightscale::Sqs::Message)\n      # make sure that queue size has decreased\n    assert_equal size-1, queue.size\n      # peek message 1\n    m1p = queue.peek(m1.id)\n    assert m1p.is_a?(Rightscale::Sqs::Message)\n    assert_equal m1.id,   m1p.id\n    assert_equal m1.body, m1p.body\n      # change message visibility\n    assert m1.visibility = 30\n      # delete messsage\n    assert m1.delete\n      # make sure that queue size has decreased again\n    assert_equal size-2, queue.size\n  end\n  \n  def test_26\n    queue = Rightscale::Sqs::Queue.create(@s, \"#{@queue_name}_24\", false)\n      # lock message \n    queue.receive(100)\n      # clear queue\n    assert queue.clear \n      # queue size is greater than zero\n    assert queue.size>0\n    queue.push('123456')\n    assert_raise(Rightscale::AwsError) { queue.delete }\n    assert queue.delete(true)\n  end\n\n  def test_27_set_amazon_problems\n    original_problems = Rightscale::SqsInterface.amazon_problems\n    assert(original_problems.length > 0)\n    Rightscale::SqsInterface.amazon_problems= original_problems << \"A New Problem\"\n    new_problems = Rightscale::SqsInterface.amazon_problems\n    assert_equal(new_problems, original_problems)\n\n    Rightscale::SqsInterface.amazon_problems= nil\n    assert_nil(Rightscale::SqsInterface.amazon_problems)\n  end\n\n  def test_29_signature_version_0\n    sqs = Rightscale::SqsInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0')\n    assert_nothing_raised do\n      sqs.list_queues\n    end\n    # check that the request has correct signature version\n    assert sqs.last_request.path.include?('SignatureVersion=0')\n  end\n  \nend\n"
  },
  {
    "path": "test/sqs/test_right_sqs_gen2.rb",
    "content": "require File.dirname(__FILE__) + '/test_helper.rb'\n\nclass TestSqsGen2 < Test::Unit::TestCase\n\n  GRANTEE_EMAIL_ADDRESS = 'fester@example.com'\n  RIGHT_MESSAGE_TEXT    = 'Right test message'\n\n  \n  def setup\n    $stdout.sync = true\n    @grantee_aws_id = '100000000001'\n    @sqs = Rightscale::SqsGen2Interface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n    @queue_name  = 'right_sqs_test_gen2_queue'\n    @queue2_name = @queue_name + '_2'\n    @queue3_name = @queue_name + '_3'\n    @queue4_name = @queue_name + '_4'\n      # for classes\n    @s = Rightscale::SqsGen2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)\n  end\n  \n  # Wait for the queue to appear in the queues list.\n  # Amazon needs some time to after the queue creation to place\n  # it to the accessible queues list. If we dont want to get\n  # the additional faults then wait a bit...\n  def wait_for_queue_url(queue_name)\n    queue_url = nil\n    do_sleep(180) do\n      queue_url = @sqs.queue_url_by_name(queue_name)\n    end\n    sleep 30\n    queue_url\n  end\n\n  def do_sleep(delay, &block)\n    puts \"sleeping #{block ? 'up to ' : ''}#{delay} seconds:\"\n    wake_up_at = Time.now+delay\n    while Time.now < wake_up_at do\n      sleep 1\n      print '.'\n      break if block && block.call\n    end\n    puts\n  end\n    \n  #---------------------------\n  # Rightscale::SqsInterface\n  #---------------------------\n\n  def test_01_create_queue\n    queue_url = @sqs.create_queue @queue_name\n    assert queue_url[/http.*#{@queue_name}/], 'New queue creation failed'\n  end\n\n  def test_02_list_queues\n    wait_for_queue_url(@queue_name)\n    queues = @sqs.list_queues('right_')\n    assert queues.size>0, 'Must more that 0 queues in list'\n  end\n\n  def test_03_set_and_get_queue_attributes\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert queue_url[/https.*#{@queue_name}/], \"#{@queue_name} must exist!\"\n    assert @sqs.set_queue_attributes(queue_url, 'VisibilityTimeout', 111), 'Set_queue_attributes fail'\n    do_sleep 60 # Amazon needs some time to change attribute\n    assert_equal '111', @sqs.get_queue_attributes(queue_url)['VisibilityTimeout'], 'New VisibilityTimeout must be equal to 111'\n  end\n\n  def test_04_get_queue_attributes_forms\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    all = nil\n    assert_nothing_raised do\n      all = @sqs.get_queue_attributes(queue_url, 'All')\n    end\n    assert_nothing_raised do\n      assert all, @sqs.get_queue_attributes(queue_url)\n    end\n    assert_nothing_raised do\n      attributes = @sqs.get_queue_attributes(queue_url, 'ApproximateNumberOfMessages', 'VisibilityTimeout')\n      assert_equal 2, attributes.size\n    end\n    assert_nothing_raised do\n      attributes = @sqs.get_queue_attributes(queue_url, ['ApproximateNumberOfMessages', 'VisibilityTimeout'])\n      assert_equal 2, attributes.size\n    end\n  end\n\n  def test_05_add_permissions\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert @sqs.add_permissions(queue_url, 'test01', @grantee_aws_id, 'SendMessage')\n    assert @sqs.add_permissions(queue_url, 'test02', @grantee_aws_id, ['DeleteMessage','ReceiveMessage'])\n    do_sleep 60\n  end\n\n  def test_06_test_permissions\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    permissions = @sqs.get_queue_attributes(queue_url, 'Policy')\n    assert !permissions.right_blank?\n  end\n\n  def test_07_revoke_permissions\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert @sqs.remove_permissions(queue_url, 'test01')\n    assert @sqs.remove_permissions(queue_url, 'test02')\n  end\n\n  def test_14_send_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n      # send 5 messages for the tests below\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    assert @sqs.send_message(queue_url, RIGHT_MESSAGE_TEXT)\n    do_sleep 60\n  end\n  \n  def test_15_get_queue_length\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert_equal 5, @sqs.get_queue_length(queue_url), 'Queue must have 5 messages'\n  end\n\n  def test_16_receive_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    r_message = @sqs.receive_message(queue_url, 1)[0]\n    assert r_message, \"Receive returned no message(s), but this is not necessarily incorrect\"\n    assert_equal RIGHT_MESSAGE_TEXT, r_message['Body'], 'Receive message got wrong message text'\n  end\n  \n  def test_17_delete_message\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    message = @sqs.receive_message(queue_url)[0]\n    assert @sqs.delete_message(queue_url, message['ReceiptHandle']), 'Delete_message fail'\n    assert @sqs.pop_message(queue_url), 'Pop_message fail'\n  end\n  \n  def test_18_clear_and_delete_queue\n    queue_url = @sqs.queue_url_by_name(@queue_name)\n    assert @sqs.delete_queue(queue_url) \n  end\n  \n  #---------------------------\n  # Rightscale::Sqs classes\n  #---------------------------\n\n  def test_20_sqs_create_queue\n    assert @s, 'Rightscale::SqsGen2 must exist'\n      # get queues list\n    queues_size = @s.queues.size\n      # create new queue\n    queue = @s.queue(@queue2_name, true)\n      # check that it is created\n    assert queue.is_a?(Rightscale::SqsGen2::Queue)\n    wait_for_queue_url(queue.name)\n      # check that amount of queues has increased\n    assert_equal queues_size + 1, @s.queues.size\n    do_sleep 10\n  end\n\n  def test_21_sqs_delete_queue\n    queue = @s.queue(@queue2_name, false)\n    assert queue.delete\n  end\n  \n  def test_22_queue_create\n      # create new queue\n    queue = Rightscale::SqsGen2::Queue.create(@s, @queue3_name, true)\n      # check that it is created\n    assert queue.is_a?(Rightscale::SqsGen2::Queue)\n    wait_for_queue_url(\"#{@queue_name}_21\")\n    do_sleep 10\n  end\n  \n  def test_23_queue_attributes\n    queue = Rightscale::SqsGen2::Queue.create(@s, @queue3_name, false)\n      # get a list of attrinutes\n    attributes = queue.get_attribute\n    assert attributes.is_a?(Hash) && attributes.size>0\n      # get attribute value and increase it by 10\n    v = (queue.get_attribute('VisibilityTimeout').to_i + 10).to_s\n      # set attribute\n    assert queue.set_attribute('VisibilityTimeout', v)\n      # wait a bit\n    do_sleep 60\n      # check that attribute has changed\n    assert_equal v, queue.get_attribute('VisibilityTimeout')\n      # get queue visibility timeout\n    assert_equal v, queue.visibility\n      # change it \n    queue.visibility = queue.visibility.to_i + 10\n      # make sure that it is changed\n    assert v.to_i + 10, queue.visibility\n  end\n\n  def test_24\n    queue = Rightscale::SqsGen2::Queue.create(@s, @queue3_name, false)\n    assert queue.delete\n  end\n  \n  def test_25_send_size\n    queue = Rightscale::SqsGen2::Queue.create(@s, @queue4_name, true)\n      # send 5 messages\n    assert queue.push('a1')\n    assert queue.push('a2')\n    assert queue.push('a3')\n    assert queue.push('a4')\n    assert queue.push('a5')\n      #\n    do_sleep(300){ queue.size == 5 }\n      # check queue size\n    assert_equal 5, queue.size\n      # send one more\n    assert queue.push('a6')\n      #\n    do_sleep(300){ queue.size == 6 }\n      # check queue size again\n    assert_equal 6, queue.size\n  end\n  \n  def test_26_message_receive_pop_delete\n    queue = Rightscale::SqsGen2::Queue.create(@s,  @queue4_name, false)\n      # get queue size\n    size = queue.size\n      # get first message\n    m1 = queue.receive(10)\n    assert m1.is_a?(Rightscale::SqsGen2::Message)\n      # pop second message\n    m2 = queue.pop\n    assert m2.is_a?(Rightscale::SqsGen2::Message)\n      #\n    do_sleep 30\n      # make sure that queue size has decreased\n    assert_equal size-1, queue.size\n      # delete messsage\n    assert m1.delete\n      #\n    do_sleep 15\n      # make sure that queue size has decreased again\n    assert_equal size-2, queue.size\n  end\n \n  def test_27\n    queue = Rightscale::SqsGen2::Queue.create(@s, @queue4_name, false)\n      # lock message \n    queue.receive(100)\n      # clear queue\n    assert queue.clear \n      # queue size is greater than zero\n    assert queue.size>0\n      # delete queue\n    assert queue.delete\n  end\n\n  def test_28_set_amazon_problems\n    original_problems = Rightscale::SqsGen2Interface.amazon_problems\n    assert(original_problems.length > 0)\n    Rightscale::SqsGen2Interface.amazon_problems= original_problems << \"A New Problem\"\n    new_problems = Rightscale::SqsGen2Interface.amazon_problems\n    assert_equal(new_problems, original_problems)\n\n    Rightscale::SqsGen2Interface.amazon_problems= nil\n    assert_nil(Rightscale::SqsGen2Interface.amazon_problems)\n  end\n  \nend\n"
  },
  {
    "path": "test/test_credentials.rb",
    "content": "class TestCredentials\n\n  @@aws_access_key_id = nil \n  @@aws_secret_access_key = nil \n  @@account_number = nil\n\n  def self.aws_access_key_id\n    @@aws_access_key_id\n  end\n  def self.aws_access_key_id=(newval)\n    @@aws_access_key_id = newval\n  end\n  def self.account_number\n    @@account_number\n  end\n  def self.account_number=(newval)\n    @@account_number = newval\n  end\n  def self.aws_secret_access_key\n    @@aws_secret_access_key\n  end\n  def self.aws_secret_access_key=(newval)\n    @@aws_secret_access_key = newval\n  end\n\n  def self.get_credentials\n    Dir.chdir do\n      begin\n        Dir.chdir('./.rightscale') do \n          require 'testcredentials'\n        end\n      rescue Exception => e\n        puts \"Couldn't chdir to ~/.rightscale: #{e.message}\"\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/ts_right_aws.rb",
    "content": "require 'test/unit'\n$: << File.dirname(__FILE__)\nrequire 'test_credentials'\nTestCredentials.get_credentials\n\nrequire 'http_connection'\nrequire 'awsbase/test_right_awsbase.rb'\nrequire 'ec2/test_right_ec2.rb'\nrequire 's3/test_right_s3.rb'\nrequire 's3/test_right_s3_stubbed.rb'\nrequire 'sqs/test_right_sqs.rb'\nrequire 'sqs/test_right_sqs_gen2.rb'\nrequire 'sdb/test_right_sdb.rb'\nrequire 'acf/test_right_acf.rb'\nrequire 'sns/test_right_sns.rb'"
  }
]