Skip to content
Permalink
Browse files

Added initial support for invalidation.

Invalidations can be done by creating a new "Invalidation Request":

>>> import boto
>>> cf = boto.connect_cloudfront()
>>> cf.create_invalidation_request("distribution_id", ["/path1","/path2"])
  • Loading branch information...
Chris Moyer Chris Moyer
Chris Moyer authored and Chris Moyer committed Sep 2, 2010
1 parent fc8305c commit 8ff181af25d3a4018d1fb29fa78a0d745046f900
Showing with 133 additions and 1 deletion.
  1. +14 −0 bin/cfadmin
  2. +22 −1 boto/cloudfront/__init__.py
  3. +97 −0 boto/cloudfront/invalidation.py
14 bin/cfadmin 100644 → 100755
@@ -46,6 +46,20 @@ def ls(cf):
print "Streaming Distributions"
_print_distributions(cf.get_all_streaming_distributions())

def invalidate(cf, origin_or_id, *paths):
"""Create a cloudfront invalidation request"""
if not paths:
print "Usage: cfadmin invalidate distribution_origin_or_id [path] [path2]..."
sys.exit(1)
dist = None
for d in cf.get_all_distributions():
if d.id == origin_or_id or d.origin == origin_or_id:
dist = d
break
if not dist:
print "Distribution not found: %s" % origin_or_id
sys.exit(1)
cf.create_invalidation_request(dist.id, paths)

if __name__ == "__main__":
import boto
@@ -30,13 +30,14 @@
from boto.cloudfront.identity import OriginAccessIdentity
from boto.cloudfront.identity import OriginAccessIdentitySummary
from boto.cloudfront.identity import OriginAccessIdentityConfig
from boto.cloudfront.invalidation import InvalidationBatch
from boto.resultset import ResultSet
from boto.cloudfront.exception import CloudFrontServerError

class CloudFrontConnection(AWSAuthConnection):

DefaultHost = 'cloudfront.amazonaws.com'
Version = '2010-07-15'
Version = '2010-08-01'

def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
port=None, proxy=None, proxy_port=None,
@@ -220,4 +221,24 @@ def delete_origin_access_identity(self, access_id, etag):
return self._delete_object(access_id, etag,
'origin-access-identity/cloudfront')

# Object Invalidation

def create_invalidation_request(self, distribution_id, paths, caller_reference=None):
"""Creates a new invalidation request
:see: http://docs.amazonwebservices.com/AmazonCloudFront/2010-08-01/APIReference/index.html?CreateInvalidation.html
"""
# We allow you to pass in either an array or
# an InvalidationBatch object
if not isinstance(paths, InvalidationBatch):
paths = InvalidationBatch(paths)
paths.connection = self
response = self.make_request('POST', '/%s/distribution/%s/invalidation' % (self.Version, distribution_id),
{'Content-Type' : 'text/xml'}, data=paths.to_xml())
body = response.read()
if response.status == 201:
h = handler.XmlHandler(paths, self)
xml.sax.parseString(body, h)
return paths
else:
raise CloudFrontServerError(response.status, response.reason, body)

@@ -0,0 +1,97 @@
# Copyright (c) 2006-2010 Chris Moyer http://coredumped.org/
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import uuid
import urllib

class InvalidationBatch(object):
"""A simple invalidation request.
:see: http://docs.amazonwebservices.com/AmazonCloudFront/2010-08-01/APIReference/index.html?InvalidationBatchDatatype.html
"""

def __init__(self, paths=[], connection=None, distribution=None, caller_reference=''):
"""Create a new invalidation request:
:paths: An array of paths to invalidate
"""
self.paths = paths
self.distribution = distribution
self.caller_reference = caller_reference
if not self.caller_reference:
self.caller_reference = str(uuid.uuid4())

# If we passed in a distribution,
# then we use that as the connection object
if distribution:
self.connection = connection
else:
self.connection = connection

def add(self, path):
"""Add another path to this invalidation request"""
return self.paths.append(path)

def remove(self, path):
"""Remove a path from this invalidation request"""
return self.paths.remove(path)

def __iter__(self):
return iter(self.paths)

def __getitem__(self, i):
return self.paths[i]

def __setitem__(self, k, v):
self.paths[k] = v

def escape(self, p):
"""Escape a path, make sure it begins with a slash and contains no invalid characters"""
if not p[0] == "/":
p = "/%s" % p
return urllib.quote(p)

def to_xml(self):
"""Get this batch as XML"""
assert self.connection != None
s = '<?xml version="1.0" encoding="UTF-8"?>\n'
s += '<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/%s/">\n' % self.connection.Version
for p in self.paths:
s += ' <Path>%s</Path>\n' % self.escape(p)
s += ' <CallerReference>%s</CallerReference>\n' % self.caller_reference
s += '</InvalidationBatch>\n'
return s

def startElement(self, name, attrs, connection):
if name == "InvalidationBatch":
self.paths = []
return None

def endElement(self, name, value, connection):
if name == 'Path':
self.paths.append(value)
elif name == "Status":
self.status = value
elif name == "Id":
self.id = id
elif name == "CreateTime":
self.create_time = value
elif name == "CallerReference":
self.caller_reference = value
return None

0 comments on commit 8ff181a

Please sign in to comment.
You can’t perform that action at this time.