<pre style='margin:0'>
Rainer Müller (raimue) pushed a commit to branch master
in repository macports-infrastructure.
</pre>
<p><a href="https://github.com/macports/macports-infrastructure/commit/02f5a137ebdbe62583e76814865b3c52f9090b81">https://github.com/macports/macports-infrastructure/commit/02f5a137ebdbe62583e76814865b3c52f9090b81</a></p>
<pre style="white-space: pre; background: #F8F8F8">The following commit(s) were added to refs/heads/master by this push:
<span style='display:block; white-space:pre;color:#404040;'> new 02f5a13 buildbot: Automatically purge CDN after deploying
</span>02f5a13 is described below
<span style='display:block; white-space:pre;color:#808000;'>commit 02f5a137ebdbe62583e76814865b3c52f9090b81
</span>Author: Rainer Müller <raimue@macports.org>
AuthorDate: Thu Oct 19 19:39:15 2017 +0200
<span style='display:block; white-space:pre;color:#404040;'> buildbot: Automatically purge CDN after deploying
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> If credentials are configured in config.json, CDNPurgeStep will run
</span><span style='display:block; white-space:pre;color:#404040;'> a shell command to purge the caches of the modified zone on MaxCDN. The
</span><span style='display:block; white-space:pre;color:#404040;'> shell command is executed directly on the master as it does not need any
</span><span style='display:block; white-space:pre;color:#404040;'> build artifacts and otherwise the script and its modules would need to
</span><span style='display:block; white-space:pre;color:#404040;'> be transferred to the buildslave first.
</span><span style='display:block; white-space:pre;color:#404040;'>
</span><span style='display:block; white-space:pre;color:#404040;'> maxcdn-purge.py as a little helper script which uses the MaxCDN Python
</span><span style='display:block; white-space:pre;color:#404040;'> API module to purge the cache of a zone given by its zone ID.
</span>---
buildbot/config.json.sample | 10 +-
buildbot/master.cfg | 20 +++
buildbot/maxcdn-purge/.gitignore | 1 +
buildbot/maxcdn-purge/maxcdn | 1 +
buildbot/maxcdn-purge/maxcdn-0.0.7/PKG-INFO | 11 ++
.../maxcdn-0.0.7/maxcdn.egg-info/PKG-INFO | 11 ++
.../maxcdn-0.0.7/maxcdn.egg-info/SOURCES.txt | 9 ++
.../maxcdn.egg-info/dependency_links.txt | 1 +
.../maxcdn-0.0.7/maxcdn.egg-info/requires.txt | 3 +
.../maxcdn-0.0.7/maxcdn.egg-info/top_level.txt | 1 +
.../maxcdn-purge/maxcdn-0.0.7/maxcdn/__init__.py | 1 +
.../maxcdn-purge/maxcdn-0.0.7/maxcdn/maxcdn.py | 90 +++++++++++++
buildbot/maxcdn-purge/maxcdn-0.0.7/setup.cfg | 5 +
buildbot/maxcdn-purge/maxcdn-0.0.7/setup.py | 35 +++++
buildbot/maxcdn-purge/maxcdn-0.0.7/test/test.py | 145 +++++++++++++++++++++
buildbot/maxcdn-purge/maxcdn-purge.py | 36 +++++
16 files changed, 378 insertions(+), 2 deletions(-)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/config.json.sample b/buildbot/config.json.sample
</span><span style='display:block; white-space:pre;color:#808080;'>index 9907348..68ad0de 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/buildbot/config.json.sample
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/config.json.sample
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -5,12 +5,17 @@
</span> "slaveprefix": "/opt/local",
"toolsprefix": "/opt/mports",
"deploy": {
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ "maxcdn": {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "key": "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "secret": ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ },
</span> "www": {
"host": "",
"user": "",
"sshkeyfile": "",
"sshknownhostsfile": "ssh_known_hosts",
<span style='display:block; white-space:pre;background:#ffe0e0;'>- "destpath": ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "destpath": "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "maxcdnzoneid": 0
</span> },
"portindex": {
"host": "",
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -23,7 +28,8 @@
</span> "user": "",
"sshkeyfile": "",
"sshknownhostsfile": "ssh_known_hosts",
<span style='display:block; white-space:pre;background:#ffe0e0;'>- "destpath": ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "destpath": "",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "maxcdnzoneid": 0
</span> },
"man": {
"host": "",
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/master.cfg b/buildbot/master.cfg
</span><span style='display:block; white-space:pre;color:#808080;'>index 8f8847a..de7633b 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/buildbot/master.cfg
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/master.cfg
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -593,6 +593,22 @@ def make_ssh_deploy_step(sshkeyfile, sshknownhostsfile, step):
</span> mode=0600),
step)
<span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class CDNPurgeStep(steps.MasterShellCommand):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ # XXX: Ensure py*-requests-oauthlib is available
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, zoneid, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ super(CDNPurgeStep, self).__init__(
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ name='cdn',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ description='purging',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ descriptionDone='purged',
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ command="./maxcdn-purge/maxcdn-purge.py %s" % (zoneid,),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ env={'MAXCDN_KEY': config['deploy']['maxcdn']['key'],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 'MAXCDN_SECRET': config['deploy']['maxcdn']['secret'],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 'PATH': "${PATH}"},
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> if 'www' in config['deploy']:
jobs_www_factory = util.BuildFactory()
# TODO: incremental mode with cleanup?
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -618,6 +634,8 @@ if 'www' in config['deploy']:
</span> user=config['deploy']['www']['user'],
srcpath='www/',
destpath=config['deploy']['www']['destpath'])))
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if 'maxcdn' in config['deploy'] and 'maxcdnzoneid' in config['deploy']['www']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ jobs_www_factory.addStep(CDNPurgeStep(zoneid=config['deploy']['www']['maxcdnzoneid']))
</span>
if 'portindex' in config['deploy']:
jobs_portindex_factory = util.BuildFactory()
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -702,6 +720,8 @@ if 'guide' in config['deploy']:
</span> user=config['deploy']['guide']['user'],
srcpath='guide/guide/html/',
destpath=config['deploy']['guide']['destpath'])))
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if 'maxcdn' in config['deploy'] and 'maxcdnzoneid' in config['deploy']['guide']:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ jobs_guide_factory.addStep(CDNPurgeStep(zoneid=config['deploy']['guide']['maxcdnzoneid']))
</span>
if 'man' in config['deploy']:
jobs_man_factory = util.BuildFactory()
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/.gitignore b/buildbot/maxcdn-purge/.gitignore
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..0d20b64
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/.gitignore
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+*.pyc
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn b/buildbot/maxcdn-purge/maxcdn
</span>new file mode 120000
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..b1214ca
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn-0.0.7/maxcdn
</span>\ No newline at end of file
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/PKG-INFO b/buildbot/maxcdn-purge/maxcdn-0.0.7/PKG-INFO
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..77ac22e
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/PKG-INFO
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,11 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Metadata-Version: 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Name: maxcdn
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Version: 0.0.7
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Summary: A Python REST Client for MaxCDN REST Web Services
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Home-page: http://github.com/maxcdn/python-maxcdn
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Author: Joshua P. Mervine
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Author-email: joshua@mervine.net
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+License: MIT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Description: UNKNOWN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Keywords: MaxCDN CDN API REST
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Platform: UNKNOWN
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/PKG-INFO b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/PKG-INFO
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..77ac22e
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/PKG-INFO
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,11 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Metadata-Version: 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Name: maxcdn
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Version: 0.0.7
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Summary: A Python REST Client for MaxCDN REST Web Services
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Home-page: http://github.com/maxcdn/python-maxcdn
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Author: Joshua P. Mervine
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Author-email: joshua@mervine.net
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+License: MIT
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Description: UNKNOWN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Keywords: MaxCDN CDN API REST
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Platform: UNKNOWN
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/SOURCES.txt b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/SOURCES.txt
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..9305979
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/SOURCES.txt
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,9 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+setup.py
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn/__init__.py
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn/maxcdn.py
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn.egg-info/PKG-INFO
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn.egg-info/SOURCES.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn.egg-info/dependency_links.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn.egg-info/requires.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn.egg-info/top_level.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+test/test.py
</span>\ No newline at end of file
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/dependency_links.txt b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/dependency_links.txt
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..8b13789
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/dependency_links.txt
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/requires.txt b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/requires.txt
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..5f8af11
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/requires.txt
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,3 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+requests
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+requests_oauthlib
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+certifi
</span>\ No newline at end of file
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/top_level.txt b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/top_level.txt
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..2b0bda9
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn.egg-info/top_level.txt
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/__init__.py b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/__init__.py
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..91632b4
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/__init__.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from .maxcdn import MaxCDN
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/maxcdn.py b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/maxcdn.py
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..957f7c2
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/maxcdn/maxcdn.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,90 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from requests_oauthlib import OAuth1Session as OAuth1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# handle python 3.x
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ import urlparse
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+except ImportError:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ from urllib import parse as urlparse
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class MaxCDN(object):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, alias, key, secret, server="rws.maxcdn.com", **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.url = "https://%s/%s" % (server, alias)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.client = OAuth1(key, client_secret=secret, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _get_headers(self, json=True):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ headers = {"User-Agent": "Python MaxCDN API Client"}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if json:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ headers["Content-Type"] = "application/json"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return headers
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _get_url(self, end_point):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if not end_point.startswith("/"):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return "{0}/{1}".format(self.url, end_point)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.url + end_point
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _parse_json(self, response):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return response.json()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ except ValueError as e:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ raise self.ServerError(response, str(e))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def _data_request(self, method, end_point, data, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if data is None and "params" in kwargs:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ params = kwargs.pop("params")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if type(params) is str:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ params = urlparse.parse_qs(params)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ data = params
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ action = getattr(self.client, method)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ response = action(self._get_url(end_point), data=data,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ headers=self._get_headers(json=True), **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (response.status_code > 299):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ raise self.ServerError(response)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._parse_json(response)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def get(self, end_point, data=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._data_request("get", end_point, data=data, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def patch(self, end_point, data=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._data_request("post", end_point, data=data, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def post(self, end_point, data=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._data_request("post", end_point, data=data, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def put(self, end_point, data=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._data_request("put", end_point, data=data, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def delete(self, end_point, data=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._data_request("delete", end_point, data=data, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def purge(self, zoneid, file_or_files=None, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ path = "/zones/pull.json/%s/cache" % (zoneid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if file_or_files is not None:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.delete(path, data={"files": file_or_files}, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self.delete(path, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ class ServerError(Exception):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, response, message=None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ resp = response.json()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if message is None:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ message = "{0}:: {1}".format(resp['error']['type'],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ resp['error']['message'])
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.reason = resp['error']['type']
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ except ValueError:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if message is None:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ message = "{0} {1} from {2}".format(response.status_code,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ response.reason,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ response.url)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.reason = response.reason
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.headers = response.headers
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.code = response.status_code
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.body = response._content
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.url = response.url
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ super(Exception, self).__init__(message)
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.cfg b/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.cfg
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..861a9f5
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.cfg
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,5 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+[egg_info]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+tag_build =
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+tag_date = 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+tag_svn_revision = 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.py b/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.py
</span>new file mode 100755
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..f3eb6ed
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/setup.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,35 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#!/usr/bin/env python
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# -*- coding: utf-8 -*-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+options = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "name": "maxcdn",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "version": "0.0.7",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "description": "A Python REST Client for MaxCDN REST Web Services",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "author": "Joshua P. Mervine",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "author_email": "joshua@mervine.net",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "license": "MIT",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "keywords": "MaxCDN CDN API REST",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "packages": ['maxcdn'],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "url": 'http://github.com/maxcdn/python-maxcdn'
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+install_requires = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "requests",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "requests_oauthlib",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "certifi"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+tests_require = [
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "nose",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "mock"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+include_package_data = True
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+try:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ from setuptools import setup
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ options["install_requires"] = install_requires
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ options["include_package_data"] = include_package_data
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ options["tests_require"] = tests_require
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ setup(**options)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+except ImportError:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("ERROR: setuptools wasn't found, please install it")
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-0.0.7/test/test.py b/buildbot/maxcdn-purge/maxcdn-0.0.7/test/test.py
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..7b4275a
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-0.0.7/test/test.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,145 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import mock
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import unittest
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import requests
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from maxcdn import MaxCDN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+###############################################################################
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Mock as needed.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+def mock_request(method, url, *args, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pass
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class Response(object):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, method, code=200, data={"foo": "bar"}):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._json = {"code": code, "method": method, "data": data}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.status_code = code
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.reason = "Success"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.headers = ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.url = "http://www.example.com/foo/bar"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def json(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return self._json
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class ErrorResponse(object):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def __init__(self, code=200, badcontent="Some bad content."):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self._content = badcontent
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.status_code = code
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.reason = "Error Response"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.headers = ""
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.url = "http://www.example.com/foo/bar"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def json(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ raise ValueError("No JSON object could be decoded")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+def response(method, **kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return Response(method, **kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+def error_response(**kwargs):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return ErrorResponse(**kwargs)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+###############################################################################
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+class MaxCDNTests(unittest.TestCase):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def setUp(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.alias = "test_alias"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.key = "test_key"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.secret = "test_secret"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.server = "rws.example.com"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.maxcdn = MaxCDN(self.alias, self.key,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.secret, server=self.server)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_init(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertTrue(self.maxcdn)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.url,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "https://rws.example.com/test_alias")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_get_url(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn._get_url("/foo.json"),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "https://rws.example.com/test_alias/foo.json")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn._get_url("foo.json"),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ "https://rws.example.com/test_alias/foo.json")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_data_request(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for meth in ["post", "put", "delete"]:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response(meth))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ data = {"foo": "bar"}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn._data_request(meth, meth + ".json", data=data)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": meth, "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=error_response())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ with self.assertRaises(MaxCDN.ServerError, None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.maxcdn._data_request(meth, meth+".json", data={"foo":"bar"})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_get(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response("get"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "get", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.get("/get.json"), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=error_response())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ with self.assertRaises(MaxCDN.ServerError, None):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.maxcdn.get("/get.json")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_post(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response("post"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn.post("/post.json", data={"foo": "bar"})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "post", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn.post("/post.json", params={"foo": "bar"})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn.post("/post.json", params="foo=bar")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_put(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response("put"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "put", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.put("/put.json"), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_delete(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response("delete"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "delete", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.delete("/delete.json"), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn.patch("/delete.json", file_or_files="/foo.css")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ files = ["/foo.css", "/bar.css"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "delete", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ rsp = self.maxcdn.patch("/delete.json", file_or_files=files)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(rsp, expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ def test_purge(self):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ requests.Session.request = mock.create_autospec(mock_request,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return_value=response("delete"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ expected = {"code": 200, "method": "delete", "data": {"foo": "bar"}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.purge(12345), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.purge(12345, "/master.css"), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ files = ["/master.css", "/other.css"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ self.assertEqual(self.maxcdn.purge(12345, files), expected)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if __name__ == '__main__':
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ unittest.main()
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/buildbot/maxcdn-purge/maxcdn-purge.py b/buildbot/maxcdn-purge/maxcdn-purge.py
</span>new file mode 100755
<span style='display:block; white-space:pre;color:#808080;'>index 0000000..ac4befd
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/buildbot/maxcdn-purge/maxcdn-purge.py
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,36 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+#!/usr/bin/env python
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from __future__ import print_function
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import sys
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import os
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+import pprint as pp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+from maxcdn import MaxCDN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if not all(k in os.environ for k in ["MAXCDN_KEY", "MAXCDN_SECRET"]):
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("Error: MAXCDN_KEY or MAXCDN_SECRET not set in environment!", file=sys.stderr)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.exit(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if len(sys.argv) != 2:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("Usage: {} <zoneid>".format(sys.argv[0]), file=sys.stderr)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.exit(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+zoneid = sys.argv[1]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+MAXCDN_ALIAS = "macports"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+MAXCDN_KEY = os.environ["MAXCDN_KEY"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+MAXCDN_SECRET = os.environ["MAXCDN_SECRET"]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Initialize MaxCDN API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+maxcdn = MaxCDN(MAXCDN_ALIAS, MAXCDN_KEY, MAXCDN_SECRET)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+# Purge requested zone
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+res = maxcdn.purge(zoneid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+if not 'code' in res:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("Error: Unexpected response:", file=sys.stderr)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ pp.pprint(res, file=sys.stderr)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.exit(1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+elif res['code'] == 200:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("Zone {} purged.".format(zoneid))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+else:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ print("Purging of zone {} failed with code: " + res['code'], file=sys.stderr)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ sys.exit(1)
</span></pre><pre style='margin:0'>
</pre>