blob: 631125ea6a6d14e0acbf36469e865d77da42056b [file] [log] [blame]
Alexd9fd85e2019-05-16 16:58:24 -05001import os
2import tarfile as tarfile
3import tempfile
4
5from cfg_checker.common import logger_cli
6from cfg_checker.common.exception import ConfigException
7
8
9class 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