dustMason-right_aws 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History.txt +305 -0
  2. data/Manifest.txt +60 -0
  3. data/README.txt +165 -0
  4. data/Rakefile +112 -0
  5. data/lib/acf/right_acf_interface.rb +549 -0
  6. data/lib/acf/right_acf_invalidations.rb +144 -0
  7. data/lib/acf/right_acf_origin_access_identities.rb +230 -0
  8. data/lib/acf/right_acf_streaming_interface.rb +229 -0
  9. data/lib/acw/right_acw_interface.rb +248 -0
  10. data/lib/as/right_as_interface.rb +698 -0
  11. data/lib/awsbase/benchmark_fix.rb +39 -0
  12. data/lib/awsbase/right_awsbase.rb +1174 -0
  13. data/lib/awsbase/support.rb +35 -0
  14. data/lib/awsbase/version.rb +9 -0
  15. data/lib/ec2/right_ec2.rb +458 -0
  16. data/lib/ec2/right_ec2_ebs.rb +465 -0
  17. data/lib/ec2/right_ec2_images.rb +413 -0
  18. data/lib/ec2/right_ec2_instances.rb +785 -0
  19. data/lib/ec2/right_ec2_monitoring.rb +70 -0
  20. data/lib/ec2/right_ec2_placement_groups.rb +108 -0
  21. data/lib/ec2/right_ec2_reserved_instances.rb +174 -0
  22. data/lib/ec2/right_ec2_security_groups.rb +396 -0
  23. data/lib/ec2/right_ec2_spot_instances.rb +425 -0
  24. data/lib/ec2/right_ec2_tags.rb +139 -0
  25. data/lib/ec2/right_ec2_vpc.rb +583 -0
  26. data/lib/ec2/right_ec2_windows_mobility.rb +84 -0
  27. data/lib/elb/right_elb_interface.rb +571 -0
  28. data/lib/iam/right_iam_access_keys.rb +71 -0
  29. data/lib/iam/right_iam_groups.rb +195 -0
  30. data/lib/iam/right_iam_interface.rb +341 -0
  31. data/lib/iam/right_iam_mfa_devices.rb +67 -0
  32. data/lib/iam/right_iam_users.rb +251 -0
  33. data/lib/rds/right_rds_interface.rb +1309 -0
  34. data/lib/right_aws.rb +88 -0
  35. data/lib/route_53/right_route_53_interface.rb +630 -0
  36. data/lib/s3/right_s3.rb +1123 -0
  37. data/lib/s3/right_s3_interface.rb +1198 -0
  38. data/lib/sdb/active_sdb.rb +1107 -0
  39. data/lib/sdb/right_sdb_interface.rb +753 -0
  40. data/lib/sns/right_sns.rb +205 -0
  41. data/lib/sns/right_sns_interface.rb +343 -0
  42. data/lib/sqs/right_sqs.rb +387 -0
  43. data/lib/sqs/right_sqs_gen2.rb +342 -0
  44. data/lib/sqs/right_sqs_gen2_interface.rb +523 -0
  45. data/lib/sqs/right_sqs_interface.rb +593 -0
  46. data/right_aws.gemspec +91 -0
  47. data/test/acf/test_helper.rb +2 -0
  48. data/test/acf/test_right_acf.rb +138 -0
  49. data/test/awsbase/test_helper.rb +2 -0
  50. data/test/awsbase/test_right_awsbase.rb +12 -0
  51. data/test/ec2/test_helper.rb +2 -0
  52. data/test/ec2/test_right_ec2.rb +108 -0
  53. data/test/http_connection.rb +87 -0
  54. data/test/rds/test_helper.rb +2 -0
  55. data/test/rds/test_right_rds.rb +120 -0
  56. data/test/s3/test_helper.rb +2 -0
  57. data/test/s3/test_right_s3.rb +421 -0
  58. data/test/s3/test_right_s3_stubbed.rb +97 -0
  59. data/test/sdb/test_active_sdb.rb +357 -0
  60. data/test/sdb/test_batch_put_attributes.rb +54 -0
  61. data/test/sdb/test_helper.rb +3 -0
  62. data/test/sdb/test_right_sdb.rb +253 -0
  63. data/test/sns/test_helper.rb +2 -0
  64. data/test/sns/test_right_sns.rb +73 -0
  65. data/test/sqs/test_helper.rb +2 -0
  66. data/test/sqs/test_right_sqs.rb +285 -0
  67. data/test/sqs/test_right_sqs_gen2.rb +264 -0
  68. data/test/test_credentials.rb +37 -0
  69. data/test/ts_right_aws.rb +14 -0
  70. metadata +244 -0
data/right_aws.gemspec ADDED
@@ -0,0 +1,91 @@
1
+ #-- -*- mode: ruby; encoding: utf-8 -*-
2
+ # Copyright: Copyright (c) 2010 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'rubygems'
25
+ require File.expand_path(File.join(File.dirname(__FILE__), "lib", "awsbase", "version"))
26
+
27
+ Gem::Specification.new do |spec|
28
+ spec.name = 'dustMason-right_aws'
29
+ spec.rubyforge_project = 'rightaws'
30
+ spec.version = RightAws::VERSION::STRING
31
+ spec.authors = ['RightScale, Inc.']
32
+ spec.email = 'support@rightscale.com'
33
+ spec.summary = 'Interface classes for the Amazon EC2, SQS, and S3 Web Services'
34
+ spec.has_rdoc = true
35
+ spec.rdoc_options = ['--main', 'README.txt', '--title', '']
36
+ spec.extra_rdoc_files = ['README.txt']
37
+ spec.required_ruby_version = '>= 1.8.7'
38
+ spec.require_path = 'lib'
39
+
40
+ spec.add_dependency('right_http_connection', '>= 1.2.5')
41
+ #spec.requirements << "uuidtools >= 1.0.7 if you want to use ActiveSdb"
42
+ spec.requirements << "libxml-ruby >= 0.5.2.0 is encouraged"
43
+
44
+ spec.add_development_dependency('rake')
45
+ spec.add_development_dependency('rcov')
46
+
47
+ spec.description = <<-EOF
48
+ == DESCRIPTION:
49
+
50
+ The RightScale AWS gems have been designed to provide a robust, fast, and secure interface to Amazon EC2, EBS, S3, SQS, SDB, and CloudFront.
51
+ These gems have been used in production by RightScale since late 2006 and are being maintained to track enhancements made by Amazon.
52
+ The RightScale AWS gems comprise:
53
+
54
+ - RightAws::Ec2 -- interface to Amazon EC2 (Elastic Compute Cloud) and the
55
+ associated EBS (Elastic Block Store)
56
+ - RightAws::S3 and RightAws::S3Interface -- interface to Amazon S3 (Simple Storage Service)
57
+ - RightAws::Sqs and RightAws::SqsInterface -- interface to first-generation Amazon SQS (Simple Queue Service) (API version 2007-05-01)
58
+ - RightAws::SqsGen2 and RightAws::SqsGen2Interface -- interface to second-generation Amazon SQS (Simple Queue Service) (API version 2008-01-01)
59
+ - RightAws::SdbInterface and RightAws::ActiveSdb -- interface to Amazon SDB (SimpleDB)
60
+ - RightAws::AcfInterface -- interface to Amazon CloudFront, a content distribution service
61
+
62
+ == FEATURES:
63
+
64
+ - Full programmmatic access to EC2, EBS, S3, SQS, SDB, and CloudFront.
65
+ - Complete error handling: all operations check for errors and report complete
66
+ error information by raising an AwsError.
67
+ - Persistent HTTP connections with robust network-level retry layer using
68
+ RightHttpConnection). This includes socket timeouts and retries.
69
+ - Robust HTTP-level retry layer. Certain (user-adjustable) HTTP errors returned
70
+ by Amazon's services are classified as temporary errors.
71
+ These errors are automaticallly retried using exponentially increasing intervals.
72
+ The number of retries is user-configurable.
73
+ - Fast REXML-based parsing of responses (as fast as a pure Ruby solution allows).
74
+ - Uses libxml (if available) for faster response parsing.
75
+ - Support for large S3 list operations. Buckets and key subfolders containing
76
+ many (> 1000) keys are listed in entirety. Operations based on list (like
77
+ bucket clear) work on arbitrary numbers of keys.
78
+ - Support for streaming GETs from S3, and streaming PUTs to S3 if the data source is a file.
79
+ - Support for single-threaded usage, multithreaded usage, as well as usage with multiple
80
+ AWS accounts.
81
+ - Support for both first- and second-generation SQS (API versions 2007-05-01
82
+ and 2008-01-01). These versions of SQS are not compatible.
83
+ - Support for signature versions 0 and 1 on SQS, SDB, and EC2.
84
+ - Interoperability with any cloud running Eucalyptus (http://eucalyptus.cs.ucsb.edu)
85
+ - Test suite (requires AWS account to do "live" testing).
86
+ EOF
87
+
88
+ candidates = Dir.glob('{lib,test}/**/*') + ['History.txt', 'Manifest.txt', 'README.txt', 'Rakefile', 'right_aws.gemspec']
89
+ spec.files = candidates.sort
90
+ spec.test_files = Dir.glob('test/**/*')
91
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../../lib/right_aws'
@@ -0,0 +1,138 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestAcf < Test::Unit::TestCase
4
+
5
+ RIGHT_OBJECT_TEXT = 'Right test message'
6
+
7
+ STDOUT.sync = true
8
+
9
+ def setup
10
+ @acf= Rightscale::AcfInterface.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)
11
+ @s3 = Rightscale::S3.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key)
12
+ @bucket_name = "right-acf-awesome-test-bucket-xxx1"
13
+ @bucket_domain = "#{@bucket_name}.s3.amazonaws.com"
14
+ end
15
+
16
+ def test_01_list_distributions_part1
17
+ distributions = nil
18
+ assert_nothing_raised(Rightscale::AwsError) do
19
+ distributions = @acf.list_distributions
20
+ end
21
+ assert distributions.is_a?(Array)
22
+ end
23
+
24
+ def test_02_try_to_create_for_bad_bucket
25
+ # a bucket does not exist
26
+ assert_raise(Rightscale::AwsError) do
27
+ @acf.create_distribution("right-cloudfront-awesome-test-bucket-not-exist", "Mustn't to be born", true)
28
+ end
29
+ # a bucket is not a domain naming complied guy
30
+ bucket_name = 'right_cloudfront_awesome_test_bucket_BAD_XXX'
31
+ @s3.bucket(bucket_name, :create)
32
+ assert_raise(Rightscale::AwsError) do
33
+ @acf.create_distribution(bucket_name, "Mustn't to be born", true)
34
+ end
35
+ end
36
+
37
+ def test_02_x_delete_bad_bucket
38
+ bucket_name = 'right_cloudfront_awesome_test_bucket_BAD_XXX'
39
+ @s3.bucket(bucket_name, false).delete
40
+ end
41
+
42
+ def test_03_create
43
+ comment = 'WooHoo!!!'
44
+ # create a test bucket
45
+ @s3.bucket(@bucket_name, :create)
46
+ # create a distribution
47
+ distribution = @acf.create_distribution(@bucket_domain, comment, true)
48
+ assert_equal comment, distribution[:comment]
49
+ assert distribution[:cnames].size == 0
50
+ assert distribution[:enabled]
51
+ end
52
+
53
+ def test_04_list_distributions_part2
54
+ distributions = @acf.list_distributions
55
+ assert distributions.size > 0
56
+ end
57
+
58
+ def get_test_distribution
59
+ @acf.list_distributions.select{ |d| d[:origin] == @bucket_domain }.first
60
+ end
61
+
62
+ def test_05_get_distribution
63
+ old = get_test_distribution
64
+ assert_nothing_raised do
65
+ @acf.get_distribution(old[:aws_id])
66
+ end
67
+ end
68
+
69
+ def test_06_get_and_set_config
70
+ config = nil
71
+ old = get_test_distribution
72
+ assert_nothing_raised do
73
+ config = @acf.get_distribution_config(old[:aws_id])
74
+ end
75
+ # change a config
76
+ config[:enabled] = false
77
+ config[:cnames] << 'xxx1.myawesomesite.com'
78
+ config[:cnames] << 'xxx2.myawesomesite.com'
79
+ # set config
80
+ set_config_result = nil
81
+ assert_nothing_raised do
82
+ set_config_result = @acf.set_distribution_config(old[:aws_id], config)
83
+ end
84
+ assert set_config_result
85
+ # reget the config and check
86
+ new_config = nil
87
+ assert_nothing_raised do
88
+ new_config = @acf.get_distribution_config(old[:aws_id])
89
+ end
90
+ assert !new_config[:enabled]
91
+ assert_equal new_config[:cnames].sort, ['xxx1.myawesomesite.com', 'xxx2.myawesomesite.com']
92
+ assert_not_equal config[:e_tag], new_config[:e_tag]
93
+
94
+ # try to update the old config again (must fail because ETAG has changed)
95
+ assert_raise(Rightscale::AwsError) do
96
+ @acf.set_distribution_config(old[:aws_id], config)
97
+ end
98
+ end
99
+
100
+ def test_08_delete_distribution
101
+ # we need ETAG so use get_distribution
102
+ distribution = @acf.get_distribution(get_test_distribution[:aws_id])
103
+ # try to delete a distribution
104
+ # should fail because
105
+ if distribution[:status] == 'InProgress'
106
+ # should fail because the distribution is not deployed yet
107
+ assert_raise(Rightscale::AwsError) do
108
+ @acf.delete_distribution(distribution[:aws_id], distribution[:e_tag])
109
+ end
110
+ # wait for a deployed state
111
+ print "waiting up to 5 min while the distribution is being deployed: "
112
+ 100.times do
113
+ print '.'
114
+ distribution = @acf.get_distribution(distribution[:aws_id])
115
+ if distribution[:status] == 'Deployed'
116
+ print ' done'
117
+ break
118
+ end
119
+ sleep 3
120
+ end
121
+ puts
122
+ end
123
+
124
+ # only disabled and deployed distribution can be deleted
125
+ assert_equal 'Deployed', distribution[:status]
126
+ assert !distribution[:enabled]
127
+
128
+ # delete the distribution
129
+ assert_nothing_raised do
130
+ @acf.delete_distribution(distribution[:aws_id], distribution[:e_tag])
131
+ end
132
+ end
133
+
134
+ def test_09_drop_bucket
135
+ assert @s3.bucket(@bucket_name).delete
136
+ end
137
+
138
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../../lib/right_aws'
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'pp'
3
+
4
+ class TestAwsbase < Test::Unit::TestCase
5
+
6
+ def setup
7
+ end
8
+
9
+ def test_01_create_describe_key_pairs
10
+ end
11
+
12
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../../lib/right_aws'
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ require 'pp'
3
+
4
+ class TestEc2 < Test::Unit::TestCase
5
+
6
+ # Some of RightEc2 instance methods concerning instance launching and image registration
7
+ # are not tested here due to their potentially risk.
8
+
9
+ def setup
10
+ @ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id,
11
+ TestCredentials.aws_secret_access_key)
12
+ @key = 'right_ec2_awesome_test_key'
13
+ @group = 'right_ec2_awesome_test_security_group'
14
+ end
15
+
16
+ def test_01_create_describe_key_pairs
17
+ new_key = @ec2.create_key_pair(@key)
18
+ assert new_key[:aws_material][/BEGIN RSA PRIVATE KEY/], "New key material is absent"
19
+ keys = @ec2.describe_key_pairs
20
+ assert keys.map{|key| key[:aws_key_name] }.include?(@key), "#{@key} must exist"
21
+ end
22
+
23
+ def test_02_create_security_group
24
+ assert @ec2.create_security_group(@group,'My awesone test group'), 'Create_security_group fail'
25
+ group = @ec2.describe_security_groups([@group])[0]
26
+ assert_equal @group, group[:aws_group_name], 'Group must be created but does not exist'
27
+ end
28
+
29
+ def test_03_perms_add
30
+ assert @ec2.authorize_security_group_named_ingress(@group, TestCredentials.account_number, 'default')
31
+ assert @ec2.authorize_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')
32
+ end
33
+
34
+ def test_04_check_new_perms_exist
35
+ assert_equal 2, @ec2.describe_security_groups([@group])[0][:aws_perms].size
36
+ end
37
+
38
+ def test_05_perms_remove
39
+ assert @ec2.revoke_security_group_IP_ingress(@group, 80,80,'udp','192.168.1.0/8')
40
+ assert @ec2.revoke_security_group_named_ingress(@group,
41
+ TestCredentials.account_number, 'default')
42
+ end
43
+
44
+ def test_06_describe_images
45
+ images = @ec2.describe_images
46
+ assert images.size>0, 'Amazon must have at least some public images'
47
+ # unknown image
48
+ assert_raise(Rightscale::AwsError){ @ec2.describe_images(['ami-ABCDEFGH'])}
49
+ end
50
+
51
+ def test_07_describe_instanses
52
+ assert @ec2.describe_instances
53
+ # unknown image
54
+ assert_raise(Rightscale::AwsError){ @ec2.describe_instances(['i-ABCDEFGH'])}
55
+ end
56
+
57
+ def test_08_delete_security_group
58
+ assert @ec2.delete_security_group(@group), 'Delete_security_group fail'
59
+ end
60
+
61
+ def test_09_delete_key_pair
62
+ assert @ec2.delete_key_pair(@key), 'Delete_key_pair fail'
63
+ ## Hmmm... Amazon does not through the exception any more. It now just returns a 'true' if the key does not exist any more...
64
+ ## # key must be deleted already
65
+ ## assert_raise(Rightscale::AwsError) { @ec2.delete_key_pair(@key) }
66
+ end
67
+
68
+ def test_10_signature_version_0
69
+ ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :signature_version => '0')
70
+ images = ec2.describe_images
71
+ assert images.size>0, 'Amazon must have at least some public images'
72
+ # check that the request has correct signature version
73
+ assert ec2.last_request.path.include?('SignatureVersion=0')
74
+ end
75
+
76
+ def test_11_regions
77
+ regions = nil
78
+ assert_nothing_raised do
79
+ regions = @ec2.describe_regions
80
+ end
81
+ # check we got more that 0 regions
82
+ assert regions.size > 0
83
+ # check an access to regions
84
+ regions.each do |region|
85
+ regional_ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :region => region)
86
+ # do we have a correct endpoint server?
87
+ assert_equal "#{region}.ec2.amazonaws.com", regional_ec2.params[:server]
88
+ # get a list of images from every region
89
+ images = nil
90
+ assert_nothing_raised do
91
+ images = regional_ec2.describe_regions
92
+ end
93
+ # every region must have images
94
+ assert images.size > 0
95
+ end
96
+ end
97
+
98
+ def test_12_endpoint_url
99
+ ec2 = Rightscale::Ec2.new(TestCredentials.aws_access_key_id, TestCredentials.aws_secret_access_key, :endpoint_url => 'a://b.c:0/d/', :region => 'z')
100
+ # :endpoint_url has a priority hence :region should be ommitted
101
+ assert_equal 'a', ec2.params[:protocol]
102
+ assert_equal 'b.c', ec2.params[:server]
103
+ assert_equal '/d/', ec2.params[:service]
104
+ assert_equal 0, ec2.params[:port]
105
+ assert_nil ec2.params[:region]
106
+ end
107
+
108
+ end
@@ -0,0 +1,87 @@
1
+ =begin
2
+ Copyright (c) 2007 RightScale, Inc.
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ 'Software'), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ =end
23
+
24
+ # Stub extension/redefinition of RightHttpConnection for testing purposes.
25
+ require 'net/http'
26
+ require 'rubygems'
27
+ require 'right_http_connection'
28
+
29
+ module Net
30
+ class HTTPResponse
31
+ alias_method :real_body, :body
32
+ def setmsg(msg)
33
+ @mymsg = msg
34
+ end
35
+
36
+ def body
37
+ # defined?() helps us to get rid of a bunch of 'warnings'
38
+ (defined?(@mymsg) && @mymsg) ? @mymsg : real_body
39
+ end
40
+ end
41
+ end
42
+
43
+ module Rightscale
44
+
45
+ class HttpConnection
46
+ @@response_stack = []
47
+
48
+ alias_method :real_request, :request
49
+
50
+ def request(request_params, &block)
51
+ if(@@response_stack.length == 0)
52
+ return real_request(request_params, &block)
53
+ end
54
+
55
+ if(block)
56
+ # Do something special
57
+ else
58
+ next_response = HttpConnection::pop()
59
+ classname = Net::HTTPResponse::CODE_TO_OBJ["#{next_response[:code]}"]
60
+ response = classname.new("1.1", next_response[:code], next_response[:msg])
61
+ if(next_response[:msg])
62
+ response.setmsg(next_response[:msg])
63
+ end
64
+ response
65
+ end
66
+ end
67
+
68
+ def self.reset
69
+ @@response_stack = []
70
+ end
71
+
72
+ def self.push(code, msg=nil)
73
+ response = {:code => code, :msg => msg}
74
+ @@response_stack << response
75
+ end
76
+
77
+ def self.pop
78
+ @@response_stack.pop
79
+ end
80
+
81
+ def self.length
82
+ @@response_stack.length
83
+ end
84
+
85
+ end
86
+
87
+ end