Merge pull request #2957 from OneMainF/add-managedblockchain
Add managedblockchain network functions #2956
This commit is contained in:
commit
e4caea586d
@ -75,6 +75,7 @@ mock_kms = lazy_load(".kms", "mock_kms")
|
||||
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")
|
||||
|
@ -39,6 +39,7 @@ BACKENDS = {
|
||||
"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"),
|
||||
|
9
moto/managedblockchain/__init__.py
Normal file
9
moto/managedblockchain/__init__.py
Normal file
@ -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
moto/managedblockchain/exceptions.py
Normal file
27
moto/managedblockchain/exceptions.py
Normal file
@ -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
moto/managedblockchain/models.py
Normal file
176
moto/managedblockchain/models.py
Normal file
@ -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
moto/managedblockchain/responses.py
Normal file
90
moto/managedblockchain/responses.py
Normal file
@ -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
moto/managedblockchain/urls.py
Normal file
9
moto/managedblockchain/urls.py
Normal file
@ -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
moto/managedblockchain/utils.py
Normal file
29
moto/managedblockchain/utils.py
Normal file
@ -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)
|
||||
)
|
142
tests/test_managedblockchain/test_managedblockchain_networks.py
Normal file
142
tests/test_managedblockchain/test_managedblockchain_networks.py
Normal file
@ -0,0 +1,142 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import boto3
|
||||
import sure # noqa
|
||||
|
||||
from moto.managedblockchain.exceptions import BadRequestException
|
||||
from moto import mock_managedblockchain
|
||||
|
||||
|
||||
default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}}
|
||||
|
||||
default_votingpolicy = {
|
||||
"ApprovalThresholdPolicy": {
|
||||
"ThresholdPercentage": 50,
|
||||
"ProposalDurationInHours": 24,
|
||||
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
|
||||
}
|
||||
}
|
||||
|
||||
default_memberconfiguration = {
|
||||
"Name": "testmember1",
|
||||
"Description": "Test Member 1",
|
||||
"FrameworkConfiguration": {
|
||||
"Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"}
|
||||
},
|
||||
"LogPublishingConfiguration": {
|
||||
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_network():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
)
|
||||
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
|
||||
response["MemberId"].should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Find in full list
|
||||
response = conn.list_networks()
|
||||
mbcnetworks = response["Networks"]
|
||||
mbcnetworks.should.have.length_of(1)
|
||||
mbcnetworks[0]["Name"].should.equal("testnetwork1")
|
||||
|
||||
# Get network details
|
||||
network_id = mbcnetworks[0]["Id"]
|
||||
response = conn.get_network(NetworkId=network_id)
|
||||
response["Network"]["Name"].should.equal("testnetwork1")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_network_withopts():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_network(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
)
|
||||
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
|
||||
response["MemberId"].should.match("m-[A-Z0-9]{26}")
|
||||
|
||||
# Find in full list
|
||||
response = conn.list_networks()
|
||||
mbcnetworks = response["Networks"]
|
||||
mbcnetworks.should.have.length_of(1)
|
||||
mbcnetworks[0]["Description"].should.equal("Test Network 1")
|
||||
|
||||
# Get network details
|
||||
network_id = mbcnetworks[0]["Id"]
|
||||
response = conn.get_network(NetworkId=network_id)
|
||||
response["Network"]["Description"].should.equal("Test Network 1")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_network_noframework():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_network.when.called_with(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_VINYL",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_network_badframeworkver():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.create_network.when.called_with(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.X",
|
||||
FrameworkConfiguration=default_frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
).should.throw(
|
||||
Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC"
|
||||
)
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_create_network_badedition():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
frameworkconfiguration = {"Fabric": {"Edition": "SUPER"}}
|
||||
|
||||
response = conn.create_network.when.called_with(
|
||||
Name="testnetwork1",
|
||||
Description="Test Network 1",
|
||||
Framework="HYPERLEDGER_FABRIC",
|
||||
FrameworkVersion="1.2",
|
||||
FrameworkConfiguration=frameworkconfiguration,
|
||||
VotingPolicy=default_votingpolicy,
|
||||
MemberConfiguration=default_memberconfiguration,
|
||||
).should.throw(Exception, "Invalid request body")
|
||||
|
||||
|
||||
@mock_managedblockchain
|
||||
def test_get_network_badnetwork():
|
||||
conn = boto3.client("managedblockchain", region_name="us-east-1")
|
||||
|
||||
response = conn.get_network.when.called_with(
|
||||
NetworkId="n-BADNETWORK",
|
||||
).should.throw(Exception, "Network n-BADNETWORK not found")
|
Loading…
Reference in New Issue
Block a user