Skip to content

Commit

Permalink
Merge pull request fog#2498 from smashwilson/mock-rackspace-storage
Browse files Browse the repository at this point in the history
[Rackspace | Storage] Mocks for the Storage service
  • Loading branch information
Kyle Rames committed Jan 6, 2014
2 parents 51c1c41 + a917c1a commit 95f2b1f
Show file tree
Hide file tree
Showing 36 changed files with 1,045 additions and 222 deletions.
36 changes: 21 additions & 15 deletions lib/fog/rackspace/cdn.rb
Expand Up @@ -23,6 +23,17 @@ module Base
"X-Cdn-Ssl-Uri" => :ssl_uri
}.freeze

def apply_options(options)
# api_key and username missing from instance variable sets
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_username = options[:rackspace_username]

@connection_options = options[:connection_options] || {}
@rackspace_auth_url = options[:rackspace_auth_url]
@rackspace_cdn_url = options[:rackspace_cdn_url]
@rackspace_region = options[:rackspace_region] || :dfw
end

def service_name
:cloudFilesCDN
end
Expand All @@ -35,6 +46,12 @@ def request_id_header
"X-Trans-Id"
end

# Returns true if CDN service is enabled
# @return [Boolean]
def enabled?
@enabled
end

def endpoint_uri(service_endpoint_url=nil)
@uri = super(@rackspace_cdn_url || service_endpoint_url, :rackspace_cdn_url)
end
Expand Down Expand Up @@ -97,7 +114,9 @@ def self.reset
end

def initialize(options={})
@rackspace_username = options[:rackspace_username]
apply_options(options)
authenticate(options)
@enabled = !! endpoint_uri
end

def data
Expand All @@ -119,14 +138,7 @@ class Real < Fog::Rackspace::Service
include Base

def initialize(options={})
# api_key and username missing from instance variable sets
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_username = options[:rackspace_username]

@connection_options = options[:connection_options] || {}
@rackspace_auth_url = options[:rackspace_auth_url]
@rackspace_cdn_url = options[:rackspace_cdn_url]
@rackspace_region = options[:rackspace_region] || :dfw
apply_options(options)
authenticate(options)
@enabled = false
@persistent = options[:persistent] || false
Expand All @@ -137,12 +149,6 @@ def initialize(options={})
end
end

# Returns true if CDN service is enabled
# @return [Boolean]
def enabled?
@enabled
end

# Resets CDN connection
def reload
@cdn_connection.reset
Expand Down
40 changes: 25 additions & 15 deletions lib/fog/rackspace/identity.rb
Expand Up @@ -34,18 +34,16 @@ class Identity < Fog::Service
request :update_user
request :delete_user

class Mock < Fog::Rackspace::Service
attr_reader :service_catalog

def request
Fog::Mock.not_implemented
module Common
attr_reader :service_catalog, :auth_token

def authenticate(options={})
data = self.create_token(@rackspace_username, @rackspace_api_key).body
@service_catalog = ServiceCatalog.from_response(self, data)
@auth_token = data['access']['token']['id']
end
end

class Real < Fog::Rackspace::Service
attr_reader :service_catalog, :auth_token

def initialize(options={})
def apply_options(options)
@rackspace_username = options[:rackspace_username]
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_region = options[:rackspace_region]
Expand All @@ -58,15 +56,27 @@ def initialize(options={})
@scheme = @uri.scheme
@persistent = options[:persistent] || false
@connection_options = options[:connection_options] || {}
@connection = Fog::Connection.new(@uri.to_s, @persistent, @connection_options)
end
end

class Mock < Fog::Rackspace::Service
include Common

def initialize(options={})
apply_options(options)

authenticate
end
end

class Real < Fog::Rackspace::Service
include Common

def authenticate(options={})
data = self.create_token(@rackspace_username, @rackspace_api_key).body
@service_catalog = ServiceCatalog.from_response(self, data)
@auth_token = data['access']['token']['id']
def initialize(options={})
apply_options(options)
@connection = Fog::Connection.new(@uri.to_s, @persistent, @connection_options)

authenticate
end
end
end
Expand Down
7 changes: 5 additions & 2 deletions lib/fog/rackspace/models/storage/directory.rb
Expand Up @@ -166,8 +166,11 @@ def streaming_url
def save
requires :key
create_or_update_container
raise Fog::Storage::Rackspace::Error.new("Directory can not be set as :public without a CDN provided") if public? && !cdn_enabled?
@urls = service.cdn.publish_container(self, public?)
if cdn_enabled?
@urls = service.cdn.publish_container(self, public?)
else
raise Fog::Storage::Rackspace::Error.new("Directory can not be set as :public without a CDN provided") if public?
end
true
end

Expand Down
192 changes: 192 additions & 0 deletions lib/fog/rackspace/requests/identity/create_token.rb
Expand Up @@ -20,6 +20,198 @@ def create_token(username, api_key)
)
end
end

class Mock
def create_token(username, api_key)
unless username == 'baduser' || api_key == 'bad_key'
compute_tenant = Fog::Mock.random_numbers(6)
object_tenant = generate_object_tenant

response = Excon::Response.new
response.status = 200
response.body = {
"access" => {
"token"=> {
"id" => Fog::Mock.random_hex(32),
"expires" => (Time.now.utc + 86400).strftime("%Y-%m-%dT%H:%M:%S.%LZ"),
"tenant" => { "id" => compute_tenant, "name" => compute_tenant }
},
"user" => {
"id" => Fog::Mock.random_numbers(6),
"name" => username,
"roles" => [
{
"id" => Fog::Mock.random_numbers(1),
"description" => "Fake Role for an object store",
"name" => "object-store:default"
},
{
"id" => Fog::Mock.random_numbers(1),
"description" => "Fake Role for a compute cluster",
"name" => "compute:default"
}
]
},
"serviceCatalog" => build_service_catalog(compute_tenant, object_tenant),
}
}
response
else
response = Excon::Response.new
response.status = 401
response.body = {
"unauthorized" => {
"code" => 401,
"message" => "Username or API key is invalid."
}
}
raise Excon::Errors::Unauthorized.new('Unauthorized', nil, response)
end
end

# Generate a realistic-looking object tenant ID.
def generate_object_tenant
uuid = [8, 4, 4, 4, 12].map { |n| Fog::Mock.random_hex(n) }.join('_')
"FogMockFS_#{uuid}"
end

# Construct a full, fake service catalog.
#
# @param compute_tenant [String] Tenant ID to be used in entries for
# compute-based services (most of them).
# @param object_tenant [String] Tenant ID to be used in object-store
# related entries.
#
# @return [Hash] A fully-populated, valid service catalog.
def build_service_catalog(compute_tenant, object_tenant)
[
service_catalog_entry("cloudFilesCDN", "rax:object-cdn", object_tenant,
:public_url => lambda do |r|
"https://cdn#{Fog::Mock.random_numbers(1)}.clouddrive.com/v1/#{object_tenant}"
end),

service_catalog_entry("cloudFiles", "object-store", object_tenant,
:internal_url_snet => true,
:public_url => lambda do |r|
"https://storage101.#{r}#{Fog::Mock.random_numbers(1)}.clouddrive.com/v1/#{object_tenant}"
end),

service_catalog_entry("cloudMonitoring", "rax:monitor", compute_tenant,
:single_endpoint => true, :rackspace_api_name => 'monitoring'),

service_catalog_entry("cloudServersOpenStack", "compute", compute_tenant,
:version_base_url => lambda { |r| "https://#{r}.servers.api.rackspacecloud.com" },
:version_id => "2"),

service_catalog_entry("cloudBlockStorage", "volume", compute_tenant,
:rackspace_api_name => 'blockstorage', :rackspace_api_version => '1'),

service_catalog_entry("cloudDatabases", "rax:database", compute_tenant,
:rackspace_api_name => 'databases'),

service_catalog_entry("cloudLoadBalancers", "rax:load-balander", compute_tenant,
:rackspace_api_name => 'loadbalancers'),

service_catalog_entry("cloudDNS", "rax:dns", compute_tenant,
:single_endpoint => true, :rackspace_api_name => 'dns'),

service_catalog_entry("cloudOrchestration", "orchestration", compute_tenant,
:rackspace_api_name => 'orchestration', :rackspace_api_version => '1'),

service_catalog_entry("cloudQueues", "rax:queues", compute_tenant,
:internal_url_snet => true,
:rackspace_api_name => 'queues', :rackspace_api_version => '1'),

service_catalog_entry("cloudBackup", "rax:backup", compute_tenant,
:rackspace_api_name => 'backup'),

service_catalog_entry("cloudImages", "image", compute_tenant,
:rackspace_api_name => 'images', :rackspace_api_version => '2'),

service_catalog_entry("autoscale", "rax:autoscale", compute_tenant,
:rackspace_api_name => 'autoscale'),

service_catalog_entry("cloudServers", "compute", compute_tenant,
:single_endpoint => true,
:version_base_url => lambda { |r| "https://servers.api.rackspacecloud.com" },
:version_id => '1.0')
]
end

# Generate an individual service catalog entry for a fake service
# catalog. Understands common patterns used within Rackspace
# service catalogs.
#
# @param name [String] The required "name" attribute of the
# service catalog entry.
# @param type [String] The required "type" attribute.
# @param tenant_id [String] Tenant ID to be used for this service.
#
# @param options [Hash] Control the contents of the generated entry.
# @option options [Proc] :public_url Callable invoked with each region
# (or `nil`) to generate a `publicURL` for that region.
# @option options [Boolean] :single_endpoint If `true`, only a single
# endpoint entry will be generated, rather than an endpoint for each
# region.
# @option options [Boolean] :internal_url_snet If `true`, an internalURL
# entry will be generated by prepending "snet-" to the publicURL.
# @option options [String] :rackspace_api_name If specified, will generate
# publicURL as a Rackspace API URL.
# @option options [String] :rackspace_api_version (`"1.0"`) Specify the
# version of the Rackspace API URL.
#
# @return [Hash] A valid service catalog entry.
def service_catalog_entry(name, type, tenant_id, options)
if options[:rackspace_api_name]
api_name = options[:rackspace_api_name]
api_version = options[:rackspace_api_version] || "1.0"
options[:public_url] = lambda do |r|
prefix = r ? "#{r}." : ""
"https://#{prefix}#{api_name}.api.rackspacecloud.com/v#{api_version}/#{tenant_id}"
end
end

entry = { "name" => name, "type" => type }
if options[:single_endpoint]
entry["endpoints"] = [endpoint_entry(tenant_id, nil, options)]
else
entry["endpoints"] = %w{ORD DFW SYD IAD HKG}.map do |region|
endpoint_entry(tenant_id, region, options)
end
end
entry
end

# Helper method that generates a single endpoint hash within a service
# catalog entry.
#
# @param tenant_id [String] The tenant ID used for this endpoint.
# @param region [String, nil] The region to include in this endpoint, if any.
# @param options [Hash] Options inherited from {#service_catalog_entry}.
#
# @return [Hash] A well-formed endpoint hash.
def endpoint_entry(tenant_id, region, options)
endpoint = { "tenantId" => tenant_id }
endpoint["region"] = region if region
r = region.downcase if region
endpoint["publicURL"] = options[:public_url].call(r) if options[:public_url]

if options[:internal_url_snet]
endpoint["internalURL"] = endpoint["publicURL"].gsub(%r{^https://}, "https://snet-")
end

endpoint["internalURL"] = options[:internal_url].call(r) if options[:internal_url]
if options[:version_base_url] && options[:version_id]
base = options[:version_base_url].call(r)
version = options[:version_id]
endpoint["publicURL"] = "#{base}/v#{version}/#{tenant_id}"
endpoint["versionInfo"] = "#{base}/v#{version}"
endpoint["versionList"] = base
endpoint["versionId"] = version
end
endpoint
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/fog/rackspace/requests/identity/list_tenants.rb
Expand Up @@ -17,6 +17,29 @@ def list_tenants()
response
end
end

class Mock
def list_tenants
response = Excon::Response.new
response.status = [200, 203][rand(1)]
response.body = {
"tenants" => [
{
"id" => Fog::Mock.random_numbers(6),
"name" => "Enabled tenant",
"enabled" => true
},
{
"id" => Fog::Mock.random_numbers(6),
"name" => "Disabled tenant",
"enabled" => false
},
],
"tenants_links" => []
}
response
end
end
end
end
end

0 comments on commit 95f2b1f

Please sign in to comment.