1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

[aws|cdn] add request mock support for AWS Cloudfront

This patch adds support for mocking the cloudfront API.
CDN Tests now can both be run with mocking and non mocking
activated.

Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
This commit is contained in:
Brice Figureau 2012-09-28 00:00:16 +02:00 committed by Brice Figureau
parent 9666c9c7e0
commit e877f95034
22 changed files with 489 additions and 51 deletions

View file

@ -33,12 +33,12 @@ module Fog
class Mock
def self.data
@data ||= Hash.new do |hash, region|
hash[region] = Hash.new do |region_hash, key|
region_hash[key] = {
:buckets => {}
@data ||= Hash.new do |hash, key|
hash[key] = {
:distributions => {},
:streaming_distributions => {},
:invalidations => {}
}
end
end
end
@ -50,15 +50,14 @@ module Fog
require 'mime/types'
@use_iam_profile = options[:use_iam_profile]
setup_credentials(options)
@region = options[:region]
end
def data
self.class.data[@region][@aws_access_key_id]
self.class.data[@aws_access_key_id]
end
def reset_data
self.class.data[@region].delete(@aws_access_key_id)
self.class.data.delete(@aws_access_key_id)
end
def signature(params)
@ -68,6 +67,62 @@ module Fog
def setup_credentials(options={})
@aws_access_key_id = options[:aws_access_key_id]
end
def self.distribution_id
random_id(14)
end
def self.generic_id
random_id(14)
end
def self.domain_name
"#{random_id(12).downcase}.cloudfront.net"
end
def self.random_id(length)
Fog::Mock.random_selection("abcdefghijklmnopqrstuvwxyz0123456789", length).upcase
end
CDN_ERRORS = {
:access_denies => {:code => 'AccessDenied',:msg => 'Access denied.',:status => 403},
:inappropriate_xml => {:code => 'InappropriateXML',:msg => 'The XML document you provided was well-formed and valid, but not appropriate for this operation.',:status => 400},
:internal_error => {:code => 'InternalError',:msg => 'We encountered an internal error. Please try again.',:status => 500},
:invalid_action => {:code => 'InvalidAction',:msg => 'The action specified is not valid.',:status => 400},
:invalid_argument => {:code => 'InvalidArgument',:msg => '%s', :status => 400},
:not_implemented => {:code => 'NotImplemented', :msg => 'Not implemented.',:status => 501},
:no_such_distribution => { :code => 'NoSuchDistribution', :msg => 'The specified distribution does not exist', :status => 404 },
:no_such_streaming_distribution => { :code => 'NoSuchStreamingDistribution', :msg => 'The specified streaming distribution does not exist', :status => 404 },
:no_such_invalidation => { :code => 'NoSuchInvalidation', :msg => 'The specified invalidation does not exist', :status => 404 },
:cname_exists => { :code => 'CNAMEAlreadyExists', :msg => 'One or more of the CNAMEs you provided are already associated with a different distribution', :status => 409 },
:illegal_update => { :code => 'IllegalUpdate', :msg => 'Origin and CallerReference cannot be updated.', :status => 400 },
:invalid_if_match_version => { :code => 'InvalidIfMatchVersion', :msg => 'The If-Match version is missing or not valid for the distribution.', :status => 400},
:distribution_not_disabled => { :code => 'DistributionNotDisabled', :msg => 'The distribution you are trying to delete has not been disabled.', :status => 409 },
}
def self.error(code, argument = '')
if error = CDN_ERRORS[code]
raise_error(error[:status], error[:code], error[:msg] % argument)
end
end
def self.raise_error(status, code, message='')
response = Excon::Response.new
response.status = status
response.body = <<EOF
<ErrorResponse xmlns="http://cloudfront.amazonaws.com/doc/2010-11-01/">
<Error>
<Type>Sender</Type>
<Code>#{code}</Code>
<Message>#{message}.</Message>
</Error>
<RequestId>#{Fog::AWS::Mock.request_id}</RequestId>
</ErrorResponse>
EOF
raise(Excon::Errors.status_error({:expects => 201}, response))
end
end
class Real

View file

@ -8,7 +8,7 @@ module Fog
def all(options = {})
merge_attributes(options)
data = list_distributions(options).body
merge_attributes('IsTruncated' => data['IsTruncated'], 'Marker' => data['Marker'])
merge_attributes('IsTruncated' => data['IsTruncated'], 'Marker' => data['Marker'], 'MaxItems' => data['MaxItems'])
if summary = data['DistributionSummary']
load(summary.map { |a| { 'DistributionConfig' => a } })
else

View file

@ -23,6 +23,37 @@ module Fog
end
end
class Mock
def delete_distribution(distribution_id, etag)
distribution = self.data[:distributions][distribution_id]
if distribution
if distribution['ETag'] != etag
Fog::CDN::AWS::Mock.error(:invalid_if_match_version)
end
unless distribution['DistributionConfig']['CallerReference']
Fog::CDN::AWS::Mock.error(:illegal_update)
end
if distribution['DistributionConfig']['Enabled']
Fog::CDN::AWS::Mock.error(:distribution_not_disabled)
end
self.data[:distributions].delete(distribution_id)
self.data[:invalidations].delete(distribution_id)
response = Excon::Response.new
response.status = 204
response.body = "x-amz-request-id: #{Fog::AWS::Mock.request_id}"
response
else
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
end
end
end
end
end

View file

@ -23,6 +23,36 @@ module Fog
end
end
class Mock
def delete_streaming_distribution(distribution_id, etag)
distribution = self.data[:streaming_distributions][distribution_id]
if distribution
if distribution['ETag'] != etag
Fog::CDN::AWS::Mock.error(:invalid_if_match_version)
end
unless distribution['StreamingDistributionConfig']['CallerReference']
Fog::CDN::AWS::Mock.error(:illegal_update)
end
if distribution['StreamingDistributionConfig']['Enabled']
Fog::CDN::AWS::Mock.error(:distribution_not_disabled)
end
self.data[:streaming_distributions].delete(distribution_id)
response = Excon::Response.new
response.status = 204
response.body = "x-amz-request-id: #{Fog::AWS::Mock.request_id}"
response
else
Fog::CDN::AWS::Mock.error(:no_such_streaming_distribution)
end
end
end
end
end
end

View file

@ -52,6 +52,34 @@ module Fog
end
end
class Mock
def get_distribution(distribution_id)
response = Excon::Response.new
distribution = self.data[:distributions][distribution_id]
unless distribution
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
if distribution['Status'] == 'InProgress' && (Time.now - Time.parse(distribution['LastModifiedTime']) >= Fog::Mock.delay * 2)
distribution['Status'] = 'Deployed'
end
etag = Fog::CDN::AWS::Mock.generic_id
response.status = 200
response.body = {
'InProgressInvalidationBatches' => 0,
}.merge(distribution.reject { |k,v| k == 'ETag' })
response.headers['ETag'] = etag
distribution['ETag'] = etag
response
end
end
end
end
end

View file

@ -54,6 +54,35 @@ module Fog
end
end
class Mock
def get_distribution_list(options = {})
response = Excon::Response.new
response.status = 200
distributions = self.data[:distributions].values
response.body = {
'Marker' => Fog::Mock.random_hex(16),
'IsTruncated' => false,
'MaxItems' => 100,
'DistributionSummary' => distributions.map { |d| to_distribution_summary(d) }
}
response
end
private
def to_distribution_summary(d)
{
'DomainName' => d['DomainName'],
'Id' => d['Id'],
'LastModifiedTime' => d['LastModifiedTime']
}.merge(d['DistributionConfig'])
end
end
end
end
end

View file

@ -29,6 +29,32 @@ module Fog
end
end
class Mock
def get_invalidation(distribution_id, invalidation_id)
distribution = self.data[:distributions][distribution_id]
unless distribution
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
invalidation = self.data[:invalidations][distribution_id][invalidation_id]
unless invalidation
Fog::CDN::AWS::Mock.error(:no_such_invalidation)
end
if invalidation['Status'] == 'InProgress' && (Time.now - Time.parse(invalidation['CreateTime']) >= Fog::Mock.delay * 2)
invalidation['Status'] = 'Completed'
distribution['InProgressInvalidationBatches'] -= 1
end
response = Excon::Response.new
response.status = 200
response.body = invalidation
response
end
end
end
end
end

View file

@ -37,6 +37,47 @@ module Fog
end
end
class Mock
def get_invalidation_list(distribution_id, options = {})
distribution = self.data[:distributions][distribution_id]
unless distribution
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
invalidations = (self.data[:invalidations][distribution_id] || {}).values
invalidations.each do |invalidation|
if invalidation['Status'] == 'InProgress' && (Time.now - Time.parse(invalidation['CreateTime']) >= Fog::Mock.delay * 2)
invalidation['Status'] = 'Completed'
distribution['InProgressInvalidationBatches'] -= 1
end
end
response = Excon::Response.new
response.status = 200
response.body = {
'Marker' => Fog::Mock.random_hex(16),
'IsTruncated' => false,
'MaxItems' => 100,
'InvalidationSummary' => invalidations.map { |i| to_invalidation_summary(i) }
}
response
end
private
def to_invalidation_summary(d)
{
'Id' => d['Id'],
'Status' => d['Status']
}
end
end
end
end
end

View file

@ -45,6 +45,32 @@ module Fog
end
end
class Mock
def get_streaming_distribution(distribution_id)
response = Excon::Response.new
distribution = self.data[:streaming_distributions][distribution_id]
unless distribution
Fog::CDN::AWS::Mock.error(:no_such_streaming_distribution)
end
if distribution['Status'] == 'InProgress' && (Time.now - Time.parse(distribution['LastModifiedTime']) >= Fog::Mock.delay * 2)
distribution['Status'] = 'Deployed'
end
etag = Fog::CDN::AWS::Mock.generic_id
response.status = 200
response.body = distribution.reject { |k,v| k == 'ETag' }
response.headers['ETag'] = etag
distribution['ETag'] = etag
response
end
end
end
end
end

View file

@ -54,6 +54,36 @@ module Fog
end
end
class Mock
def get_streaming_distribution_list(options = {})
response = Excon::Response.new
response.status = 200
distributions = self.data[:streaming_distributions].values
response.body = {
'Marker' => Fog::Mock.random_hex(16),
'IsTruncated' => false,
'MaxItems' => 100,
'StreamingDistributionSummary' => distributions.map { |d| to_streaming_distribution_summary(d) }
}
response
end
private
def to_streaming_distribution_summary(d)
{
'DomainName' => d['DomainName'],
'Id' => d['Id'],
'LastModifiedTime' => d['LastModifiedTime']
}.merge(d['StreamingDistributionConfig'])
end
end
end
end
end

View file

@ -86,6 +86,50 @@ module Fog
end
end
class Mock
require 'time'
def post_distribution(options = {})
if self.data[:distributions].values.any? { |d| (d['CNAME'] & (options['CNAME']||[])).empty? }
Fog::CDN::AWS::Mock.error(:invalid_argument, 'CNAME is already in use')
end
response = Excon::Response.new
response.status = 201
options['CallerReference'] = Time.now.to_i.to_s
dist_id = Fog::CDN::AWS::Mock.distribution_id
distribution = {
'DomainName' => Fog::CDN::AWS::Mock.domain_name,
'Id' => dist_id,
'Status' => 'InProgress',
'LastModifiedTime' => Time.now.utc.iso8601,
'InProgressInvalidationBatches' => 0,
'DistributionConfig' => {
'CallerReference' => options['CallerReference'],
'CNAME' => options['CNAME'] || [],
'Comment' => options['Comment'],
'Enabled' => options['Enabled'],
'Logging' => {
'Bucket' => options['Bucket'],
'Prefix' => options['Prefix']
},
'S3Origin' => options['S3Origin'],
'CustomOrigin' => options['CustomOrigin'],
'TrustedSigners' => options['TrustedSigners'] || []
}
}
self.data[:distributions][dist_id] = distribution
response.body = distribution
response
end
end
end
end
end

View file

@ -46,6 +46,39 @@ module Fog
end
end
class Mock
def post_invalidation(distribution_id, paths, caller_reference = Time.now.to_i.to_s)
distribution = self.data[:distributions][distribution_id]
if distribution
invalidation_id = Fog::CDN::AWS::Mock.distribution_id
invalidation = {
'Id' => invalidation_id,
'Status' => 'InProgress',
'CreateTime' => Time.now.utc.iso8601,
'InvalidationBatch' => {
'CallerReference' => caller_reference,
'Path' => paths
}
}
distribution['InProgressInvalidationBatches'] += 1
self.data[:invalidations][distribution_id] ||= {}
self.data[:invalidations][distribution_id][invalidation_id] = invalidation
response = Excon::Response.new
response.status = 201
response.body = invalidation
response
else
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
end
end
end
end
end

View file

@ -73,6 +73,50 @@ module Fog
end
end
class Mock
require 'time'
def post_streaming_distribution(options = {})
if self.data[:streaming_distributions].values.any? { |d| (d['CNAME'] & (options['CNAME']||[])).empty? }
Fog::CDN::AWS::Mock.error(:invalid_argument, 'CNAME is already in use')
end
response = Excon::Response.new
response.status = 201
options['CallerReference'] = Time.now.to_i.to_s
dist_id = Fog::CDN::AWS::Mock.distribution_id
distribution = {
'DomainName' => Fog::CDN::AWS::Mock.domain_name,
'Id' => dist_id,
'Status' => 'InProgress',
'LastModifiedTime' => Time.now.utc.iso8601,
'StreamingDistributionConfig' => {
'CallerReference' => options['CallerReference'],
'CNAME' => options['CNAME'] || [],
'Comment' => options['Comment'],
'Enabled' => options['Enabled'],
'Logging' => {
'Bucket' => options['Bucket'],
'Prefix' => options['Prefix']
},
'S3Origin' => options['S3Origin'],
'TrustedSigners' => options['TrustedSigners'] || []
}
}
self.data[:streaming_distributions][dist_id] = distribution
response.body = distribution
response
end
end
end
end
end

View file

@ -89,6 +89,34 @@ module Fog
end
end
class Mock
def put_distribution_config(distribution_id, etag, options = {})
distribution = self.data[:distributions][distribution_id]
if distribution
if distribution['ETag'] != etag
Fog::CDN::AWS::Mock.error(:invalid_if_match_version)
end
unless distribution['DistributionConfig']['CallerReference']
Fog::CDN::AWS::Mock.error(:illegal_update)
end
distribution['DistributionConfig'].merge!(options)
distribution['Status'] = 'InProgress'
response = Excon::Response.new
response.status = 200
response.headers['ETag'] = Fog::CDN::AWS::Mock.generic_id
response.body = distribution.merge({ 'LastModifiedTime' => Time.now.utc.iso8601 }).reject{ |k,v| k == 'ETag' }
response
else
Fog::CDN::AWS::Mock.error(:no_such_distribution)
end
end
end
end
end
end

View file

@ -78,6 +78,34 @@ module Fog
end
end
class Mock
def put_streaming_distribution_config(distribution_id, etag, options = {})
distribution = self.data[:streaming_distributions][distribution_id]
if distribution
if distribution['ETag'] != etag
Fog::CDN::AWS::Mock.error(:invalid_if_match_version)
end
unless distribution['StreamingDistributionConfig']['CallerReference']
Fog::CDN::AWS::Mock.error(:illegal_update)
end
distribution['StreamingDistributionConfig'].merge!(options)
distribution['Status'] = 'InProgress'
response = Excon::Response.new
response.status = 200
response.headers['ETag'] = Fog::CDN::AWS::Mock.generic_id
response.body = distribution.merge({ 'LastModifiedTime' => Time.now.utc.iso8601 }).reject{ |k,v| k == 'ETag' }
response
else
Fog::CDN::AWS::Mock.error(:no_such_streaming_distribution)
end
end
end
end
end
end

View file

@ -1,17 +1,13 @@
Shindo.tests("Fog::CDN[:aws] | distribution", ['aws', 'cdn']) do
params = { :s3_origin => { 'DNSName' => 'fog_test_cdn.s3.amazonaws.com'}, :enabled => true }
model_tests(Fog::CDN[:aws].distributions, params, false) do
model_tests(Fog::CDN[:aws].distributions, params, true) do
# distribution needs to be ready before being disabled
tests("#ready? - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.wait_for { ready? }
end
# and disabled before being distroyed
tests("#disable - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.disable
@instance.wait_for { ready? }
end

View file

@ -1,17 +1,13 @@
Shindo.tests("Fog::CDN[:aws] | distributions", ['aws', 'cdn']) do
params = { :s3_origin => { 'DNSName' => 'fog_test_cdn.s3.amazonaws.com'}, :enabled => true}
collection_tests(Fog::CDN[:aws].distributions, params, false) do
collection_tests(Fog::CDN[:aws].distributions, params, true) do
# distribution needs to be ready before being disabled
tests("#ready? - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.wait_for { ready? }
end
# and disabled before being distroyed
tests("#disable - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.disable
@instance.wait_for { ready? }
end

View file

@ -1,14 +1,12 @@
Shindo.tests("Fog::CDN[:aws] | invalidation", ['aws', 'cdn']) do
pending if Fog.mocking?
tests("distributions#create").succeeds do
@distribution = Fog::CDN[:aws].distributions.create(:s3_origin => {'DNSName' => 'fog_test.s3.amazonaws.com'}, :enabled => true)
end
params = { :paths => [ '/index.html', '/path/to/index.html' ] }
model_tests(@distribution.invalidations, params, false) do
model_tests(@distribution.invalidations, params, true) do
tests("#id") do
returns(true) { @instance.identity != nil }

View file

@ -1,11 +1,9 @@
Shindo.tests("Fog::CDN[:aws] | invalidations", ['aws', 'cdn']) do
pending if Fog.mocking?
tests("distributions#create").succeeds do
@distribution = Fog::CDN[:aws].distributions.create(:s3_origin => {'DNSName' => 'fog_test.s3.amazonaws.com'}, :enabled => true)
end
collection_tests(@distribution.invalidations, { :paths => [ '/index.html' ]}, false)
collection_tests(@distribution.invalidations, { :paths => [ '/index.html' ]}, true)
tests("distribution#destroy - may take 15/20 minutes to complete").succeeds do
@distribution.wait_for { ready? }

View file

@ -1,17 +1,13 @@
Shindo.tests("Fog::CDN[:aws] | streaming_distribution", ['aws', 'cdn']) do
params = { :s3_origin => { 'DNSName' => 'fog_test_cdn.s3.amazonaws.com'}, :enabled => true }
model_tests(Fog::CDN[:aws].streaming_distributions, params, false) do
model_tests(Fog::CDN[:aws].streaming_distributions, params, true) do
# distribution needs to be ready before being disabled
tests("#ready? - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.wait_for { ready? }
end
# and disabled before being distroyed
tests("#disable - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.disable
@instance.wait_for { ready? }
end

View file

@ -1,17 +1,13 @@
Shindo.tests("Fog::CDN[:aws] | streaming_distributions", ['aws', 'cdn']) do
params = { :s3_origin => { 'DNSName' => 'fog_test_cdn.s3.amazonaws.com'}, :enabled => true}
collection_tests(Fog::CDN[:aws].streaming_distributions, params, false) do
collection_tests(Fog::CDN[:aws].streaming_distributions, params, true) do
# distribution needs to be ready before being disabled
tests("#ready? - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.wait_for { ready? }
end
# and disabled before being distroyed
tests("#disable - may take 15 minutes to complete...").succeeds do
pending if Fog.mocking?
@instance.disable
@instance.wait_for { ready? }
end

View file

@ -5,7 +5,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
tests('distributions success') do
test('get current ditribution list count') do
pending if Fog.mocking?
@count= 0
response = @cf_connection.get_distribution_list
@ -18,7 +17,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
end
test('create distribution') {
pending if Fog.mocking?
result = false
@ -36,7 +34,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("get info on distribution #{@dist_id}") {
pending if Fog.mocking?
result = false
@ -53,7 +50,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test('list distributions') do
pending if Fog.mocking?
result = false
@ -76,7 +72,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
end
test("invalidate paths") {
pending if Fog.mocking?
response = @cf_connection.post_invalidation(@dist_id, ["/test.html", "/path/to/file.html"])
if response.status == 201
@ -87,7 +82,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("list invalidations") {
pending if Fog.mocking?
result = false
@ -102,7 +96,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("get invalidation information") {
pending if Fog.mocking?
result = false
@ -119,7 +112,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("disable distribution #{@dist_id} - can take 15 minutes to complete...") {
pending if Fog.mocking?
result = false
@ -135,7 +127,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("remove distribution #{@dist_id}") {
pending if Fog.mocking?
result = true
@ -158,7 +149,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
tests('streaming distributions success') do
test('get current streaming ditribution list count') do
pending if Fog.mocking?
@count= 0
response = @cf_connection.get_streaming_distribution_list
@ -171,7 +161,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
end
test('create distribution') {
pending if Fog.mocking?
result = false
@ -189,7 +178,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("get info on distribution #{@dist_id}") {
pending if Fog.mocking?
result = false
@ -206,7 +194,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test('list streaming distributions') do
pending if Fog.mocking?
result = false
@ -229,7 +216,6 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
end
test("disable distribution #{@dist_id} - can take 15 minutes to complete...") {
pending if Fog.mocking?
result = false
@ -245,13 +231,12 @@ Shindo.tests('Fog::CDN[:aws] | CDN requests', ['aws', 'cdn']) do
}
test("remove distribution #{@dist_id}") {
pending if Fog.mocking?
result = true
# unfortunately you can delete only after a distribution becomes Deployed
Fog.wait_for {
response = @cf_connection.get_distribution(@dist_id)
response = @cf_connection.get_streaming_distribution(@dist_id)
@etag = response.headers['ETag']
response.status == 200 and response.body['Status'] == 'Deployed'
}