Managedblockchain member additions (#2983)

* Added some member and proposal functions

* Added additional member and proposal functions

* Fixed admin password return and added update_member along with tests

* Added network removal and member removal proposal

* Fixed failing test

* Fixed Python 2.7 test
This commit is contained in:
James Belleau 2020-05-13 06:28:22 -05:00 committed by GitHub
parent 774a764b69
commit 9bc393801f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2638 additions and 74 deletions

View File

@ -16,6 +16,16 @@ class BadRequestException(ManagedBlockchainClientError):
) )
class InvalidRequestException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
super(InvalidRequestException, self).__init__(
"InvalidRequestException",
"An error occurred (InvalidRequestException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)
class ResourceNotFoundException(ManagedBlockchainClientError): class ResourceNotFoundException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error): def __init__(self, pretty_called_method, operation_error):
self.code = 404 self.code = 404
@ -25,3 +35,14 @@ class ResourceNotFoundException(ManagedBlockchainClientError):
pretty_called_method, operation_error pretty_called_method, operation_error
), ),
) )
class ResourceLimitExceededException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
self.code = 429
super(ResourceLimitExceededException, self).__init__(
"ResourceLimitExceededException",
"An error occurred (ResourceLimitExceededException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)

View File

@ -1,14 +1,28 @@
from __future__ import unicode_literals from __future__ import unicode_literals, division
import datetime import datetime
import re
from boto3 import Session from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from .exceptions import BadRequestException, ResourceNotFoundException from .exceptions import (
BadRequestException,
ResourceNotFoundException,
InvalidRequestException,
ResourceLimitExceededException,
)
from .utils import get_network_id, get_member_id from .utils import (
get_network_id,
get_member_id,
get_proposal_id,
get_invitation_id,
member_name_exist_in_network,
number_of_members_in_network,
admin_password_ok,
)
FRAMEWORKS = [ FRAMEWORKS = [
"HYPERLEDGER_FABRIC", "HYPERLEDGER_FABRIC",
@ -18,10 +32,20 @@ FRAMEWORKVERSIONS = [
"1.2", "1.2",
] ]
EDITIONS = [ EDITIONS = {
"STARTER", "STARTER": {
"STANDARD", "MaxMembers": 5,
] "MaxNodesPerMember": 2,
"AllowedNodeInstanceTypes": ["bc.t3.small", "bc.t3.medium"],
},
"STANDARD": {
"MaxMembers": 14,
"MaxNodesPerMember": 3,
"AllowedNodeInstanceTypes": ["bc.t3", "bc.m5", "bc.c5"],
},
}
VOTEVALUES = ["YES", "NO"]
class ManagedBlockchainNetwork(BaseModel): class ManagedBlockchainNetwork(BaseModel):
@ -48,6 +72,42 @@ class ManagedBlockchainNetwork(BaseModel):
self.member_configuration = member_configuration self.member_configuration = member_configuration
self.region = region self.region = region
@property
def network_name(self):
return self.name
@property
def network_framework(self):
return self.framework
@property
def network_framework_version(self):
return self.frameworkversion
@property
def network_creationdate(self):
return self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
@property
def network_description(self):
return self.description
@property
def network_edition(self):
return self.frameworkconfiguration["Fabric"]["Edition"]
@property
def vote_pol_proposal_duration(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ProposalDurationInHours"]
@property
def vote_pol_threshold_percentage(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdPercentage"]
@property
def vote_pol_threshold_comparator(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdComparator"]
def to_dict(self): def to_dict(self):
# Format for list_networks # Format for list_networks
d = { d = {
@ -63,7 +123,7 @@ class ManagedBlockchainNetwork(BaseModel):
return d return d
def get_format(self): def get_format(self):
# Format for get_networks # Format for get_network
frameworkattributes = { frameworkattributes = {
"Fabric": { "Fabric": {
"OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format( "OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format(
@ -93,9 +153,272 @@ class ManagedBlockchainNetwork(BaseModel):
return d return d
class ManagedBlockchainProposal(BaseModel):
def __init__(
self,
id,
networkid,
memberid,
membername,
numofmembers,
actions,
network_expirtation,
network_threshold,
network_threshold_comp,
description=None,
):
# In general, passing all values instead of creating
# an apparatus to look them up
self.id = id
self.networkid = networkid
self.memberid = memberid
self.membername = membername
self.numofmembers = numofmembers
self.actions = actions
self.network_expirtation = network_expirtation
self.network_threshold = network_threshold
self.network_threshold_comp = network_threshold_comp
self.description = description
self.creationdate = datetime.datetime.utcnow()
self.expirtationdate = self.creationdate + datetime.timedelta(
hours=network_expirtation
)
self.yes_vote_count = 0
self.no_vote_count = 0
self.outstanding_vote_count = self.numofmembers
self.status = "IN_PROGRESS"
self.votes = {}
@property
def network_id(self):
return self.networkid
@property
def proposal_status(self):
return self.status
@property
def proposal_votes(self):
return self.votes
def proposal_actions(self, action_type):
default_return = []
if action_type.lower() == "invitations":
if "Invitations" in self.actions:
return self.actions["Invitations"]
elif action_type.lower() == "removals":
if "Removals" in self.actions:
return self.actions["Removals"]
return default_return
def to_dict(self):
# Format for list_proposals
d = {
"ProposalId": self.id,
"ProposedByMemberId": self.memberid,
"ProposedByMemberName": self.membername,
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
return d
def get_format(self):
# Format for get_proposal
d = {
"ProposalId": self.id,
"NetworkId": self.networkid,
"Actions": self.actions,
"ProposedByMemberId": self.memberid,
"ProposedByMemberName": self.membername,
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"YesVoteCount": self.yes_vote_count,
"NoVoteCount": self.no_vote_count,
"OutstandingVoteCount": self.outstanding_vote_count,
}
if self.description is not None:
d["Description"] = self.description
return d
def set_vote(self, votermemberid, votermembername, vote):
if datetime.datetime.utcnow() > self.expirtationdate:
self.status = "EXPIRED"
return False
if vote.upper() == "YES":
self.yes_vote_count += 1
else:
self.no_vote_count += 1
self.outstanding_vote_count -= 1
perct_yes = (self.yes_vote_count / self.numofmembers) * 100
perct_no = (self.no_vote_count / self.numofmembers) * 100
self.votes[votermemberid] = {
"MemberId": votermemberid,
"MemberName": votermembername,
"Vote": vote.upper(),
}
if self.network_threshold_comp == "GREATER_THAN_OR_EQUAL_TO":
if perct_yes >= self.network_threshold:
self.status = "APPROVED"
elif perct_no >= self.network_threshold:
self.status = "REJECTED"
else:
if perct_yes > self.network_threshold:
self.status = "APPROVED"
elif perct_no > self.network_threshold:
self.status = "REJECTED"
return True
class ManagedBlockchainInvitation(BaseModel):
def __init__(
self,
id,
networkid,
networkname,
networkframework,
networkframeworkversion,
networkcreationdate,
region,
networkdescription=None,
):
self.id = id
self.networkid = networkid
self.networkname = networkname
self.networkdescription = networkdescription
self.networkframework = networkframework
self.networkframeworkversion = networkframeworkversion
self.networkstatus = "AVAILABLE"
self.networkcreationdate = networkcreationdate
self.status = "PENDING"
self.region = region
self.creationdate = datetime.datetime.utcnow()
self.expirtationdate = self.creationdate + datetime.timedelta(days=7)
@property
def invitation_status(self):
return self.status
@property
def invitation_networkid(self):
return self.networkid
def to_dict(self):
d = {
"InvitationId": self.id,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"Status": self.status,
"NetworkSummary": {
"Id": self.networkid,
"Name": self.networkname,
"Framework": self.networkframework,
"FrameworkVersion": self.networkframeworkversion,
"Status": self.networkstatus,
"CreationDate": self.networkcreationdate,
},
}
if self.networkdescription is not None:
d["NetworkSummary"]["Description"] = self.networkdescription
return d
def accept_invitation(self):
self.status = "ACCEPTED"
def reject_invitation(self):
self.status = "REJECTED"
def set_network_status(self, network_status):
self.networkstatus = network_status
class ManagedBlockchainMember(BaseModel):
def __init__(
self, id, networkid, member_configuration, region,
):
self.creationdate = datetime.datetime.utcnow()
self.id = id
self.networkid = networkid
self.member_configuration = member_configuration
self.status = "AVAILABLE"
self.region = region
self.description = None
@property
def network_id(self):
return self.networkid
@property
def name(self):
return self.member_configuration["Name"]
@property
def member_status(self):
return self.status
def to_dict(self):
# Format for list_members
d = {
"Id": self.id,
"Name": self.member_configuration["Name"],
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"IsOwned": True,
}
if "Description" in self.member_configuration:
self.description = self.member_configuration["Description"]
return d
def get_format(self):
# Format for get_member
frameworkattributes = {
"Fabric": {
"AdminUsername": self.member_configuration["FrameworkConfiguration"][
"Fabric"
]["AdminUsername"],
"CaEndpoint": "ca.{0}.{1}.managedblockchain.{2}.amazonaws.com:30002".format(
self.id.lower(), self.networkid.lower(), self.region
),
}
}
d = {
"NetworkId": self.networkid,
"Id": self.id,
"Name": self.name,
"FrameworkAttributes": frameworkattributes,
"LogPublishingConfiguration": self.member_configuration[
"LogPublishingConfiguration"
],
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
if "Description" in self.member_configuration:
d["Description"] = self.description
return d
def delete(self):
self.status = "DELETED"
def update(self, logpublishingconfiguration):
self.member_configuration[
"LogPublishingConfiguration"
] = logpublishingconfiguration
class ManagedBlockchainBackend(BaseBackend): class ManagedBlockchainBackend(BaseBackend):
def __init__(self, region_name): def __init__(self, region_name):
self.networks = {} self.networks = {}
self.members = {}
self.proposals = {}
self.invitations = {}
self.region_name = region_name self.region_name = region_name
def reset(self): def reset(self):
@ -113,14 +436,6 @@ class ManagedBlockchainBackend(BaseBackend):
member_configuration, member_configuration,
description=None, 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 # Check framework
if framework not in FRAMEWORKS: if framework not in FRAMEWORKS:
raise BadRequestException("CreateNetwork", "Invalid request body") raise BadRequestException("CreateNetwork", "Invalid request body")
@ -141,19 +456,25 @@ class ManagedBlockchainBackend(BaseBackend):
## Generate network ID ## Generate network ID
network_id = get_network_id() network_id = get_network_id()
## Generate memberid ID - will need to actually create member ## Generate memberid ID and initial member
member_id = get_member_id() member_id = get_member_id()
self.members[member_id] = ManagedBlockchainMember(
id=member_id,
networkid=network_id,
member_configuration=member_configuration,
region=self.region_name,
)
self.networks[network_id] = ManagedBlockchainNetwork( self.networks[network_id] = ManagedBlockchainNetwork(
id=network_id, id=network_id,
name=name, name=name,
framework=self.framework, framework=framework,
frameworkversion=self.frameworkversion, frameworkversion=frameworkversion,
frameworkconfiguration=self.frameworkconfiguration, frameworkconfiguration=frameworkconfiguration,
voting_policy=self.voting_policy, voting_policy=voting_policy,
member_configuration=self.member_configuration, member_configuration=member_configuration,
region=self.region_name, region=self.region_name,
description=self.description, description=description,
) )
# Return the network and member ID # Return the network and member ID
@ -166,10 +487,324 @@ class ManagedBlockchainBackend(BaseBackend):
def get_network(self, network_id): def get_network(self, network_id):
if network_id not in self.networks: if network_id not in self.networks:
raise ResourceNotFoundException( raise ResourceNotFoundException(
"CreateNetwork", "Network {0} not found".format(network_id) "GetNetwork", "Network {0} not found.".format(network_id)
) )
return self.networks.get(network_id) return self.networks.get(network_id)
def create_proposal(
self, networkid, memberid, actions, description=None,
):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"CreateProposal", "Network {0} not found.".format(networkid)
)
# Check if member exists
if memberid not in self.members:
raise ResourceNotFoundException(
"CreateProposal", "Member {0} not found.".format(memberid)
)
# CLI docs say that Invitations and Removals cannot both be passed - but it does
# not throw an error and can be performed
if "Invitations" in actions:
for propinvitation in actions["Invitations"]:
if re.match("[0-9]{12}", propinvitation["Principal"]) is None:
raise InvalidRequestException(
"CreateProposal",
"Account ID format specified in proposal is not valid.",
)
if "Removals" in actions:
for propmember in actions["Removals"]:
if propmember["MemberId"] not in self.members:
raise InvalidRequestException(
"CreateProposal",
"Member ID format specified in proposal is not valid.",
)
## Generate proposal ID
proposal_id = get_proposal_id()
self.proposals[proposal_id] = ManagedBlockchainProposal(
id=proposal_id,
networkid=networkid,
memberid=memberid,
membername=self.members.get(memberid).name,
numofmembers=number_of_members_in_network(self.members, networkid),
actions=actions,
network_expirtation=self.networks.get(networkid).vote_pol_proposal_duration,
network_threshold=self.networks.get(
networkid
).vote_pol_threshold_percentage,
network_threshold_comp=self.networks.get(
networkid
).vote_pol_threshold_comparator,
description=description,
)
# Return the proposal ID
d = {"ProposalId": proposal_id}
return d
def list_proposals(self, networkid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListProposals", "Network {0} not found.".format(networkid)
)
proposalsfornetwork = []
for proposal_id in self.proposals:
if self.proposals.get(proposal_id).network_id == networkid:
proposalsfornetwork.append(self.proposals[proposal_id])
return proposalsfornetwork
def get_proposal(self, networkid, proposalid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"GetProposal", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"GetProposal", "Proposal {0} not found.".format(proposalid)
)
return self.proposals.get(proposalid)
def vote_on_proposal(self, networkid, proposalid, votermemberid, vote):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"VoteOnProposal", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"VoteOnProposal", "Proposal {0} not found.".format(proposalid)
)
if votermemberid not in self.members:
raise ResourceNotFoundException(
"VoteOnProposal", "Member {0} not found.".format(votermemberid)
)
if vote.upper() not in VOTEVALUES:
raise BadRequestException("VoteOnProposal", "Invalid request body")
# Check to see if this member already voted
# TODO Verify exception
if votermemberid in self.proposals.get(proposalid).proposal_votes:
raise BadRequestException("VoteOnProposal", "Invalid request body")
# Will return false if vote was not cast (e.g., status wrong)
if self.proposals.get(proposalid).set_vote(
votermemberid, self.members.get(votermemberid).name, vote.upper()
):
if self.proposals.get(proposalid).proposal_status == "APPROVED":
## Generate invitations
for propinvitation in self.proposals.get(proposalid).proposal_actions(
"Invitations"
):
invitation_id = get_invitation_id()
self.invitations[invitation_id] = ManagedBlockchainInvitation(
id=invitation_id,
networkid=networkid,
networkname=self.networks.get(networkid).network_name,
networkframework=self.networks.get(networkid).network_framework,
networkframeworkversion=self.networks.get(
networkid
).network_framework_version,
networkcreationdate=self.networks.get(
networkid
).network_creationdate,
region=self.region_name,
networkdescription=self.networks.get(
networkid
).network_description,
)
## Delete members
for propmember in self.proposals.get(proposalid).proposal_actions(
"Removals"
):
self.delete_member(networkid, propmember["MemberId"])
def list_proposal_votes(self, networkid, proposalid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListProposalVotes", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"ListProposalVotes", "Proposal {0} not found.".format(proposalid)
)
# Output the vote summaries
proposalvotesfornetwork = []
for proposal_id in self.proposals:
if self.proposals.get(proposal_id).network_id == networkid:
for pvmemberid in self.proposals.get(proposal_id).proposal_votes:
proposalvotesfornetwork.append(
self.proposals.get(proposal_id).proposal_votes[pvmemberid]
)
return proposalvotesfornetwork
def list_invitations(self):
return self.invitations.values()
def reject_invitation(self, invitationid):
if invitationid not in self.invitations:
raise ResourceNotFoundException(
"RejectInvitation", "InvitationId {0} not found.".format(invitationid)
)
self.invitations.get(invitationid).reject_invitation()
def create_member(
self, invitationid, networkid, member_configuration,
):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"CreateMember", "Network {0} not found.".format(networkid)
)
if invitationid not in self.invitations:
raise InvalidRequestException(
"CreateMember", "Invitation {0} not valid".format(invitationid)
)
if self.invitations.get(invitationid).invitation_status != "PENDING":
raise InvalidRequestException(
"CreateMember", "Invitation {0} not valid".format(invitationid)
)
if (
member_name_exist_in_network(
self.members, networkid, member_configuration["Name"]
)
is True
):
raise InvalidRequestException(
"CreateMember",
"Member name {0} already exists in network {1}.".format(
member_configuration["Name"], networkid
),
)
networkedition = self.networks.get(networkid).network_edition
if (
number_of_members_in_network(self.members, networkid)
>= EDITIONS[networkedition]["MaxMembers"]
):
raise ResourceLimitExceededException(
"CreateMember",
"You cannot create a member in network {0}.{1} is the maximum number of members allowed in a {2} Edition network.".format(
networkid, EDITIONS[networkedition]["MaxMembers"], networkedition
),
)
memberadminpassword = member_configuration["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
]
if admin_password_ok(memberadminpassword) is False:
raise BadRequestException("CreateMember", "Invalid request body")
member_id = get_member_id()
self.members[member_id] = ManagedBlockchainMember(
id=member_id,
networkid=networkid,
member_configuration=member_configuration,
region=self.region_name,
)
# Accept the invitaiton
self.invitations.get(invitationid).accept_invitation()
# Return the member ID
d = {"MemberId": member_id}
return d
def list_members(self, networkid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListMembers", "Network {0} not found.".format(networkid)
)
membersfornetwork = []
for member_id in self.members:
if self.members.get(member_id).network_id == networkid:
membersfornetwork.append(self.members[member_id])
return membersfornetwork
def get_member(self, networkid, memberid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"GetMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"GetMember", "Member {0} not found.".format(memberid)
)
## Cannot get a member than has been delted (it does show up in the list)
if self.members.get(memberid).member_status == "DELETED":
raise ResourceNotFoundException(
"GetMember", "Member {0} not found.".format(memberid)
)
return self.members.get(memberid)
def delete_member(self, networkid, memberid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"DeleteMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"DeleteMember", "Member {0} not found.".format(memberid)
)
self.members.get(memberid).delete()
# Is this the last member in the network? (all set to DELETED)
if number_of_members_in_network(
self.members, networkid, member_status="DELETED"
) == len(self.members):
# Set network status to DELETED for all invitations
for invitation_id in self.invitations:
if (
self.invitations.get(invitation_id).invitation_networkid
== networkid
):
self.invitations.get(invitation_id).set_network_status("DELETED")
# Remove network
del self.networks[networkid]
def update_member(self, networkid, memberid, logpublishingconfiguration):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"UpdateMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"UpdateMember", "Member {0} not found.".format(memberid)
)
self.members.get(memberid).update(logpublishingconfiguration)
managedblockchain_backends = {} managedblockchain_backends = {}
for region in Session().get_available_regions("managedblockchain"): for region in Session().get_available_regions("managedblockchain"):

View File

@ -8,6 +8,9 @@ from .models import managedblockchain_backends
from .utils import ( from .utils import (
region_from_managedblckchain_url, region_from_managedblckchain_url,
networkid_from_managedblockchain_url, networkid_from_managedblockchain_url,
proposalid_from_managedblockchain_url,
invitationid_from_managedblockchain_url,
memberid_from_managedblockchain_url,
) )
@ -66,7 +69,7 @@ class ManagedBlockchainResponse(BaseResponse):
member_configuration, member_configuration,
description, description,
) )
return 201, headers, json.dumps(response) return 200, headers, json.dumps(response)
@classmethod @classmethod
def networkid_response(clazz, request, full_url, headers): def networkid_response(clazz, request, full_url, headers):
@ -88,3 +91,236 @@ class ManagedBlockchainResponse(BaseResponse):
response = json.dumps({"Network": mbcnetwork.get_format()}) response = json.dumps({"Network": mbcnetwork.get_format()})
headers["content-type"] = "application/json" headers["content-type"] = "application/json"
return 200, headers, response return 200, headers, response
@classmethod
def proposal_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposal_response(request, full_url, headers)
def _proposal_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)
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_proposals_response(network_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._proposal_response_post(
network_id, json_body, querystring, headers
)
def _all_proposals_response(self, network_id, headers):
proposals = self.backend.list_proposals(network_id)
response = json.dumps(
{"Proposals": [proposal.to_dict() for proposal in proposals]}
)
headers["content-type"] = "application/json"
return 200, headers, response
def _proposal_response_post(self, network_id, json_body, querystring, headers):
memberid = json_body["MemberId"]
actions = json_body["Actions"]
# Optional
description = json_body.get("Description", None)
response = self.backend.create_proposal(
network_id, memberid, actions, description,
)
return 200, headers, json.dumps(response)
@classmethod
def proposalid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposalid_response(request, full_url, headers)
def _proposalid_response(self, request, full_url, headers):
method = request.method
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
proposal_id = proposalid_from_managedblockchain_url(full_url)
return self._proposalid_response_get(network_id, proposal_id, headers)
def _proposalid_response_get(self, network_id, proposal_id, headers):
proposal = self.backend.get_proposal(network_id, proposal_id)
response = json.dumps({"Proposal": proposal.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
@classmethod
def proposal_votes_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposal_votes_response(request, full_url, headers)
def _proposal_votes_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)
network_id = networkid_from_managedblockchain_url(full_url)
proposal_id = proposalid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_proposal_votes_response(network_id, proposal_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._proposal_votes_response_post(
network_id, proposal_id, json_body, querystring, headers
)
def _all_proposal_votes_response(self, network_id, proposal_id, headers):
proposalvotes = self.backend.list_proposal_votes(network_id, proposal_id)
response = json.dumps({"ProposalVotes": proposalvotes})
headers["content-type"] = "application/json"
return 200, headers, response
def _proposal_votes_response_post(
self, network_id, proposal_id, json_body, querystring, headers
):
votermemberid = json_body["VoterMemberId"]
vote = json_body["Vote"]
self.backend.vote_on_proposal(
network_id, proposal_id, votermemberid, vote,
)
return 200, headers, ""
@classmethod
def invitation_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._invitation_response(request, full_url, headers)
def _invitation_response(self, request, full_url, headers):
method = request.method
if method == "GET":
return self._all_invitation_response(request, full_url, headers)
def _all_invitation_response(self, request, full_url, headers):
invitations = self.backend.list_invitations()
response = json.dumps(
{"Invitations": [invitation.to_dict() for invitation in invitations]}
)
headers["content-type"] = "application/json"
return 200, headers, response
@classmethod
def invitationid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._invitationid_response(request, full_url, headers)
def _invitationid_response(self, request, full_url, headers):
method = request.method
if method == "DELETE":
invitation_id = invitationid_from_managedblockchain_url(full_url)
return self._invitationid_response_delete(invitation_id, headers)
def _invitationid_response_delete(self, invitation_id, headers):
self.backend.reject_invitation(invitation_id)
headers["content-type"] = "application/json"
return 200, headers, ""
@classmethod
def member_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._member_response(request, full_url, headers)
def _member_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)
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_members_response(network_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._member_response_post(
network_id, json_body, querystring, headers
)
def _all_members_response(self, network_id, headers):
members = self.backend.list_members(network_id)
response = json.dumps({"Members": [member.to_dict() for member in members]})
headers["content-type"] = "application/json"
return 200, headers, response
def _member_response_post(self, network_id, json_body, querystring, headers):
invitationid = json_body["InvitationId"]
member_configuration = json_body["MemberConfiguration"]
response = self.backend.create_member(
invitationid, network_id, member_configuration,
)
return 200, headers, json.dumps(response)
@classmethod
def memberid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._memberid_response(request, full_url, headers)
def _memberid_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
network_id = networkid_from_managedblockchain_url(full_url)
member_id = memberid_from_managedblockchain_url(full_url)
if method == "GET":
return self._memberid_response_get(network_id, member_id, headers)
elif method == "PATCH":
json_body = json.loads(body.decode("utf-8"))
return self._memberid_response_patch(
network_id, member_id, json_body, headers
)
elif method == "DELETE":
return self._memberid_response_delete(network_id, member_id, headers)
def _memberid_response_get(self, network_id, member_id, headers):
member = self.backend.get_member(network_id, member_id)
response = json.dumps({"Member": member.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
def _memberid_response_patch(self, network_id, member_id, json_body, headers):
logpublishingconfiguration = json_body["LogPublishingConfiguration"]
self.backend.update_member(
network_id, member_id, logpublishingconfiguration,
)
return 200, headers, ""
def _memberid_response_delete(self, network_id, member_id, headers):
self.backend.delete_member(network_id, member_id)
headers["content-type"] = "application/json"
return 200, headers, ""

View File

@ -6,4 +6,11 @@ url_bases = ["https?://managedblockchain.(.+).amazonaws.com"]
url_paths = { url_paths = {
"{0}/networks$": ManagedBlockchainResponse.network_response, "{0}/networks$": ManagedBlockchainResponse.network_response,
"{0}/networks/(?P<networkid>[^/.]+)$": ManagedBlockchainResponse.networkid_response, "{0}/networks/(?P<networkid>[^/.]+)$": ManagedBlockchainResponse.networkid_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals$": ManagedBlockchainResponse.proposal_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)$": ManagedBlockchainResponse.proposalid_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)/votes$": ManagedBlockchainResponse.proposal_votes_response,
"{0}/invitations$": ManagedBlockchainResponse.invitation_response,
"{0}/invitations/(?P<invitationid>[^/.]+)$": ManagedBlockchainResponse.invitationid_response,
"{0}/networks/(?P<networkid>[^/.]+)/members$": ManagedBlockchainResponse.member_response,
"{0}/networks/(?P<networkid>[^/.]+)/members/(?P<memberid>[^/.]+)$": ManagedBlockchainResponse.memberid_response,
} }

View File

@ -1,4 +1,5 @@
import random import random
import re
import string import string
from six.moves.urllib.parse import urlparse from six.moves.urllib.parse import urlparse
@ -6,15 +7,18 @@ from six.moves.urllib.parse import urlparse
def region_from_managedblckchain_url(url): def region_from_managedblckchain_url(url):
domain = urlparse(url).netloc domain = urlparse(url).netloc
region = "us-east-1"
if "." in domain: if "." in domain:
return domain.split(".")[1] region = domain.split(".")[1]
else: return region
return "us-east-1"
def networkid_from_managedblockchain_url(full_url): def networkid_from_managedblockchain_url(full_url):
return full_url.split("/")[-1] id_search = re.search("\/n-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_network_id(): def get_network_id():
@ -23,7 +27,80 @@ def get_network_id():
) )
def memberid_from_managedblockchain_url(full_url):
id_search = re.search("\/m-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_member_id(): def get_member_id():
return "m-" + "".join( return "m-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26) random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
) )
def proposalid_from_managedblockchain_url(full_url):
id_search = re.search("\/p-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_proposal_id():
return "p-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
def invitationid_from_managedblockchain_url(full_url):
id_search = re.search("\/in-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_invitation_id():
return "in-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
def member_name_exist_in_network(members, networkid, membername):
membernamexists = False
for member_id in members:
if members.get(member_id).network_id == networkid:
if members.get(member_id).name == membername:
membernamexists = True
break
return membernamexists
def number_of_members_in_network(members, networkid, member_status=None):
return len(
[
membid
for membid in members
if members.get(membid).network_id == networkid
and (
member_status is None
or members.get(membid).member_status == member_status
)
]
)
def admin_password_ok(password):
if not re.search("[a-z]", password):
return False
elif not re.search("[A-Z]", password):
return False
elif not re.search("[0-9]", password):
return False
elif re.search("['\"@\\/]", password):
return False
else:
return True

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -0,0 +1,67 @@
from __future__ import unicode_literals
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}}}
},
}
default_policy_actions = {"Invitations": [{"Principal": "123456789012"}]}
multiple_policy_actions = {
"Invitations": [{"Principal": "123456789012"}, {"Principal": "123456789013"}]
}
def member_id_exist_in_list(members, memberid):
memberidxists = False
for member in members:
if member["Id"] == memberid:
memberidxists = True
break
return memberidxists
def create_member_configuration(
name, adminuser, adminpass, cloudwatchenabled, description=None
):
d = {
"Name": name,
"FrameworkConfiguration": {
"Fabric": {"AdminUsername": adminuser, "AdminPassword": adminpass}
},
"LogPublishingConfiguration": {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": cloudwatchenabled}}}
},
}
if description is not None:
d["Description"] = description
return d
def select_invitation_id_for_network(invitations, networkid, status=None):
# Get invitations based on network and maybe status
invitationsfornetwork = []
for invitation in invitations:
if invitation["NetworkSummary"]["Id"] == networkid:
if status is None or invitation["Status"] == status:
invitationsfornetwork.append(invitation["InvitationId"])
return invitationsfornetwork

View 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
from . import helpers
@mock_managedblockchain
def test_create_2_invitations():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.multiple_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"].should.have.length_of(2)
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
response["Invitations"][1]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][1]["Status"].should.equal("PENDING")
@mock_managedblockchain
def test_reject_invitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Reject - thanks but no thanks
response = conn.reject_invitation(InvitationId=invitation_id)
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("REJECTED")
@mock_managedblockchain
def test_reject_invitation_badinvitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
response = conn.reject_invitation.when.called_with(
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "InvitationId in-ABCDEFGHIJKLMNOP0123456789 not found.")

View File

@ -0,0 +1,669 @@
from __future__ import unicode_literals
import boto3
import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
from . import helpers
@mock_managedblockchain
def test_create_another_member():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
)
member_id2 = response["MemberId"]
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["Name"].should.equal("testmember2")
# Update member
logconfignewenabled = not helpers.default_memberconfiguration[
"LogPublishingConfiguration"
]["Fabric"]["CaLogs"]["Cloudwatch"]["Enabled"]
logconfignew = {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": logconfignewenabled}}}
}
conn.update_member(
NetworkId=network_id,
MemberId=member_id2,
LogPublishingConfiguration=logconfignew,
)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["LogPublishingConfiguration"]["Fabric"]["CaLogs"]["Cloudwatch"][
"Enabled"
].should.equal(logconfignewenabled)
@mock_managedblockchain
def test_create_another_member_withopts():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["Description"].should.equal("Test Member 2")
# Try to create member with already used invitation
response = conn.create_member.when.called_with(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2 Duplicate"
),
).should.throw(Exception, "Invitation {0} not valid".format(invitation_id))
# Delete member 2
conn.delete_member(NetworkId=network_id, MemberId=member_id2)
# Member is still in the list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
# But cannot get
response = conn.get_member.when.called_with(
NetworkId=network_id, MemberId=member_id2,
).should.throw(Exception, "Member {0} not found".format(member_id2))
# Delete member 1
conn.delete_member(NetworkId=network_id, MemberId=member_id)
# Network should be gone
response = conn.list_networks()
mbcnetworks = response["Networks"]
mbcnetworks.should.have.length_of(0)
# Verify the invitation network status is DELETED
# Get the invitation
response = conn.list_invitations()
response["Invitations"].should.have.length_of(1)
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["NetworkSummary"]["Status"].should.equal("DELETED")
@mock_managedblockchain
def test_create_and_delete_member():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal (create additional member)
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
both_policy_actions = {
"Invitations": [{"Principal": "123456789012"}],
"Removals": [{"MemberId": member_id2}],
}
# Create proposal (invite and remove member)
response = conn.create_proposal(
NetworkId=network_id, MemberId=member_id, Actions=both_policy_actions,
)
proposal_id2 = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id2)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id2,
VoterMemberId=member_id,
Vote="YES",
)
# Check the invitation status
response = conn.list_invitations()
invitations = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)
invitations.should.have.length_of(1)
# Member is still in the list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
foundmember2 = False
for member in members:
if member["Id"] == member_id2 and member["Status"] == "DELETED":
foundmember2 = True
foundmember2.should.equal(True)
@mock_managedblockchain
def test_create_too_many_members():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create 4 more members - create invitations for 5
for counter in range(2, 7):
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
for counter in range(2, 6):
# Get the invitation
response = conn.list_invitations()
invitation_id = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)[0]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember" + str(counter),
"admin",
"Admin12345",
False,
"Test Member " + str(counter),
),
)
member_id = response["MemberId"]
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(counter)
helpers.member_id_exist_in_list(members, member_id).should.equal(True)
# Get member details
response = conn.get_member(NetworkId=network_id, MemberId=member_id)
response["Member"]["Description"].should.equal("Test Member " + str(counter))
# Try to create the sixth
response = conn.list_invitations()
invitation_id = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)[0]
# Try to create member with already used invitation
response = conn.create_member.when.called_with(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember6", "admin", "Admin12345", False, "Test Member 6"
),
).should.throw(
Exception,
"5 is the maximum number of members allowed in a STARTER Edition network",
)
@mock_managedblockchain
def test_create_another_member_alreadyhave():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Should fail trying to create with same name
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember1", "admin", "Admin12345", False
),
).should.throw(
Exception,
"Member name {0} already exists in network {1}".format(
"testmember1", network_id
),
)
@mock_managedblockchain
def test_create_another_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.create_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
InvitationId="id-ABCDEFGHIJKLMNOP0123456789",
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_another_member_badinvitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
).should.throw(Exception, "Invitation in-ABCDEFGHIJKLMNOP0123456789 not valid")
@mock_managedblockchain
def test_create_another_member_adminpassword():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
badadminpassmemberconf = helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
)
# Too short
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badap"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(
Exception,
"Invalid length for parameter MemberConfiguration.FrameworkConfiguration.Fabric.AdminPassword",
)
# No uppercase or numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badadminpwd"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# No lowercase or numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "BADADMINPWD"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# No numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badAdminpwd"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# Invalid character
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badAdmin@pwd1"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_list_members_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_members.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.get_member.when.called_with(
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_delete_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.delete_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_delete_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.delete_member.when.called_with(
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_update_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.update_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
LogPublishingConfiguration=helpers.default_memberconfiguration[
"LogPublishingConfiguration"
],
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_update_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.update_member.when.called_with(
NetworkId=network_id,
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
LogPublishingConfiguration=helpers.default_memberconfiguration[
"LogPublishingConfiguration"
],
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -5,28 +5,7 @@ import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain from moto import mock_managedblockchain
from . import helpers
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 @mock_managedblockchain
@ -37,12 +16,14 @@ def test_create_network():
Name="testnetwork1", Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC", Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2", FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration, FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=default_votingpolicy, VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=default_memberconfiguration, MemberConfiguration=helpers.default_memberconfiguration,
) )
response["NetworkId"].should.match("n-[A-Z0-9]{26}") network_id = response["NetworkId"]
response["MemberId"].should.match("m-[A-Z0-9]{26}") member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Find in full list # Find in full list
response = conn.list_networks() response = conn.list_networks()
@ -51,7 +32,6 @@ def test_create_network():
mbcnetworks[0]["Name"].should.equal("testnetwork1") mbcnetworks[0]["Name"].should.equal("testnetwork1")
# Get network details # Get network details
network_id = mbcnetworks[0]["Id"]
response = conn.get_network(NetworkId=network_id) response = conn.get_network(NetworkId=network_id)
response["Network"]["Name"].should.equal("testnetwork1") response["Network"]["Name"].should.equal("testnetwork1")
@ -65,12 +45,14 @@ def test_create_network_withopts():
Description="Test Network 1", Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC", Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2", FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration, FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=default_votingpolicy, VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=default_memberconfiguration, MemberConfiguration=helpers.default_memberconfiguration,
) )
response["NetworkId"].should.match("n-[A-Z0-9]{26}") network_id = response["NetworkId"]
response["MemberId"].should.match("m-[A-Z0-9]{26}") member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Find in full list # Find in full list
response = conn.list_networks() response = conn.list_networks()
@ -79,7 +61,6 @@ def test_create_network_withopts():
mbcnetworks[0]["Description"].should.equal("Test Network 1") mbcnetworks[0]["Description"].should.equal("Test Network 1")
# Get network details # Get network details
network_id = mbcnetworks[0]["Id"]
response = conn.get_network(NetworkId=network_id) response = conn.get_network(NetworkId=network_id)
response["Network"]["Description"].should.equal("Test Network 1") response["Network"]["Description"].should.equal("Test Network 1")
@ -93,9 +74,9 @@ def test_create_network_noframework():
Description="Test Network 1", Description="Test Network 1",
Framework="HYPERLEDGER_VINYL", Framework="HYPERLEDGER_VINYL",
FrameworkVersion="1.2", FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration, FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=default_votingpolicy, VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=default_memberconfiguration, MemberConfiguration=helpers.default_memberconfiguration,
).should.throw(Exception, "Invalid request body") ).should.throw(Exception, "Invalid request body")
@ -108,9 +89,9 @@ def test_create_network_badframeworkver():
Description="Test Network 1", Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC", Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.X", FrameworkVersion="1.X",
FrameworkConfiguration=default_frameworkconfiguration, FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=default_votingpolicy, VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=default_memberconfiguration, MemberConfiguration=helpers.default_memberconfiguration,
).should.throw( ).should.throw(
Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC" Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC"
) )
@ -128,8 +109,8 @@ def test_create_network_badedition():
Framework="HYPERLEDGER_FABRIC", Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2", FrameworkVersion="1.2",
FrameworkConfiguration=frameworkconfiguration, FrameworkConfiguration=frameworkconfiguration,
VotingPolicy=default_votingpolicy, VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=default_memberconfiguration, MemberConfiguration=helpers.default_memberconfiguration,
).should.throw(Exception, "Invalid request body") ).should.throw(Exception, "Invalid request body")
@ -138,5 +119,5 @@ def test_get_network_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1") conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_network.when.called_with( response = conn.get_network.when.called_with(
NetworkId="n-BADNETWORK", NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-BADNETWORK not found") ).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -0,0 +1,199 @@
from __future__ import unicode_literals
import boto3
import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
from . import helpers
@mock_managedblockchain
def test_create_proposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
proposal_id.should.match("p-[A-Z0-9]{26}")
# Find in full list
response = conn.list_proposals(NetworkId=network_id)
proposals = response["Proposals"]
proposals.should.have.length_of(1)
proposals[0]["ProposalId"].should.equal(proposal_id)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
@mock_managedblockchain
def test_create_proposal_withopts():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
Description="Adding a new member",
)
proposal_id = response["ProposalId"]
proposal_id.should.match("p-[A-Z0-9]{26}")
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Description"].should.equal("Adding a new member")
@mock_managedblockchain
def test_create_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.create_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
Actions=helpers.default_policy_actions,
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_proposal_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id,
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
Actions=helpers.default_policy_actions,
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_proposal_badinvitationacctid():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Must be 12 digits
actions = {"Invitations": [{"Principal": "1234567890"}]}
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id, MemberId=member_id, Actions=actions,
).should.throw(Exception, "Account ID format specified in proposal is not valid")
@mock_managedblockchain
def test_create_proposal_badremovalmemid():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Must be 12 digits
actions = {"Removals": [{"MemberId": "m-ABCDEFGHIJKLMNOP0123456789"}]}
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id, MemberId=member_id, Actions=actions,
).should.throw(Exception, "Member ID format specified in proposal is not valid")
@mock_managedblockchain
def test_list_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_proposals.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_proposal_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.get_proposal.when.called_with(
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -0,0 +1,529 @@
from __future__ import unicode_literals
import os
import boto3
import sure # noqa
from freezegun import freeze_time
from nose import SkipTest
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain, settings
from . import helpers
@mock_managedblockchain
def test_vote_on_proposal_one_member_total_yes():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# List proposal votes
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
# Get proposal details - should be APPROVED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("APPROVED")
response["Proposal"]["YesVoteCount"].should.equal(1)
response["Proposal"]["NoVoteCount"].should.equal(0)
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
@mock_managedblockchain
def test_vote_on_proposal_one_member_total_no():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote no
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="NO",
)
# List proposal votes
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
# Get proposal details - should be REJECTED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("REJECTED")
response["Proposal"]["YesVoteCount"].should.equal(0)
response["Proposal"]["NoVoteCount"].should.equal(1)
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
@mock_managedblockchain
def test_vote_on_proposal_yes_greater_than():
conn = boto3.client("managedblockchain", region_name="us-east-1")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN",
}
}
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
@mock_managedblockchain
def test_vote_on_proposal_no_greater_than():
conn = boto3.client("managedblockchain", region_name="us-east-1")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN",
}
}
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote no with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="NO",
)
# Vote no with member 2
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id2,
Vote="NO",
)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("REJECTED")
@mock_managedblockchain
def test_vote_on_proposal_expiredproposal():
if os.environ.get("TEST_SERVER_MODE", "false").lower() == "true":
raise SkipTest("Cant manipulate time in server mode")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 1,
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
}
}
conn = boto3.client("managedblockchain", region_name="us-east-1")
with freeze_time("2015-01-01 12:00:00"):
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
with freeze_time("2015-02-01 12:00:00"):
# Vote yes - should set status to expired
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get proposal details - should be EXPIRED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("EXPIRED")
@mock_managedblockchain
def test_vote_on_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.vote_on_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badvote():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="FOO",
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_vote_on_proposal_alreadyvoted():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Vote yes with member 1 again
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_list_proposal_votes_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_proposal_votes.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_list_proposal_votes_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.list_proposal_votes.when.called_with(
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")