github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/bddtests/steps/peer_basic_impl.py (about)

     1  #
     2  # Copyright IBM Corp. 2016 All Rights Reserved.
     3  #
     4  # Licensed under the Apache License, Version 2.0 (the "License");
     5  # you may not use this file except in compliance with the License.
     6  # You may obtain a copy of the License at
     7  #
     8  #      http://www.apache.org/licenses/LICENSE-2.0
     9  #
    10  # Unless required by applicable law or agreed to in writing, software
    11  # distributed under the License is distributed on an "AS IS" BASIS,
    12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  # See the License for the specific language governing permissions and
    14  # limitations under the License.
    15  #
    16  
    17  import os
    18  import os.path
    19  import re
    20  import time
    21  import copy
    22  from behave import *
    23  from datetime import datetime, timedelta
    24  import base64
    25  import uuid
    26  
    27  import sys, requests, json
    28  
    29  import bdd_test_util
    30  import compose
    31  import bootstrap_util
    32  
    33  CORE_REST_PORT = 7050
    34  JSONRPC_VERSION = "2.0"
    35  
    36  class ContainerData:
    37      def __init__(self, containerName, ipAddress, envFromInspect, composeService, ports):
    38          self.containerName = containerName
    39          self.ipAddress = ipAddress
    40          self.envFromInspect = envFromInspect
    41          self.composeService = composeService
    42          self.ports = ports
    43  
    44      def getEnv(self, key):
    45          envValue = None
    46          for val in self.envFromInspect:
    47              if val.startswith(key):
    48                  envValue = val[len(key):]
    49                  break
    50          if envValue == None:
    51              raise Exception("ENV key not found ({0}) for container ({1})".format(key, self.containerName))
    52          return envValue
    53  
    54  def buildUrl(context, ipAddress, path):
    55      schema = "http"
    56      if 'TLS' in context.tags:
    57          schema = "https"
    58      return "{0}://{1}:{2}{3}".format(schema, ipAddress, CORE_REST_PORT, path)
    59  
    60  def currentTime():
    61      return time.strftime("%H:%M:%S")
    62  
    63  def getDockerComposeFileArgsFromYamlFile(compose_yaml):
    64      parts = compose_yaml.split()
    65      args = []
    66      for part in parts:
    67          args = args + ["-f"] + [part]
    68      return args
    69  
    70  @given(u'we compose "{composeYamlFile}"')
    71  def step_impl(context, composeYamlFile):
    72      # time.sleep(10)              # Should be replaced with a definitive interlock guaranteeing that all peers/membersrvc are ready
    73      composition = compose.Composition(context, composeYamlFile)
    74      context.compose_containers = composition.containerDataList
    75      context.composition = composition
    76  
    77  @when(u'requesting "{path}" from "{containerName}"')
    78  def step_impl(context, path, containerName):
    79      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
    80      request_url = buildUrl(context, ipAddress, path)
    81      print("Requesting path = {0}".format(request_url))
    82      resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
    83      assert resp.status_code == 200, "Failed to GET url %s:  %s" % (request_url,resp.text)
    84      context.response = resp
    85      print("")
    86  
    87  @then(u'I should get a JSON response containing "{attribute}" attribute')
    88  def step_impl(context, attribute):
    89      getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute))
    90  
    91  @then(u'I should get a JSON response containing no "{attribute}" attribute')
    92  def step_impl(context, attribute):
    93      try:
    94          getAttributeFromJSON(attribute, context.response.json(), "")
    95          assert None, "Attribute found in response (%s)" %(attribute)
    96      except AssertionError:
    97          print("Attribute not found as was expected.")
    98  
    99  def getAttributeFromJSON(attribute, jsonObject, msg):
   100      return getHierarchyAttributesFromJSON(attribute.split("."), jsonObject, msg)
   101  
   102  def getHierarchyAttributesFromJSON(attributes, jsonObject, msg):
   103      if len(attributes) > 0:
   104          assert attributes[0] in jsonObject, msg
   105          return getHierarchyAttributesFromJSON(attributes[1:], jsonObject[attributes[0]], msg)
   106      return jsonObject
   107  
   108  def formatStringToCompare(value):
   109      # double quotes are replaced by simple quotes because is not possible escape double quotes in the attribute parameters.
   110      return str(value).replace("\"", "'")
   111  
   112  @then(u'I should get a JSON response with "{attribute}" = "{expectedValue}"')
   113  def step_impl(context, attribute, expectedValue):
   114      foundValue = getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute))
   115      assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue)
   116  
   117  @then(u'I should get a JSON response with array "{attribute}" contains "{expectedValue}" elements')
   118  def step_impl(context, attribute, expectedValue):
   119      foundValue = getAttributeFromJSON(attribute, context.response.json(), "Attribute not found in response (%s)" %(attribute))
   120      assert (len(foundValue) == int(expectedValue)), "For attribute %s, expected array of size (%s), instead found (%s)" % (attribute, expectedValue, len(foundValue))
   121  
   122  @given(u'I wait "{seconds}" seconds')
   123  def step_impl(context, seconds):
   124      time.sleep(float(seconds))
   125  
   126  @when(u'I wait "{seconds}" seconds')
   127  def step_impl(context, seconds):
   128      time.sleep(float(seconds))
   129  
   130  @then(u'I wait "{seconds}" seconds')
   131  def step_impl(context, seconds):
   132      time.sleep(float(seconds))
   133  
   134  @when(u'I deploy lang chaincode "{chaincodePath}" of "{chainLang}" with ctor "{ctor}" to "{containerName}"')
   135  def step_impl(context, chaincodePath, chainLang, ctor, containerName):
   136      print("Printing chaincode language " + chainLang)
   137  
   138      chaincode = {
   139          "path": chaincodePath,
   140          "language": chainLang,
   141          "constructor": ctor,
   142          "args": getArgsFromContext(context),
   143      }
   144  
   145      deployChainCodeToContainer(context, chaincode, containerName)
   146  
   147  def getArgsFromContext(context):
   148      args = []
   149      if 'table' in context:
   150         # There is ctor arguments
   151         args = context.table[0].cells
   152      return args
   153  
   154  @when(u'I deploy chaincode "{chaincodePath}" with ctor "{ctor}" to "{containerName}"')
   155  def step_impl(context, chaincodePath, ctor, containerName):
   156      chaincode = {
   157          "path": chaincodePath,
   158          "language": "GOLANG",
   159          "constructor": ctor,
   160          "args": getArgsFromContext(context),
   161      }
   162  
   163      deployChainCodeToContainer(context, chaincode, containerName)
   164  
   165  @when(u'I deploy chaincode with name "{chaincodeName}" and with ctor "{ctor}" to "{containerName}"')
   166  def step_impl(context, chaincodeName, ctor, containerName):
   167      chaincode = {
   168          "name": chaincodeName,
   169          "language": "GOLANG",
   170          "constructor": ctor,
   171          "args": getArgsFromContext(context),
   172      }
   173  
   174      deployChainCodeToContainer(context, chaincode, containerName)
   175      time.sleep(2.0) # After #2068 implemented change this to only apply after a successful ping
   176  
   177  def deployChainCodeToContainer(context, chaincode, containerName):
   178      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   179      request_url = buildUrl(context, ipAddress, "/chaincode")
   180      print("Requesting path = {0}".format(request_url))
   181  
   182      chaincodeSpec = createChaincodeSpec(context, chaincode)
   183      chaincodeOpPayload = createChaincodeOpPayload("deploy", chaincodeSpec)
   184  
   185      resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False)
   186      assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   187      context.response = resp
   188      chaincodeName = resp.json()['result']['message']
   189      chaincodeSpec['chaincodeID']['name'] = chaincodeName
   190      context.chaincodeSpec = chaincodeSpec
   191      print(json.dumps(chaincodeSpec, indent=4))
   192      print("")
   193  
   194  def createChaincodeSpec(context, chaincode):
   195      chaincode = validateChaincodeDictionary(chaincode)
   196      args = prepend(chaincode["constructor"], chaincode["args"])
   197      # Create a ChaincodeSpec structure
   198      chaincodeSpec = {
   199          "type": getChaincodeTypeValue(chaincode["language"]),
   200          "chaincodeID": {
   201              "path" : chaincode["path"],
   202              "name" : chaincode["name"]
   203          },
   204          "ctorMsg":  {
   205              "args" : args
   206          },
   207      }
   208  
   209      if 'userName' in context:
   210          chaincodeSpec["secureContext"] = context.userName
   211      if 'metadata' in context:
   212          chaincodeSpec["metadata"] = context.metadata
   213  
   214      return chaincodeSpec
   215  
   216  def validateChaincodeDictionary(chaincode):
   217      chaincodeFields = ["path", "name", "language", "constructor", "args"]
   218  
   219      for field in chaincodeFields:
   220          if field not in chaincode:
   221              chaincode[field] = ""
   222  
   223      return chaincode
   224  
   225  def getChaincodeTypeValue(chainLang):
   226      if chainLang == "GOLANG":
   227          return 1
   228      elif chainLang =="JAVA":
   229          return 4
   230      elif chainLang == "NODE":
   231          return 2
   232      elif chainLang == "CAR":
   233          return 3
   234      elif chainLang == "UNDEFINED":
   235          return 0
   236      return 1
   237  
   238  @when(u'I mock deploy chaincode with name "{chaincodeName}"')
   239  def step_impl(context, chaincodeName):
   240      chaincode = {
   241          "name": chaincodeName,
   242          "language": "GOLANG"
   243      }
   244  
   245      context.chaincodeSpec = createChaincodeSpec(context, chaincode)
   246  
   247  @then(u'I should have received a chaincode name')
   248  def step_impl(context):
   249      if 'chaincodeSpec' in context:
   250          assert context.chaincodeSpec['chaincodeID']['name'] != ""
   251          # Set the current transactionID to the name passed back
   252          context.transactionID = context.chaincodeSpec['chaincodeID']['name']
   253      elif 'grpcChaincodeSpec' in context:
   254          assert context.grpcChaincodeSpec.chaincodeID.name != ""
   255          # Set the current transactionID to the name passed back
   256          context.transactionID = context.grpcChaincodeSpec.chaincodeID.name
   257      else:
   258          fail('chaincodeSpec not in context')
   259  
   260  @when(u'I invoke chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}" with "{idGenAlg}"')
   261  def step_impl(context, chaincodeName, functionName, containerName, idGenAlg):
   262      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   263      invokeChaincode(context, "invoke", functionName, containerName, idGenAlg)
   264  
   265  @when(u'I invoke chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}" "{times}" times')
   266  def step_impl(context, chaincodeName, functionName, containerName, times):
   267      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   268      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   269      request_url = buildUrl(context, ipAddress, "/chain")
   270      resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   271      assert resp.status_code == 200, "Failed to get chain height %s:  %s" % (request_url,resp.text)
   272      context.chainheight = getAttributeFromJSON("height", resp.json(), "Height not found in response.")
   273      context.txcount = times
   274      for i in range(int(times)):
   275          invokeChaincode(context, "invoke", functionName, containerName)
   276  
   277  @when(u'I invoke chaincode "{chaincodeName}" function name "{functionName}" with attributes "{attrs}" on "{containerName}"')
   278  def step_impl(context, chaincodeName, functionName, attrs, containerName):
   279      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   280      assert attrs, "attrs were not specified"
   281      invokeChaincode(context, "invoke", functionName, containerName, None, attrs.split(","))
   282  
   283  @when(u'I invoke chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}"')
   284  def step_impl(context, chaincodeName, functionName, containerName):
   285      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   286      invokeChaincode(context, "invoke", functionName, containerName)
   287  
   288  @when(u'I invoke master chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}"')
   289  def step_impl(context, chaincodeName, functionName, containerName):
   290      invokeMasterChaincode(context, "invoke", chaincodeName, functionName, containerName)
   291  
   292  @then(u'I should have received a transactionID')
   293  def step_impl(context):
   294      assert 'transactionID' in context, 'transactionID not found in context'
   295      assert context.transactionID != ""
   296      pass
   297  
   298  @when(u'I unconditionally query chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}"')
   299  def step_impl(context, chaincodeName, functionName, containerName):
   300      invokeChaincode(context, "query", functionName, containerName)
   301  
   302  @when(u'I query chaincode "{chaincodeName}" function name "{functionName}" on "{containerName}"')
   303  def step_impl(context, chaincodeName, functionName, containerName):
   304      invokeChaincode(context, "query", functionName, containerName)
   305  
   306  def createChaincodeOpPayload(method, chaincodeSpec):
   307      chaincodeOpPayload = {
   308          "jsonrpc": JSONRPC_VERSION,
   309          "method" : method,
   310          "params" : chaincodeSpec,
   311          "id"     : 1
   312      }
   313      return chaincodeOpPayload
   314  
   315  def invokeChaincode(context, devopsFunc, functionName, containerName, idGenAlg=None, attributes=[]):
   316      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   317      # Update the chaincodeSpec ctorMsg for invoke
   318      args = []
   319      if 'table' in context:
   320         # There is ctor arguments
   321         args = context.table[0].cells
   322      args = prepend(functionName, args)
   323      for idx, attr in enumerate(attributes):
   324          attributes[idx] = attr.strip()
   325  
   326      context.chaincodeSpec['attributes'] = attributes
   327  
   328      #If idGenAlg is passed then, we still using the deprecated devops API because this parameter can't be passed in the new API.
   329      if idGenAlg != None:
   330          context.chaincodeSpec['ctorMsg']['args'] = to_bytes(args)
   331          invokeUsingDevopsService(context, devopsFunc, functionName, containerName, idGenAlg)
   332      else:
   333          context.chaincodeSpec['ctorMsg']['args'] = args
   334          invokeUsingChaincodeService(context, devopsFunc, functionName, containerName)
   335  
   336  def invokeUsingChaincodeService(context, devopsFunc, functionName, containerName):
   337      # Invoke the POST
   338      chaincodeOpPayload = createChaincodeOpPayload(devopsFunc, context.chaincodeSpec)
   339  
   340      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   341  
   342      request_url = buildUrl(context, ipAddress, "/chaincode")
   343      print("{0} POSTing path = {1}".format(currentTime(), request_url))
   344      print("Using attributes {0}".format(context.chaincodeSpec['attributes']))
   345  
   346      resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False)
   347      assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   348      context.response = resp
   349      print("RESULT from {0} of chaincode from peer {1}".format(functionName, containerName))
   350      print(json.dumps(context.response.json(), indent = 4))
   351      if 'result' in resp.json():
   352          result = resp.json()['result']
   353          if 'message' in result:
   354              transactionID = result['message']
   355              context.transactionID = transactionID
   356  
   357  def invokeUsingDevopsService(context, devopsFunc, functionName, containerName, idGenAlg):
   358      # Invoke the POST
   359      chaincodeInvocationSpec = {
   360          "chaincodeSpec" : context.chaincodeSpec
   361      }
   362      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   363      if idGenAlg is not None:
   364  	    chaincodeInvocationSpec['idGenerationAlg'] = idGenAlg
   365      request_url = buildUrl(context, ipAddress, "/devops/{0}".format(devopsFunc))
   366      print("{0} POSTing path = {1}".format(currentTime(), request_url))
   367  
   368      resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeInvocationSpec), verify=False)
   369      assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   370      context.response = resp
   371      print("RESULT from {0} of chaincode from peer {1}".format(functionName, containerName))
   372      print(json.dumps(context.response.json(), indent = 4))
   373      if 'message' in resp.json():
   374          transactionID = context.response.json()['message']
   375          context.transactionID = transactionID
   376  
   377  def invokeMasterChaincode(context, devopsFunc, chaincodeName, functionName, containerName):
   378      args = []
   379      if 'table' in context:
   380         args = context.table[0].cells
   381      args = prepend(functionName, args)
   382      typeGolang = 1
   383      chaincodeSpec = {
   384          "type": typeGolang,
   385          "chaincodeID": {
   386              "name" : chaincodeName
   387          },
   388          "ctorMsg":  {
   389              "args" : args
   390          }
   391      }
   392      if 'userName' in context:
   393          chaincodeSpec["secureContext"] = context.userName
   394  
   395      chaincodeOpPayload = createChaincodeOpPayload(devopsFunc, chaincodeSpec)
   396  
   397      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   398      request_url = buildUrl(context, ipAddress, "/chaincode")
   399      print("{0} POSTing path = {1}".format(currentTime(), request_url))
   400  
   401      resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False)
   402      assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   403      context.response = resp
   404      print("RESULT from {0} of chaincode from peer {1}".format(functionName, containerName))
   405      print(json.dumps(context.response.json(), indent = 4))
   406      if 'result' in resp.json():
   407          result = resp.json()['result']
   408          if 'message' in result:
   409              transactionID = result['message']
   410              context.transactionID = transactionID
   411  
   412  @then(u'I wait "{seconds}" seconds for chaincode to build')
   413  def step_impl(context, seconds):
   414      """ This step takes into account the chaincodeImagesUpToDate tag, in which case the wait is reduce to some default seconds"""
   415      reducedWaitTime = 4
   416      if 'chaincodeImagesUpToDate' in context.tags:
   417          print("Assuming images are up to date, sleeping for {0} seconds instead of {1} in scenario {2}".format(reducedWaitTime, seconds, context.scenario.name))
   418          time.sleep(float(reducedWaitTime))
   419      else:
   420          time.sleep(float(seconds))
   421  
   422  @then(u'I wait "{seconds}" seconds for transaction to be committed to block on "{containerName}"')
   423  def step_impl(context, seconds, containerName):
   424      assert 'transactionID' in context, "transactionID not found in context"
   425      ipAddress = bdd_test_util.ipFromContainerNamePart(containerName, context.compose_containers)
   426      request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID))
   427      print("{0} GETing path = {1}".format(currentTime(), request_url))
   428  
   429      resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   430      assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   431      context.response = resp
   432  
   433  def multiRequest(context, seconds, containerDataList, pathBuilderFunc):
   434      """Perform a multi request against the system"""
   435      # Build map of "containerName" : response
   436      respMap = {container.containerName:None for container in containerDataList}
   437      # Set the max time before stopping attempts
   438      maxTime = datetime.now() + timedelta(seconds = int(seconds))
   439      for container in containerDataList:
   440          ipAddress = container.ipAddress
   441          request_url = buildUrl(context, ipAddress, pathBuilderFunc(context, container))
   442  
   443          # Loop unless failure or time exceeded
   444          while (datetime.now() < maxTime):
   445              print("{0} GETing path = {1}".format(currentTime(), request_url))
   446              resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   447              respMap[container.containerName] = resp
   448          else:
   449              raise Exception("Max time exceeded waiting for multiRequest with current response map = {0}".format(respMap))
   450  
   451  @then(u'I wait up to "{seconds}" seconds for transaction to be committed to all peers')
   452  def step_impl(context, seconds):
   453      assert 'transactionID' in context, "transactionID not found in context"
   454      assert 'compose_containers' in context, "compose_containers not found in context"
   455  
   456      # Build map of "containerName" : resp.statusCode
   457      respMap = {container.containerName:0 for container in context.compose_containers}
   458  
   459      # Set the max time before stopping attempts
   460      maxTime = datetime.now() + timedelta(seconds = int(seconds))
   461      for container in context.compose_containers:
   462          ipAddress = container.ipAddress
   463          request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID))
   464  
   465          # Loop unless failure or time exceeded
   466          while (datetime.now() < maxTime):
   467              print("{0} GETing path = {1}".format(currentTime(), request_url))
   468              resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   469              if resp.status_code == 404:
   470                  # Pause then try again
   471                  respMap[container.containerName] = 404
   472                  time.sleep(1)
   473                  continue
   474              elif resp.status_code == 200:
   475                  # Success, continue
   476                  respMap[container.containerName] = 200
   477                  break
   478              else:
   479                  raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code))
   480          else:
   481              raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap))
   482      print("Result of request to all peers = {0}".format(respMap))
   483      print("")
   484  
   485  @then(u'I check the transaction ID if it is "{tUUID}"')
   486  def step_impl(context, tUUID):
   487      assert 'transactionID' in context, "transactionID not found in context"
   488      assert context.transactionID == tUUID, "transactionID is not tUUID"
   489  
   490  @then(u'I wait up to "{seconds}" seconds for transaction to be committed to peers')
   491  def step_impl(context, seconds):
   492      assert 'transactionID' in context, "transactionID not found in context"
   493      assert 'compose_containers' in context, "compose_containers not found in context"
   494      assert 'table' in context, "table (of peers) not found in context"
   495  
   496      aliases =  context.table.headings
   497      containerDataList = bdd_test_util.getContainerDataValuesFromContext(context, aliases, lambda containerData: containerData)
   498  
   499      # Build map of "containerName" : resp.statusCode
   500      respMap = {container.containerName:0 for container in containerDataList}
   501  
   502      # Set the max time before stopping attempts
   503      maxTime = datetime.now() + timedelta(seconds = int(seconds))
   504      for container in containerDataList:
   505          ipAddress = container.ipAddress
   506          request_url = buildUrl(context, ipAddress, "/transactions/{0}".format(context.transactionID))
   507  
   508          # Loop unless failure or time exceeded
   509          while (datetime.now() < maxTime):
   510              print("{0} GETing path = {1}".format(currentTime(), request_url))
   511              resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   512              if resp.status_code == 404:
   513                  # Pause then try again
   514                  respMap[container.containerName] = 404
   515                  time.sleep(1)
   516                  continue
   517              elif resp.status_code == 200:
   518                  # Success, continue
   519                  respMap[container.containerName] = 200
   520                  break
   521              else:
   522                  raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code))
   523          else:
   524              raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap))
   525      print("Result of request to all peers = {0}".format(respMap))
   526      print("")
   527  
   528  @then(u'I wait up to "{seconds}" seconds for transactions to be committed to peers')
   529  def step_impl(context, seconds):
   530      assert 'chainheight' in context, "chainheight not found in context"
   531      assert 'txcount' in context, "txcount not found in context"
   532      assert 'compose_containers' in context, "compose_containers not found in context"
   533      assert 'table' in context, "table (of peers) not found in context"
   534  
   535      aliases =  context.table.headings
   536      containerDataList = bdd_test_util.getContainerDataValuesFromContext(context, aliases, lambda containerData: containerData)
   537  
   538      # Build map of "containerName" : resp.statusCode
   539      respMap = {container.containerName:0 for container in containerDataList}
   540  
   541      # Set the max time before stopping attempts
   542      maxTime = datetime.now() + timedelta(seconds = int(seconds))
   543      for container in containerDataList:
   544          ipAddress = container.ipAddress
   545          request_url = buildUrl(context, ipAddress, "/chain")
   546  
   547          # Loop unless failure or time exceeded
   548          while (datetime.now() < maxTime):
   549              print("{0} GETing path = {1}".format(currentTime(), request_url))
   550              resp = requests.get(request_url, headers={'Accept': 'application/json'}, verify=False)
   551              if resp.status_code == 404:
   552                  # Pause then try again
   553                  respMap[container.containerName] = 404
   554                  time.sleep(1)
   555                  continue
   556              elif resp.status_code == 200:
   557                  height = getAttributeFromJSON("height", resp.json(), "Height not found in response.")
   558  		if height >= int(context.chainheight) + int(context.txcount):
   559                          # Success, continue
   560                          respMap[container.containerName] = 200
   561                          break
   562  		else:
   563  		        continue
   564              else:
   565                  raise Exception("Error requesting {0}, returned result code = {1}".format(request_url, resp.status_code))
   566          else:
   567              raise Exception("Max time exceeded waiting for transactions with current response map = {0}".format(respMap))
   568      print("Result of request to all peers = {0}".format(respMap))
   569      print("")
   570  
   571  
   572  @then(u'I should get a rejection message in the listener after stopping it')
   573  def step_impl(context):
   574      assert "eventlistener" in context, "no eventlistener is started"
   575      context.eventlistener.terminate()
   576      output = context.eventlistener.stdout.read()
   577      rejection = "Received rejected transaction"
   578      assert rejection in output, "no rejection message was found"
   579      assert output.count(rejection) == 1, "only one rejection message should be found"
   580  
   581  
   582  @when(u'I query chaincode "{chaincodeName}" function name "{functionName}" on all peers')
   583  def step_impl(context, chaincodeName, functionName):
   584      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   585      assert 'compose_containers' in context, "compose_containers not found in context"
   586      # Update the chaincodeSpec ctorMsg for invoke
   587      args = []
   588      if 'table' in context:
   589         # There is ctor arguments
   590         args = context.table[0].cells
   591      args = prepend(functionName, args)
   592      context.chaincodeSpec['ctorMsg']['args'] = args #context.table[0].cells if ('table' in context) else []
   593      # Invoke the POST
   594      chaincodeOpPayload = createChaincodeOpPayload("query", context.chaincodeSpec)
   595  
   596      responses = []
   597      for container in context.compose_containers:
   598          request_url = buildUrl(context, container.ipAddress, "/chaincode")
   599          print("{0} POSTing path = {1}".format(currentTime(), request_url))
   600          resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), verify=False)
   601          assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   602          responses.append(resp)
   603      context.responses = responses
   604  
   605  @when(u'I unconditionally query chaincode "{chaincodeName}" function name "{functionName}" with value "{value}" on peers')
   606  def step_impl(context, chaincodeName, functionName, value):
   607      query_common(context, chaincodeName, functionName, value, False)
   608  
   609  @when(u'I query chaincode "{chaincodeName}" function name "{functionName}" with value "{value}" on peers')
   610  def step_impl(context, chaincodeName, functionName, value):
   611      query_common(context, chaincodeName, functionName, value, True)
   612  
   613  def query_common(context, chaincodeName, functionName, value, failOnError):
   614      assert 'chaincodeSpec' in context, "chaincodeSpec not found in context"
   615      assert 'compose_containers' in context, "compose_containers not found in context"
   616      assert 'table' in context, "table (of peers) not found in context"
   617      assert 'peerToSecretMessage' in context, "peerToSecretMessage map not found in context"
   618  
   619      aliases =  context.table.headings
   620      containerDataList = bdd_test_util.getContainerDataValuesFromContext(context, aliases, lambda containerData: containerData)
   621  
   622      # Update the chaincodeSpec ctorMsg for invoke
   623      context.chaincodeSpec['ctorMsg']['args'] = [functionName, value]
   624      # Invoke the POST
   625      # Make deep copy of chaincodeSpec as we will be changing the SecurityContext per call.
   626      chaincodeOpPayload = createChaincodeOpPayload("query", copy.deepcopy(context.chaincodeSpec))
   627  
   628      responses = []
   629      for container in containerDataList:
   630          # Change the SecurityContext per call
   631          chaincodeOpPayload['params']["secureContext"] = context.peerToSecretMessage[container.composeService]['enrollId']
   632          print("Container {0} enrollID = {1}".format(container.containerName, container.getEnv("CORE_SECURITY_ENROLLID")))
   633          request_url = buildUrl(context, container.ipAddress, "/chaincode")
   634          print("{0} POSTing path = {1}".format(currentTime(), request_url))
   635          resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(chaincodeOpPayload), timeout=30, verify=False)
   636          if failOnError:
   637              assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   638          print("RESULT from {0} of chaincode from peer {1}".format(functionName, container.containerName))
   639          print(json.dumps(resp.json(), indent = 4))
   640          responses.append(resp)
   641      context.responses = responses
   642  
   643  @then(u'I should get a JSON response from all peers with "{attribute}" = "{expectedValue}"')
   644  def step_impl(context, attribute, expectedValue):
   645      assert 'responses' in context, "responses not found in context"
   646      for resp in context.responses:
   647          foundValue = getAttributeFromJSON(attribute, resp.json(), "Attribute not found in response (%s)" %(attribute))
   648          assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue)
   649  
   650  @then(u'I should get a JSON response from peers with "{attribute}" = "{expectedValue}"')
   651  def step_impl(context, attribute, expectedValue):
   652      assert 'responses' in context, "responses not found in context"
   653      assert 'compose_containers' in context, "compose_containers not found in context"
   654      assert 'table' in context, "table (of peers) not found in context"
   655  
   656      for resp in context.responses:
   657          foundValue = getAttributeFromJSON(attribute, resp.json(), "Attribute not found in response (%s)" %(attribute))
   658          assert (formatStringToCompare(foundValue) == expectedValue), "For attribute %s, expected (%s), instead found (%s)" % (attribute, expectedValue, foundValue)
   659  
   660  @given(u'I register with CA supplying username "{userName}" and secret "{secret}" on peers')
   661  def step_impl(context, userName, secret):
   662      assert 'compose_containers' in context, "compose_containers not found in context"
   663      assert 'table' in context, "table (of peers) not found in context"
   664  
   665      # Get list of IPs to login to
   666      aliases =  context.table.headings
   667      containerDataList = bdd_test_util.getContainerDataValuesFromContext(context, aliases, lambda containerData: containerData)
   668  
   669      secretMsg = {
   670          "enrollId": userName,
   671          "enrollSecret" : secret
   672      }
   673  
   674      # Login to each container specified
   675      for containerData in containerDataList:
   676          request_url = buildUrl(context, containerData.ipAddress, "/registrar")
   677          print("{0} POSTing path = {1}".format(currentTime(), request_url))
   678  
   679          resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(secretMsg), verify=False)
   680          assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   681          context.response = resp
   682          print("message = {0}".format(resp.json()))
   683  
   684          # Create new User entry
   685          bdd_test_util.registerUser(context, secretMsg, containerData.composeService)
   686  
   687      # Store the username in the context
   688      context.userName = userName
   689      # if we already have the chaincodeSpec, change secureContext
   690      if 'chaincodeSpec' in context:
   691          context.chaincodeSpec["secureContext"] = context.userName
   692  
   693  
   694  @given(u'I use the following credentials for querying peers')
   695  def step_impl(context):
   696      assert 'compose_containers' in context, "compose_containers not found in context"
   697      assert 'table' in context, "table (of peers, username, secret) not found in context"
   698  
   699      peerToSecretMessage = {}
   700  
   701      # Login to each container specified using username and secret
   702      for row in context.table.rows:
   703          peer, userName, secret = row['peer'], row['username'], row['secret']
   704          secretMsg = {
   705              "enrollId": userName,
   706              "enrollSecret" : secret
   707          }
   708  
   709          ipAddress = bdd_test_util.ipFromContainerNamePart(peer, context.compose_containers)
   710          request_url = buildUrl(context, ipAddress, "/registrar")
   711          print("POSTing to service = {0}, path = {1}".format(peer, request_url))
   712  
   713          resp = requests.post(request_url, headers={'Content-type': 'application/json'}, data=json.dumps(secretMsg), verify=False)
   714          assert resp.status_code == 200, "Failed to POST to %s:  %s" %(request_url, resp.text)
   715          context.response = resp
   716          print("message = {0}".format(resp.json()))
   717          peerToSecretMessage[peer] = secretMsg
   718      context.peerToSecretMessage = peerToSecretMessage
   719  
   720  
   721  @given(u'I stop peers')
   722  def step_impl(context):
   723      compose_op(context, "stop")
   724  
   725  
   726  @given(u'I start a listener')
   727  def step_impl(context):
   728      gopath = os.environ.get('GOPATH')
   729      assert gopath is not None, "Please set GOPATH properly!"
   730      listener = os.path.join(gopath, "src/github.com/hyperledger/fabric/build/bin/block-listener")
   731      assert os.path.isfile(listener), "Please build the block-listener binary!"
   732      bdd_test_util.start_background_process(context, "eventlistener", [listener, "-listen-to-rejections"] )
   733  
   734  
   735  @given(u'I start peers')
   736  def step_impl(context):
   737      compose_op(context, "start")
   738  
   739  @given(u'I pause peers')
   740  def step_impl(context):
   741      compose_op(context, "pause")
   742  
   743  @given(u'I unpause peers')
   744  def step_impl(context):
   745      compose_op(context, "unpause")
   746  
   747  def compose_op(context, op):
   748      assert 'table' in context, "table (of peers) not found in context"
   749      assert 'composition' in context, "composition not found in context"
   750      services =  context.table.headings
   751      context.composition.issueCommand([op] + services)
   752      context.compose_containers = context.composition.containerDataList
   753  
   754  def to_bytes(strlist):
   755      return [base64.standard_b64encode(s.encode('ascii')) for s in strlist]
   756  
   757  def prepend(elem, l):
   758      if l is None or l == "":
   759          tail = []
   760      else:
   761          tail = l
   762      if elem is None:
   763  	return tail
   764      return [elem] + tail
   765  
   766  @given(u'I do nothing')
   767  def step_impl(context):
   768      pass