Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add managedblockchain network functions #2956 #2957

Merged
merged 4 commits into from
May 7, 2020
Merged
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
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
jpbelleau marked this conversation as resolved.
Show resolved Hide resolved
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