Alex | d9fd85e | 2019-05-16 16:58:24 -0500 | [diff] [blame] | 1 | import os |
| 2 | import tarfile as tarfile |
| 3 | import tempfile |
| 4 | |
| 5 | from cfg_checker.common import logger_cli |
| 6 | from cfg_checker.common.exception import ConfigException |
| 7 | |
| 8 | |
| 9 | class TGZFile(object): |
| 10 | basefile = None |
| 11 | |
| 12 | def __init__(self, _filepath, label=None): |
| 13 | # Check if this filename exists |
| 14 | if not os.path.exists(_filepath): |
| 15 | # If the archive not exists, create it |
| 16 | # simple labelfile for a non-empty archive |
| 17 | _labelname = "labelfile" |
| 18 | with tempfile.TemporaryFile() as _tempfile: |
| 19 | _tempfile.write(label.encode('utf-8')) |
| 20 | _tempfile.flush() |
| 21 | _tempfile.seek(0) |
| 22 | # create tgz |
| 23 | with tarfile.open(_filepath, "w:gz") as tgz: |
| 24 | _info = tgz.gettarinfo( |
| 25 | arcname=_labelname, |
| 26 | fileobj=_tempfile |
| 27 | ) |
| 28 | tgz.addfile(_info, fileobj=_tempfile) |
| 29 | logger_cli.debug("... created file '{}'".format(_filepath)) |
| 30 | self.basefile = _filepath |
| 31 | |
| 32 | elif not os.path.isfile(_filepath): |
| 33 | # if path exists, and it is not a file |
| 34 | raise ConfigException( |
| 35 | "Supplied path of '{}' is not a file".format( |
| 36 | _filepath |
| 37 | ) |
| 38 | ) |
| 39 | elif not tarfile.is_tarfile(_filepath): |
| 40 | # if file exists, and it is not a tar file |
| 41 | raise ConfigException( |
| 42 | "Supplied file of '{}' is not a TAR stream".format( |
| 43 | _filepath |
| 44 | ) |
| 45 | ) |
| 46 | else: |
| 47 | self.basefile = _filepath |
| 48 | |
| 49 | def get_file(self, name): |
| 50 | if self.has_file(name): |
| 51 | with tarfile.open(self.basefile, "r:gz") as tgz: |
| 52 | _tgzitem = tgz.extractfile(tgz.getmember(name)) |
| 53 | return _tgzitem.read() |
| 54 | else: |
| 55 | return None |
| 56 | |
| 57 | def add_file(self, name, buf=None, replace=False): |
| 58 | _files = [] |
| 59 | with tarfile.open(self.basefile) as r: |
| 60 | _files = r.getnames() |
| 61 | _exists = name in _files |
| 62 | if _exists and not replace: |
| 63 | # file exists and replace flag is not set |
| 64 | return False |
| 65 | |
| 66 | # check if there is work to do |
| 67 | if not buf and not os.path.exists(name): |
| 68 | # Nothing to do: no buffer or file to add |
| 69 | return False |
| 70 | elif name in self.list_files() and not replace: |
| 71 | # file already there and replace flag not set |
| 72 | return False |
| 73 | |
| 74 | _a = "replace" if replace else "add" |
| 75 | logger_cli.debug("... about to {} '{}' ({:.2f}MB) -> '{}'".format( |
| 76 | _a, |
| 77 | name, |
| 78 | float(len(buf))/1024/1024, |
| 79 | self.basefile |
| 80 | )) |
| 81 | |
| 82 | # unzip tar, add file, zip it back |
| 83 | _tmpdir = tempfile.mkdtemp() |
| 84 | logger_cli.debug("... created tempdir '{}'".format(_tmpdir)) |
| 85 | # extract them |
| 86 | _files = [] |
| 87 | with tarfile.open(self.basefile) as r: |
| 88 | # all names extracted |
| 89 | _files = r.getnames() |
| 90 | # extract 'em |
| 91 | logger_cli.debug("... extracting contents") |
| 92 | r.extractall(_tmpdir) |
| 93 | |
| 94 | # create file |
| 95 | if buf: |
| 96 | _p = os.path.join(_tmpdir, name) |
| 97 | logger_cli.debug("... writing new file to '{}'".format( |
| 98 | _p |
| 99 | )) |
| 100 | if not _exists or replace: |
| 101 | with open(_p, "w") as w: |
| 102 | w.write(buf) |
| 103 | if not _exists: |
| 104 | _files.append(name) |
| 105 | # create the archive |
| 106 | logger_cli.debug("... rebuilding archive") |
| 107 | with tarfile.open(self.basefile, "w:gz") as tgz: |
| 108 | for _file in _files: |
| 109 | _p = os.path.join(_tmpdir, _file) |
| 110 | tgz.add(_p, arcname=_file) |
| 111 | os.remove(_p) |
| 112 | os.rmdir(_tmpdir) |
| 113 | return True |
| 114 | |
| 115 | def list_files(self): |
| 116 | # get names |
| 117 | with tarfile.open(self.basefile, "r:gz") as tgz: |
| 118 | _names = tgz.getnames() |
| 119 | # filter filenames only, skip path |
| 120 | if any(['/' in _n for _n in _names]): |
| 121 | _n = [] |
| 122 | for f in _names: |
| 123 | if '/' in f: |
| 124 | _n.append(f.rsplit('/', 1)[1]) |
| 125 | else: |
| 126 | _n.append(f) |
| 127 | return _n |
| 128 | else: |
| 129 | return _names |
| 130 | |
| 131 | def has_file(self, name): |
| 132 | if name in self.list_files(): |
| 133 | logger_cli.debug("... '{}' has '{}'".format(self.basefile, name)) |
| 134 | return True |
| 135 | else: |
| 136 | logger_cli.debug("... '{}' lacks '{}'".format(self.basefile, name)) |
| 137 | return False |