From 88b3a04d9e918ee0c31cab110663236aede8c800 Mon Sep 17 00:00:00 2001 From: Wilk Maia Date: Wed, 31 Oct 2018 12:21:56 -0300 Subject: [PATCH 1/3] Use token instead of api_key or user/pass --- fastly/__init__.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 08e2175..191b7c9 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -103,14 +103,9 @@ class FastlyDirectorType(object): class FastlyConnection(object): - def __init__(self, api_key): + def __init__(self, token): self._session = None - self._api_key = api_key - self._fully_authed = False - - @property - def fully_authed(self): - return self._fully_authed + self._token = token def login(self, user, password): body = self._formdata({ @@ -118,7 +113,6 @@ def login(self, user, password): "password": password, }, ["user", "password"]) content = self._fetch("/login", method="POST", body=body) - self._fully_authed = True return FastlySession(self, content) def list_backends(self, service_id, version_number): @@ -999,10 +993,7 @@ def _fetch(self, url, method="GET", body=None, headers={}): print("Fetch: %s %s" % (method, url)) if body: print("Body: %s" % body) - if self._fully_authed: - hdrs["Cookie"] = self._session - else: - hdrs["Fastly-Key"] = self._api_key + hdrs["Fastly-Key"] = self._token hdrs["Content-Accept"] = "application/json" hdrs["User-Agent"] = "fastly-python-v%s" % __version__ @@ -1541,8 +1532,6 @@ class FastlyWordpress(FastlyObject, IServiceVersionObject): ] -def connect(api_key, username=None, password=None): - conn = FastlyConnection(api_key) - if username is not None and password is not None: - conn.login(username, password) +def connect(token): + conn = FastlyConnection(token) return conn From 8b327100b1c1fbe6194faf294cf9d5d33bab4150 Mon Sep 17 00:00:00 2001 From: Wilk Maia Date: Wed, 31 Oct 2018 14:57:52 -0300 Subject: [PATCH 2/3] Fix found issues - Spaces being used instead of tabs on some places - Deprecated `basestring` usage - `version` import was wrong - `urllib.urlencode` usage was wrong (the `urlencode` method is now on `urllib.parse`) --- fastly/__init__.py | 34 +++++++++++++++++----------------- fastly/version.py | 2 +- setup.py | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 191b7c9..6c87fe4 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -32,7 +32,7 @@ import re import urllib -from version import __version__ +from fastly.version import __version__ FASTLY_SCHEME = "https" FASTLY_HOST = "api.fastly.com" @@ -160,8 +160,8 @@ def create_backend( "request_condition": request_condition, "healthcheck": healthcheck, "comment": comment, - "ssl_cert_hostname": ssl_cert_hostname, - "ssl_sni_hostname": ssl_sni_hostname, + "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) @@ -263,8 +263,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'] + 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, safe='')), method="PUT", body=body) return FastlyCondition(self, content) @@ -348,8 +348,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'] + 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, safe='')), method="PUT", body=body) return FastlyDirector(self, content) @@ -488,8 +488,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'] + 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, safe='')), method="PUT", body=body) return FastlyHeader(self, content) @@ -984,7 +984,7 @@ def _formdata(self, fields, valid=[]): data[key] = fields[key] if isinstance(data[key], bool): data[key] = str(int(data[key])) - return urllib.urlencode(data) + return urllib.parse.urlencode(data) def _fetch(self, url, method="GET", body=None, headers={}): hdrs = {} @@ -1025,7 +1025,7 @@ def _check(self, resp, content): if payload is None: raise Exception("HTTP Error %d occurred." % status) - elif isinstance(payload, basestring): + elif isinstance(payload, str): raise Exception("HTTP Error %d occurred. { %s }" % (status, payload)) else: payload["status"] = "error" @@ -1137,16 +1137,16 @@ class FastlyBackend(FastlyObject, IServiceVersionObject): "request_condition", "healthcheck", "comment", - "ssl_cert_hostname", - "ssl_sni_hostname", + "ssl_cert_hostname", + "ssl_sni_hostname", "min_tls_version", "max_tls_version", ] @property def healthcheck(self): - if not self.__getattr__('healthcheck'): - return None + if not self.__getattr__('healthcheck'): + return None return self._conn.get_healthcheck(self.service_id, self.version, self.__getattr__("healthcheck")) @@ -1174,7 +1174,7 @@ class FastlyCondition(FastlyObject, IServiceVersionObject): "type", "statement", "priority", - "comment", + "comment", ] @@ -1217,7 +1217,7 @@ class FastlyDirector(FastlyObject, IServiceVersionObject, IDateStampedObject): "deleted", "capacity", "comment", - "backends", + "backends", ] diff --git a/fastly/version.py b/fastly/version.py index 8a81504..858de17 100644 --- a/fastly/version.py +++ b/fastly/version.py @@ -1 +1 @@ -__version__ = '1.0.4' +__version__ = '1.0.5' diff --git a/setup.py b/setup.py index 3c632db..9a39521 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ def read(fname): setup( name="fastly-python", - version="1.0.4", + version="1.0.5", author="Chris Zacharias", author_email="chris@imgix.com", description=("A Python client libary for the Fastly API."), From 0c3d93ac0292cff34a7248aff91dc2fc6d0e971f Mon Sep 17 00:00:00 2001 From: Wilk Maia Date: Wed, 31 Oct 2018 15:00:07 -0300 Subject: [PATCH 3/3] Add some ACL and Dictionary functionalities --- fastly/__init__.py | 125 ++++++++++++++++++++++++++++++++++++++++++++- fastly/version.py | 2 +- setup.py | 2 +- 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/fastly/__init__.py b/fastly/__init__.py index 6c87fe4..e42075e 100755 --- a/fastly/__init__.py +++ b/fastly/__init__.py @@ -967,7 +967,72 @@ def delete_wordpress(self, service_id, version_number, name): 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) - + + # ACL related methods + def get_acl(self, service_id, version_number, name): + content = self._fetch("/service/{}/version/{}/acl/{}".format(service_id, version_number, name)) + return FastlyACL(self, content) + + def create_acl(self, service_id, version_number, name): + body = self._formdata({ + "name": name, + }, ["name"]) + + content = self._fetch("/service/{}/version/{}/acl".format(service_id, version_number), method="POST", body=body) + return FastlyACL(self, content) + + def create_acl_entry(self, service_id, acl_id, ip, subnet=None): + body = self._formdata({ + "ip": ip, + "subnet": subnet, + }, ["ip", "subnet"]) + + content = self._fetch("/service/{}/acl/{}/entry".format(service_id, acl_id), method="POST", body=body) + return FastlyACLEntry(self, content) + + def get_acl_entries(self, service_id, acl_id): + content = self._fetch("/service/{}/acl/{}/entries".format(service_id, acl_id)) + result = [] + for el in content: + result.append(FastlyACLEntry(self, el)) + + return result + + def delete_acl_entry(self, service_id, acl_id, id): + content = self._fetch("/service/{}/acl/{}/entry/{}".format(service_id, acl_id, id), method="DELETE") + return self._status(content) + + # Dictionary related methods + def get_dic(self, service_id, version_number, name): + content = self._fetch("/service/{}/version/{}/dictionary/{}".format(service_id, version_number, name)) + return FastlyDictionary(self, content) + + def create_dic(self, service_id, version_number, name): + body = self._formdata({ + "name": name, + }, ["name"]) + + content = self._fetch("/service/{}/version/{}/dictionary".format(service_id, version_number), method="POST", body=body) + return FastlyDictionary(self, content) + + def create_dic_item(self, service_id, dic_id, key, value): + body = self._formdata({ + "item_key": key, + "item_value": value, + }, ["item_key", "item_value"]) + + content = self._fetch("/service/{}/dictionary/{}/item".format(service_id, dic_id), method="POST", body=body) + return FastlyDictionaryItem(self, content) + + def get_dic_item(self, service_id, dic_id, key): + content = self._fetch("/service/{}/dictionary/{}/item/{}".format(service_id, dic_id, key)) + return FastlyDictionaryItem(self, content) + + def delete_dic_item(self, service_id, dic_id, key): + key = urllib.parse.quote(key, safe='') + content = self._fetch("/service/{}/dictionary/{}/item/{}".format(service_id, dic_id, key), method="DELETE") + return self._status(content) + def _status(self, status): if not isinstance(status, FastlyStatus): status = FastlyStatus(self, status) @@ -990,7 +1055,6 @@ def _fetch(self, url, method="GET", body=None, headers={}): hdrs = {} hdrs.update(headers) - print("Fetch: %s %s" % (method, url)) if body: print("Body: %s" % body) hdrs["Fastly-Key"] = self._token @@ -1532,6 +1596,63 @@ class FastlyWordpress(FastlyObject, IServiceVersionObject): ] +class FastlyACL(FastlyObject, object): + """An ACL is a named access control list for matching against a client's IP address during VCL processing""" + FIELDS = [ + "id", + "name", + "version", + "service_id", + "created_at", + "updated_at", + "deleted_at", + ] + + +class FastlyACLEntry(FastlyObject, object): + """An ACL entry holds an IP address with optional subnet to make up an entry in an ACL""" + FIELDS = [ + "ip", + "subnet", + "acl_id", + "negated", + "comment", + "id", + "service_id", + ] + + +class FastlyDictionary(FastlyObject, object): + """A Dictionary is a table that stores key value pairs accessible to VCL functions during VCL processing""" + FIELDS = [ + "id", + "name", + "version", + "service_id", + "created_at", + "updated_at", + "deleted_at", + ] + + +class FastlyDictionaryItem(FastlyObject, object): + """A DictionaryItem holds a key and value that make up an entry in a Dictionary""" + FIELDS = [ + "item_key", + "item_value", + "dictionary_id", + "service_id", + ] + + @property + def key(self): + return self.item_key + + @property + def value(self): + return self.item_value + + def connect(token): conn = FastlyConnection(token) return conn diff --git a/fastly/version.py b/fastly/version.py index 858de17..1a72d32 100644 --- a/fastly/version.py +++ b/fastly/version.py @@ -1 +1 @@ -__version__ = '1.0.5' +__version__ = '1.1.0' diff --git a/setup.py b/setup.py index 9a39521..ba5bd2c 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ def read(fname): setup( name="fastly-python", - version="1.0.5", + version="1.1.0", author="Chris Zacharias", author_email="chris@imgix.com", description=("A Python client libary for the Fastly API."),