From 404536a2a6fec44256c59b06c04b5281f5df851d Mon Sep 17 00:00:00 2001 From: xichen1 Date: Mon, 27 Nov 2023 19:31:01 -0800 Subject: [PATCH] Simplify cc read process Extract only necessary info from cc package. Optimize performance. Add postman testcase for cc upload. Signed-off-by: xichen1 --- src/api-engine/api/models.py | 6 -- src/api-engine/api/routes/chaincode/views.py | 79 ++++++++++++------- ...r Cello Api Engine.postman_collection.json | 56 +++++++++++++ 3 files changed, 106 insertions(+), 35 deletions(-) diff --git a/src/api-engine/api/models.py b/src/api-engine/api/models.py index 4bf81418e..8a99bb52b 100644 --- a/src/api-engine/api/models.py +++ b/src/api-engine/api/models.py @@ -809,15 +809,9 @@ class ChainCode(models.Model): editable=False, unique=True ) - name = models.CharField( - help_text="name of chainCode", max_length=128 - ) label = models.CharField( help_text="label of chainCode", max_length=128 ) - version = models.CharField( - help_text="version of chainCode", max_length=128 - ) creator = models.CharField( help_text="creator of chainCode", max_length=128 ) diff --git a/src/api-engine/api/routes/chaincode/views.py b/src/api-engine/api/routes/chaincode/views.py index 82dc78bb2..4f35504d0 100644 --- a/src/api-engine/api/routes/chaincode/views.py +++ b/src/api-engine/api/routes/chaincode/views.py @@ -31,12 +31,37 @@ ChaincodeListResponse ) from api.common import ok, err +import threading class ChainCodeViewSet(viewsets.ViewSet): """Class represents Channel related operations.""" permission_classes = [IsAuthenticated, ] + def _read_cc_pkg(self, pk, filename, ccpackage_path): + try: + meta_path = os.path.join(ccpackage_path, "metadata.json") + # extract metadata file + with tarfile.open(os.path.join(ccpackage_path, filename)) as tared_file: + metadata_file = tared_file.getmember("metadata.json") + tared_file.extract(metadata_file, path=ccpackage_path) + + with open(meta_path, 'r') as f: + metadata = json.load(f) + language = metadata["type"] + label = metadata["label"] + + if os.path.exists(meta_path): + os.remove(meta_path) + + chaincode = ChainCode.objects.get(id=pk) + chaincode.language = language + chaincode.label = label + chaincode.save() + + except Exception as e: + raise e + @swagger_auto_schema( query_serializer=PageQuerySerializer, responses=with_common_response( @@ -88,15 +113,15 @@ def list(self, request): {status.HTTP_201_CREATED: ChainCodeIDSerializer} ), ) - @action(detail=False, methods=['post']) + @action(detail=False, methods=['post'], url_path="chaincodeRepo") def package(self, request): serializer = ChainCodePackageBody(data=request.data) if serializer.is_valid(raise_exception=True): file = serializer.validated_data.get("file") description = serializer.validated_data.get("description") uuid = make_uuid() - fd, temp_path = tempfile.mkstemp() try: + fd, temp_cc_path = tempfile.mkstemp() # try to calculate packageid with open(fd, 'wb') as f: for chunk in file.chunks(): @@ -106,16 +131,16 @@ def package(self, request): qs = Node.objects.filter(type="peer", organization=org) if not qs.exists(): return Response( - err("At least 1 peer node is required for the chaincode package upload."), + err("at least 1 peer node is required for the chaincode package upload."), status=status.HTTP_400_BAD_REQUEST ) peer_node = qs.first() envs = init_env_vars(peer_node, org) peer_channel_cli = PeerChainCode("v2.2.0", **envs) - return_code, content = peer_channel_cli.lifecycle_calculatepackageid(temp_path) + return_code, content = peer_channel_cli.lifecycle_calculatepackageid(temp_cc_path) if (return_code != 0): return Response( - err("Calculate packageid failed for {}.".format(content)), + err("calculate packageid failed for {}.".format(content)), status=status.HTTP_400_BAD_REQUEST ) packageid = content.strip() @@ -124,45 +149,41 @@ def package(self, request): cc = ChainCode.objects.filter(package_id=packageid) if cc.exists(): return Response( - err("Packageid {} already exists.".format(packageid)), + err("package with id {} already exists.".format(packageid)), status=status.HTTP_400_BAD_REQUEST ) - # try to save chaincode package - ccpackage_path = os.path.join(FABRIC_CHAINCODE_STORE, packageid) - if not os.path.exists(ccpackage_path): - os.makedirs(ccpackage_path) - # tared_file = tarfile.TarFile(temp_path) - with tarfile.open(temp_path) as tared_file: - tared_file.extractall(ccpackage_path) - - with open(os.path.join(ccpackage_path, "metadata.json"), 'r') as f: - metadata = json.load(f) - language = metadata["type"] - label = metadata["label"] - - os.system("rm -rf {}/*".format(ccpackage_path)) - - ccpackage = os.path.join(ccpackage_path, file.name) - shutil.copy(temp_path, ccpackage) chaincode = ChainCode( id=uuid, package_id=packageid, - language=language, creator=org.name, - label=label, description=description, ) chaincode.save() + + # save chaincode package locally + ccpackage_path = os.path.join(FABRIC_CHAINCODE_STORE, packageid) + if not os.path.exists(ccpackage_path): + os.makedirs(ccpackage_path) + ccpackage = os.path.join(ccpackage_path, file.name) + shutil.copy(temp_cc_path, ccpackage) + + # start thread to read package meta info, update db + try: + threading.Thread(target=self._read_cc_pkg, + args=(uuid, file.name, ccpackage_path)).start() + except Exception as e: + raise e + + return Response( + ok("success"), status=status.HTTP_200_OK + ) except Exception as e: return Response( err(e.args), status=status.HTTP_400_BAD_REQUEST ) finally: - os.remove(temp_path) - return Response( - ok("success"), status=status.HTTP_200_OK - ) + os.remove(temp_cc_path) @swagger_auto_schema( method="post", diff --git a/tests/postman/test/Hyperledger Cello Api Engine.postman_collection.json b/tests/postman/test/Hyperledger Cello Api Engine.postman_collection.json index 567e4e1af..b3887fe61 100644 --- a/tests/postman/test/Hyperledger Cello Api Engine.postman_collection.json +++ b/tests/postman/test/Hyperledger Cello Api Engine.postman_collection.json @@ -2817,6 +2817,57 @@ } }, "response": [] + }, + { + "name": "Upload Chaincode Package", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Authorization", + "value": "JWT {{token}}", + "type": "text" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "file", + "type": "file", + "src": "mycc.tar.gz" + }, + { + "key": "description", + "value": "CC Description", + "type": "text" + } + ] + }, + "url": { + "raw": "http://127.0.0.1:8080/api/v1/chaincodes/chaincodeRepo", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8080", + "path": [ + "api", + "v1", + "chaincodes", + "chaincodeRepo" + ] + } + }, + "response": [] } ], "event": [ @@ -2847,6 +2898,11 @@ { "key": "webRoot", "value": "api/v1" + }, + { + "key": "token", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzAxMTQ0NjIyLCJpYXQiOjE3MDExNDEwMjIsImp0aSI6ImQyOGZjNTAzZWQxODRkYTg4MWNjMTRhYjA4ZWI1MTIwIiwidXNlcl9pZCI6IjRmNjViYjNhLTdhZmQtNDdjZi1iODE1LTk2ZDY0NWQ2OGYxNyJ9.gAMBXqJwtPcO2tCKJ_4wgl34qAEddiGSsJ5r5CRluU8", + "type": "string" } ] } \ No newline at end of file