Skip to content

Instantly share code, notes, and snippets.

@elgreg
Created September 13, 2010 15:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elgreg/577468 to your computer and use it in GitHub Desktop.
Save elgreg/577468 to your computer and use it in GitHub Desktop.
Index: S3/CloudFront.py
===================================================================
--- S3/CloudFront.py (revision 392)
+++ S3/CloudFront.py (working copy)
@@ -155,6 +155,104 @@
return ET.tostring(tree)
+class InvalidationList(object):
+ ## <InvalidationList>
+ ## <Marker/>
+ ## <NextMarker>[Invalidation ID]</NextMarker>
+ ## <MaxItems>2</MaxItems>
+ ## <IsTruncated>true</IsTruncated>
+ ## <InvalidationSummary>
+ ## <Id>[Second Invalidation ID]</Id>
+ ## <Status>Completed</Status>
+ ## </InvalidationSummary>
+ ## <InvalidationSummary>
+ ## <Id>[First Invalidation ID]</Id>
+ ## <Status>Completed</Status>
+ ## </InvalidationSummary>
+ ## </InvalidationList>
+
+ def __init__(self, xml):
+ tree = getTreeFromXml(xml)
+ if tree.tag != "InvalidationList":
+ raise ValueError("Expected <InvalidationList /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+
+ def uri(self):
+ return S3Uri("cf://%s" % self.info['Id'])
+
+
+
+class Invalidation(object):
+ ## <Invalidation xmlns="http://cloudfront.amazonaws.com/doc/2010-08-01/">
+ ## <Status>InProgress</Status>
+ ## <Id>I456</Id>
+ ## <CreateTime>[date]</CreateTime>
+ ## <InvalidationBatch>
+ ## ...
+ ## See the description of the InvalidationBatch element
+ ## for a list of child elements that can appear here
+ ## ...
+ ## </InvalidationBatch>
+ ## </Invalidation>
+
+ EMPTY_CONFIG = "<Invalidation><Status/><Id/><CreateTime/><InvalidationBatch/></Invalidation>"
+ xmlns = "http://cloudfront.amazonaws.com/doc/2010-08-01/"
+ def __init__(self, xml = None, tree = None):
+ if not xml:
+ xml = Invalidation.EMPTY_CONFIG
+
+ if not tree:
+ tree = getTreeFromXml(xml)
+
+ if tree.tag != "Invalidation":
+ raise ValueError("Expected <Invalidation /> xml, got: <%s />" % tree.tag)
+ self.parse(tree)
+
+ def parse(self, tree):
+ self.info = getDictFromTree(tree)
+
+
+class InvalidationBatch(object):
+ ## <InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/2010-08-01/">
+ ## <Path>/image1.jpg</Path>
+ ## <CallerReference>my-batch</CallerReference>
+ ## </InvalidationBatch>
+
+ EMPTY_CONFIG = "<InvalidationBatch><Path/><CallerReference/></InvalidationBatch>"
+ xmlns = "http://cloudfront.amazonaws.com/doc/2010-08-01/"
+ def __init__(self, xml = None, tree = None, CallerReference = None, Paths = []):
+ if not xml:
+ xml = InvalidationBatch.EMPTY_CONFIG
+
+ if not tree:
+ tree = getTreeFromXml(xml)
+
+ if tree.tag != "InvalidationBatch":
+ raise ValueError("Expected <InvalidationBatch /> xml, got: <%s />" % tree.tag)
+
+ self.parse(tree, CallerReference, Paths)
+
+ def parse(self, tree, CallerReference = None, Paths = []):
+ self.info = getDictFromTree(tree)
+ if CallerReference:
+ self.info['CallerReference'] = CallerReference
+ if len(Paths) > 0:
+ self.info['Paths'] = []
+ for path in Paths:
+ self.info['Paths'].append(path)
+
+
+ def __str__(self):
+ tree = ET.Element("InvalidationBatch")
+ tree.attrib['xmlns'] = InvalidationBatch.xmlns
+ for path in self.info['Paths']:
+ appendXmlTextNode("Path", path, tree)
+ appendXmlTextNode("CallerReference", self.info['CallerReference'], tree)
+ return ET.tostring(tree)
+
class CloudFront(object):
operations = {
"CreateDist" : { 'method' : "POST", 'resource' : "" },
@@ -163,6 +261,9 @@
"GetDistInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s" },
"GetDistConfig" : { 'method' : "GET", 'resource' : "/%(dist_id)s/config" },
"SetDistConfig" : { 'method' : "PUT", 'resource' : "/%(dist_id)s/config" },
+ "Invalidate" : { 'method' : "POST", 'resource' : "/%(dist_id)s/invalidation"},
+ "InvalidationList" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation"},
+ "InvalidationInfo" : { 'method' : "GET", 'resource' : "/%(dist_id)s/invalidation/%(inv_id)s"},
}
## Maximum attempts of re-issuing failed requests
@@ -280,11 +381,11 @@
## Low-level methods for handling CloudFront requests
## --------------------------------------------------
- def send_request(self, op_name, dist_id = None, body = None, headers = {}, retries = _max_retries):
+ def send_request(self, op_name, dist_id = None, inv_id = None, body = None, headers = {}, retries = _max_retries):
operation = self.operations[op_name]
if body:
headers['content-type'] = 'text/plain'
- request = self.create_request(operation, dist_id, headers)
+ request = self.create_request(operation, dist_id, inv_id, headers)
conn = self.get_connection()
debug("send_request(): %s %s" % (request['method'], request['resource']))
conn.request(request['method'], request['resource'], body, request['headers'])
@@ -314,9 +415,13 @@
return response
- def create_request(self, operation, dist_id = None, headers = None):
- resource = self.config.cloudfront_resource + (
- operation['resource'] % { 'dist_id' : dist_id })
+ def create_request(self, operation, dist_id = None, inv_id = None, headers = None):
+ if(inv_id):
+ resource = self.config.cloudfront_resource + (
+ operation['resource'] % { 'dist_id' : dist_id, 'inv_id' : inv_id })
+ else:
+ resource = self.config.cloudfront_resource + (
+ operation['resource'] % { 'dist_id' : dist_id })
if not headers:
headers = {}
@@ -477,3 +582,48 @@
pretty_output("Comment", dc.info['Comment'])
pretty_output("Enabled", dc.info['Enabled'])
pretty_output("Etag", response['headers']['etag'])
+
+ @staticmethod
+ def invlist(args):
+ cf = CloudFront(Config())
+ cfuris = []
+
+ for arg in args:
+ cfuris.append(S3Uri(arg))
+ if cfuris[-1].type != 'cf':
+ raise ParameterError("CloudFront URI required instead of: %s" % arg)
+ for cfuri in cfuris:
+ response = cf.InvalidationList(cfuri)
+ pprint.PrettyPrinter(indent=2).pprint(response)
+
+ @staticmethod
+ def inv(args):
+ cf = CloudFront(Config())
+ files = []
+ # First arg should be the cf uri
+ # next args should be strings of files
+ pprint.PrettyPrinter().pprint(args)
+
+ cfuri = S3Uri(args[0])
+
+ if cfuri.type != 'cf':
+ raise ParameterError("CloudFront URI required instead of: %s" % args[0])
+
+
+ for filePath in args[1:]:
+ files.append(filePath)
+
+ response = cf.Invalidate(cfuri, files)
+
+ @staticmethod
+ def invinfo(args):
+ cf = CloudFront(Config())
+
+ cfuri = S3Uri(args[0])
+
+ if cfuri.type != 'cf':
+ raise ParameterError("CloudFront URI required instead of: %s" % args[0])
+
+ invid = args[1]
+
+ response = cf.InvalidationInfo(cfuri, invid)
Index: S3/Config.py
===================================================================
--- S3/Config.py (revision 392)
+++ S3/Config.py (working copy)
@@ -19,7 +19,7 @@
host_bucket = "%(bucket)s.s3.amazonaws.com"
simpledb_host = "sdb.amazonaws.com"
cloudfront_host = "cloudfront.amazonaws.com"
- cloudfront_resource = "/2008-06-30/distribution"
+ cloudfront_resource = "/2010-08-01/distribution"
verbosity = logging.WARNING
progress_meter = True
progress_class = Progress.ProgressCR
Index: s3cmd
===================================================================
--- s3cmd (revision 392)
+++ s3cmd (working copy)
@@ -1356,6 +1356,9 @@
{"cmd":"cfcreate", "label":"Create CloudFront distribution point", "param":"s3://BUCKET", "func":CfCmd.create, "argc":1},
{"cmd":"cfdelete", "label":"Delete CloudFront distribution point", "param":"cf://DIST_ID", "func":CfCmd.delete, "argc":1},
{"cmd":"cfmodify", "label":"Change CloudFront distribution point parameters", "param":"cf://DIST_ID", "func":CfCmd.modify, "argc":1},
+ {"cmd":"cfinv", "label":"Invalidate a file for a distribution", "param":"cf://DIST_ID FILE [FILE...]", "func":CfCmd.inv, "argc":2},
+ {"cmd":"cfinvlist", "label":"List invalidation requests for a distribution", "param":"cf://DIST_ID", "func":CfCmd.invlist, "argc":1},
+ {"cmd":"cfinvinfo", "label":"Get the status of a particular invalidation requestion for a distirbution", "param":"cf://DIST_ID cf:INV_ID", "func":CfCmd.invinfo, "argc":2},
]
def format_commands(progname, commands_list):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment