route53 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ module Route53
2
+ class AWSResponse
3
+ attr_reader :raw_data
4
+
5
+ MESSAGES = {
6
+ "InvalidClientTokenId" => "You may have a missing or incorrect secret or access key. Please double check your configuration files and amazon account",
7
+ "MissingAuthenticationToken" => "You may have a missing or incorrect secret or access key. Please double check your configuration files and amazon account",
8
+ "OptInRequired" => "In order to use Amazon's Route 53 service you first need to signup for it. Please see http://aws.amazon.com/route53/ for your account information and use the associated access key and secret.",
9
+ "Other" => "It looks like you've run into an unhandled error. Please send a detailed bug report with the entire input and output from the program to support@50projects.com or to https://github.com/pcorliss/ruby_route_53/issues and we'll do out best to help you.",
10
+ "SignatureDoesNotMatch" => "It looks like your secret key is incorrect or no longer valid. Please check your amazon account information for the proper key.",
11
+ "HostedZoneNotEmpty" => "You'll need to first delete the contents of this zone. You can do so using the '--remove' option as part of the command line interface.",
12
+ "InvalidChangeBatch" => "You may have tried to delete a NS or SOA record. This error is safe to ignore if you're just trying to delete all records as part of a zone prior to deleting the zone. Or you may have tried to create a record that already exists. Otherwise please file a bug by sending a detailed bug report with the entire input and output from the program to support@50projects.com or to https://github.com/pcorliss/ruby_route_53/issues and we'll do out best to help you.",
13
+ "ValidationError" => "Check over your input again to make sure the record to be created is valid. The error message should give you some hints on what went wrong. If you're still having problems please file a bug by sending a detailed bug report with the entire input and output from the program to support@50projects.com or to https://github.com/pcorliss/ruby_route_53/issues and we'll do out best to help you.",
14
+ "ServiceUnavailable" => "It looks like Amazon Route 53 is having availability problems. If the error persists, you may want to check http://status.aws.amazon.com/ for more information on the current system status."
15
+ }
16
+
17
+ def initialize(resp,conn)
18
+ @raw_data = unescape(resp)
19
+ if error?
20
+ $stderr.puts "ERROR: Amazon returned an error for the request."
21
+ $stderr.puts "ERROR: RAW_XML: "+@raw_data
22
+ $stderr.puts "ERROR: "+error_message
23
+ $stderr.puts ""
24
+ $stderr.puts "What now? "+helpful_message
25
+ #exit 1
26
+ end
27
+ @conn = conn
28
+ @created = Time.now
29
+ puts "Raw: #{@raw_data}" if @conn.verbose
30
+ end
31
+
32
+ def error?
33
+ return Nokogiri::XML(@raw_data).search("ErrorResponse").size > 0
34
+ end
35
+
36
+ def error_message
37
+ xml = Nokogiri::XML(@raw_data)
38
+ msg_code = xml.search("Code")
39
+ msg_text = xml.search("Message")
40
+ return (msg_code.size > 0 ? msg_code.first.inner_text : "") + (msg_text.size > 0 ? ': ' + msg_text.first.inner_text : "")
41
+ end
42
+
43
+ def helpful_message
44
+ xml = Nokogiri::XML(@raw_data)
45
+ msg_code = xml.search("Code").first.inner_text
46
+ MESSAGES[msg_code] || MESSAGES["Other"]
47
+ end
48
+
49
+ def complete?
50
+ return true if error?
51
+ if @change_url.nil?
52
+ change = Nokogiri::XML(@raw_data).search("ChangeInfo")
53
+ if change.size > 0
54
+ @change_url = change.first.search("Id").first.inner_text
55
+ else
56
+ return false
57
+ end
58
+ end
59
+ if @complete.nil? || @complete == false
60
+ status = Nokogiri::XML(@conn.request(@conn.base_url+@change_url).raw_data).search("Status")
61
+ @complete = status.size > 0 && status.first.inner_text == "INSYNC" ? true : false
62
+ if !@complete && @created - Time.now > 60
63
+ $stderr.puts "WARNING: Amazon Route53 Change timed out on Sync. This may not be an issue as it may just be Amazon being assy. Then again your request may not have completed.'"
64
+ @complete = true
65
+ end
66
+ end
67
+ return @complete
68
+ end
69
+
70
+ def pending?
71
+ #Return opposite of complete via XOR
72
+ return complete? ^ true
73
+ end
74
+
75
+ def to_s
76
+ return @raw_data
77
+ end
78
+
79
+ def unescape(string)
80
+ string.gsub(/\\0(\d{2})/) { $1.oct.chr }
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,93 @@
1
+ module Route53
2
+ class Connection
3
+ attr_reader :base_url
4
+ attr_reader :api
5
+ attr_reader :endpoint
6
+ attr_reader :verbose
7
+
8
+ def initialize(accesskey,secret,api='2012-12-12',endpoint='https://route53.amazonaws.com/',verbose=false,ssl_no_verify=false)
9
+ @accesskey = accesskey
10
+ @secret = secret
11
+ @api = api
12
+ @endpoint = endpoint
13
+ @base_url = endpoint+@api
14
+ @verbose = verbose
15
+ @ssl_no_verify = ssl_no_verify
16
+ end
17
+
18
+ def request(url,type = "GET",data = nil)
19
+ puts "URL: #{url}" if @verbose
20
+ puts "Type: #{type}" if @verbose
21
+ puts "Req: #{data}" if type != "GET" && @verbose
22
+ uri = URI(url)
23
+ http = Net::HTTP.new(uri.host, uri.port)
24
+ http.use_ssl = true if uri.scheme == "https"
25
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if RUBY_VERSION.start_with?("1.8") or @ssl_no_verify
26
+ time = get_date
27
+ hmac = HMAC::SHA256.new(@secret)
28
+ hmac.update(time)
29
+ signature = Base64.encode64(hmac.digest).chomp
30
+ headers = {
31
+ 'Date' => time,
32
+ 'X-Amzn-Authorization' => "AWS3-HTTPS AWSAccessKeyId=#{@accesskey},Algorithm=HmacSHA256,Signature=#{signature}",
33
+ 'Content-Type' => 'text/xml; charset=UTF-8'
34
+ }
35
+ resp = http.send_request(type,uri.path+"?"+(uri.query.nil? ? "" : uri.query),data,headers)
36
+ #puts "Resp:"+resp.to_s if @verbose
37
+ #puts "RespBody: #{resp.body}" if @verbose
38
+ return AWSResponse.new(resp.body,self)
39
+ end
40
+
41
+ def get_zones(name = nil)
42
+ truncated = true
43
+ query = []
44
+ zones = []
45
+ while truncated
46
+ if !name.nil? && name.start_with?("/hostedzone/")
47
+ resp = request("#{@base_url}#{name}")
48
+ truncated = false
49
+ else
50
+ resp = request("#{@base_url}/hostedzone?"+query.join("&"))
51
+ end
52
+ return nil if resp.error?
53
+ zone_list = Nokogiri::XML(resp.raw_data)
54
+ elements = zone_list.search("HostedZone")
55
+ elements.each do |e|
56
+ zones.push(Zone.new(e.search("Name").first.inner_text,
57
+ e.search("Id").first.inner_text,
58
+ self))
59
+ end
60
+ truncated = (zone_list.search("IsTruncated").first.inner_text == "true") if truncated
61
+ query = ["marker="+zone_list.search("NextMarker").first.inner_text] if truncated
62
+ end
63
+ unless name.nil? || name.start_with?("/hostedzone/")
64
+ name_arr = name.split('.')
65
+ (0 ... name_arr.size).each do |i|
66
+ search_domain = name_arr.last(name_arr.size-i).join('.')+"."
67
+ zone_select = zones.select { |z| z.name == search_domain }
68
+ return zone_select
69
+ end
70
+ return nil
71
+ end
72
+ return zones
73
+ end
74
+
75
+ def get_date
76
+ #return Time.now.utc.rfc2822
77
+ #Cache date for 30 seconds to reduce extra calls
78
+ if @date_stale.nil? || @date_stale < Time.now - 30
79
+ uri = URI(@endpoint)
80
+ http = Net::HTTP.new(uri.host, uri.port)
81
+ http.use_ssl = true if uri.scheme == "https"
82
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if RUBY_VERSION.start_with?("1.8") or @ssl_no_verify
83
+ resp = nil
84
+ puts "Making Date Request" if @verbose
85
+ http.start { |http| resp = http.head('/date') }
86
+ @date = resp['Date']
87
+ @date_stale = Time.now
88
+ puts "Received Date." if @verbose
89
+ end
90
+ return @date
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,99 @@
1
+ module Route53
2
+ class DNSRecord
3
+ attr_reader :name
4
+ attr_reader :type
5
+ attr_reader :ttl
6
+ attr_reader :values
7
+ attr_reader :weight
8
+ attr_reader :ident
9
+ attr_reader :zone_apex
10
+
11
+ def initialize(name,type,ttl,values,zone,zone_apex=nil,weight=nil,ident=nil, evaluate_target_health=false)
12
+ @name = name
13
+ unless @name.end_with?(".")
14
+ @name += "."
15
+ end
16
+ @type = type.upcase
17
+ @ttl = ttl
18
+ @values = values
19
+ @zone = zone
20
+ @zone_apex = zone_apex
21
+ @weight = weight
22
+ @ident = ident
23
+ @evaluate_target_health = evaluate_target_health
24
+ end
25
+
26
+ def gen_change_xml(xml,action)
27
+ xml.Change { |change|
28
+ change.Action(action.upcase)
29
+ change.ResourceRecordSet { |record|
30
+ record.Name(@name)
31
+ record.Type(@type)
32
+ record.SetIdentifier(@ident) if @ident
33
+ record.Weight(@weight) if @weight
34
+ record.TTL(@ttl) unless @zone_apex
35
+ if @zone_apex
36
+ record.AliasTarget { |targets|
37
+ targets.HostedZoneId(@zone_apex)
38
+ targets.DNSName(@values.first)
39
+ targets.EvaluateTargetHealth(@evaluate_target_health)
40
+ }
41
+ else
42
+ record.ResourceRecords { |resources|
43
+ @values.each { |val|
44
+ resources.ResourceRecord { |record|
45
+ record.Value(val)
46
+ }
47
+ }
48
+ }
49
+ end
50
+ }
51
+ }
52
+ end
53
+
54
+ def delete(comment=nil)
55
+ @zone.perform_actions([{:action => "DELETE", :record => self}],comment)
56
+ end
57
+
58
+ def create(comment=nil)
59
+ @zone.perform_actions([{:action => "CREATE", :record => self}],comment)
60
+ end
61
+
62
+ #Need to modify to a param hash
63
+ def update(name,type,ttl,values,comment=nil, zone_apex = nil)
64
+ prev = self.clone
65
+ @name = name unless name.nil?
66
+ @type = type unless type.nil?
67
+ @ttl = ttl unless ttl.nil?
68
+ @values = values unless values.nil?
69
+ @zone_apex = zone_apex unless zone_apex.nil?
70
+ @zone.perform_actions([
71
+ {:action => "DELETE", :record => prev},
72
+ {:action => "CREATE", :record => self},
73
+ ],comment)
74
+ end
75
+
76
+ #Returns the raw array so the developer can update large batches manually
77
+ #Need to modify to a param hash
78
+ def update_dirty(name,type,ttl,values,zone_apex = nil)
79
+ prev = self.clone
80
+ @name = name unless name.nil?
81
+ @type = type unless type.nil?
82
+ @ttl = ttl unless ttl.nil?
83
+ @values = values unless values.nil?
84
+ @zone_apex = zone_apex unless zone_apex.nil?
85
+ return [{:action => "DELETE", :record => prev},
86
+ {:action => "CREATE", :record => self}]
87
+ end
88
+
89
+ def to_s
90
+ if @weight
91
+ "#{@name} #{@type} #{@ttl} '#{@ident}' #{@weight} #{@values.join(",")}"
92
+ elsif @zone_apex
93
+ "#{@name} #{@type} #{@zone_apex} #{@values.join(",")}"
94
+ else
95
+ "#{@name} #{@type} #{@ttl} #{@values.join(",")}"
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,3 +1,3 @@
1
1
  module Route53
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,134 @@
1
+ module Route53
2
+ class Zone
3
+ attr_reader :host_url
4
+ attr_reader :name
5
+ attr_reader :records
6
+ attr_reader :conn
7
+
8
+ def initialize(name,host_url,conn)
9
+ @name = name
10
+ unless @name.end_with?(".")
11
+ @name += "."
12
+ end
13
+ @host_url = host_url
14
+ @conn = conn
15
+ end
16
+
17
+ def nameservers
18
+ return @nameservers if @nameservers
19
+ response = Nokogiri::XML(@conn.request(@conn.base_url + @host_url).to_s)
20
+ @nameservers = response.search("NameServer").map(&:inner_text)
21
+ @nameservers
22
+ end
23
+
24
+ def delete_zone
25
+ @conn.request(@conn.base_url + @host_url,"DELETE")
26
+ end
27
+
28
+ def create_zone(comment = nil)
29
+ xml_str = ""
30
+ xml = Builder::XmlMarkup.new(:target=>xml_str, :indent=>2)
31
+ xml.instruct!
32
+ xml.CreateHostedZoneRequest(:xmlns => @conn.endpoint+'doc/'+@conn.api+'/') { |create|
33
+ create.Name(@name)
34
+ # AWS lists this as required
35
+ # "unique string that identifies the request and that
36
+ # allows failed CreateHostedZone requests to be retried without the risk of executing the operation twice."
37
+ # Just going to pass a random string instead.
38
+ create.CallerReference(rand(2**32).to_s(16))
39
+ create.HostedZoneConfig { |conf|
40
+ conf.Comment(comment)
41
+ }
42
+ }
43
+ #puts "XML:\n#{xml_str}" if @conn.verbose
44
+ resp = @conn.request(@conn.base_url + "/hostedzone","POST",xml_str)
45
+ resp_xml = Nokogiri::XML(resp.raw_data)
46
+ @host_url = resp_xml.search("HostedZone").first.search("Id").first.inner_text
47
+ return resp
48
+ end
49
+
50
+ def get_records(type="ANY")
51
+ return nil if host_url.nil?
52
+
53
+ truncated = true
54
+ query = []
55
+ dom_records = []
56
+ while truncated
57
+ resp = @conn.request(@conn.base_url+@host_url+"/rrset?"+query.join("&"))
58
+ if resp.error?
59
+ return nil
60
+ end
61
+ zone_file = Nokogiri::XML(resp.raw_data)
62
+ records = zone_file.search("ResourceRecordSet")
63
+
64
+ records.each do |record|
65
+ #puts "Name:"+record.search("Name").first.inner_text if @conn.verbose
66
+ #puts "Type:"+record.search("Type").first.inner_text if @conn.verbose
67
+ #puts "TTL:"+record.search("TTL").first.inner_text if @conn.verbose
68
+ #record.search("Value").each do |val|
69
+ # #puts "Val:"+val.inner_text if @conn.verbose
70
+ #end
71
+ zone_apex_records = record.search("HostedZoneId")
72
+ values = record.search("Value").map { |val| val.inner_text }
73
+ values << record.search("DNSName").first.inner_text unless zone_apex_records.empty?
74
+ weight_records = record.search("Weight")
75
+ ident_records = record.search("SetIdentifier")
76
+ dom_records.push(DNSRecord.new(record.search("Name").first.inner_text,
77
+ record.search("Type").first.inner_text,
78
+ ((record.search("TTL").first.nil? ? '' : record.search("TTL").first.inner_text) if zone_apex_records.empty?),
79
+ values,
80
+ self,
81
+ (zone_apex_records.first.inner_text unless zone_apex_records.empty?),
82
+ (weight_records.first.inner_text unless weight_records.empty?),
83
+ (ident_records.first.inner_text unless ident_records.empty?)
84
+ ))
85
+ end
86
+
87
+ truncated = (zone_file.search("IsTruncated").first.inner_text == "true")
88
+ if truncated
89
+ next_name = zone_file.search("NextRecordName").first.inner_text
90
+ next_type = zone_file.search("NextRecordType").first.inner_text
91
+ query = ["name="+next_name,"type="+next_type]
92
+ end
93
+ end
94
+ @records = dom_records
95
+ if type != 'ANY'
96
+ return dom_records.select { |r| r.type == type }
97
+ end
98
+ return dom_records
99
+ end
100
+
101
+ #When deleting a record an optional value is available to specify just a single value within a recordset like an MX record
102
+ #Takes an array of [:action => , :record => ] where action is either CREATE or DELETE and record is a DNSRecord
103
+ def gen_change_xml(change_list,comment=nil)
104
+ #Get zone list and pick zone that matches most ending chars
105
+
106
+ xml_str = ""
107
+ xml = Builder::XmlMarkup.new(:target=>xml_str, :indent=>2)
108
+ xml.instruct!
109
+ xml.ChangeResourceRecordSetsRequest(:xmlns => @conn.endpoint+'doc/'+@conn.api+'/') { |req|
110
+ req.ChangeBatch { |batch|
111
+ batch.Comment(comment) unless comment.nil?
112
+ batch.Changes { |changes|
113
+ change_list.each { |change_item|
114
+ change_item[:record].gen_change_xml(changes,change_item[:action])
115
+ }
116
+ }
117
+ }
118
+ }
119
+ #puts "XML:\n#{xml_str}" if @conn.verbose
120
+ return xml_str
121
+ end
122
+
123
+ #For modifying multiple or single records within a single transaction
124
+ def perform_actions(change_list,comment=nil)
125
+ xml_str = gen_change_xml(change_list,comment)
126
+ @conn.request(@conn.base_url + @host_url+"/rrset","POST",xml_str)
127
+ end
128
+
129
+
130
+ def to_s
131
+ return "#{@name} #{@host_url}"
132
+ end
133
+ end
134
+ end
@@ -14,9 +14,16 @@ Gem::Specification.new do |s|
14
14
  s.required_rubygems_version = ">= 1.3.5"
15
15
 
16
16
  s.add_dependency "ruby-hmac"
17
- s.add_dependency "hpricot"
17
+ s.add_dependency "nokogiri", '~> 1.5.10'
18
18
  s.add_dependency "builder"
19
-
19
+
20
+ s.add_development_dependency "rspec"
21
+ s.add_development_dependency "vcr"
22
+ s.add_development_dependency "webmock", '~> 1.15.0'
23
+ s.add_development_dependency "pry"
24
+ s.add_development_dependency "wirble"
25
+ s.add_development_dependency "rake"
26
+
20
27
  s.files = `git ls-files`.split("\n")
21
28
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
22
29
  s.require_path = 'lib'
@@ -0,0 +1,198 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: head
5
+ uri: https://route53.amazonaws.com/date
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - '*/*'
12
+ User-Agent:
13
+ - Ruby
14
+ response:
15
+ status:
16
+ code: 200
17
+ message: OK
18
+ headers:
19
+ X-Amzn-Requestid:
20
+ - 9391c336-879f-11e3-8a96-59f82340bc25
21
+ Content-Type:
22
+ - content/unknown
23
+ Content-Length:
24
+ - '2'
25
+ Date:
26
+ - Mon, 27 Jan 2014 22:08:38 GMT
27
+ body:
28
+ encoding: UTF-8
29
+ string: ''
30
+ http_version:
31
+ recorded_at: Mon, 27 Jan 2014 22:08:39 GMT
32
+ - request:
33
+ method: get
34
+ uri: https://route53.amazonaws.com/2012-12-12/hostedzone/Z3HXUG22R8JWBK?
35
+ body:
36
+ encoding: US-ASCII
37
+ string: ''
38
+ headers:
39
+ Date:
40
+ - Mon, 27 Jan 2014 22:08:38 GMT
41
+ X-Amzn-Authorization:
42
+ - AWS3-HTTPS AWSAccessKeyId=AKIAIK7CBPSH72TVCGBA,Algorithm=HmacSHA256,Signature=5iNDIpJBlCE+2sAhhanRAOsppsBpm5chQpw/DuSvwZo=
43
+ Content-Type:
44
+ - text/xml; charset=UTF-8
45
+ Accept-Encoding:
46
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
47
+ Accept:
48
+ - '*/*'
49
+ User-Agent:
50
+ - Ruby
51
+ response:
52
+ status:
53
+ code: 200
54
+ message: OK
55
+ headers:
56
+ X-Amzn-Requestid:
57
+ - 93b3ca64-879f-11e3-9add-f35e9c3aa272
58
+ Content-Type:
59
+ - text/xml
60
+ Content-Length:
61
+ - '556'
62
+ Date:
63
+ - Mon, 27 Jan 2014 22:08:39 GMT
64
+ body:
65
+ encoding: UTF-8
66
+ string: |-
67
+ <?xml version="1.0"?>
68
+ <GetHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/"><HostedZone><Id>/hostedzone/Z3HXUG22R8JWBK</Id><Name>50projects.com.</Name><CallerReference>a9f6b14e</CallerReference><Config/><ResourceRecordSetCount>22</ResourceRecordSetCount></HostedZone><DelegationSet><NameServers><NameServer>ns-535.awsdns-02.net</NameServer><NameServer>ns-1725.awsdns-23.co.uk</NameServer><NameServer>ns-308.awsdns-38.com</NameServer><NameServer>ns-1452.awsdns-53.org</NameServer></NameServers></DelegationSet></GetHostedZoneResponse>
69
+ http_version:
70
+ recorded_at: Mon, 27 Jan 2014 22:08:40 GMT
71
+ - request:
72
+ method: post
73
+ uri: https://route53.amazonaws.com/2012-12-12/hostedzone?
74
+ body:
75
+ encoding: UTF-8
76
+ string: |
77
+ <?xml version="1.0" encoding="UTF-8"?>
78
+ <CreateHostedZoneRequest xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
79
+ <Name>example.com.</Name>
80
+ <CallerReference>dbdc62da</CallerReference>
81
+ <HostedZoneConfig>
82
+ <Comment/>
83
+ </HostedZoneConfig>
84
+ </CreateHostedZoneRequest>
85
+ headers:
86
+ Date:
87
+ - Mon, 27 Jan 2014 22:08:38 GMT
88
+ X-Amzn-Authorization:
89
+ - AWS3-HTTPS AWSAccessKeyId=AKIAIK7CBPSH72TVCGBA,Algorithm=HmacSHA256,Signature=5iNDIpJBlCE+2sAhhanRAOsppsBpm5chQpw/DuSvwZo=
90
+ Content-Type:
91
+ - text/xml; charset=UTF-8
92
+ Accept-Encoding:
93
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
94
+ Accept:
95
+ - '*/*'
96
+ User-Agent:
97
+ - Ruby
98
+ response:
99
+ status:
100
+ code: 201
101
+ message: Created
102
+ headers:
103
+ X-Amzn-Requestid:
104
+ - 25358b9e-87a0-11e3-bf8c-1f13207fd337
105
+ Location:
106
+ - https://route53.amazonaws.com/2012-12-12//hostedzone/Z3E84CELCS8770
107
+ Content-Type:
108
+ - text/xml
109
+ Content-Length:
110
+ - '716'
111
+ Date:
112
+ - Mon, 27 Jan 2014 22:12:47 GMT
113
+ body:
114
+ encoding: UTF-8
115
+ string: |-
116
+ <?xml version="1.0"?>
117
+ <CreateHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/"><HostedZone><Id>/hostedzone/Z3E84CELCS8770</Id><Name>example.com.</Name><CallerReference>dbdc62da</CallerReference><Config><Comment></Comment></Config><ResourceRecordSetCount>2</ResourceRecordSetCount></HostedZone><ChangeInfo><Id>/change/C2ZW86S7M9XIU1</Id><Status>PENDING</Status><SubmittedAt>2014-01-27T22:12:47.576Z</SubmittedAt></ChangeInfo><DelegationSet><NameServers><NameServer>ns-1621.awsdns-10.co.uk</NameServer><NameServer>ns-510.awsdns-63.com</NameServer><NameServer>ns-1232.awsdns-26.org</NameServer><NameServer>ns-806.awsdns-36.net</NameServer></NameServers></DelegationSet></CreateHostedZoneResponse>
118
+ http_version:
119
+ recorded_at: Mon, 27 Jan 2014 22:12:47 GMT
120
+ - request:
121
+ method: get
122
+ uri: https://route53.amazonaws.com/2012-12-12/change/C2ZW86S7M9XIU1?
123
+ body:
124
+ encoding: US-ASCII
125
+ string: ''
126
+ headers:
127
+ Date:
128
+ - Mon, 27 Jan 2014 22:08:38 GMT
129
+ X-Amzn-Authorization:
130
+ - AWS3-HTTPS AWSAccessKeyId=AKIAIK7CBPSH72TVCGBA,Algorithm=HmacSHA256,Signature=5iNDIpJBlCE+2sAhhanRAOsppsBpm5chQpw/DuSvwZo=
131
+ Content-Type:
132
+ - text/xml; charset=UTF-8
133
+ Accept-Encoding:
134
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
135
+ Accept:
136
+ - '*/*'
137
+ User-Agent:
138
+ - Ruby
139
+ response:
140
+ status:
141
+ code: 200
142
+ message: OK
143
+ headers:
144
+ X-Amzn-Requestid:
145
+ - 278a2a47-87a0-11e3-a58f-435e724ec933
146
+ Content-Type:
147
+ - text/xml
148
+ Content-Length:
149
+ - '246'
150
+ Date:
151
+ - Mon, 27 Jan 2014 22:12:47 GMT
152
+ body:
153
+ encoding: UTF-8
154
+ string: |-
155
+ <?xml version="1.0"?>
156
+ <GetChangeResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/"><ChangeInfo><Id>/change/C2ZW86S7M9XIU1</Id><Status>PENDING</Status><SubmittedAt>2014-01-27T22:12:47.576Z</SubmittedAt></ChangeInfo></GetChangeResponse>
157
+ http_version:
158
+ recorded_at: Mon, 27 Jan 2014 22:12:48 GMT
159
+ - request:
160
+ method: delete
161
+ uri: https://route53.amazonaws.com/2012-12-12/hostedzone/Z3E84CELCS8770?
162
+ body:
163
+ encoding: US-ASCII
164
+ string: ''
165
+ headers:
166
+ Date:
167
+ - Mon, 27 Jan 2014 22:08:38 GMT
168
+ X-Amzn-Authorization:
169
+ - AWS3-HTTPS AWSAccessKeyId=AKIAIK7CBPSH72TVCGBA,Algorithm=HmacSHA256,Signature=5iNDIpJBlCE+2sAhhanRAOsppsBpm5chQpw/DuSvwZo=
170
+ Content-Type:
171
+ - text/xml; charset=UTF-8
172
+ Accept-Encoding:
173
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
174
+ Accept:
175
+ - '*/*'
176
+ User-Agent:
177
+ - Ruby
178
+ response:
179
+ status:
180
+ code: 400
181
+ message: Bad Request
182
+ headers:
183
+ X-Amzn-Requestid:
184
+ - 8f57cd8b-87a0-11e3-b239-49b331f5c62c
185
+ Content-Type:
186
+ - text/xml
187
+ Content-Length:
188
+ - '348'
189
+ Date:
190
+ - Mon, 27 Jan 2014 22:15:41 GMT
191
+ body:
192
+ encoding: UTF-8
193
+ string: |-
194
+ <?xml version="1.0"?>
195
+ <ErrorResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/"><Error><Type>Sender</Type><Code>RequestExpired</Code><Message>Request timestamp: Mon, 27 Jan 2014 22:08:38 GMT expired. It must be within 300 secs/ of server time.</Message></Error><RequestId>8f57cd8b-87a0-11e3-b239-49b331f5c62c</RequestId></ErrorResponse>
196
+ http_version:
197
+ recorded_at: Mon, 27 Jan 2014 22:15:42 GMT
198
+ recorded_with: VCR 2.8.0