Skip to content
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,5 @@ MANIFEST

.pypirc
venv/
env/
.idea/
3 changes: 2 additions & 1 deletion ais_service_discovery/call_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
sqs_adaptor = SqsAdaptor(sqs)
send = Send(sqs_adaptor)

def run_service(service, body, opts={}):

def run_service(service, body, opts = {}):
type = service['Attributes']['type']
if type in ['cloud-function', 'function', 'lambda']:
return func.call(**{
Expand Down
40 changes: 40 additions & 0 deletions ais_service_discovery/discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from boto3 import client
from .services import CloudmapAdapter, Services

service_discovery = client('servicediscovery')


class DiscoveryFactory:
@staticmethod
def instance(namespace, service):
cloudmap_adaptor = CloudmapAdapter(service_discovery)
adapter = Services(cloudmap_adaptor)
return Discovery(adapter, namespace, service)


class Discovery:
def __init__(self, adapter: Services, namespace, service):
self.adapter = adapter
self.namespace = namespace
self.service = service

def register(self, instance, kind, attributes):
if self.namespace and self.service:
attrs = {**attributes, 'type': kind}
return self.adapter.register(self.namespace, self.service, instance, attrs)
else:
raise Exception('you must defined a namespace and service')

def discover(self, instance):
result = self.adapter.discover(self.namespace, self.service, instance)
return list(result['Instances'])

def create_namespace(self, namespace):
return self.adapter.create_namespace(namespace)

def create_service(self, service):
return self.adapter.create_service(self.namespace, service)

def update_instance(self, instance, kind, attributes):
attrs = {**attributes, 'type': kind}
return self.adapter.update_instance(self.namespace, self.service, instance, attrs)
13 changes: 13 additions & 0 deletions ais_service_discovery/register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from boto3 import client
from .services import default_namespace, extract_service_parts, Services, \
CloudmapAdapter

service_discovery = client('servicediscovery')

cloudmap_adaptor = CloudmapAdapter(service_discovery)
services = Services(cloudmap_adaptor)


def register(namespace: str, service: str, instance: str, kind: str, attributes: dict):
attrs = {**attributes, 'type': kind}
return services.register(namespace, service, instance, attrs)
12 changes: 12 additions & 0 deletions ais_service_discovery/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,15 @@ def __init__(self, adapter):

def discover(self, *args, **kwargs):
return self.adapter.discover(*args, **kwargs)

def register(self, *args, **kwargs):
return self.adapter.register(*args, **kwargs)

def create_namespace(self, *args, **kwargs):
return self.adapter.create_namespace(*args, **kwargs)

def create_service(self, *args, **kwargs):
return self.adapter.create_service(*args, **kwargs)

def update_instance(self, *args, **kwargs):
return self.adapter.update_instance(*args, **kwargs)
81 changes: 75 additions & 6 deletions ais_service_discovery/services/cloudmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,85 @@ class CloudmapAdapter:
def __init__(self, client):
self.client = client

def filter_instances(self, response, id):
filter_function = lambda instance : instance['InstanceId'] == id

def filter_instances(self, response, instance_id):
filter_function = lambda instance : instance['InstanceId'] == instance_id
return {
**response,
'Instances': filter(filter_function, response['Instances'])
}

def discover(self, namespace, name, id):
def discover(self, namespace, service, instance):
return self.filter_instances(self.client.discover_instances(
NamespaceName=namespace,
ServiceName=name
), id)
ServiceName=service,
), instance)

def register(self, namespace, service, instance, attributes):
namespace_id = self.get_namespace_id(namespace)
srv = self.get_service(namespace_id, service)
if srv is None:
srv = self.create_service(namespace, service)
existing = list(self.discover(namespace, service, instance)['Instances'])
if existing:
return existing
self.client.register_instance(
ServiceId=srv,
InstanceId=instance,
Attributes=attributes,
)
return list(self.discover(namespace, service, instance)['Instances'])

def get_service(self, namespace, service):
response = self.client.list_services(
Filters=[
{
'Name': 'NAMESPACE_ID',
'Values': [
namespace,
],
'Condition': 'EQ',
},
]
)
for item in response['Services']:
if item['Name'] == service:
return item['Id']
return None

def get_namespace_id(self, namespace):
response = self.client.list_namespaces()
result = response['Namespaces']
for item in result:
if item['Name'] == namespace:
return item['Id']
return None

def create_namespace(self, namespace):
response = self.client.create_http_namespace(
Name=namespace,
)
return response['Namespace']

def create_service(self, namespace, service):
namespace_id = self.get_namespace_id(namespace)
response = self.client.create_service(
Name=service,
NamespaceId=namespace_id,
Type='HTTP'
)
return response['Service']['Id']

def update_instance(self, namespace, service, instance, attributes):
self.deregister_instance(namespace, service, instance)
return self.register(namespace, service, instance, attributes)

def deregister_instance(self, namespace, service, instance):
namespace_id = self.get_namespace_id(namespace)
service_id = self.get_service(namespace_id, service)
instances = list(self.discover(namespace, service, instance)['Instances'])
if len(instances) == 0:
return None
self.client.deregister_instance(
ServiceId=service_id,
InstanceId=instances[0]['InstanceId'],
)
24 changes: 12 additions & 12 deletions ais_service_discovery/services/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def get_handler(service_id):


def extract_service_parts(service_id):
'''
"""
This takes a service_id in the user-facing form and
reconsitutes it into parts an in our internal form.
For example, `namespace.service->handler`, becomes
Expand All @@ -28,7 +28,7 @@ def extract_service_parts(service_id):
This is because we have to differentiate between the three segments as
there's no way of assuring which is a namespace and which is a service,
as a namespace is optional.
'''
"""
# If service ID contains a NAMESPACE_NOTATION - which denotes that
# the ID contains both a namespace and a service.
# Split the two, check for a handler
Expand All @@ -40,26 +40,26 @@ def extract_service_parts(service_id):
if has_handler:
[service_name, handler] = has_handler
return {
'namespace': namespace,
'service': service_name,
'handler': handler
'namespace': namespace,
'service': service_name,
'handler': handler
}
return {
'namespace': namespace,
'service': service
'namespace': namespace,
'service': service
}
# If service has a function handler
# return service with handler in base format.
has_handler = get_handler(service)
if has_handler:
[service_name, handler] = has_handler
return {
'namespace': default_namespace(),
'service': service_name,
'handler': handler
'namespace': default_namespace(),
'service': service_name,
'handler': handler,
}
# Default behavior, returns serviceId given, with the default namespace.
return {
'namespace': default_namespace(),
'service': service_id,
'namespace': default_namespace(),
'service': service_id,
}
Empty file added example/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions example/discovery_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from ais_service_discovery.discovery import DiscoveryFactory

discovery = DiscoveryFactory.instance("example-namespace", "test-service")

instance = discovery.register("test", "dataset", {
"schema": "stage",
"table": "tester",
})

print(instance)

result = discovery.update_instance("test", "dataset", {"testing": "123"})
print("result: ", result)

instance = discovery.discover("dataset")
print("instance: ", instance)