Skip to content
This repository was archived by the owner on Feb 5, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 145 additions & 35 deletions fastly/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -103,22 +103,16 @@ 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({
"user": user,
"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):
Expand Down Expand Up @@ -166,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)
Expand Down Expand Up @@ -269,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)
Expand Down Expand Up @@ -354,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)
Expand Down Expand Up @@ -494,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)
Expand Down Expand Up @@ -973,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)
Expand All @@ -990,19 +1049,15 @@ 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 = {}
hdrs.update(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__
Expand Down Expand Up @@ -1034,7 +1089,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"
Expand Down Expand Up @@ -1146,16 +1201,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"))


Expand Down Expand Up @@ -1183,7 +1238,7 @@ class FastlyCondition(FastlyObject, IServiceVersionObject):
"type",
"statement",
"priority",
"comment",
"comment",
]


Expand Down Expand Up @@ -1226,7 +1281,7 @@ class FastlyDirector(FastlyObject, IServiceVersionObject, IDateStampedObject):
"deleted",
"capacity",
"comment",
"backends",
"backends",
]


Expand Down Expand Up @@ -1541,8 +1596,63 @@ 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)
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
2 changes: 1 addition & 1 deletion fastly/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0.4'
__version__ = '1.1.0'
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def read(fname):

setup(
name="fastly-python",
version="1.0.4",
version="1.1.0",
author="Chris Zacharias",
author_email="chris@imgix.com",
description=("A Python client libary for the Fastly API."),
Expand Down