tasks.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import tempfile
  2. import contextlib
  3. import logging
  4. from celery import shared_task
  5. from projects.models import Project
  6. from files.models import FileBlob, File
  7. from hashlib import sha1
  8. from symbolic import (
  9. Archive,
  10. )
  11. from .models import DebugInformationFile
  12. def getLogger():
  13. return logging.getLogger("glitchtip.difs")
  14. class ChecksumMismatched(Exception):
  15. pass
  16. class UnsupportedFile(Exception):
  17. pass
  18. DIF_STATE_CREATED = "created"
  19. DIF_STATE_OK = "ok"
  20. DIF_STATE_NOT_FOUND = "not_found"
  21. @shared_task
  22. def difs_assemble(project_slug, name, checksum, chunks, debug_id):
  23. try:
  24. project = Project.objects.filter(
  25. slug=project_slug
  26. ).get()
  27. file = difs_get_file_from_chunks(checksum, chunks)
  28. if file is None:
  29. file = difs_create_file_from_chunks(name, checksum, chunks)
  30. difs_create_difs(project, name, file)
  31. except ChecksumMismatched:
  32. getLogger().error(f"difs_assemble: Checksum mismatched: {name}")
  33. except Exception as e:
  34. getLogger().error(f"difs_assemble: {e}")
  35. def difs_get_file_from_chunks(checksum, chunks):
  36. files = File.objects.filter(checksum=checksum)
  37. for file in files:
  38. blobs = file.blobs.all()
  39. file_chunks = [blob.checksum for blob in blobs]
  40. if file_chunks == chunks:
  41. return file
  42. return None
  43. def difs_create_file_from_chunks(name, checksum, chunks):
  44. blobs = FileBlob.objects.filter(checksum__in=chunks)
  45. total_checksum = sha1(b'')
  46. size = 0
  47. for blob in blobs:
  48. size = size + blob.blob.size
  49. with open(blob.blob.path, "rb") as binary_file:
  50. content = binary_file.read()
  51. total_checksum.update(content)
  52. total_checksum = total_checksum.hexdigest()
  53. if checksum != total_checksum:
  54. raise ChecksumMismatched()
  55. file = File(
  56. name=name,
  57. headers={},
  58. size=size,
  59. checksum=checksum
  60. )
  61. file.save()
  62. file.blobs.set(blobs)
  63. return file
  64. @contextlib.contextmanager
  65. def difs_concat_file_blobs_to_disk(blobs):
  66. output = tempfile.NamedTemporaryFile(delete=False)
  67. for blob in blobs:
  68. with open(blob.blob.path, "rb") as binary_file:
  69. content = binary_file.read()
  70. output.write(content)
  71. output.flush()
  72. output.seek(0)
  73. try:
  74. yield output
  75. finally:
  76. output.close()
  77. def difs_extract_metadata_from_file(file):
  78. with difs_concat_file_blobs_to_disk(file.blobs.all()) as input:
  79. # Only one kind of file format is supported now
  80. try:
  81. archive = Archive.open(input.name)
  82. except Exception as e:
  83. getLogger().error(f"Extract metadata error: {e}")
  84. raise UnsupportedFile()
  85. else:
  86. return [
  87. {
  88. "arch": obj.arch,
  89. "file_format": obj.file_format,
  90. "code_id": obj.code_id,
  91. "debug_id": obj.debug_id,
  92. "kind": obj.kind,
  93. "features": list(obj.features),
  94. "symbol_type": "native"
  95. }
  96. for obj in archive.iter_objects()
  97. ]
  98. def difs_create_difs(project, name, file):
  99. metadatalist = difs_extract_metadata_from_file(file)
  100. for metadata in metadatalist:
  101. dif = DebugInformationFile.objects.filter(
  102. project_id=project.id,
  103. file=file
  104. ).first()
  105. if dif is not None:
  106. continue
  107. code_id = metadata["code_id"]
  108. debug_id = metadata["debug_id"]
  109. arch = metadata["arch"]
  110. kind = metadata["kind"]
  111. features = metadata["features"]
  112. symbol_type = metadata["symbol_type"]
  113. dif = DebugInformationFile(
  114. project=project,
  115. name=name,
  116. file=file,
  117. data={
  118. "arch": arch,
  119. "debug_id": debug_id,
  120. "code_id": code_id,
  121. "kind": kind,
  122. "features": features,
  123. "symbol_type": symbol_type
  124. }
  125. )
  126. dif.save()