Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Refactor old cc pkg upload API #583

Merged
merged 4 commits into from
Dec 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/api-engine/api/lib/peer/chaincode.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,25 @@ def query(self, orderer_url, orderer_tls_rootcert, channel_name, cc_name, args):
except Exception as e:
err_msg = "query failed for {}!".format(e)
raise Exception(err_msg)

def lifecycle_calculatepackageid(self, cc_path):
"""
calculate the chaincode packageid.
:param cc_path: where the chaincode package is
:return: calculated packageid
"""
try:
res = subprocess.Popen("{} lifecycle chaincode calculatepackageid {} "
.format(self.peer, cc_path),
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = res.communicate()
return_code = res.returncode
if return_code == 0:
content = str(stdout, encoding="utf-8")
return return_code, content
else:
stderr = str(stderr, encoding="utf-8")
return return_code, stderr
except Exception as e:
err_msg = "calculated chaincode packageid failed for {}!".format(e)
raise Exception(err_msg)
12 changes: 10 additions & 2 deletions src/api-engine/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -804,9 +804,17 @@ class ChainCode(models.Model):
editable=False,
unique=True
)
package_id = models.CharField(
help_text="package_id of chainCode", max_length=128,
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
)
Expand All @@ -816,8 +824,8 @@ class ChainCode(models.Model):
language = models.CharField(
help_text="language of chainCode", max_length=128
)
md5 = models.CharField(
help_text="md5 of chainCode", max_length=128
description = models.CharField(
help_text="description of chainCode", max_length=128, blank=True, null=True
)
create_ts = models.DateTimeField(
help_text="Create time of chainCode", auto_now_add=True
Expand Down
23 changes: 9 additions & 14 deletions src/api-engine/api/routes/chaincode/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from api.models import ChainCode
from api.common.serializers import ListResponseSerializer
import hashlib
import os


def upload_to(instance, filename):
Expand All @@ -18,25 +18,20 @@ class ChainCodeIDSerializer(serializers.Serializer):


class ChainCodePackageBody(serializers.Serializer):
name = serializers.CharField(max_length=128, required=True)
version = serializers.CharField(max_length=128, required=True)
language = serializers.CharField(max_length=128, required=True)
md5 = serializers.CharField(max_length=128, required=True)
file = serializers.FileField()

description = serializers.CharField(max_length=128, required=False)

def validate(self, attrs):
md5_get = self.md5_for_file(attrs["file"])
if md5_get != attrs["md5"]:
raise serializers.ValidationError("md5 not same.")
extension_get = self.extension_for_file(attrs["file"])
if not extension_get:
raise serializers.ValidationError("unsupported package type")
return super().validate(attrs)

@staticmethod
def md5_for_file(chunks):
md5 = hashlib.md5()
for data in chunks:
md5.update(data)
return md5.hexdigest()

def extension_for_file(file):
extension = file.name.endswith('.tar.gz')
return extension

class ChainCodeNetworkSerializer(serializers.Serializer):
id = serializers.UUIDField(help_text="Network ID")
Expand Down
100 changes: 49 additions & 51 deletions src/api-engine/api/routes/chaincode/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
import os
import zipfile
import tempfile, shutil, tarfile, json

from drf_yasg.utils import swagger_auto_schema
from api.config import FABRIC_CHAINCODE_STORE
Expand Down Expand Up @@ -92,76 +92,74 @@ def list(self, request):
def package(self, request):
serializer = ChainCodePackageBody(data=request.data)
if serializer.is_valid(raise_exception=True):
name = serializer.validated_data.get("name")
version = serializer.validated_data.get("version")
language = serializer.validated_data.get("language")
md5 = serializer.validated_data.get("md5")
file = serializer.validated_data.get("file")
id = make_uuid()

description = serializer.validated_data.get("description")
uuid = make_uuid()
fd, temp_path = tempfile.mkstemp()
try:
file_path = os.path.join(FABRIC_CHAINCODE_STORE, id)
if not os.path.exists(file_path):
os.makedirs(file_path)
fileziped = os.path.join(file_path, file.name)
with open(fileziped, 'wb') as f:
# try to calculate packageid
with open(fd, 'wb') as f:
for chunk in file.chunks():
f.write(chunk)
f.close()
zipped_file = zipfile.ZipFile(fileziped)
for filename in zipped_file.namelist():
zipped_file.extract(filename, file_path)

# When there is go.mod in the chain code, execute the go mod vendor command to obtain dependencies.
chaincode_path = file_path
found = False
for _, dirs, _ in os.walk(file_path):
if found:
break
elif dirs:
for each in dirs:
chaincode_path += "/" + each
if os.path.exists(chaincode_path + "/go.mod"):
cwd = os.getcwd()
print("cwd:", cwd)
os.chdir(chaincode_path)
os.system("go mod vendor")
found = True
os.chdir(cwd)
break
# if can not find go.mod, use the dir after extract zipped_file
if not found:
for _, dirs, _ in os.walk(file_path):
chaincode_path = file_path + "/" + dirs[0]
break

org = request.user.organization
qs = Node.objects.filter(type="peer", organization=org)
if not qs.exists():
raise ResourceNotFound
return Response(
err("At least 1 peer node is required for the chaincode package upload."),
xichen1 marked this conversation as resolved.
Show resolved Hide resolved
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)
res = peer_channel_cli.lifecycle_package(
name, version, chaincode_path, language)
os.system("rm -rf {}/*".format(file_path))
os.system("mv {}.tar.gz {}".format(name, file_path))
if res != 0:
return Response(err("package chaincode failed."), status=status.HTTP_400_BAD_REQUEST)
return_code, content = peer_channel_cli.lifecycle_calculatepackageid(temp_path)
if (return_code != 0):
return Response(
err("Calculate packageid failed for {}.".format(content)),
status=status.HTTP_400_BAD_REQUEST
)
packageid = content.strip()

# check if packageid exists
cc = ChainCode.objects.filter(package_id=packageid)
if cc.exists():
return Response(
err("Packageid {} 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)
xichen1 marked this conversation as resolved.
Show resolved Hide resolved
chaincode = ChainCode(
id=id,
name=name,
version=version,
id=uuid,
package_id=packageid,
language=language,
creator=org.name,
md5=md5
label=label,
description=description,
)
chaincode.save()
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
)
Expand Down