Skip to content

Commit

Permalink
Merge pull request #2957 from OneMainF/add-managedblockchain
Browse files Browse the repository at this point in the history
Add managedblockchain network functions #2956
  • Loading branch information
bblommers authored May 7, 2020
2 parents ed109da + 9881306 commit e4caea5
Show file tree
Hide file tree
Showing 9 changed files with 484 additions and 0 deletions.
1 change: 1 addition & 0 deletions moto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def f(*args, **kwargs):
mock_kms_deprecated = lazy_load(".kms", "mock_kms_deprecated")
mock_logs = lazy_load(".logs", "mock_logs")
mock_logs_deprecated = lazy_load(".logs", "mock_logs_deprecated")
mock_managedblockchain = lazy_load(".managedblockchain", "mock_managedblockchain")
mock_opsworks = lazy_load(".opsworks", "mock_opsworks")
mock_opsworks_deprecated = lazy_load(".opsworks", "mock_opsworks_deprecated")
mock_organizations = lazy_load(".organizations", "mock_organizations")
Expand Down
1 change: 1 addition & 0 deletions moto/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"kms": ("kms", "kms_backends"),
"lambda": ("awslambda", "lambda_backends"),
"logs": ("logs", "logs_backends"),
"managedblockchain": ("managedblockchain", "managedblockchain_backends"),
"moto_api": ("core", "moto_api_backends"),
"opsworks": ("opsworks", "opsworks_backends"),
"organizations": ("organizations", "organizations_backends"),
Expand Down
9 changes: 9 additions & 0 deletions moto/managedblockchain/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import unicode_literals
from .models import managedblockchain_backends
from ..core.models import base_decorator, deprecated_base_decorator

managedblockchain_backend = managedblockchain_backends["us-east-1"]
mock_managedblockchain = base_decorator(managedblockchain_backends)
mock_managedblockchain_deprecated = deprecated_base_decorator(
managedblockchain_backends
)
27 changes: 27 additions & 0 deletions moto/managedblockchain/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from __future__ import unicode_literals
from moto.core.exceptions import RESTError


class ManagedBlockchainClientError(RESTError):
code = 400


class BadRequestException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
super(BadRequestException, self).__init__(
"BadRequestException",
"An error occurred (BadRequestException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)


class ResourceNotFoundException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
self.code = 404
super(ResourceNotFoundException, self).__init__(
"ResourceNotFoundException",
"An error occurred (BadRequestException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)
176 changes: 176 additions & 0 deletions moto/managedblockchain/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
from __future__ import unicode_literals

import datetime

from boto3 import Session

from moto.core import BaseBackend, BaseModel

from .exceptions import BadRequestException, ResourceNotFoundException

from .utils import get_network_id, get_member_id

FRAMEWORKS = [
"HYPERLEDGER_FABRIC",
]

FRAMEWORKVERSIONS = [
"1.2",
]

EDITIONS = [
"STARTER",
"STANDARD",
]


class ManagedBlockchainNetwork(BaseModel):
def __init__(
self,
id,
name,
framework,
frameworkversion,
frameworkconfiguration,
voting_policy,
member_configuration,
region,
description=None,
):
self.creationdate = datetime.datetime.utcnow()
self.id = id
self.name = name
self.description = description
self.framework = framework
self.frameworkversion = frameworkversion
self.frameworkconfiguration = frameworkconfiguration
self.voting_policy = voting_policy
self.member_configuration = member_configuration
self.region = region

def to_dict(self):
# Format for list_networks
d = {
"Id": self.id,
"Name": self.name,
"Framework": self.framework,
"FrameworkVersion": self.frameworkversion,
"Status": "AVAILABLE",
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
if self.description is not None:
d["Description"] = self.description
return d

def get_format(self):
# Format for get_networks
frameworkattributes = {
"Fabric": {
"OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format(
self.id.lower(), self.region
),
"Edition": self.frameworkconfiguration["Fabric"]["Edition"],
}
}

vpcendpointname = "com.amazonaws.{0}.managedblockchain.{1}".format(
self.region, self.id.lower()
)

d = {
"Id": self.id,
"Name": self.name,
"Framework": self.framework,
"FrameworkVersion": self.frameworkversion,
"FrameworkAttributes": frameworkattributes,
"VpcEndpointServiceName": vpcendpointname,
"VotingPolicy": self.voting_policy,
"Status": "AVAILABLE",
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
if self.description is not None:
d["Description"] = self.description
return d


class ManagedBlockchainBackend(BaseBackend):
def __init__(self, region_name):
self.networks = {}
self.region_name = region_name

def reset(self):
region_name = self.region_name
self.__dict__ = {}
self.__init__(region_name)

def create_network(
self,
name,
framework,
frameworkversion,
frameworkconfiguration,
voting_policy,
member_configuration,
description=None,
):
self.name = name
self.framework = framework
self.frameworkversion = frameworkversion
self.frameworkconfiguration = frameworkconfiguration
self.voting_policy = voting_policy
self.member_configuration = member_configuration
self.description = description

# Check framework
if framework not in FRAMEWORKS:
raise BadRequestException("CreateNetwork", "Invalid request body")

# Check framework version
if frameworkversion not in FRAMEWORKVERSIONS:
raise BadRequestException(
"CreateNetwork",
"Invalid version {0} requested for framework HYPERLEDGER_FABRIC".format(
frameworkversion
),
)

# Check edition
if frameworkconfiguration["Fabric"]["Edition"] not in EDITIONS:
raise BadRequestException("CreateNetwork", "Invalid request body")

## Generate network ID
network_id = get_network_id()

## Generate memberid ID - will need to actually create member
member_id = get_member_id()

self.networks[network_id] = ManagedBlockchainNetwork(
id=network_id,
name=name,
framework=self.framework,
frameworkversion=self.frameworkversion,
frameworkconfiguration=self.frameworkconfiguration,
voting_policy=self.voting_policy,
member_configuration=self.member_configuration,
region=self.region_name,
description=self.description,
)

# Return the network and member ID
d = {"NetworkId": network_id, "MemberId": member_id}
return d

def list_networks(self):
return self.networks.values()

def get_network(self, network_id):
if network_id not in self.networks:
raise ResourceNotFoundException(
"CreateNetwork", "Network {0} not found".format(network_id)
)
return self.networks.get(network_id)


managedblockchain_backends = {}
for region in Session().get_available_regions("managedblockchain"):
managedblockchain_backends[region] = ManagedBlockchainBackend(region)
90 changes: 90 additions & 0 deletions moto/managedblockchain/responses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from __future__ import unicode_literals

import json
from six.moves.urllib.parse import urlparse, parse_qs

from moto.core.responses import BaseResponse
from .models import managedblockchain_backends
from .utils import (
region_from_managedblckchain_url,
networkid_from_managedblockchain_url,
)


class ManagedBlockchainResponse(BaseResponse):
def __init__(self, backend):
super(ManagedBlockchainResponse, self).__init__()
self.backend = backend

@classmethod
def network_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._network_response(request, full_url, headers)

def _network_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
parsed_url = urlparse(full_url)
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
if method == "GET":
return self._all_networks_response(request, full_url, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._network_response_post(json_body, querystring, headers)

def _all_networks_response(self, request, full_url, headers):
mbcnetworks = self.backend.list_networks()
response = json.dumps(
{"Networks": [mbcnetwork.to_dict() for mbcnetwork in mbcnetworks]}
)
headers["content-type"] = "application/json"
return 200, headers, response

def _network_response_post(self, json_body, querystring, headers):
name = json_body["Name"]
framework = json_body["Framework"]
frameworkversion = json_body["FrameworkVersion"]
frameworkconfiguration = json_body["FrameworkConfiguration"]
voting_policy = json_body["VotingPolicy"]
member_configuration = json_body["MemberConfiguration"]

# Optional
description = json_body.get("Description", None)

response = self.backend.create_network(
name,
framework,
frameworkversion,
frameworkconfiguration,
voting_policy,
member_configuration,
description,
)
return 201, headers, json.dumps(response)

@classmethod
def networkid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._networkid_response(request, full_url, headers)

def _networkid_response(self, request, full_url, headers):
method = request.method

if method == "GET":
network_id = networkid_from_managedblockchain_url(full_url)
return self._networkid_response_get(network_id, headers)

def _networkid_response_get(self, network_id, headers):
mbcnetwork = self.backend.get_network(network_id)
response = json.dumps({"Network": mbcnetwork.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
9 changes: 9 additions & 0 deletions moto/managedblockchain/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import unicode_literals
from .responses import ManagedBlockchainResponse

url_bases = ["https?://managedblockchain.(.+).amazonaws.com"]

url_paths = {
"{0}/networks$": ManagedBlockchainResponse.network_response,
"{0}/networks/(?P<networkid>[^/.]+)$": ManagedBlockchainResponse.networkid_response,
}
29 changes: 29 additions & 0 deletions moto/managedblockchain/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import random
import string

from six.moves.urllib.parse import urlparse


def region_from_managedblckchain_url(url):
domain = urlparse(url).netloc

if "." in domain:
return domain.split(".")[1]
else:
return "us-east-1"


def networkid_from_managedblockchain_url(full_url):
return full_url.split("/")[-1]


def get_network_id():
return "n-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)


def get_member_id():
return "m-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
Loading

0 comments on commit e4caea5

Please sign in to comment.