07e8c587151c80ee655931ea00861f0016c11ff8
[osm/SO.git] / rwlaunchpad / plugins / rwlaunchpadtasklet / rift / tasklets / rwlaunchpad / extract.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import collections
19 import io
20 import os
21 import shutil
22 import tarfile
23 import tempfile
24 import tornado.httputil
25
26 import rift.package.package
27 import rift.package.convert
28 import rift.package.image
29 import rift.package.checksums
30
31 from .convert_pkg import ConvertPackage
32
33
34 class ExtractError(Exception):
35 pass
36
37
38 class UnreadableHeadersError(ExtractError):
39 pass
40
41
42 class MissingTerminalBoundary(ExtractError):
43 pass
44
45
46 class UnreadableDescriptorError(ExtractError):
47 pass
48
49
50 class UnreadablePackageError(ExtractError):
51 pass
52
53
54 class PackageImage(object):
55 def __init__(self, log, image_name, image_hdl, checksum=None):
56 self.name = image_name
57 self.image_hdl = image_hdl
58
59 if checksum is None:
60 log.debug("Image %s checksum not provided, calculating checksum...")
61 checksum = rift.package.checksums.checksum(self.image_hdl)
62 log.debug("Image %s checksum: %s", self.name, checksum)
63
64 self.checksum = checksum
65
66
67 class UploadPackageExtractor(object):
68 def __init__(self, log):
69 self._log = log
70
71 def create_packages_from_upload(self, uploaded_file, extracted_pkgfile):
72 def create_package_from_descriptor_file(desc_hdl):
73 # Uploaded package was a plain descriptor file
74 bytes_hdl = io.BytesIO(desc_hdl.read())
75 bytes_hdl.name = uploaded_file
76 try:
77 package = rift.package.package.DescriptorPackage.from_descriptor_file_hdl(
78 self._log, bytes_hdl
79 )
80 except rift.package.package.PackageError as e:
81 msg = "Could not create descriptor package from descriptor: %s" % str(e)
82 self._log.error(msg)
83 raise UnreadableDescriptorError(msg) from e
84
85 return package
86
87 def create_package_from_tar_file(tar_hdl):
88 # Uploaded package was in a .tar.gz format
89 tar_archive = rift.package.package.TarPackageArchive(
90 self._log, tar_hdl,
91 )
92 try:
93 package = tar_archive.create_package()
94 except rift.package.package.PackageError as e:
95 msg = "Could not create package from tar archive: %s" % str(e)
96 self._log.error(msg)
97 raise UnreadablePackageError(msg) from e
98
99 return package
100
101 self._log.info("creating package from uploaded descriptor file/package")
102 tmp_pkgs = []
103 upload_hdl = None
104 try:
105 # This file handle will be passed to TemporaryPackage to be closed
106 # and the underlying file removed.
107 upload_hdl = open(extracted_pkgfile, "r+b")
108
109 # Process the package archive
110 if tarfile.is_tarfile(extracted_pkgfile):
111 package = create_package_from_tar_file(upload_hdl)
112 tmp_pkgs.append(rift.package.package.TemporaryPackage(self._log,
113 package,
114 upload_hdl))
115
116 # Check if this is just a descriptor file
117 elif rift.package.convert.ProtoMessageSerializer.is_supported_file(uploaded_file):
118 package = create_package_from_descriptor_file(upload_hdl)
119 tmp_pkgs.append(rift.package.package.TemporaryPackage(self._log,
120 package,
121 upload_hdl))
122
123 else:
124 # See if the package can be converted
125 files = ConvertPackage(self._log,
126 uploaded_file,
127 extracted_pkgfile).convert(delete=True)
128
129 if files is None or not len(files):
130 # Not converted successfully
131 msg = "Uploaded file was neither a tar.gz or descriptor file"
132 self._log.error(msg)
133 raise UnreadablePackageError(msg)
134
135 # Close the open file handle as this file is not used anymore
136 upload_hdl.close()
137
138 for f in files:
139 self._log.debug("Upload converted file: {}".format(f))
140 upload_hdl = open(f, "r+b")
141 package = create_package_from_tar_file(upload_hdl)
142 if package.descriptor_id:
143 tmp_pkgs.append(rift.package.package.TemporaryPackage(self._log,
144 package,
145 upload_hdl))
146
147 except Exception as e:
148 # Cleanup any TemporaryPackage instances created
149 for t in tmp_pkgs:
150 t.close()
151
152 # Close the handle if not already closed
153 try:
154 if upload_hdl is not None:
155 upload_hdl.close()
156 except OSError as e:
157 self._log.warning("Failed to close file handle: %s", str(e))
158
159 try:
160 self._log.debug("Removing extracted package file: %s", extracted_pkgfile)
161 os.remove(extracted_pkgfile)
162 except OSError as e:
163 self._log.warning("Failed to remove extracted package dir: %s", str(e))
164
165 raise e
166
167 return tmp_pkgs