From 71226782fbe849278af9e4a5f79ae35583489b51 Mon Sep 17 00:00:00 2001 From: Spider Person Date: Fri, 20 Jun 2014 12:06:18 +0200 Subject: [PATCH 01/29] fix broken ref in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4cf7c47..003abae 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ def read(fname): url = "https://github.com/zebrafishlabs/fastly-python", packages=['fastly', 'tests'], scripts=['bin/fastly_upload_vcl.py', 'bin/fastly_purge_url.py'], - long_description=read('README'), + long_description=read('README.md'), classifiers=[ "Development Status :: 3 - Alpha", "Topic :: Software Development :: Libraries :: Python Modules", From 3065debb5f3219fb3dd047df3f4850a8179e57fc Mon Sep 17 00:00:00 2001 From: david birdsong Date: Thu, 3 Mar 2016 16:29:27 -0800 Subject: [PATCH 02/29] missing deps --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 003abae..6de2d27 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,9 @@ def read(fname): keywords = "fastly", url = "https://github.com/zebrafishlabs/fastly-python", packages=['fastly', 'tests'], + install_requires=[ + 'httplib2', + ], scripts=['bin/fastly_upload_vcl.py', 'bin/fastly_purge_url.py'], long_description=read('README.md'), classifiers=[ From abb423fb992588124e6d8cd69aca8b929819d35c Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 2 Jun 2016 13:38:15 +0200 Subject: [PATCH 03/29] Add convenience functions for header.{destination,source} This allows roundtripping through the API where you can use the fields returned by the API to give as parameters to {create,update}_header. --- fastly/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..5cafd99 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1347,6 +1347,15 @@ class FastlyHeader(FastlyObject, IServiceVersionObject): "cache_condition", ] + @property + def destination(self): + return self.dst + + @property + def source(self): + return self.src + + class FastlyHealthCheck(FastlyObject, IServiceVersionObject): """Healthchecks are used to customize the way Fastly checks on your Backends. Only Backends that have successful Healthchecks will be sent traffic, thus assuring that the failure of one server does not affect visitors.""" From 5fcb5e92f75a5903ae288c3e85fd811251753aaa Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 2 Jun 2016 13:41:43 +0200 Subject: [PATCH 04/29] Add SSL cert validation and a timeout Ideally those should be configurable, but I'd rather it fails safe than unsafe. --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..4754b01 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1063,7 +1063,7 @@ def _fetch(self, url, method="GET", body=None, headers={}): if not hdrs.has_key("Content-Type") and method in ["POST", "PUT"]: hdrs["Content-Type"] = "application/x-www-form-urlencoded" - conn = httplib2.Http(disable_ssl_certificate_validation=True) + conn = httplib2.Http(disable_ssl_certificate_validation=False, timeout=10) endpoint = "%s://%s%s" % (FASTLY_SCHEME, FASTLY_HOST, url) return self._check(*conn.request(endpoint, method, body=body, headers=hdrs)) From c0faf01d911486d058a01f45df087452b636349f Mon Sep 17 00:00:00 2001 From: Thom Mahoney Date: Tue, 19 Jul 2016 15:14:10 -0700 Subject: [PATCH 05/29] use Fastly-Key instead of X-Fastly-Key --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..95c043f 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1057,7 +1057,7 @@ def _fetch(self, url, method="GET", body=None, headers={}): if self._fully_authed: hdrs["Cookie"] = self._session else: - hdrs["X-Fastly-Key"] = self._api_key + hdrs["Fastly-Key"] = self._api_key hdrs["Content-Accept"] = "application/json" if not hdrs.has_key("Content-Type") and method in ["POST", "PUT"]: From 1a2a82b75d44c2c7b34e1adb47934b3e51aa782c Mon Sep 17 00:00:00 2001 From: Adam McElwee Date: Wed, 12 Oct 2016 13:53:19 -0500 Subject: [PATCH 06/29] URL encode any strings that are used as path components --- fastly/__init__.py | 86 +++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..144e543 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -169,20 +169,20 @@ def create_backend(self, def get_backend(self, service_id, version_number, name): """Get the backend for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name))) return FastlyBackend(self, content) def update_backend(self, service_id, version_number, name_key, **kwargs): """Update the backend for a particular service and version.""" body = self._formdata(kwargs, FastlyBackend.FIELDS) - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyBackend(self, content) def delete_backend(self, service_id, version_number, name): """Delete the backend for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -221,20 +221,20 @@ def create_cache_settings(self, def get_cache_settings(self, service_id, version_number, name): """Get a specific cache settings object.""" - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name))) return FastlyCacheSettings(self, content) def update_cache_settings(self, service_id, version_number, name_key, **kwargs): """Update a specific cache settings object.""" body = self._formdata(kwargs, FastlyCacheSettings.FIELDS) - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyCacheSettings(self, content) def delete_cache_settings(self, service_id, version_number, name): """Delete a specific cache settings object.""" - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -266,20 +266,20 @@ def create_condition(self, def get_condition(self, service_id, version_number, name): """Gets a specified condition.""" - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name))) return FastlyCondition(self, content) def update_condition(self, service_id, version_number, name_key, **kwargs): """Updates the specified condition.""" body = self._formdata(kwargs, FastlyCondition.FIELDS) - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyCondition(self, content) def delete_condition(self, service_id, version_number, name): """Deletes the specified condition.""" - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -358,38 +358,38 @@ def create_director(self, service_id, version_number, def get_director(self, service_id, version_number, name): """Get the director for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name))) return FastlyDirector(self, content) def update_director(self, service_id, version_number, name_key, **kwargs): """Update the director for a particular service and version.""" body = self._formdata(kwargs, FastlyDirector.FIELDS) - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyDirector(self, content) def delete_director(self, service_id, version_number, name): """Delete the director for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) def get_director_backend(self, service_id, version_number, director_name, backend_name): """Returns the relationship between a Backend and a Director. If the Backend has been associated with the Director, it returns a simple record indicating this. Otherwise, returns a 404.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, backend_name), method="GET") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="GET") return FastlyDirectorBackend(self, content) def create_director_backend(self, service_id, version_number, director_name, backend_name): """Establishes a relationship between a Backend and a Director. The Backend is then considered a member of the Director and can be used to balance traffic onto.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, backend_name), method="POST") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="POST") return FastlyDirectorBackend(self, content) def delete_director_backend(self, service_id, version_number, director_name, backend_name): """Deletes the relationship between a Backend and a Director. The Backend is no longer considered a member of the Director and thus will not have traffic balanced onto it from this Director.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, backend_name), method="DELETE") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="DELETE") return self._status(content) @@ -416,26 +416,26 @@ def create_domain(self, def get_domain(self, service_id, version_number, name): """Get the domain for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name))) return FastlyDomain(self, content) def update_domain(self, service_id, version_number, name_key, **kwargs): """Update the domain for a particular service and version.""" body = self._formdata(kwargs, FastlyDomain.FIELDS) - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyDomain(self, content) def delete_domain(self, service_id, version_number, name): """Delete the domain for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(self, content) def check_domain(self, service_id, version_number, name): """Checks the status of a domain's DNS record. Returns an array of 3 items. The first is the details for the domain. The second is the current CNAME of the domain. The third is a boolean indicating whether or not it has been properly setup to use Fastly.""" - content = self._fetch("/service/%s/version/%d/domain/%s/check" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/domain/%s/check" % (service_id, version_number, urllib.quote(name))) return FastlyDomainCheck(self, content) @@ -479,20 +479,20 @@ def create_header(self, service_id, version_number, name, destination, source, _ def get_header(self, service_id, version_number, name): """Retrieves a Header object by name.""" - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name))) return FastlyHeader(self, content) def update_header(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Header object by name.""" body = self._formdata(kwargs, FastlyHeader.FIELDS) - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHeader(self, content) def delete_header(self, service_id, version_number, name): """Deletes a Header object by name.""" - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -536,20 +536,20 @@ def create_healthcheck(self, def get_healthcheck(self, service_id, version_number, name): """Get the healthcheck for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name))) return FastlyHealthCheck(self, content) def update_healthcheck(self, service_id, version_number, name_key, **kwargs): """Update the healthcheck for a particular service and version.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHealthCheck(self, content) def delete_healthcheck(self, service_id, version_number, name): """Delete the healthcheck for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -607,20 +607,20 @@ def create_request_setting(self, def get_request_setting(self, service_id, version_number, name): """Gets the specified Request Settings object.""" - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name))) return FastlyRequestSetting(self, content) def update_request_setting(self, service_id, version_number, name_key, **kwargs): """Updates the specified Request Settings object.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyRequestSetting(self, content) def delete_request_setting(self, service_id, version_number, name): """Removes the specfied Request Settings object.""" - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -646,20 +646,20 @@ def create_response_object(self, service_id, version_number, name, status="200", def get_response_object(self, service_id, version_number, name): """Gets the specified Response Object.""" - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name))) return FastlyResponseObject(self, content) def update_response_object(self, service_id, version_number, name_key, **kwargs): """Updates the specified Response Object.""" body = self._formdata(kwargs, FastlyResponseObject.FIELDS) - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyResponseObject(self, content) def delete_response_object(self, service_id, version_number, name): """Deletes the specified Response Object.""" - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -695,7 +695,7 @@ def get_service_details(self, service_id): def get_service_by_name(self, service_name): """Get a specific service by name.""" - content = self._fetch("/service/search?name=%s" % service_name) + content = self._fetch("/service/search?name=%s" % urllib.quote(service_name)) return FastlyService(self, content) @@ -783,20 +783,20 @@ def create_syslog(self, def get_syslog(self, service_id, version_number, name): """Get the Syslog for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name))) return FastlySyslog(self, content) def update_syslog(self, service_id, version_number, name_key, **kwargs): """Update the Syslog for a particular service and version.""" body = self._formdata(kwargs, FastlySyslog.FIELDS) - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlySyslog(self, content) def delete_syslog(self, service_id, version_number, name): """Delete the Syslog for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -881,13 +881,13 @@ def download_vcl(self, service_id, version_number, name): def get_vcl(self, service_id, version_number, name, include_content=True): """Get the uploaded VCL for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/vcl/%s?include_content=%d" % (service_id, version_number, name, int(include_content))) + content = self._fetch("/service/%s/version/%d/vcl/%s?include_content=%d" % (service_id, version_number, urllib.quote(name), int(include_content))) return FastlyVCL(self, content) def get_vcl_html(self, service_id, version_number, name): """Get the uploaded VCL for a particular service and version with HTML syntax highlighting.""" - content = self._fetch("/service/%s/version/%d/vcl/%s/content" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/vcl/%s/content" % (service_id, version_number, urllib.quote(name))) return content.get("content", None) @@ -905,20 +905,20 @@ def get_generated_vcl_html(self, service_id, version_number): def set_main_vcl(self, service_id, version_number, name): """Set the specified VCL as the main.""" - content = self._fetch("/service/%s/version/%d/vcl/%s/main" % (service_id, version_number, name), method="PUT") + content = self._fetch("/service/%s/version/%d/vcl/%s/main" % (service_id, version_number, urllib.quote(name)), method="PUT") return FastlyVCL(self, content) def update_vcl(self, service_id, version_number, name_key, **kwargs): """Update the uploaded VCL for a particular service and version.""" body = self._formdata(kwargs, FastlyVCL.FIELDS) - content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyVCL(self, content) def delete_vcl(self, service_id, version_number, name): """Delete the uploaded VCL for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) @@ -1004,20 +1004,20 @@ def create_wordpress(self, def get_wordpress(self, service_id, version_number, name): """Get information on a specific wordpress.""" - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name))) return FastlyWordpress(self, content) def update_wordpress(self, service_id, version_number, name_key, **kwargs): """Update a specified wordpress.""" body = self._formdata(kwargs, FastlyWordpress.FIELDS) - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, name_key), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyWordpress(self, content) def delete_wordpress(self, service_id, version_number, name): """Delete a specified wordpress.""" - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) From 7e5be1bf600d648ec416f10eb5a900f29d367f06 Mon Sep 17 00:00:00 2001 From: Adam McElwee Date: Sat, 8 Oct 2016 18:38:00 -0500 Subject: [PATCH 07/29] Add FastlyGzip and CRUD operations for it --- fastly/__init__.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..1843bfe 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -451,6 +451,43 @@ def get_event_log(self, object_id): return FastlyEventLog(self, content) + def list_gzip(self, service_id, version_number): + """List all gzip configurations for a particular service and version""" + content = self._fetch("/service/%s/version/%d/gzip" % (service_id, version_number)) + return map(lambda x: FastlyGzip(self, x), content) + + + def create_gzip(self, service_id, version_number, name, cache_condition=None, content_types=None, extensions=None): + body = self._formdata({ + "name": name, + "cache_condition": cache_condition, + "content_types": content_types, + "extensions": extensions + }, FastlyGzip.FIELDS) + """Creates a new Gzip object.""" + content = self._fetch("/service/%s/version/%d/gzip" % (service_id, version_number), method="POST", body=body) + return FastlyGzip(self, content) + + + def get_gzip(self, service_id, version_number, name): + """Retrieves a Header object by name.""" + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name)) + return FastlyGzip(self, content) + + + def update_gzip(self, service_id, version_number, name, **kwargs): + """Modifies an existing Gzip object by name.""" + body = self._formdata(kwargs, FastlyGzip.FIELDS) + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name), method="PUT", body=body) + return FastlyHeader(self, content) + + + def delete_gzip(self, service_id, version_number, name): + """Deletes a Gzip object by name.""" + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name), method="DELETE") + return self._status(content) + + def list_headers(self, service_id, version_number): """Retrieves all Header objects for a particular Version of a Service.""" content = self._fetch("/service/%s/version/%d/header" % (service_id, version_number)) @@ -1328,6 +1365,18 @@ class FastlyEventLog(FastlyObject): ] +class FastlyGzip(FastlyObject, IServiceVersionObject): + """Gzip configuration allows you to choose resources to automatically compress.""" + FIELDS = [ + "cache_condition", + "content_types", + "extensions", + "name", + "service_id", + "version" + ] + + class FastlyHeader(FastlyObject, IServiceVersionObject): """Header objects are used to add, modify, or delete headers from requests and responses. The header content can be simple strings or be derived from variables inside Varnish. Regular expressions can be used to customize the headers even further.""" FIELDS = [ From 0ab78c01b7fd24f3737fa3d29372718f8853026d Mon Sep 17 00:00:00 2001 From: Adam McElwee Date: Wed, 12 Oct 2016 14:11:07 -0500 Subject: [PATCH 08/29] urllib.quote for gzip ops --- fastly/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 1843bfe..bae9fb7 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -471,20 +471,20 @@ def create_gzip(self, service_id, version_number, name, cache_condition=None, co def get_gzip(self, service_id, version_number, name): """Retrieves a Header object by name.""" - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name)) + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name))) return FastlyGzip(self, content) - def update_gzip(self, service_id, version_number, name, **kwargs): + def update_gzip(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Gzip object by name.""" body = self._formdata(kwargs, FastlyGzip.FIELDS) - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHeader(self, content) def delete_gzip(self, service_id, version_number, name): """Deletes a Gzip object by name.""" - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, name), method="DELETE") + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) From c4eccb937d9c8e89e78025462216623d06b0ac49 Mon Sep 17 00:00:00 2001 From: Adam McElwee Date: Wed, 12 Oct 2016 14:13:50 -0500 Subject: [PATCH 09/29] Return correct type in update_gzip --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index bae9fb7..017e85f 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -479,7 +479,7 @@ def update_gzip(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Gzip object by name.""" body = self._formdata(kwargs, FastlyGzip.FIELDS) content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) - return FastlyHeader(self, content) + return FastlyGzip(self, content) def delete_gzip(self, service_id, version_number, name): From 21ebb37508cc1767cfc594864466d89b52eb59f6 Mon Sep 17 00:00:00 2001 From: Katie Sandford Date: Thu, 13 Oct 2016 08:57:30 +0100 Subject: [PATCH 10/29] Add missing comma to FastlyService fields list --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 032b693..80abce1 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1426,7 +1426,7 @@ class FastlyService(FastlyObject): "customer_id", "publish_key", "active_version", - "versions" + "versions", "comment", ] From 6ee8dd827d141105c973b31d4f6d1db293ee25d5 Mon Sep 17 00:00:00 2001 From: Cindy Sridharan Date: Thu, 6 Apr 2017 15:19:28 -0700 Subject: [PATCH 11/29] modified fields of the /syslog endpoint to reflect latest API --- fastly/__init__.py | 155 +++++++++++++++++++++------------------------ 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index b673ac4..367c750 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -3,40 +3,41 @@ # Author: Chris Zacharias (chris@imgix.com) # Copyright (c) 2012, Zebrafish Labs Inc. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: -# +# # Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. -# +# # Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation +# this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. -# +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +from datetime import datetime import httplib2 -import urllib -import re import json -from datetime import datetime +import re +import urllib FASTLY_SCHEME = "https" FASTLY_HOST = "api.fastly.com" FASTLY_SESSION_REGEX = re.compile("(fastly\.session=[^;]+);") + class FastlyRoles(object): USER = "user" BILLING = "billing" @@ -51,52 +52,52 @@ class FastlyCacheSettingsAction(object): class FastlyConditionType(object): - RESPONSE="response" - CACHE="cache" - REQUEST="request" - FETCH="fetch" + RESPONSE = "response" + CACHE = "cache" + REQUEST = "request" + FETCH = "fetch" class FastlyHeaderAction(object): - SET="set" - APPEND="append" - DELETE="delete" - REGEX="regex" - REGEX_ALL="regex_repeat" + SET = "set" + APPEND = "append" + DELETE = "delete" + REGEX = "regex" + REGEX_ALL = "regex_repeat" class FastlyHeaderType(object): - RESPONSE="response" - FETCH="fetch" - CACHE="cache" - REQUEST="request" + RESPONSE = "response" + FETCH = "fetch" + CACHE = "cache" + REQUEST = "request" class FastlyRequestSettingAction(object): - LOOKUP="lookup" - PASS="pass" + LOOKUP = "lookup" + PASS = "pass" class FastlyForwardedForAction(object): - CLEAR="clear" - LEAVE="leave" - APPEND="append" - APPEND_ALL="append_all" - OVERWRITE="overwrite" + CLEAR = "clear" + LEAVE = "leave" + APPEND = "append" + APPEND_ALL = "append_all" + OVERWRITE = "overwrite" class FastlyStatsType(object): - ALL="all" - DAILY="daily" - HOURLY="hourly" - MINUTELY="minutely" + ALL = "all" + DAILY = "daily" + HOURLY = "hourly" + MINUTELY = "minutely" class FastlyDirectorType(object): - RANDOM=1 - ROUNDROBIN=2 - HASH=3 - CLIENT=4 + RANDOM = 1 + ROUNDROBIN = 2 + HASH = 3 + CLIENT = 4 class FastlyConnection(object): @@ -109,7 +110,6 @@ def __init__(self, api_key): def fully_authed(self): return self._fully_authed - def login(self, user, password): body = self._formdata({ "user": user, @@ -119,18 +119,15 @@ def login(self, user, password): self._fully_authed = True return FastlySession(self, content) - def list_backends(self, service_id, version_number): """List all backends for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number)) return map(lambda x: FastlyBackend(self, x), content) - - def create_backend(self, + def create_backend(self, service_id, - version_number, - name, + version_number, + name, address, use_ssl=False, port=80, @@ -166,42 +163,36 @@ def create_backend(self, content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number), method="POST", body=body) return FastlyBackend(self, content) - def get_backend(self, service_id, version_number, name): """Get the backend for a particular service and version.""" content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name))) return FastlyBackend(self, content) - def update_backend(self, service_id, version_number, name_key, **kwargs): """Update the backend for a particular service and version.""" body = self._formdata(kwargs, FastlyBackend.FIELDS) content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyBackend(self, content) - def delete_backend(self, service_id, version_number, name): """Delete the backend for a particular service and version.""" content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def check_backends(self, service_id, version_number): """Performs a health check against each backend in version. If the backend has a specific type of healthcheck, that one is performed, otherwise a HEAD request to / is performed. The first item is the details on the Backend itself. The second item is details of the specific HTTP request performed as a health check. The third item is the response details.""" content = self._fetch("/service/%s/version/%d/backend/check_all" % (service_id, version_number)) # TODO: Use a strong-typed class for output? return content - def list_cache_settings(self, service_id, version_number): """Get a list of all cache settings for a particular service and version.""" content = self._fetch("/service/%s/version/%d/cache_settings" % (service_id, version_number)) return map(lambda x: FastlyCacheSettings(self, x), content) - - def create_cache_settings(self, - service_id, - version_number, + def create_cache_settings(self, + service_id, + version_number, name, action, ttl=None, @@ -218,32 +209,27 @@ def create_cache_settings(self, content = self._fetch("/service/%s/version/%d/cache_settings" % (service_id, version_number), method="POST", body=body) return FastlyCacheSettings(self, content) - def get_cache_settings(self, service_id, version_number, name): """Get a specific cache settings object.""" content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name))) return FastlyCacheSettings(self, content) - def update_cache_settings(self, service_id, version_number, name_key, **kwargs): """Update a specific cache settings object.""" body = self._formdata(kwargs, FastlyCacheSettings.FIELDS) content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyCacheSettings(self, content) - def delete_cache_settings(self, service_id, version_number, name): """Delete a specific cache settings object.""" content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def list_conditions(self, service_id, version_number): """Gets all conditions for a particular service and version.""" content = self._fetch("/service/%s/version/%d/condition" % (service_id, version_number)) return map(lambda x: FastlyCondition(self, x), content) - def create_condition(self, service_id, version_number, @@ -263,20 +249,17 @@ def create_condition(self, content = self._fetch("/service/%s/version/%d/condition" % (service_id, version_number), method="POST", body=body) return FastlyCondition(self, content) - def get_condition(self, service_id, version_number, name): """Gets a specified condition.""" content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name))) return FastlyCondition(self, content) - def update_condition(self, service_id, version_number, name_key, **kwargs): """Updates the specified condition.""" body = self._formdata(kwargs, FastlyCondition.FIELDS) content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyCondition(self, content) - def delete_condition(self, service_id, version_number, name): """Deletes the specified condition.""" content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") @@ -1498,19 +1481,25 @@ class FastlySettings(FastlyObject, IServiceVersionObject): class FastlySyslog(FastlyObject, IServiceVersionObject, IDateStampedObject): """Fastly will stream log messages to the location, and in the format, specified in the Syslog object.""" FIELDS = [ - "name", - "service_id", - "version", "address", + "created_at", + "deleted_at", + "format", + "format_version", + "hostname", + "ipv4", + "message_type", + "name", + "placement", "port", - "use_tls", + "response_condition", + "service_id", "tls_ca_cert", + "tls_hostname", "token", - "format", - "response_condition", - "created", - "updated", - "deleted", + "updated_at", + "use_tls", + "version", ] @@ -1573,31 +1562,31 @@ def settings(self): @property def backends(self): - return dict([ (b.name, b) for b in self._conn.list_backends(self.service_id, int(self.number))]) + return dict([(b.name, b) for b in self._conn.list_backends(self.service_id, int(self.number))]) @property def healthchecks(self): - return dict([ (h.name, h) for h in self._conn.list_healthchecks(self.service_id, int(self.number))]) + return dict([(h.name, h) for h in self._conn.list_healthchecks(self.service_id, int(self.number))]) @property def domains(self): - return dict([ (d.name, d) for d in self._conn.list_domains(self.service_id, int(self.number))]) + return dict([(d.name, d) for d in self._conn.list_domains(self.service_id, int(self.number))]) @property def directors(self): - return dict([ (d.name, d) for d in self._conn.list_directors(self.service_id, int(self.number))]) + return dict([(d.name, d) for d in self._conn.list_directors(self.service_id, int(self.number))]) @property def origins(self): - return dict([ (o.name, o) for o in self._conn.list_origins(self.service_id, int(self.number))]) + return dict([(o.name, o) for o in self._conn.list_origins(self.service_id, int(self.number))]) @property def syslogs(self): - return dict([ (s.name, s) for s in self._conn.list_syslogs(self.service_id, int(self.number))]) + return dict([(s.name, s) for s in self._conn.list_syslogs(self.service_id, int(self.number))]) @property def vcls(self): - return dict([ (v.name, v) for v in self._conn.list_vcls(self.service_id, int(self.number))]) + return dict([(v.name, v) for v in self._conn.list_vcls(self.service_id, int(self.number))]) class FastlyWordpress(FastlyObject, IServiceVersionObject): From 6d8128a36e471246621d801d10a3a5cc0ade18a8 Mon Sep 17 00:00:00 2001 From: Geoffrey Schorkopf Date: Thu, 6 Apr 2017 21:09:42 -0400 Subject: [PATCH 12/29] Adds User-Agent header to _fetch * Creates a new file for __version__ --- fastly/__init__.py | 2 ++ fastly/_version.py | 1 + setup.py | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 fastly/_version.py diff --git a/fastly/__init__.py b/fastly/__init__.py index 367c750..9a1d6e4 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -27,6 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. from datetime import datetime +from _version import __version__ import httplib2 import json import re @@ -1080,6 +1081,7 @@ def _fetch(self, url, method="GET", body=None, headers={}): hdrs["Fastly-Key"] = self._api_key hdrs["Content-Accept"] = "application/json" + hdrs["User-Agent"] = ("fastly-python-v%s" % __version__) if not hdrs.has_key("Content-Type") and method in ["POST", "PUT"]: hdrs["Content-Type"] = "application/x-www-form-urlencoded" diff --git a/fastly/_version.py b/fastly/_version.py new file mode 100644 index 0000000..8a81504 --- /dev/null +++ b/fastly/_version.py @@ -0,0 +1 @@ +__version__ = '1.0.4' diff --git a/setup.py b/setup.py index 6de2d27..6afc82d 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,13 @@ import os from setuptools import setup +exec(open('fastly/_version.py').read()) def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() setup( name = "fastly-python", - version = "1.0.4", + version = __version__, author = "Chris Zacharias", author_email = "chris@imgix.com", description = ("A Python client libary for the Fastly API."), From 1a7f5e286ceed2172ad7e5f71a2650a1c3c61479 Mon Sep 17 00:00:00 2001 From: Cindy Sridharan Date: Thu, 6 Apr 2017 18:22:51 -0700 Subject: [PATCH 13/29] deprecate has_key calls --- fastly/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index cd31ea8..42d73c2 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1081,8 +1081,8 @@ def _fetch(self, url, method="GET", body=None, headers={}): hdrs["Fastly-Key"] = self._api_key hdrs["Content-Accept"] = "application/json" - hdrs["User-Agent"] = ("fastly-python-v%s" % __version__) - if not hdrs.has_key("Content-Type") and method in ["POST", "PUT"]: + hdrs["User-Agent"] = "fastly-python-v%s" % __version__ + if "Content-Type" not in hdrs and method in ["POST", "PUT"]: hdrs["Content-Type"] = "application/x-www-form-urlencoded" conn = httplib2.Http(disable_ssl_certificate_validation=False, timeout=10) @@ -1101,7 +1101,7 @@ def _check(self, resp, content): if status == 200: # Keep track of the session. Only really set during /login - if resp.has_key("set-cookie"): + if "set-cookie" in resp: set_cookie = resp["set-cookie"] match = FASTLY_SESSION_REGEX.search(set_cookie) if match is not None: From d5de8b6134acd531597a993da8b3f91f6a94c34f Mon Sep 17 00:00:00 2001 From: Cindy Sridharan Date: Fri, 7 Apr 2017 15:05:43 -0700 Subject: [PATCH 14/29] flake8 --- fastly/__init__.py | 162 +++++++---------------------- fastly/{_version.py => version.py} | 0 setup.py | 24 +++-- 3 files changed, 48 insertions(+), 138 deletions(-) rename fastly/{_version.py => version.py} (100%) diff --git a/fastly/__init__.py b/fastly/__init__.py index 42d73c2..3cb66f3 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -27,12 +27,13 @@ # POSSIBILITY OF SUCH DAMAGE. from datetime import datetime -from _version import __version__ import httplib2 import json import re import urllib +from version import __version__ + FASTLY_SCHEME = "https" FASTLY_HOST = "api.fastly.com" @@ -125,7 +126,8 @@ def list_backends(self, service_id, version_number): content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number)) return map(lambda x: FastlyBackend(self, x), content) - def create_backend(self, + def create_backend( + self, service_id, version_number, name, @@ -191,7 +193,8 @@ def list_cache_settings(self, service_id, version_number): content = self._fetch("/service/%s/version/%d/cache_settings" % (service_id, version_number)) return map(lambda x: FastlyCacheSettings(self, x), content) - def create_cache_settings(self, + def create_cache_settings( + self, service_id, version_number, name, @@ -231,13 +234,14 @@ def list_conditions(self, service_id, version_number): content = self._fetch("/service/%s/version/%d/condition" % (service_id, version_number)) return map(lambda x: FastlyCondition(self, x), content) - def create_condition(self, - service_id, + def create_condition( + self, + service_id, version_number, name, _type, statement, - priority="10", + priority="10", comment=None): """Creates a new condition.""" body = self._formdata({ @@ -266,7 +270,6 @@ def delete_condition(self, service_id, version_number, name): content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def content_edge_check(self, url): """Retrieve headers and MD5 hash of the content for a particular url from each Fastly edge server.""" prefixes = ["http://", "https://"] @@ -277,52 +280,47 @@ def content_edge_check(self, url): content = self._fetch("/content/edge_check/%s" % url) return content - def get_current_customer(self): """Get the logged in customer.""" content = self._fetch("/current_customer") return FastlyCustomer(self, content) - def get_customer(self, customer_id): """Get a specific customer.""" content = self._fetch("/customer/%s" % customer_id) return FastlyCustomer(self, content) - def get_customer_details(self, customer_id): """Get a specific customer, owner, and billing contact.""" content = self._fetch("/customer/details/%s" % customer_id) return content - def list_customer_users(self, customer_id): """List all users from a specified customer id.""" content = self._fetch("/customer/users/%s" % customer_id) return map(lambda x: FastlyUser(self, x), content) - def update_customer(self, customer_id, **kwargs): """Update a customer.""" body = self._formdata(kwargs, FastlyCustomer.FIELDS) content = self._fetch("/customer/%s" % customer_id, method="PUT", body=body) return FastlyCustomer(self, content) - def delete_customer(self, customer_id): """Delete a customer.""" content = self._fetch("/customer/%s" % customer_id, method="DELETE") return self._status(content) - def list_directors(self, service_id, version_number): """List the directors for a particular service and version.""" content = self._fetch("/service/%s/version/%d/director" % (service_id, version_number)) return map(lambda x: FastlyDirector(self, x), content) - - def create_director(self, service_id, version_number, - name, + def create_director( + self, + service_id, + version_number, + name, quorum=75, _type=FastlyDirectorType.RANDOM, retries=5, @@ -339,54 +337,47 @@ def create_director(self, service_id, version_number, content = self._fetch("/service/%s/version/%d/director" % (service_id, version_number), method="POST", body=body) return FastlyDirector(self, content) - def get_director(self, service_id, version_number, name): """Get the director for a particular service and version.""" content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name))) return FastlyDirector(self, content) - def update_director(self, service_id, version_number, name_key, **kwargs): """Update the director for a particular service and version.""" body = self._formdata(kwargs, FastlyDirector.FIELDS) content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyDirector(self, content) - def delete_director(self, service_id, version_number, name): """Delete the director for a particular service and version.""" content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def get_director_backend(self, service_id, version_number, director_name, backend_name): """Returns the relationship between a Backend and a Director. If the Backend has been associated with the Director, it returns a simple record indicating this. Otherwise, returns a 404.""" content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="GET") return FastlyDirectorBackend(self, content) - def create_director_backend(self, service_id, version_number, director_name, backend_name): """Establishes a relationship between a Backend and a Director. The Backend is then considered a member of the Director and can be used to balance traffic onto.""" content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="POST") return FastlyDirectorBackend(self, content) - def delete_director_backend(self, service_id, version_number, director_name, backend_name): """Deletes the relationship between a Backend and a Director. The Backend is no longer considered a member of the Director and thus will not have traffic balanced onto it from this Director.""" content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="DELETE") return self._status(content) - def list_domains(self, service_id, version_number): """List the domains for a particular service and version.""" content = self._fetch("/service/%s/version/%d/domain" % (service_id, version_number)) return map(lambda x: FastlyDomain(self, x), content) - - def create_domain(self, - service_id, - version_number, - name, + def create_domain( + self, + service_id, + version_number, + name, comment=None): """Create a domain for a particular service and version.""" body = self._formdata({ @@ -397,50 +388,42 @@ def create_domain(self, content = self._fetch("/service/%s/version/%d/domain" % (service_id, version_number), method="POST", body=body) return FastlyDomain(self, content) - def get_domain(self, service_id, version_number, name): """Get the domain for a particular service and version.""" content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name))) return FastlyDomain(self, content) - def update_domain(self, service_id, version_number, name_key, **kwargs): """Update the domain for a particular service and version.""" body = self._formdata(kwargs, FastlyDomain.FIELDS) content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyDomain(self, content) - def delete_domain(self, service_id, version_number, name): """Delete the domain for a particular service and version.""" content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(self, content) - def check_domain(self, service_id, version_number, name): """Checks the status of a domain's DNS record. Returns an array of 3 items. The first is the details for the domain. The second is the current CNAME of the domain. The third is a boolean indicating whether or not it has been properly setup to use Fastly.""" content = self._fetch("/service/%s/version/%d/domain/%s/check" % (service_id, version_number, urllib.quote(name))) return FastlyDomainCheck(self, content) - def check_domains(self, service_id, version_number): """Checks the status of all domain DNS records for a Service Version. Returns an array items in the same format as the single domain /check.""" content = self._fetch("/service/%s/version/%d/domain/check_all" % (service_id, version_number)) return map(lambda x: FastlyDomainCheck(self, x), content) - def get_event_log(self, object_id): """Get the specified event log.""" content = self._fetch("/event_log/%s" % object_id, method="GET") return FastlyEventLog(self, content) - def list_gzip(self, service_id, version_number): """List all gzip configurations for a particular service and version""" content = self._fetch("/service/%s/version/%d/gzip" % (service_id, version_number)) return map(lambda x: FastlyGzip(self, x), content) - def create_gzip(self, service_id, version_number, name, cache_condition=None, content_types=None, extensions=None): body = self._formdata({ "name": name, @@ -452,32 +435,27 @@ def create_gzip(self, service_id, version_number, name, cache_condition=None, co content = self._fetch("/service/%s/version/%d/gzip" % (service_id, version_number), method="POST", body=body) return FastlyGzip(self, content) - def get_gzip(self, service_id, version_number, name): """Retrieves a Header object by name.""" content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name))) return FastlyGzip(self, content) - def update_gzip(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Gzip object by name.""" body = self._formdata(kwargs, FastlyGzip.FIELDS) content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyGzip(self, content) - def delete_gzip(self, service_id, version_number, name): """Deletes a Gzip object by name.""" content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def list_headers(self, service_id, version_number): """Retrieves all Header objects for a particular Version of a Service.""" content = self._fetch("/service/%s/version/%d/header" % (service_id, version_number)) return map(lambda x: FastlyHeader(self, x), content) - def create_header(self, service_id, version_number, name, destination, source, _type=FastlyHeaderType.RESPONSE, action=FastlyHeaderAction.SET, regex=None, substitution=None, ignore_if_set=None, priority=10, response_condition=None, cache_condition=None, request_condition=None): body = self._formdata({ "name": name, @@ -497,34 +475,30 @@ def create_header(self, service_id, version_number, name, destination, source, _ content = self._fetch("/service/%s/version/%d/header" % (service_id, version_number), method="POST", body=body) return FastlyHeader(self, content) - def get_header(self, service_id, version_number, name): """Retrieves a Header object by name.""" content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name))) return FastlyHeader(self, content) - def update_header(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Header object by name.""" body = self._formdata(kwargs, FastlyHeader.FIELDS) content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHeader(self, content) - def delete_header(self, service_id, version_number, name): """Deletes a Header object by name.""" content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def list_healthchecks(self, service_id, version_number): """List all of the healthchecks for a particular service and version.""" content = self._fetch("/service/%s/version/%d/healthcheck" % (service_id, version_number)) return map(lambda x: FastlyHealthCheck(self, x), content) - - def create_healthcheck(self, - service_id, + def create_healthcheck( + self, + service_id, version_number, name, host, @@ -554,45 +528,39 @@ def create_healthcheck(self, content = self._fetch("/service/%s/version/%d/healthcheck" % (service_id, version_number), method="POST", body=body) return FastlyHealthCheck(self, content) - def get_healthcheck(self, service_id, version_number, name): """Get the healthcheck for a particular service and version.""" content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name))) return FastlyHealthCheck(self, content) - def update_healthcheck(self, service_id, version_number, name_key, **kwargs): """Update the healthcheck for a particular service and version.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHealthCheck(self, content) - def delete_healthcheck(self, service_id, version_number, name): """Delete the healthcheck for a particular service and version.""" content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - - def purge_url(self, host, path): + def purge_url(self, host, path): """Purge an individual URL.""" - content = self._fetch(path, method="PURGE", headers={ "Host": host }) + content = self._fetch(path, method="PURGE", headers={"Host": host}) return FastlyPurge(self, content) - def check_purge_status(self, purge_id): """Get the status and times of a recently completed purge.""" content = self._fetch("/purge?id=%s" % purge_id) return map(lambda x: FastlyPurgeStatus(self, x), content) - def list_request_settings(self, service_id, version_number): """Returns a list of all Request Settings objects for the given service and version.""" content = self._fetch("/service/%s/version/%d/request_settings" % (service_id, version_number)) return map(lambda x: FastlyRequestSetting(self, x), content) - - def create_request_setting(self, + def create_request_setting( + self, service_id, version_number, name, @@ -625,32 +593,27 @@ def create_request_setting(self, content = self._fetch("/service/%s/version/%d/request_settings" % (service_id, version_number), method="POST", body=body) return FastlyRequestSetting(self, content) - def get_request_setting(self, service_id, version_number, name): """Gets the specified Request Settings object.""" content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name))) return FastlyRequestSetting(self, content) - def update_request_setting(self, service_id, version_number, name_key, **kwargs): """Updates the specified Request Settings object.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyRequestSetting(self, content) - def delete_request_setting(self, service_id, version_number, name): """Removes the specfied Request Settings object.""" content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def list_response_objects(self, service_id, version_number): """Returns all Response Objects for the specified service and version.""" content = self._fetch("/service/%s/version/%d/response_object" % (service_id, version_number)) return map(lambda x: FastlyResponseObject(self, x), content) - def create_response_object(self, service_id, version_number, name, status="200", response="OK", content="", request_condition=None, cache_condition=None): """Creates a new Response Object.""" body = self._formdata({ @@ -664,26 +627,22 @@ def create_response_object(self, service_id, version_number, name, status="200", content = self._fetch("/service/%s/version/%d/response_object" % (service_id, version_number), method="POST", body=body) return FastlyResponseObject(self, content) - def get_response_object(self, service_id, version_number, name): """Gets the specified Response Object.""" content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name))) return FastlyResponseObject(self, content) - def update_response_object(self, service_id, version_number, name_key, **kwargs): """Updates the specified Response Object.""" body = self._formdata(kwargs, FastlyResponseObject.FIELDS) content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyResponseObject(self, content) - def delete_response_object(self, service_id, version_number, name): """Deletes the specified Response Object.""" content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def create_service(self, customer_id, name, publish_key=None, comment=None): """Create a service.""" body = self._formdata({ @@ -695,88 +654,75 @@ def create_service(self, customer_id, name, publish_key=None, comment=None): content = self._fetch("/service", method="POST", body=body) return FastlyService(self, content) - def list_services(self): """List Services.""" content = self._fetch("/service") return map(lambda x: FastlyService(self, x), content) - def get_service(self, service_id): """Get a specific service by id.""" content = self._fetch("/service/%s" % service_id) return FastlyService(self, content) - def get_service_details(self, service_id): """List detailed information on a specified service.""" content = self._fetch("/service/%s/details" % service_id) return FastlyService(self, content) - def get_service_by_name(self, service_name): """Get a specific service by name.""" content = self._fetch("/service/search?name=%s" % urllib.quote(service_name)) return FastlyService(self, content) - def update_service(self, service_id, **kwargs): """Update a service.""" body = self._formdata(kwargs, FastlyService.FIELDS) content = self._fetch("/service/%s" % service_id, method="PUT", body=body) return FastlyService(self, content) - def delete_service(self, service_id): """Delete a service.""" content = self._fetch("/service/%s" % service_id, method="DELETE") return self._status(content) - def list_domains_by_service(self, service_id): """List the domains within a service.""" content = self._fetch("/service/%s/domain" % service_id, method="GET") return map(lambda x: FastlyDomain(self, x), content) - def purge_service(self, service_id): """Purge everything from a service.""" content = self._fetch("/service/%s/purge_all" % service_id, method="POST") return self._status(content) - def purge_service_by_key(self, service_id, key): """Purge a particular service by a key.""" content = self._fetch("/service/%s/purge/%s" % (service_id, key), method="POST") return self._status(content) - def get_settings(self, service_id, version_number): """Get the settings for a particular service and version.""" content = self._fetch("/service/%s/version/%d/settings" % (service_id, version_number)) return FastlySettings(self, content) - def update_settings(self, service_id, version_number, settings={}): """Update the settings for a particular service and version.""" body = urllib.urlencode(settings) content = self._fetch("/service/%s/version/%d/settings" % (service_id, version_number), method="PUT", body=body) return FastlySettings(self, content) - def get_stats(self, service_id, stat_type=FastlyStatsType.ALL): """Get the stats from a service.""" content = self._fetch("/service/%s/stats/%s" % (service_id, stat_type)) return content - def list_syslogs(self, service_id, version_number): """List all of the Syslogs for a particular service and version.""" content = self._fetch("/service/%s/version/%d/syslog" % (service_id, version_number)) return map(lambda x: FastlySyslog(self, x), content) - - def create_syslog(self, + def create_syslog( + self, service_id, version_number, name, @@ -801,26 +747,22 @@ def create_syslog(self, content = self._fetch("/service/%s/version/%d/syslog" % (service_id, version_number), method="POST", body=body) return FastlySyslog(self, content) - def get_syslog(self, service_id, version_number, name): """Get the Syslog for a particular service and version.""" content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name))) return FastlySyslog(self, content) - def update_syslog(self, service_id, version_number, name_key, **kwargs): """Update the Syslog for a particular service and version.""" body = self._formdata(kwargs, FastlySyslog.FIELDS) content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlySyslog(self, content) - def delete_syslog(self, service_id, version_number, name): """Delete the Syslog for a particular service and version.""" content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def change_password(self, old_password, new_password): """Update the user's password to a new one.""" body = self._formdata({ @@ -830,19 +772,16 @@ def change_password(self, old_password, new_password): content = self._fetch("/current_user/password", method="POST", body=body) return FastlyUser(self, content) - def get_current_user(self): """Get the logged in user.""" content = self._fetch("/current_user") return FastlyUser(self, content) - def get_user(self, user_id): """Get a specific user.""" content = self._fetch("/user/%s" % user_id) return FastlyUser(self, content) - def create_user(self, customer_id, name, login, password, role=FastlyRoles.USER, require_new_password=True): """Create a user.""" body = self._formdata({ @@ -856,32 +795,27 @@ def create_user(self, customer_id, name, login, password, role=FastlyRoles.USER, content = self._fetch("/user", method="POST", body=body) return FastlyUser(self, content) - def update_user(self, user_id, **kwargs): """Update a user.""" body = self._formdata(kwargs, FastlyUser.FIELDS) content = self._fetch("/user/%s" % user_id, method="PUT", body=body) return FastlyUser(self, content) - def delete_user(self, user_id): """Delete a user.""" content = self._fetch("/user/%s" % user_id, method="DELETE") return self._status(content) - def request_password_reset(self, user_id): """Requests a password reset for the specified user.""" content = self._fetch("/user/%s/password/request_reset" % (user_id), method="POST") return FastlyUser(self, content) - def list_vcls(self, service_id, version_number): """List the uploaded VCLs for a particular service and version.""" content = self._fetch("/service/%s/version/%d/vcl" % (service_id, version_number)) return map(lambda x: FastlyVCL(self, x), content) - def upload_vcl(self, service_id, version_number, name, content, main=None, comment=None): """Upload a VCL for a particular service and version.""" body = self._formdata({ @@ -893,56 +827,47 @@ def upload_vcl(self, service_id, version_number, name, content, main=None, comme content = self._fetch("/service/%s/version/%d/vcl" % (service_id, version_number), method="POST", body=body) return FastlyVCL(self, content) - def download_vcl(self, service_id, version_number, name): """Download the specified VCL.""" # TODO: Not sure what to do here, the documentation shows invalid response. Will have to test. raise Exception("Not implemented") - def get_vcl(self, service_id, version_number, name, include_content=True): """Get the uploaded VCL for a particular service and version.""" content = self._fetch("/service/%s/version/%d/vcl/%s?include_content=%d" % (service_id, version_number, urllib.quote(name), int(include_content))) return FastlyVCL(self, content) - def get_vcl_html(self, service_id, version_number, name): """Get the uploaded VCL for a particular service and version with HTML syntax highlighting.""" content = self._fetch("/service/%s/version/%d/vcl/%s/content" % (service_id, version_number, urllib.quote(name))) return content.get("content", None) - def get_generated_vcl(self, service_id, version_number): """Display the generated VCL for a particular service and version.""" content = self._fetch("/service/%s/version/%d/generated_vcl" % (service_id, version_number)) return FastlyVCL(self, content) - def get_generated_vcl_html(self, service_id, version_number): """Display the content of generated VCL with HTML syntax highlighting.""" content = self._fetch("/service/%s/version/%d/generated_vcl/content" % (service_id, version_number)) return content.get("content", None) - def set_main_vcl(self, service_id, version_number, name): """Set the specified VCL as the main.""" content = self._fetch("/service/%s/version/%d/vcl/%s/main" % (service_id, version_number, urllib.quote(name)), method="PUT") return FastlyVCL(self, content) - def update_vcl(self, service_id, version_number, name_key, **kwargs): """Update the uploaded VCL for a particular service and version.""" body = self._formdata(kwargs, FastlyVCL.FIELDS) content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyVCL(self, content) - def delete_vcl(self, service_id, version_number, name): """Delete the uploaded VCL for a particular service and version.""" content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - def create_version(self, service_id, inherit_service_id=None, comment=None): """Create a version for a particular service.""" body = self._formdata({ @@ -953,12 +878,10 @@ def create_version(self, service_id, inherit_service_id=None, comment=None): content = self._fetch("/service/%s/version" % service_id, method="POST", body=body) return FastlyVersion(self, content) - def list_versions(self, service_id): - content = self._fetch("/service/%s/version"% service_id) + content = self._fetch("/service/%s/version" % service_id) return map(lambda x: FastlyVersion(self, x), content) - def get_version(self, service_id, version_number): """Get the version for a particular service.""" content = self._fetch("/service/%s/version/%d" % (service_id, version_number)) @@ -970,44 +893,38 @@ def update_version(self, service_id, version_number, **kwargs): content = self._fetch("/service/%s/version/%d/" % (service_id, version_number), method="PUT", body=body) return FastlyVersion(self, content) - def clone_version(self, service_id, version_number): """Clone the current configuration into a new version.""" content = self._fetch("/service/%s/version/%d/clone" % (service_id, version_number), method="PUT") return FastlyVersion(self, content) - def activate_version(self, service_id, version_number): """Activate the current version.""" content = self._fetch("/service/%s/version/%d/activate" % (service_id, version_number), method="PUT") return FastlyVersion(self, content) - def deactivate_version(self, service_id, version_number): """Deactivate the current version.""" content = self._fetch("/service/%s/version/%d/deactivate" % (service_id, version_number), method="PUT") return FastlyVersion(self, content) - def validate_version(self, service_id, version_number): """Validate the version for a particular service and version.""" content = self._fetch("/service/%s/version/%d/validate" % (service_id, version_number)) return self._status(content) - def lock_version(self, service_id, version_number): """Locks the specified version.""" content = self._fetch("/service/%s/version/%d/lock" % (service_id, version_number)) return self._status(content) - def list_wordpressess(self, service_id, version_number): """Get all of the wordpresses for a specified service and version.""" content = self._fetch("/service/%s/version/%d/wordpress" % (service_id, version_number)) return map(lambda x: FastlyWordpress(self, x), content) - - def create_wordpress(self, + def create_wordpress( + self, service_id, version_number, name, @@ -1022,42 +939,36 @@ def create_wordpress(self, content = self._fetch("/service/%s/version/%d/wordpress" % (service_id, version_number), method="POST", body=body) return FastlyWordpress(self, content) - def get_wordpress(self, service_id, version_number, name): """Get information on a specific wordpress.""" content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name))) return FastlyWordpress(self, content) - def update_wordpress(self, service_id, version_number, name_key, **kwargs): """Update a specified wordpress.""" body = self._formdata(kwargs, FastlyWordpress.FIELDS) content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyWordpress(self, content) - def delete_wordpress(self, service_id, version_number, name): """Delete a specified wordpress.""" content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") return self._status(content) - # TODO: Is this broken? def delete_version(self, service_id, version_number): content = self._fetch("/service/%s/version/%d" % (service_id, version_number), method="DELETE") return self._status(content) - def _status(self, status): if not isinstance(status, FastlyStatus): status = FastlyStatus(self, status) if status.status != "ok": - raise FastlyError("FastlyError: %s" % status.msg) + raise FastlyError("FastlyError: %s" % status.msg) return True - def _formdata(self, fields, valid=[]): data = {} for key in fields.keys(): @@ -1067,7 +978,6 @@ def _formdata(self, fields, valid=[]): data[key] = str(int(data[key])) return urllib.urlencode(data) - def _fetch(self, url, method="GET", body=None, headers={}): hdrs = {} hdrs.update(headers) @@ -1089,14 +999,14 @@ def _fetch(self, url, method="GET", body=None, headers={}): endpoint = "%s://%s%s" % (FASTLY_SCHEME, FASTLY_HOST, url) return self._check(*conn.request(endpoint, method, body=body, headers=hdrs)) - def _check(self, resp, content): status = resp.status payload = None if content: try: payload = json.loads(content) - except ValueError: # Could not decode, usually HTML + # Could not decode, usually HTML + except ValueError: payload = content if status == 200: @@ -1197,7 +1107,6 @@ class FastlySession(FastlyObject): def customer(self): return FastlyCustomer(self._conn, self._data["customer"]) - @property def user(self): return FastlyUser(self._conn, self._data["user"]) @@ -1390,7 +1299,6 @@ def source(self): return self.src - class FastlyHealthCheck(FastlyObject, IServiceVersionObject): """Healthchecks are used to customize the way Fastly checks on your Backends. Only Backends that have successful Healthchecks will be sent traffic, thus assuring that the failure of one server does not affect visitors.""" FIELDS = [ diff --git a/fastly/_version.py b/fastly/version.py similarity index 100% rename from fastly/_version.py rename to fastly/version.py diff --git a/setup.py b/setup.py index 6afc82d..6622ef0 100644 --- a/setup.py +++ b/setup.py @@ -1,19 +1,21 @@ -import os from setuptools import setup -exec(open('fastly/_version.py').read()) +import os + +from fastly.version import __version__ + def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() + return open(os.path.join(os.path.dirname(__file__), fname)).read() setup( - name = "fastly-python", - version = __version__, - author = "Chris Zacharias", - author_email = "chris@imgix.com", - description = ("A Python client libary for the Fastly API."), - license = "BSD", - keywords = "fastly", - url = "https://github.com/zebrafishlabs/fastly-python", + name="fastly-python", + version=__version__, + author="Chris Zacharias", + author_email="chris@imgix.com", + description=("A Python client libary for the Fastly API."), + license="BSD", + keywords="fastly", + url="https://github.com/zebrafishlabs/fastly-python", packages=['fastly', 'tests'], install_requires=[ 'httplib2', From 17a346ad51bf5c3b2032ffd3d854bb4b1d0d2f78 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 2 Jun 2016 13:31:56 +0200 Subject: [PATCH 15/29] Only pass content to status for delete_domain self is an implied parameter. Avoids backtrace. --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 3cb66f3..c6d2721 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -402,7 +402,7 @@ def update_domain(self, service_id, version_number, name_key, **kwargs): def delete_domain(self, service_id, version_number, name): """Delete the domain for a particular service and version.""" content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") - return self._status(self, content) + return self._status(content) def check_domain(self, service_id, version_number, name): """Checks the status of a domain's DNS record. Returns an array of 3 items. The first is the details for the domain. The second is the current CNAME of the domain. The third is a boolean indicating whether or not it has been properly setup to use Fastly.""" From e76de1feaf91ba3d2251ce6ce50d5c1cd1972ca9 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 2 Jun 2016 13:32:38 +0200 Subject: [PATCH 16/29] Avoid infinite loop in backend.healtcheck() Call __getattr__ directly, else we end up calling ourselves which ends in tears. --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index c6d2721..80867d5 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1136,7 +1136,7 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): @property def healthcheck(self): - return self._conn.get_healthcheck(self.service_id, self.version, self.healthcheck) + return self._conn.get_healthcheck(self.service_id, self.version, self.__getattr__("healthcheck")) class FastlyCacheSettings(FastlyObject, IServiceVersionObject): From ffe78218854681a905d7b7a8c799bc5eeb88c791 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 2 Jun 2016 13:33:29 +0200 Subject: [PATCH 17/29] Comment is valid for conditions, make that available --- fastly/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index 80867d5..8eab69b 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1163,6 +1163,7 @@ class FastlyCondition(FastlyObject, IServiceVersionObject): "type", "statement", "priority", + "comment", ] From 452e0468139664fd6ad4725214d18e2bf0abfbba Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Tue, 25 Oct 2016 09:53:12 +0200 Subject: [PATCH 18/29] Allow retrieving the backends from a director --- fastly/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index 8eab69b..4a1bb6f 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1206,6 +1206,7 @@ class FastlyDirector(FastlyObject, IServiceVersionObject, IDateStampedObject): "deleted", "capacity", "comment", + "backends", ] From 2f21e448cbc80814a076302a47d9702f104c2702 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Wed, 26 Oct 2016 11:48:56 +0200 Subject: [PATCH 19/29] Use the same keyword name in create_header and update_header --- fastly/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 4a1bb6f..df2a529 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -456,11 +456,11 @@ def list_headers(self, service_id, version_number): content = self._fetch("/service/%s/version/%d/header" % (service_id, version_number)) return map(lambda x: FastlyHeader(self, x), content) - def create_header(self, service_id, version_number, name, destination, source, _type=FastlyHeaderType.RESPONSE, action=FastlyHeaderAction.SET, regex=None, substitution=None, ignore_if_set=None, priority=10, response_condition=None, cache_condition=None, request_condition=None): + def create_header(self, service_id, version_number, name, dst, src, _type=FastlyHeaderType.RESPONSE, action=FastlyHeaderAction.SET, regex=None, substitution=None, ignore_if_set=None, priority=10, response_condition=None, cache_condition=None, request_condition=None): body = self._formdata({ "name": name, - "dst": destination, - "src": source, + "dst": dst, + "src": src, "type": _type, "action": action, "regex": regex, From 6c0864bb33e95a9b920925ee2e64883c559f9341 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Fri, 18 Nov 2016 09:28:10 +0100 Subject: [PATCH 20/29] Make it possible to use the same kwargs for update_{condition,director,header} as their create_ counterparts --- fastly/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index df2a529..fbac8c2 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -261,6 +261,8 @@ def get_condition(self, service_id, version_number, name): def update_condition(self, service_id, version_number, name_key, **kwargs): """Updates the specified condition.""" + if '_type' in kwargs: + kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyCondition.FIELDS) content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyCondition(self, content) @@ -344,6 +346,8 @@ def get_director(self, service_id, version_number, name): def update_director(self, service_id, version_number, name_key, **kwargs): """Update the director for a particular service and version.""" + if '_type' in kwargs: + kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyDirector.FIELDS) content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyDirector(self, content) @@ -482,6 +486,8 @@ def get_header(self, service_id, version_number, name): def update_header(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Header object by name.""" + if '_type' in kwargs: + kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyHeader.FIELDS) content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) return FastlyHeader(self, content) From ce257ab3c04879c6e2a09c4cafcd722adac9cabd Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Wed, 15 Mar 2017 09:39:11 +0100 Subject: [PATCH 21/29] Add support for setting SNI host name --- fastly/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index fbac8c2..96346d5 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -144,7 +144,8 @@ def create_backend( shield=None, request_condition=None, healthcheck=None, - comment=None): + comment=None, + ssl_sni_hostname=None,): """Create a backend for a particular service and version.""" body = self._formdata({ "name": name, @@ -162,6 +163,7 @@ def create_backend( "request_condition": request_condition, "healthcheck": healthcheck, "comment": comment, + "ssl_sni_hostname": ssl_sni_hostname, }, FastlyBackend.FIELDS) content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number), method="POST", body=body) return FastlyBackend(self, content) @@ -1138,6 +1140,7 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): "request_condition", "healthcheck", "comment", + "ssl_sni_hostname", ] @property From a903710f8dcd3457752d71eaeb0e37b01c0a27b0 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Tue, 2 May 2017 10:09:22 +0200 Subject: [PATCH 22/29] Return None if healthcheck is None, rather than trying to retrieve one called None from the API --- fastly/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index 96346d5..86ca4a6 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1145,6 +1145,8 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): @property def healthcheck(self): + if self.__getattr__('healthcheck') is None: + return None return self._conn.get_healthcheck(self.service_id, self.version, self.__getattr__("healthcheck")) From fc4c2c27919efa6b85dbf1f4c352f855cb999c75 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Sun, 6 Aug 2017 22:08:49 +0200 Subject: [PATCH 23/29] Pass safe='' to urllib.quote Names can (often) contain slashes, so we need to make sure those are encoded as well. --- fastly/__init__.py | 92 +++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 86ca4a6..bdb844e 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -170,18 +170,18 @@ def create_backend( def get_backend(self, service_id, version_number, name): """Get the backend for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyBackend(self, content) def update_backend(self, service_id, version_number, name_key, **kwargs): """Update the backend for a particular service and version.""" body = self._formdata(kwargs, FastlyBackend.FIELDS) - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyBackend(self, content) def delete_backend(self, service_id, version_number, name): """Delete the backend for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/backend/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def check_backends(self, service_id, version_number): @@ -217,18 +217,18 @@ def create_cache_settings( def get_cache_settings(self, service_id, version_number, name): """Get a specific cache settings object.""" - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyCacheSettings(self, content) def update_cache_settings(self, service_id, version_number, name_key, **kwargs): """Update a specific cache settings object.""" body = self._formdata(kwargs, FastlyCacheSettings.FIELDS) - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyCacheSettings(self, content) def delete_cache_settings(self, service_id, version_number, name): """Delete a specific cache settings object.""" - content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/cache_settings/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def list_conditions(self, service_id, version_number): @@ -258,7 +258,7 @@ def create_condition( def get_condition(self, service_id, version_number, name): """Gets a specified condition.""" - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyCondition(self, content) def update_condition(self, service_id, version_number, name_key, **kwargs): @@ -266,12 +266,12 @@ def update_condition(self, service_id, version_number, name_key, **kwargs): if '_type' in kwargs: kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyCondition.FIELDS) - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyCondition(self, content) def delete_condition(self, service_id, version_number, name): """Deletes the specified condition.""" - content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/condition/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def content_edge_check(self, url): @@ -343,7 +343,7 @@ def create_director( def get_director(self, service_id, version_number, name): """Get the director for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyDirector(self, content) def update_director(self, service_id, version_number, name_key, **kwargs): @@ -351,27 +351,27 @@ def update_director(self, service_id, version_number, name_key, **kwargs): if '_type' in kwargs: kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyDirector.FIELDS) - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyDirector(self, content) def delete_director(self, service_id, version_number, name): """Delete the director for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/director/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def get_director_backend(self, service_id, version_number, director_name, backend_name): """Returns the relationship between a Backend and a Director. If the Backend has been associated with the Director, it returns a simple record indicating this. Otherwise, returns a 404.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="GET") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name, safe='')), method="GET") return FastlyDirectorBackend(self, content) def create_director_backend(self, service_id, version_number, director_name, backend_name): """Establishes a relationship between a Backend and a Director. The Backend is then considered a member of the Director and can be used to balance traffic onto.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="POST") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name, safe='')), method="POST") return FastlyDirectorBackend(self, content) def delete_director_backend(self, service_id, version_number, director_name, backend_name): """Deletes the relationship between a Backend and a Director. The Backend is no longer considered a member of the Director and thus will not have traffic balanced onto it from this Director.""" - content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/director/%s/backend/%s" % (service_id, version_number, director_name, urllib.quote(backend_name, safe='')), method="DELETE") return self._status(content) def list_domains(self, service_id, version_number): @@ -396,23 +396,23 @@ def create_domain( def get_domain(self, service_id, version_number, name): """Get the domain for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyDomain(self, content) def update_domain(self, service_id, version_number, name_key, **kwargs): """Update the domain for a particular service and version.""" body = self._formdata(kwargs, FastlyDomain.FIELDS) - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyDomain(self, content) def delete_domain(self, service_id, version_number, name): """Delete the domain for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/domain/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def check_domain(self, service_id, version_number, name): """Checks the status of a domain's DNS record. Returns an array of 3 items. The first is the details for the domain. The second is the current CNAME of the domain. The third is a boolean indicating whether or not it has been properly setup to use Fastly.""" - content = self._fetch("/service/%s/version/%d/domain/%s/check" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/domain/%s/check" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyDomainCheck(self, content) def check_domains(self, service_id, version_number): @@ -443,18 +443,18 @@ def create_gzip(self, service_id, version_number, name, cache_condition=None, co def get_gzip(self, service_id, version_number, name): """Retrieves a Header object by name.""" - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyGzip(self, content) def update_gzip(self, service_id, version_number, name_key, **kwargs): """Modifies an existing Gzip object by name.""" body = self._formdata(kwargs, FastlyGzip.FIELDS) - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyGzip(self, content) def delete_gzip(self, service_id, version_number, name): """Deletes a Gzip object by name.""" - content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/gzip/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def list_headers(self, service_id, version_number): @@ -483,7 +483,7 @@ def create_header(self, service_id, version_number, name, dst, src, _type=Fastly def get_header(self, service_id, version_number, name): """Retrieves a Header object by name.""" - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyHeader(self, content) def update_header(self, service_id, version_number, name_key, **kwargs): @@ -491,12 +491,12 @@ def update_header(self, service_id, version_number, name_key, **kwargs): if '_type' in kwargs: kwargs['type'] = kwargs['_type'] body = self._formdata(kwargs, FastlyHeader.FIELDS) - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyHeader(self, content) def delete_header(self, service_id, version_number, name): """Deletes a Header object by name.""" - content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/header/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def list_healthchecks(self, service_id, version_number): @@ -538,18 +538,18 @@ def create_healthcheck( def get_healthcheck(self, service_id, version_number, name): """Get the healthcheck for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyHealthCheck(self, content) def update_healthcheck(self, service_id, version_number, name_key, **kwargs): """Update the healthcheck for a particular service and version.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyHealthCheck(self, content) def delete_healthcheck(self, service_id, version_number, name): """Delete the healthcheck for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/healthcheck/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def purge_url(self, host, path): @@ -603,18 +603,18 @@ def create_request_setting( def get_request_setting(self, service_id, version_number, name): """Gets the specified Request Settings object.""" - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyRequestSetting(self, content) def update_request_setting(self, service_id, version_number, name_key, **kwargs): """Updates the specified Request Settings object.""" body = self._formdata(kwargs, FastlyHealthCheck.FIELDS) - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyRequestSetting(self, content) def delete_request_setting(self, service_id, version_number, name): """Removes the specfied Request Settings object.""" - content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/request_settings/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def list_response_objects(self, service_id, version_number): @@ -637,18 +637,18 @@ def create_response_object(self, service_id, version_number, name, status="200", def get_response_object(self, service_id, version_number, name): """Gets the specified Response Object.""" - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyResponseObject(self, content) def update_response_object(self, service_id, version_number, name_key, **kwargs): """Updates the specified Response Object.""" body = self._formdata(kwargs, FastlyResponseObject.FIELDS) - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyResponseObject(self, content) def delete_response_object(self, service_id, version_number, name): """Deletes the specified Response Object.""" - content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/response_object/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def create_service(self, customer_id, name, publish_key=None, comment=None): @@ -679,7 +679,7 @@ def get_service_details(self, service_id): def get_service_by_name(self, service_name): """Get a specific service by name.""" - content = self._fetch("/service/search?name=%s" % urllib.quote(service_name)) + content = self._fetch("/service/search?name=%s" % urllib.quote(service_name, safe='')) return FastlyService(self, content) def update_service(self, service_id, **kwargs): @@ -757,18 +757,18 @@ def create_syslog( def get_syslog(self, service_id, version_number, name): """Get the Syslog for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlySyslog(self, content) def update_syslog(self, service_id, version_number, name_key, **kwargs): """Update the Syslog for a particular service and version.""" body = self._formdata(kwargs, FastlySyslog.FIELDS) - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlySyslog(self, content) def delete_syslog(self, service_id, version_number, name): """Delete the Syslog for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/syslog/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def change_password(self, old_password, new_password): @@ -842,12 +842,12 @@ def download_vcl(self, service_id, version_number, name): def get_vcl(self, service_id, version_number, name, include_content=True): """Get the uploaded VCL for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/vcl/%s?include_content=%d" % (service_id, version_number, urllib.quote(name), int(include_content))) + content = self._fetch("/service/%s/version/%d/vcl/%s?include_content=%d" % (service_id, version_number, urllib.quote(name, safe=''), int(include_content))) return FastlyVCL(self, content) def get_vcl_html(self, service_id, version_number, name): """Get the uploaded VCL for a particular service and version with HTML syntax highlighting.""" - content = self._fetch("/service/%s/version/%d/vcl/%s/content" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/vcl/%s/content" % (service_id, version_number, urllib.quote(name, safe=''))) return content.get("content", None) def get_generated_vcl(self, service_id, version_number): @@ -862,18 +862,18 @@ def get_generated_vcl_html(self, service_id, version_number): def set_main_vcl(self, service_id, version_number, name): """Set the specified VCL as the main.""" - content = self._fetch("/service/%s/version/%d/vcl/%s/main" % (service_id, version_number, urllib.quote(name)), method="PUT") + content = self._fetch("/service/%s/version/%d/vcl/%s/main" % (service_id, version_number, urllib.quote(name, safe='')), method="PUT") return FastlyVCL(self, content) def update_vcl(self, service_id, version_number, name_key, **kwargs): """Update the uploaded VCL for a particular service and version.""" body = self._formdata(kwargs, FastlyVCL.FIELDS) - content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyVCL(self, content) def delete_vcl(self, service_id, version_number, name): """Delete the uploaded VCL for a particular service and version.""" - content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/vcl/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) def create_version(self, service_id, inherit_service_id=None, comment=None): @@ -949,18 +949,18 @@ def create_wordpress( def get_wordpress(self, service_id, version_number, name): """Get information on a specific wordpress.""" - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name))) + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name, safe=''))) return FastlyWordpress(self, content) def update_wordpress(self, service_id, version_number, name_key, **kwargs): """Update a specified wordpress.""" body = self._formdata(kwargs, FastlyWordpress.FIELDS) - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name_key)), method="PUT", body=body) + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name_key, safe='')), method="PUT", body=body) return FastlyWordpress(self, content) def delete_wordpress(self, service_id, version_number, name): """Delete a specified wordpress.""" - content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name)), method="DELETE") + content = self._fetch("/service/%s/version/%d/wordpress/%s" % (service_id, version_number, urllib.quote(name, safe='')), method="DELETE") return self._status(content) # TODO: Is this broken? From 94c4f515e6f36ba629d7840333c79de6ecde1584 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Tue, 2 May 2017 10:45:16 +0200 Subject: [PATCH 24/29] Add support for setting ssl_cert_hostname --- fastly/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fastly/__init__.py b/fastly/__init__.py index bdb844e..dfaeaee 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -145,6 +145,7 @@ def create_backend( request_condition=None, healthcheck=None, comment=None, + ssl_cert_hostname=None, ssl_sni_hostname=None,): """Create a backend for a particular service and version.""" body = self._formdata({ @@ -163,6 +164,7 @@ def create_backend( "request_condition": request_condition, "healthcheck": healthcheck, "comment": comment, + "ssl_cert_hostname": ssl_cert_hostname, "ssl_sni_hostname": ssl_sni_hostname, }, FastlyBackend.FIELDS) content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number), method="POST", body=body) @@ -1140,6 +1142,7 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): "request_condition", "healthcheck", "comment", + "ssl_cert_hostname", "ssl_sni_hostname", ] From 33f2fb40c2375a62a99f4a61ccd433df2f056c3f Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Sun, 6 Aug 2017 23:50:11 +0200 Subject: [PATCH 25/29] Treat healthcheck == None and healthcheck == '' the same Fastly's API doesn't properly differentiate between healthcheck-as-a-string and as healthcheck-as-an-object, which means you will get healthcheck == '' back when it says "(none)" in the UI. --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index dfaeaee..03e03b0 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1148,7 +1148,7 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): @property def healthcheck(self): - if self.__getattr__('healthcheck') is None: + if self.__getattr__('healthcheck') is None or self.__getattr__('healthcheck') == '': return None return self._conn.get_healthcheck(self.service_id, self.version, self.__getattr__("healthcheck")) From 5b2a60f67917d8f4ef6891bcfc12c6ad5e9068a9 Mon Sep 17 00:00:00 2001 From: Tollef Fog Heen Date: Thu, 24 Aug 2017 22:02:46 +0200 Subject: [PATCH 26/29] Simplify getattr call somewhat, "" is false in python --- fastly/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 03e03b0..b7fa4e7 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -1148,7 +1148,7 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): @property def healthcheck(self): - if self.__getattr__('healthcheck') is None or self.__getattr__('healthcheck') == '': + if not self.__getattr__('healthcheck'): return None return self._conn.get_healthcheck(self.service_id, self.version, self.__getattr__("healthcheck")) From 36a8ac6707b763fa6d7c60567ca8d7e17ccff974 Mon Sep 17 00:00:00 2001 From: Chris Zacharias Date: Fri, 8 Sep 2017 21:07:54 -0700 Subject: [PATCH 27/29] Hard-coding the version into the setup.py file so that it stops breaking builds that depend on this Python package. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6622ef0..b873abd 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ def read(fname): setup( name="fastly-python", - version=__version__, + version="1.0.4", author="Chris Zacharias", author_email="chris@imgix.com", description=("A Python client libary for the Fastly API."), From 004f043dca2edc18bcf99e1941717de95918be80 Mon Sep 17 00:00:00 2001 From: Chris Zacharias Date: Fri, 8 Sep 2017 21:08:54 -0700 Subject: [PATCH 28/29] Hard-coding the version into the setup.py file so that it stops breaking builds that depend on this Python package. --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index b873abd..3c632db 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,6 @@ from setuptools import setup import os -from fastly.version import __version__ - - def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() From 5955a03796b5f56b3af186e1f88f6648235fdbe1 Mon Sep 17 00:00:00 2001 From: Peter Palfrader Date: Fri, 23 Feb 2018 19:48:37 +0100 Subject: [PATCH 29/29] Allow setting min/max tls version for a backend --- fastly/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index b7fa4e7..08e2175 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -146,7 +146,9 @@ def create_backend( healthcheck=None, comment=None, ssl_cert_hostname=None, - ssl_sni_hostname=None,): + ssl_sni_hostname=None, + min_tls_version=None, + max_tls_version=None,): """Create a backend for a particular service and version.""" body = self._formdata({ "name": name, @@ -166,6 +168,8 @@ def create_backend( "comment": comment, "ssl_cert_hostname": ssl_cert_hostname, "ssl_sni_hostname": ssl_sni_hostname, + "min_tls_version": min_tls_version, + "max_tls_version": max_tls_version, }, FastlyBackend.FIELDS) content = self._fetch("/service/%s/version/%d/backend" % (service_id, version_number), method="POST", body=body) return FastlyBackend(self, content) @@ -1144,6 +1148,8 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): "comment", "ssl_cert_hostname", "ssl_sni_hostname", + "min_tls_version", + "max_tls_version", ] @property