Rackspace's new CDN (V2)

This commit is contained in:
Matt Darby 2014-12-22 16:50:16 -05:00 committed by Matt Darby
parent 659cb6d66f
commit 36605c31c8
20 changed files with 724 additions and 0 deletions

View File

@ -8,6 +8,8 @@ class Rackspace < Fog::Bin
Fog::Rackspace::BlockStorage Fog::Rackspace::BlockStorage
when :cdn when :cdn
Fog::CDN::Rackspace Fog::CDN::Rackspace
when :cdn_v2
Fog::Rackspace::CDNV2
when :compute when :compute
Fog::Compute::Rackspace Fog::Compute::Rackspace
when :compute_v2 when :compute_v2
@ -43,6 +45,8 @@ class Rackspace < Fog::Bin
when :cdn when :cdn
Fog::Logger.warning("Rackspace[:cdn] is not recommended, use CDN[:rackspace] for portability") Fog::Logger.warning("Rackspace[:cdn] is not recommended, use CDN[:rackspace] for portability")
Fog::CDN.new(:provider => 'Rackspace') Fog::CDN.new(:provider => 'Rackspace')
when :cdn_v2
Fog::Rackspace::CDNV2.new
when :compute when :compute
Fog::Logger.warning("Rackspace[:compute] is not recommended, use Compute[:rackspace] for portability") Fog::Logger.warning("Rackspace[:compute] is not recommended, use Compute[:rackspace] for portability")
Fog::Compute.new(:provider => 'Rackspace') Fog::Compute.new(:provider => 'Rackspace')

View File

@ -1,6 +1,7 @@
require 'fog/rackspace/auto_scale' require 'fog/rackspace/auto_scale'
require 'fog/rackspace/block_storage' require 'fog/rackspace/block_storage'
require 'fog/rackspace/cdn' require 'fog/rackspace/cdn'
require 'fog/rackspace/cdn_v2'
require 'fog/rackspace/compute' require 'fog/rackspace/compute'
require 'fog/rackspace/compute_v2' require 'fog/rackspace/compute_v2'
require 'fog/rackspace/databases' require 'fog/rackspace/databases'

141
lib/fog/rackspace/cdn_v2.rb Normal file
View File

@ -0,0 +1,141 @@
require 'fog/rackspace/core'
module Fog
module Rackspace
class CDNV2 < Fog::Service
requires :rackspace_api_key, :rackspace_username
recognizes :rackspace_auth_url, :persistent, :rackspace_cdn_ssl, :rackspace_region, :rackspace_cdn_url
PREVIEW_API_URL = "preview.cdn.api.rackspacecloud.com"
model_path 'fog/rackspace/models/cdn_v2'
model :service
collection :services
model :flavor
collection :flavors
request_path 'fog/rackspace/requests/cdn_v2'
request :create_service
request :delete_assets
request :delete_service
request :get_flavor
request :get_home_document
request :get_ping
request :get_service
request :list_flavors
request :list_services
request :update_service
class ServiceError < Fog::Rackspace::Errors::ServiceError; end
class InternalServerError < Fog::Rackspace::Errors::InternalServerError; end
class BadRequest < Fog::Rackspace::Errors::BadRequest; end
class Mock < Fog::Rackspace::Service
include Fog::Rackspace::MockData
def initialize(options)
@rackspace_api_key = options[:rackspace_api_key]
end
def request(params)
Fog::Mock.not_implemented
end
def response(params={})
body = params[:body] || {}
status = params[:status] || 200
headers = params[:headers] || {}
response = Excon::Response.new(:body => body, :headers => headers, :status => status)
if params.key?(:expects) && ![*params[:expects]].include?(response.status)
raise(Excon::Errors.status_error(params, response))
else response
end
end
end
class Real < Fog::Rackspace::Service
def initialize(options = {})
@rackspace_api_key = options[:rackspace_api_key]
@rackspace_username = options[:rackspace_username]
@rackspace_auth_url = options[:rackspace_auth_url]
@rackspace_must_reauthenticate = false
@connection_options = options[:connection_options] || {}
authenticate
@persistent = options[:persistent] || false
@connection = Fog::Core::Connection.new(endpoint_uri.to_s, @persistent, @connection_options)
end
def request(params, parse_json = true)
# TODO: This MUST be removed when CDNV2 is live
rewrite_cdn_v2_host!
# TODO: This MUST be removed when CDNV2 is live
super
rescue Excon::Errors::NotFound => error
raise NotFound.slurp(error, self)
rescue Excon::Errors::BadRequest => error
raise BadRequest.slurp(error, self)
rescue Excon::Errors::InternalServerError => error
raise InternalServerError.slurp(error, self)
rescue Excon::Errors::HTTPStatusError => error
raise ServiceError.slurp(error, self)
end
# TODO: This MUST be removed when CDNV2 is live
def rewrite_cdn_v2_host!
conn = @connection.instance_variable_get("@excon")
conn.data[:host] = PREVIEW_API_URL
conn.instance_variable_set("@socket_key", PREVIEW_API_URL)
if conn.data.has_key?(:__construction_args)
conn.data[:__construction_args][:host] = PREVIEW_API_URL
end
end
# TODO: This MUST be removed when CDNV2 is live
def request_uri(path, options={})
return path if options == {}
require "addressable/uri"
Addressable::URI.new({:path=>path, :query_values=>options}).request_uri
end
def ping
self.get_ping.status == 204
end
def home_document
self.get_home_document.body
end
def authenticate(options={})
super({
:rackspace_api_key => @rackspace_api_key,
:rackspace_username => @rackspace_username,
:rackspace_auth_url => @rackspace_auth_url,
:connection_options => @connection_options
})
end
def service_name
:rackCDN
end
def request_id_header
"x-raxcdn-id"
end
def region
@rackspace_region
end
def endpoint_uri(service_endpoint_url=nil)
@uri = super(@rackspace_endpoint || service_endpoint_url)
end
end
end
end
end

View File

@ -88,6 +88,7 @@ module Fog
service(:auto_scale, 'AutoScale') service(:auto_scale, 'AutoScale')
service(:block_storage, 'BlockStorage') service(:block_storage, 'BlockStorage')
service(:cdn, 'CDN') service(:cdn, 'CDN')
service(:cdn_v2, 'CDN v2')
service(:compute, 'Compute') service(:compute, 'Compute')
service(:compute_v2, 'Compute v2') service(:compute_v2, 'Compute v2')
service(:dns, 'DNS') service(:dns, 'DNS')

View File

@ -0,0 +1,351 @@
#Rackspace CDNV2
This document explains how to get started using CDNV2 with Fog. It assumes you have read the [Getting Started with Fog and the Rackspace Open Cloud](getting_started.md) document.
## Starting irb console
Start by executing the following command:
irb
Once `irb` has launched you need to require the Fog library.
If using Ruby 1.8.x execute:
require 'rubygems'
require 'fog'
If using Ruby 1.9.x execute:
require 'fog'
## Create Service
Next, create a connection to Rackspace's CDNV2 API:
Using a US-based account:
service = Fog::Rackspace::CDNV2.new({
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_region => :ord, # Defaults to :dfw
})
Using a UK-based account:
service = Fog::Compute.new({
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_auth_url => Fog::Rackspace::UK_AUTH_ENDPOINT,
:rackspace_region => :lon,
})
To learn more about obtaining cloud credentials refer to the [Getting Started with Fog and the Rackspace Open Cloud](getting_started.md) document.
By default `Fog::Rackspace::CDNV2` will authenticate against the US authentication endpoint and connect to the DFW region. You can specify alternative authentication endpoints using the key `:rackspace_auth_url`. Please refer to [Alternate Authentication Endpoints](http://docs.rackspace.com/auth/api/v2.0/auth-client-devguide/content/Endpoints-d1e180.html) for a list of alternative Rackspace authentication endpoints.
Alternative regions are specified using the key `:rackspace_region `. A list of regions available for Cloud Servers can be found by executing the following:
identity_service = Fog::Identity({
:provider => 'Rackspace', # Rackspace Fog provider
:rackspace_username => RACKSPACE_USER_NAME, # Your Rackspace Username
:rackspace_api_key => RACKSPACE_API, # Your Rackspace API key
:rackspace_auth_url => Fog::Rackspace::UK_AUTH_ENDPOINT # Not specified for US Cloud
})
identity_service.service_catalog.display_service_regions :cloudServersOpenStack
### Optional Connection Parameters
Fog supports passing additional connection parameters to its underlying HTTP library (Excon) using the `:connection_options` parameter.
<table>
<tr>
<th>Key</th>
<th>Description</th>
</tr>
<tr>
<td>:connect_timeout</td>
<td>Connection timeout (default: 60 seconds)</td>
</tr>
<tr>
<td>:read_timeout</td>
<td>Read timeout for connection (default: 60 seconds)</td> </tr>
<tr>
<td>:write_timeout</td>
<td>Write timeout for connection (default: 60 seconds)</td>
</tr>
<tr>
<td>:proxy</td>
<td>Proxy for HTTP and HTTPS connections</td>
</tr>
<tr>
<td>:ssl_ca_path</td>
<td>Path to SSL certificate authorities</td>
</tr>
<tr>
<td>:ssl_ca_file</td>
<td>SSL certificate authority file</td>
</tr>
<tr>
<td>:ssl_verify_peer</td>
<td>SSL verify peer (default: true)</td>
</tr>
</table>
## Fog Abstractions
Fog provides both a **model** and **request** abstraction. The request abstraction provides the most efficient interface and the model abstraction wraps the request abstraction to provide a convenient `ActiveModel` like interface.
### Request Layer
The request abstraction maps directly to the [CDNV2 API](http://docs.rackspace.com/networks/api/v2/cn-devguide/content/ch_overview.html). It provides the most efficient interface to the Rackspace CDNV2
To see a list of requests supported by the service:
service.requests
This returns:
[:create_service, :delete_assets, :delete_service, :get_flavor, :get_home_document, :get_ping, :get_service, :list_flavors, :list_services, :update_service]
#### Example Request
To request a list of services:
response = service.list_services
This returns in the following `Excon::Response`:
#<Excon::Response:0x007ff0ea6b4c88 @data={:body=>{"services"=>[{"name"=>"SomethingDifferent.net", "domains"=>[{"domain"=>"google.com", "protocol"=>"http"}], "origins"=>[{"origin"=>"google.com", "port"=>80, "ssl"=>false, "rules"=>[]}], "restrictions"=>[], "caching"=>[], "status"=>"create_in_progress", "flavor_id"=>"cdn", "errors"=>[]
To view the status of the response:
response.status
**Note**: Fog is aware of valid HTTP response statuses for each request type. If an unexpected HTTP response status occurs, Fog will raise an exception.
To view response body:
response.body
This will return:
{"services"=>[{"name"=>"SomethingDifferent.net", "domains"=>[{"domain"=>"google.com",...
To learn more about CDNV2 request methods refer to [rdoc](http://www.rubydoc.info/gems/fog/Fog/Rackspace/CDNV2/Real). To learn more about Excon refer to [Excon GitHub repo](https://github.com/geemus/excon).
### Model Layer
Fog models behave in a manner similar to `ActiveModel`. Models will generally respond to `create`, `save`, `persisted?`, `destroy`, `reload` and `attributes` methods. Additionally, fog will automatically create attribute accessors.
Here is a summary of common model methods:
<table>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
<tr>
<td>create</td>
<td>
Accepts hash of attributes and creates object.<br>
Note: creation is a non-blocking call and you will be required to wait for a valid state before using resulting object.
</td>
</tr>
<tr>
<td>save</td>
<td>Saves object.<br>
Note: not all objects support updating object.</td>
</tr>
<tr>
<td>persisted?</td>
<td>Returns true if the object has been persisted.</td>
</tr>
<tr>
<td>destroy</td>
<td>
Destroys object.<br>
Note: this is a non-blocking call and object deletion might not be instantaneous.
</td>
<tr>
<td>reload</td>
<td>Updates object with latest state from service.</td>
<tr>
<td>ready?</td>
<td>Returns true if object is in a ready state and able to perform actions. This method will raise an exception if object is in an error state.</td>
</tr>
<tr>
<td>attributes</td>
<td>Returns a hash containing the list of model attributes and values.</td>
</tr>
<td>identity</td>
<td>
Returns the identity of the object.<br>
Note: This might not always be equal to object.id.
</td>
</tr>
<tr>
<td>wait_for</td>
<td>This method periodically reloads model and then yields to specified block until block returns true or a timeout occurs.</td>
</tr>
</table>
The remainder of this document details the model abstraction.
## List Services
To retrieve a list of available services:
service.services
This returns a collection of `Fog::Rackspace::CDNV2::Service` models:
<Fog::Rackspace::CDNV2::Services
[
<Fog::Rackspace::CDNV2::Service
id="087ffeb0-462d-4f44-b24a-2914fbfb1d42",
name="SomethingDifferent.net",
domains=[{"domain"=>"google.com", "protocol"=>"http"}],
origins=[{"origin"=>"google.com", "port"=>80, "ssl"=>false, "rules"=>[]}],
caching=[],
restrictions=[],
flavor_id="cdn",
status="create_in_progress",
links=[{"href"=>"", "rel"=>"self"}, {"href"=>"...", "rel"=>"flavor"}]
...
## Create Service
Create a service:
s = service.services.new
s.name = "work.com"
s.flavor_id = "cdn"
s.add_domain "google.com"
s.add_origin "google.com"
s.save
## Update Service
You may add, remove, or update. -- **DOCS NEEDED** --
s = service.services.first
s.add_operation({
op: "add",
path: "/domains/0",
value: {
origin: "cdn.somewhere.org",
port: 80,
ssl: false,
rules: [
{
name: "Something",
request_url: "google.com"
}
]
}
})
s.save
## Get Service
To retrieve individual service:
service.services.get "087ffeb0-462d-4f44-b24a-2914fbfb1d42"
This returns an `Fog::Rackspace::CDNV2::Service` instance:
<Fog::Rackspace::CDNV2::Service
id="087ffeb0-462d-4f44-b24a-2914fbfb1d42"
name="work.com",
domains=[{"domain"=>"google.com", "protocol"=>"http"}],
origins=[{"origin"=>"google.com", "port"=>80, "ssl"=>false, "rules"=>[]}],
caching=[],
restrictions=[],
flavor_id="cdn",
status="create_in_progress",
links=[{"href"=>"", "rel"=>"self"}, {"href"=>"...", "rel"=>"flavor"}]
## Delete Service
To delete a service:
service.destroy
**Note**: The service is not immediately destroyed, but it does occur shortly there after.
## Delete Service Assets
To delete a service's assets (or any owned asset via url):
service.destroy_assets(url: "/")
**Note**: The service's asset is not immediately destroyed, but it does occur shortly there after.
## List Flavors
To retrieve a list of available flavors:
service.flavors
This returns a collection of `Fog::Rackspace::CDNV2::Flavor` models:
<Fog::Rackspace::CDNV2::Flavors
[
<Fog::Rackspace::CDNV2::Flavor
id="cdn",
providers=[{"provider"=>"akamai", "links"=>[{"href"=>"http://www.akamai.com", "rel"=>"provider_url"}]}],
links=[{"href"=>"...", "rel"=>"self"}]
>
]
>
## Get Flavor
To retrieve individual flavor:
service.flavors.get "cdn"
This returns an `Fog::Rackspace::CDNV2::Flavor` instance:
<Fog::Rackspace::CDNV2::Flavor
id="cdn",
providers=[{"provider"=>"akamai", "links"=>[{"href"=>"http://www.akamai.com", "rel"=>"provider_url"}]}],
links=[{"href"=>"...", "rel"=>"self"}]
>
## Ping
To ping the CDN:
service.ping
This returns an boolean based on successful ping.
## Get Home Document
To retrieve the home document:
service.home_document
This returns a JSON blob that describes the home document.
## Additional Resources
* [fog.io](http://fog.io/)
* [Fog rdoc](http://rubydoc.info/gems/fog/)
* [Fog Github repo](https://github.com/fog/fog)
* [Fog Github Issues](https://github.com/fog/fog/issues)
* [Excon Github repo](https://github.com/geemus/excon)
* [Rackspace Networking API](http://docs.rackspace.com/networking/api/v2/cs-devguide/content/ch_preface.html)
## Support and Feedback
Your feedback is appreciated! If you have specific issues with the **fog** SDK, you should file an [issue via Github](https://github.com/fog/fog/issues).
For general feedback and support requests, send an email to: <sdk-support@rackspace.com>.

View File

@ -0,0 +1,14 @@
require 'fog/core/model'
module Fog
module Rackspace
class CDNV2 < Fog::Service
class Flavor < Fog::Model
identity :id
attribute :providers
attribute :links
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'fog/core/collection'
require 'fog/rackspace/models/cdn_v2/flavor'
module Fog
module Rackspace
class CDNV2 < Fog::Service
class Flavors < Fog::Collection
model Fog::Rackspace::CDNV2::Flavor
def all
data = service.list_flavors.body['flavors']
load(data)
end
def get(name)
data = service.get_flavor(name).body
new(data)
rescue Fog::Rackspace::CDNV2::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,66 @@
require 'fog/core/model'
module Fog
module Rackspace
class CDNV2 < Fog::Service
class Service < Fog::Model
attr_accessor :operations
UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
identity :id
attribute :name
attribute :domains
attribute :origins
attribute :caching
attribute :restrictions
attribute :flavor_id
attribute :status
attribute :links
def initialize(options={})
self.operations = []
super
end
def add_domain(domain, options={})
self.domains ||= []
self.domains << {domain: domain}.merge(options)
self.domains
end
def add_origin(origin, options={})
self.origins ||= []
self.origins << {origin: origin}.merge(options)
self.origins
end
def add_operation(options={})
self.operations << options
end
def save
if id.nil?
data = service.create_service(self)
loc = data.headers["Location"]
id = UUID_REGEX.match(loc)[0]
merge_attributes(id: id)
else
service.update_service(self)
end
end
def destroy
service.delete_service(self)
end
def destroy_assets(options={})
service.delete_assets(self, options)
end
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'fog/core/collection'
require 'fog/rackspace/models/cdn_v2/service'
module Fog
module Rackspace
class CDNV2 < Fog::Service
class Services < Fog::Collection
model Fog::Rackspace::CDNV2::Service
def all(options={})
data = service.list_services(options).body['services']
load(data)
end
def get(id)
data = service.get_service(id).body
new(data)
rescue Fog::Rackspace::CDNV2::NotFound
nil
end
end
end
end
end

View File

@ -0,0 +1,10 @@
class Fog::Rackspace::CDNV2::Real
def create_service(service)
request(
:expects => [201, 202],
:method => 'POST',
:body => Fog::JSON.encode(service),
:path => "services"
)
end
end

View File

@ -0,0 +1,12 @@
class Fog::Rackspace::CDNV2::Real
def delete_assets(service, options={})
uri = request_uri("services/#{service.id}/assets", options)
request(
:expects => 202,
:method => 'DELETE',
:path => uri,
:headers => { :accept => "*/*" }
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def delete_service(service)
request(
:expects => 202,
:method => 'DELETE',
:path => "services/#{service.id}"
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def get_flavor(id)
request(
:expects => [200],
:method => 'GET',
:path => "flavors/#{id}"
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def get_home_document
request(
:expects => [200],
:method => 'GET',
:path => ""
)
end
end

View File

@ -0,0 +1,10 @@
class Fog::Rackspace::CDNV2::Real
def get_ping
request(
:expects => [204],
:method => 'GET',
:path => "ping",
:headers => { :accept => "*/*" }
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def get_service(id)
request(
:expects => 200,
:method => 'GET',
:path => "services/#{id}"
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def list_flavors
request(
:expects => [200],
:method => 'GET',
:path => "flavors"
)
end
end

View File

@ -0,0 +1,9 @@
class Fog::Rackspace::CDNV2::Real
def list_services(options={})
request(
:expects => [200],
:method => 'GET',
:path => request_uri("services", options)
)
end
end

View File

@ -0,0 +1,10 @@
class Fog::Rackspace::CDNV2::Real
def update_service(service)
request(
:expects => [201, 202],
:method => 'PATCH',
:body => Fog::JSON.encode(service.operations),
:path => "services/#{service.id}"
)
end
end

View File

@ -96,6 +96,8 @@ module Fog
"https://storage101.#{r}#{Fog::Mock.random_numbers(1)}.clouddrive.com/v1/#{object_tenant}" "https://storage101.#{r}#{Fog::Mock.random_numbers(1)}.clouddrive.com/v1/#{object_tenant}"
end), end),
service_catalog_entry("rackCDN", "rax:cdn", compute_tenant, :rackspace_api_version => '1.0'),
service_catalog_entry("cloudMonitoring", "rax:monitor", compute_tenant, service_catalog_entry("cloudMonitoring", "rax:monitor", compute_tenant,
:single_endpoint => true, :rackspace_api_name => 'monitoring'), :single_endpoint => true, :rackspace_api_name => 'monitoring'),