Quickstart for Rackspace CDN#

Rackspace CDN allows you to CDN enable your website. It offers features such as pulling content from your website’s origin servers, caching rules, access restrictions, and purging content from the CDN’s edge servers.

Differences from Cloud Files#

  • Users of Cloud Files can CDN-enable a container, thereby distributing the contents of that container to the CDN’s edge nodes. In comparison, users of Rackspace CDN specify the origins that host the content, and the CDN pulls the content from these origins. Origins may refer to Dedicated Servers, Cloud Servers, Cloud Load Balancers, or even servers hosted outside of Rackspace. It is not yet possible to specify a Cloud Files container as an origin.

  • Cloud Files limits the number of purges per account, per day to 25. Rackspace CDN has no such limit on purges.

  • Cloud Files supports streaming video from CDN-enabled containers as well as serving CDN-enabled content over SSL/TLS. Rackspace CDN does not yet support either of these features.

Concepts#

CDN

A content delivery network (CDN) is a system of multiple computers that contains copies of data stored at various network nodes. A CDN is designed to improve performance of publicly distributed assets. Assets can be anything from website content, to web application components, to media such as videos, ads, and interactive experiences. CDNs decrease the load time of these assets by caching them on edge servers, also called points of presence (PoPs). Edge servers are distributed around the globe, meaning requests only travel to a local location to grab assets, rather than to and from a data center based far from the end user.

Edge node

CDN providers have many points of presence (PoP) servers around the world. These servers are known as edge nodes. These edge nodes cache the content and serve it directly to customers, thus reducing transit time to a customers location.

Edge server

An edge server is the same as an edge node.

Origin

An origin is an address (IP or domain) from which the CDN provider pulls content. A service can have multiple origins.

Flavor

A flavor is a configuration option. A flavor enables you to choose from a generic setting that is powered by one or more CDN providers.

Service

A service represents your web application that has its content cached to the edge nodes.

Status

The status indicates the current state of the service. The time it takes for a service configuration to be distributed amongst a CDN provider cache can vary.

Purge

Purging removes content from the edge servers - thus invalidating the content - so that it can be refreshed from your origin servers.

Caching rule

A caching rule provides you with fine-grained control over the time-to-live (TTL) of an object. When the TTL expires for an object, the edge node pulls the object from the origin again.

Restriction

A restriction enables you to define rules about who can or cannot access content from the cache. Examples of a restriction are allowing requests only from certain domains, geographies, or IP addresses.

Authentication#

To use this service you have to authenticate first. To do this, you will need your Rackspace username and API key. Your username is the one you use to login to the Cloud Control Panel at http://mycloud.rackspace.com/.

To find your API key, use the instructions in View and reset your API key.

You can specify a default region. Here is a list of available regions:

  • DFW (Dallas-Fort Worth, TX, US)

  • HKG (Hong Kong, China)

  • IAD (Blacksburg, VA, US)

  • LON (London, England)

  • SYD (Sydney, Australia)

Some users have access to another region in ORD (Chicago, IL). New users will not have this region.

Once you have these pieces of information, you can pass them into the SDK by replacing {username}, {apiKey}, and {region} with your info:

// Sample Project @ https://github.com/openstacknetsdk/Demos/tree/master/RackspaceQuickstart
using Marvin.JsonPatch;
using net.openstack.Core.Domain;
using net.openstack.Providers.Rackspace;
using OpenStack;
using OpenStack.ContentDeliveryNetworks.v1;
using System.Collections.Generic;
using Flavor = OpenStack.ContentDeliveryNetworks.v1.Flavor;

var identity = new CloudIdentity
{
    APIKey = "{apikey}",
    Username = "{username}"
};
var authProvider = new CloudIdentityProvider(identity);
var cdnService = new ContentDeliveryNetworkService(authProvider, "{region}");
import (
  "github.com/rackspace/gophercloud"
  "github.com/rackspace/gophercloud/rackspace"
  "github.com/rackspace/gophercloud/rackspace/cdn/v1/base"
  "github.com/rackspace/gophercloud/rackspace/cdn/v1/services"
  "github.com/rackspace/gophercloud/rackspace/cdn/v1/serviceassets"
  "github.com/rackspace/gophercloud/rackspace/cdn/v1/flavors"

  osServices "github.com/rackspace/gophercloud/openstack/cdn/v1/services"
  osServiceAssets "github.com/rackspace/gophercloud/openstack/cdn/v1/serviceassets"
  osFlavors "github.com/rackspace/gophercloud/openstack/cdn/v1/flavors"
)

ao := gophercloud.AuthOptions{
  Username: "{username}",
  APIKey: "{apiKey}",
}
provider, err := rackspace.AuthenticatedClient(ao)

serviceClient, err := rackspace.NewCDNV1(provider, gophercloud.EndpointOpts{})
// Authentication in jclouds is lazy and happens on the first call to the cloud.
PoppyApi poppyApi = ContextBuilder.newBuilder("rackspace-cdn-us")
    .credentials("{username}", "{apiKey}")
    .buildApi(PoppyApi.class);
pkgcloud = require('pkgcloud');

var rackspace = pkgcloud.cdn.createClient({
  provider: 'rackspace',
  username: '{username}',
  apiKey: '{apiKey}',
  region: '{region}'
});
require 'vendor/autoload.php';

use OpenCloud\Rackspace;

// Instantiate a Rackspace client.
$client = new Rackspace(Rackspace::US_IDENTITY_ENDPOINT, array(
    'username' => '{username}',
    'apiKey'   => '{apiKey}'
));
import pyrax

pyrax.set_setting("identity_type", "rackspace")
pyrax.set_credentials('{username}', '{apiKey}')

cdn = pyrax.cloud_cdn
require 'fog'

@client = Fog::Compute.new(
  :provider => 'rackspace',
  :rackspace_username => '{username}',
  :rackspace_api_key => '{apiKey}'
)
# {username}, {apiKey} below are placeholders, do not enclose '{}' when you replace them with actual credentials.

curl -s https://identity.api.rackspacecloud.com/v2.0/tokens -X 'POST' \
   -d '{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username":"{username}", "apiKey":"{apiKey}"}}}' \
   -H "Content-Type: application/json" | python -m json.tool

# From the resulting json, set three environment variables: tenant, TOKEN and endpoint

export TENANT="{tenantId}"
export TOKEN="{tokenId}"
export ENDPOINT="{publicUrl}" # For CDN service

Use the API#

Some of the basic operations you can perform with this API are described below.

Get flavors#

First you must determine which flavors (CDN configurations) are available to you for creating a new service (CDN-enabling your web site):

IEnumerable<Flavor> flavors = await cdnService.ListFlavorsAsync();
err := flavors.List(client).EachPage(func(page pagination.Page) (bool, error) {
  flavorList, err := osFlavors.ExtractFlavors(page)
  for _, flavor := range flavorList {
          // ...
  }
  return true, nil
})
FlavorApi flavorApi = cdnApi.getFlavorApi();
List<FlavorApi> flavorList = flavorApi.list().toList();
client.getFlavors(function(err, flavors) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your flavors array here
});
// Obtain a CDN service object from the client.
$cdnService = $client->cdnService();

// List flavors
$flavors = $cdnService->listFlavors();
flavors = cdn.list_flavors()
@client.flavors
$ curl -X GET $ENDPOINT/flavors \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Get flavor#

You can retrieve a specific flavor’s details using its ID:

Flavor flavor = await cdnService.GetFlavorAsync("{flavorId}");
flavor, err := flavors.Get(client, flavorId).Extract()
FlavorApi flavorApi = cdnApi.getFlavorApi();
Flavor flavor = flavorApi.get("{flavorId}");
client.getFlavor({flavorId}, function(err, flavor) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your flavor here
});
$flavor = $cdnService->getFlavor('{flavorId}');
flavor = cdn.get_flavor('{flavorId}')
@client.flavors.get({flavorId})
$ curl -X GET $ENDPOINT/flavors/{flavorId} \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Create service#

Once you have chosen a flavor, you can create a service to CDN-enable your web site:

ServiceDefinition serviceDefinition = new ServiceDefinition("example_site", "{flavorId}",
    domains: new[] {new ServiceDomain("www.example.com")},
    origins: new[] {new ServiceOrigin("example.com")});
string serviceId = await cdnService.CreateServiceAsync(serviceDefinition);
await cdnService.WaitForServiceDeployedAsync(serviceId);
createOpts := osServices.CreateOpts{
  Name: "example_site",
  Domains: []osServices.Domain{
          osServices.Domain{
                  Domain: "www.example.com",
          },
  },
  Origins: []osServices.Origin{
          osServices.Origin{
                  Origin: "example.com",
          },
  },
  FlavorID: "{flavorId}",
}
location, err := services.Create(client, createOpts).Extract()
ServiceApi serviceApi = cdnApi.getServiceApi();
URI serviceURI = serviceApi.create(
               org.jclouds.openstack.poppy.v1.domain.CreateService.builder()
                     .name("jclouds_test_service")
                     .domains(
                           ImmutableList.of(
                                 Domain.builder().domain("www.example.com").build()))
                     .origins(ImmutableList.of(
                           Origin.builder()
                                 .origin("example.com")
                                 .build()))
                     .caching(ImmutableList.<Caching>of())
                     .restrictions(ImmutableList.<Restriction>of())
                     .flavorId("{flavorId}")
                     .build()
       );
var service = {
  name: 'example_site',
  domains: [
    {
      domain: 'www.example.com'
    }
  ],
  origins: [
    {
      origin: 'example.com'
    }
  ],
  flavorId: '{flavorId}'
};

client.createService(service, function(err, service) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your newly created service here
});
$service = $cdnService->createService(array(
    'name'     => 'example_site',
    'domains'  => array(
        array(
            'domain' => 'www.example.com'
        )
    ),
    'origins'  => array(
        array(
            'origin' => 'example.com'
        )
    ),
    'flavorId' => '{flavorId}'
));
service = cdn.create_service("example_site", "{flavorId}",
                             [ { "domain": "www.example.com" } ],
                             [ { "origin": "example.com" } ])
@service = @client.services.new
@service.name = "www.example.com"
@service.flavor_id = "cdn"
@service.add_domain "www.example.com"
@service.add_origin "example.com"
@service.save
$ curl -X POST -d \
  '{
  "name": "mywebsite.com",
  "domains": [
      {
          "domain": "www.mywebsite.com"
      },
      {
          "domain": "blog.mywebsite.com"
      }
  ],
  "origins": [
      {
          "origin": "mywebsite.com",
          "port": 80,
          "ssl": false,
          "rules": []
      }
  },
  "flavor_id": "cdn"
  }' \
  -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  $ENDPOINT/services | python -m json.tool

List services#

To see the list of services you have created:

IPage<Service> services = await cdnService.ListServicesAsync();
err := services.List(client, nil).EachPage(func(page pagination.Page) (bool, error) {
  serviceList, err := osServices.ExtractServices(page)
  for _, service := range serviceList {
          // ...
  }
  return true, nil
})
ServiceApi serviceApi = cdnApi.getServiceApi();
List<Service> serviceList = serviceApi.list().concat().toList();
client.getServices(function(err, services) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your services array here
});
$services = $cdnService->listServices();
services = cdn.list_services()
@client.services
$ curl -X GET $ENDPOINT/services \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Get service#

You can retrieve a specific service’s details using its ID:

Service service = await cdnService.GetServiceAsync("{serviceId}");
service, err := services.Get(client, serviceIdOrURL).Extract()
ServiceApi serviceApi = cdnApi.getServiceApi();
Service service = serviceApi.get("{serviceId}");
client.getService({serviceId}, function(err, service) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your service here
});
$service = $cdnService->getService('{serviceId}');
service = cdn.get_service({serviceId})
@service = @client.services.get({serviceId})
$ curl -X GET $ENDPOINT/services/{serviceId} \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

The service’s details include a set of links. Look for links with their “rel” set to “access_url”. There will be as many such links as there are domains for your web site. You can setup CNAMEs to the domains specified by the “href” values of these links, from each of your web site’s domains.

Once your DNS provider has propagated these CNAMEs across the Internet, visiting any of your web site’s domains will ensure that your web site’s content is being served from the CDN.

Purge all service assets#

You can purge all assets for your service:

await cdnService.PurgeCachedAssetsAsync("{serviceId}");
deleteOpts := osServiceAssets.DeleteOpts{
  All: true,
}
err := serviceassets.Delete(client, serviceIDOrURL, deleteOpts).ExtractErr()
// Only supported in SNAPSHOT versions of this SDK
ServiceApi serviceApi = cdnApi.getServiceApi();
serviceApi.deleteAssets("{serviceId}"));
client.deleteServiceCachedAssets(service, function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
$service->purgeAssets();
service.delete_assets(all=True)
@service.destroy_assets(url: "/")
$ curl -X DELETE $ENDPOINT/services/{serviceId}/assets?all=true \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Purge a specific service asset#

You can purge a specific asset for your service using the asset’s URL:

await cdnService.PurgeCachedAssetAsync("{serviceId}", "{relativeUrlOfAsset}");
deleteOpts := osServiceAssets.DeleteOpts{
  URL: "{relativeUrlOfAsset}",
}
err := serviceassets.Delete(client, serviceIDOrURL, deleteOpts).ExtractErr()
// Only supported in SNAPSHOT versions of this SDK
ServiceApi serviceApi = cdnApi.getServiceApi();
serviceApi.deleteAsset("{serviceId}", "image/1.jpg"));
client.deleteServiceCachedAssets(service, '{relativeUrlOfAsset}', function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
$service->purgeAssets('{relativeUrlOfAsset}');
service.delete_assets(url="{relativeUrlOfAsset}")
@service.destroy_assets(url: "{relativeUrlOfAsset}")
$ curl -X DELETE $ENDPOINT/services/{serviceId}/assets?url={relativeUrlOfAsset} \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

Update service#

To update an existing service:

var patch = new JsonPatchDocument<ServiceDefinition>();
patch.Replace(svc => svc.Name, "newServiceName");
await cdnService.UpdateServiceAsync("{serviceId}", patch);
await cdnService.WaitForServiceDeployedAsync("{serviceId}");
updateOpts := osServices.UpdateOpts{
  osServices.Append{
    Value: osServices.Origin{
      Origin: "44.33.22.11",
      Port: 80,
      SSL: false,
    },
  },
}
location, err := services.Update(client, serviceIdOrURL, updateOpts).Extract()
ServiceApi serviceApi = cdnApi.getServiceApi();
Service serviceToUpdate = serviceApi.get("{serviceId}");
UpdateService updated = serviceToUpdate.toUpdatableService().name("updated_name").build();
serviceApi.update("{serviceId}", serviceToUpdate, updated);
service.origins = [
  {
    origin: '44.33.22.11',
    port: 80,
    ssl: false
  }
];

client.updateService(service, function(err, service) {
  if (err) {
    // TODO handle as appropriate
  }

  // TODO use your updated service here
});
$service->update(array(
    'origins' => array(
        array(
            'origin' => '44.33.22.11',
            'port'   => 80,
            'ssl'    => false
        )
    )
));
service.patch([{"op":"replace", "path":"/name", "value":"newServiceName"}])
@service.name = "newServiceName"
@service.save
$ curl -X PATCH -d \
  '[
      {
          "op": "replace",
          "path": "/name",
          "value": "newServiceName"
      },
      {
          "op": "add",
          "path": "/domains/-",
          "value": {
              "domain": "newDomain.com",
              "protocol": "http"
          }
      },
      {
          "op": "remove",
          "path": "/origins/0"
      }
  ]' \
  -H "X-Auth-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  $ENDPOINT/services/{serviceId} | python -m json.tool

Delete service#

To delete a service and purge all its assets in the process:

await cdnService.DeleteServiceAsync("{serviceId}");
await cdnService.WaitForServiceDeletedAsync("{serviceId}");
err := services.Delete(client, serviceIdOrURL).ExtractErr()
ServiceApi serviceApi = cdnApi.getServiceApi();
serviceApi.delete("{serviceId}");
client.deleteService(service, function(err) {
  if (err) {
    // TODO handle err as appropriate
  }
});
$service->delete();
service.delete()
@service.destroy
$ curl -X DELETE $ENDPOINT/services/{serviceId} \
  -H "X-Auth-Token: $TOKEN" \
  -H "Accept: application/json" | python -m json.tool

More information#

This quickstart is intentionally brief, demonstrating only a few basic operations. To learn more about interacting with Rackspace cloud services, explore the following sites: