Move metadata handling to the Hierarchy class

This commit is contained in:
Eric Torres 2019-03-30 14:13:10 -07:00
parent 874f94c5fc
commit 5cca10a12b
4 changed files with 66 additions and 32 deletions

View File

@ -4,6 +4,7 @@
:synopsis: Classes for creating the backup hierarchy. :synopsis: Classes for creating the backup hierarchy.
""" """
import logging import logging
import pickle
from pathlib import Path from pathlib import Path
@ -11,6 +12,11 @@ from pathlib import Path
syslog = logging.getLogger(__name__) syslog = logging.getLogger(__name__)
# ========== Constants ==========
METADATA_READ = "rb"
METADATA_WRITE = "wb"
# ========== Classes ========== # ========== Classes ==========
class Hierarchy: class Hierarchy:
"""A class for organizing the backup root hierarchy. """A class for organizing the backup root hierarchy.
@ -24,6 +30,12 @@ class Hierarchy:
* path * path
* name * name
* metadata_path * metadata_path
Methods
-------
* read_metadata - read this Hierarchy's metadata from a file
and return it
* write_metadata - write this Hierarchy's metadata to a file
""" """
def __init__(self, dest): def __init__(self, dest):
@ -77,6 +89,20 @@ class Hierarchy:
""" """
return self.path / ".metadata" return self.path / ".metadata"
def read_metadata(self):
"""Read this repository's metadata from its file and
then return it.
:rtype: dict
"""
with self.metadata_path.open(mode=METADATA_READ) as mfile:
return pickle.load(mfile)
def write_metadata(self):
"""Write this repository's metadata to its file."""
with self.metadata_path.open(mode=METADATA_WRITE) as mfile:
pickle.dump(self._data, mfile)
# ========== Functions ========== # ========== Functions ==========
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -5,7 +5,6 @@
""" """
import logging import logging
import datetime import datetime
import pickle
from rbackup.hierarchy.hierarchy import Hierarchy from rbackup.hierarchy.hierarchy import Hierarchy
from rbackup.hierarchy.snapshot import Snapshot from rbackup.hierarchy.snapshot import Snapshot
@ -16,8 +15,6 @@ syslog = logging.getLogger(__name__)
# ========== Constants ========== # ========== Constants ==========
METADATA_READ = "rb"
METADATA_WRITE = "wb"
DIRMODE = 0o755 DIRMODE = 0o755
FILEMODE = 0o644 FILEMODE = 0o644
@ -55,7 +52,10 @@ class Repository(Hierarchy):
Methods Methods
------- -------
* create_snapshot() - create a new snapshot, then update current_snapshot * create_snapshot - create a new snapshot, then update current_snapshot
* gen_snapshot_path - generate a path for a snapshot given by name
* read_metadata (inherited from Hierarchy)
* write_metadata (inherited from Hierarchy)
Directory Structure Directory Structure
------------------- -------------------
@ -79,25 +79,16 @@ class Repository(Hierarchy):
self._snapshot_index = 0 self._snapshot_index = 0
if not self.metadata_path.exists(): if not self.metadata_path.exists():
self._data = {"snapshots": [], "current_snapshot": None}
self._init_new_repository() self._init_new_repository()
else: else:
self._read_metadata() self._data = self.read_metadata()
def _init_new_repository(self): def _init_new_repository(self):
self.metadata_path.parent.mkdir(mode=DIRMODE, exist_ok=True) self.metadata_path.parent.mkdir(mode=DIRMODE, exist_ok=True)
self.metadata_path.touch(mode=FILEMODE) self.metadata_path.touch(mode=FILEMODE)
self._data = {"snapshots": [], "current_snapshot": None} self.write_metadata()
self._write_metadata()
def _read_metadata(self):
with self.metadata_path.open(mode=METADATA_READ) as mfile:
self._data = pickle.load(mfile)
def _write_metadata(self):
with self.metadata_path.open(mode=METADATA_WRITE) as mfile:
pickle.dump(self._data, mfile)
def __len__(self): def __len__(self):
"""Return the number of snapshots in this Repository.""" """Return the number of snapshots in this Repository."""
@ -210,7 +201,12 @@ class Repository(Hierarchy):
self._data["current_snapshot"] = Snapshot(path) self._data["current_snapshot"] = Snapshot(path)
self._data["snapshots"].append(self._data["current_snapshot"]) self._data["snapshots"].append(self._data["current_snapshot"])
self._write_metadata() self.write_metadata()
try:
self._data["current_snapshot"].path.mkdir(mode=DIRMODE, parents=True)
except FileExistsError as e:
raise e
syslog.debug("Snapshot created") syslog.debug("Snapshot created")
syslog.debug(f"Snapshot name: {self.current_snapshot.name}") syslog.debug(f"Snapshot name: {self.current_snapshot.name}")

View File

@ -24,6 +24,11 @@ class Snapshot(Hierarchy):
* etc_dir * etc_dir
* home_dir * home_dir
* root_home_dir * root_home_dir
Methods
-------
* read_metadata (inherited from Hierarchy)
* write_metadata (inherited from Hierarchy)
""" """
def __init__(self, path): def __init__(self, path):

View File

@ -29,18 +29,21 @@ class TestRepositoryPreCreate(unittest.TestCase):
self.patched_path = patch.object( self.patched_path = patch.object(
Repository, "metadata_path", new_callable=PropertyMock Repository, "metadata_path", new_callable=PropertyMock
) )
self.patched_r_metadata = patch.object(Repository, "read_metadata")
self.patched_w_metadata = patch.object(Repository, "write_metadata")
self.patched_snapshot = patch( self.patched_snapshot = patch(
f"{TESTING_PACKAGE}.repository.Snapshot", spec_set=Snapshot f"{TESTING_PACKAGE}.repository.Snapshot", spec_set=Snapshot
) )
self.patched_pickle = patch(f"{TESTING_PACKAGE}.repository.pickle")
self.mocked_r_metadata = self.patched_r_metadata.start()
self.mocked_w_metadata = self.patched_w_metadata.start()
self.mocked_path = self.patched_path.start() self.mocked_path = self.patched_path.start()
self.mocked_snapshot = self.patched_snapshot.start() self.mocked_snapshot = self.patched_snapshot.start()
self.mocked_pickle = self.patched_pickle.start() self.mocked_pickle = self.patched_pickle.start()
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_empty(self, l): def test_empty(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
@ -53,7 +56,7 @@ class TestRepositoryPreCreate(unittest.TestCase):
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_len(self, l): def test_len(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
@ -64,15 +67,15 @@ class TestRepositoryPreCreate(unittest.TestCase):
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_current_snapshot(self, l): def test_current_snapshot(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
if l == []: if l == []:
self.mocked_pickle.load.return_value["current_snapshot"] = None self.mocked_r_metadata.return_value["current_snapshot"] = None
else: else:
self.mocked_pickle.load.return_value["current_snapshot"] = l[-1] self.mocked_r_metadata.return_value["current_snapshot"] = l[-1]
repo = Repository("backup") repo = Repository("backup")
if l == []: if l == []:
@ -83,8 +86,9 @@ class TestRepositoryPreCreate(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.patched_path.stop() self.patched_path.stop()
self.patched_r_metadata.stop()
self.patched_w_metadata.stop()
self.patched_snapshot.stop() self.patched_snapshot.stop()
self.patched_pickle.stop()
class TestRepositoryPostCreate(unittest.TestCase): class TestRepositoryPostCreate(unittest.TestCase):
@ -94,26 +98,28 @@ class TestRepositoryPostCreate(unittest.TestCase):
self.patched_path = patch.object( self.patched_path = patch.object(
Repository, "metadata_path", new_callable=PropertyMock Repository, "metadata_path", new_callable=PropertyMock
) )
self.patched_r_metadata = patch.object(Repository, "read_metadata")
self.patched_w_metadata = patch.object(Repository, "write_metadata")
self.patched_snapshot = patch( self.patched_snapshot = patch(
f"{TESTING_PACKAGE}.repository.Snapshot", spec_set=Snapshot f"{TESTING_PACKAGE}.repository.Snapshot", spec_set=Snapshot
) )
self.patched_pickle = patch(f"{TESTING_PACKAGE}.repository.pickle")
self.mocked_path = self.patched_path.start() self.mocked_path = self.patched_path.start()
self.mocked_r_metadata = self.patched_r_metadata.start()
self.mocked_w_metadata = self.patched_w_metadata.start()
self.mocked_snapshot = self.patched_snapshot.start() self.mocked_snapshot = self.patched_snapshot.start()
self.mocked_pickle = self.patched_pickle.start()
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_empty(self, l): def test_empty(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
if l == []: if l == []:
self.mocked_pickle.load.return_value["current_snapshot"] = None self.mocked_r_metadata.return_value["current_snapshot"] = None
else: else:
self.mocked_pickle.load.return_value["current_snapshot"] = l[-1] self.mocked_r_metadata.return_value["current_snapshot"] = l[-1]
repo = Repository("backup") repo = Repository("backup")
repo.create_snapshot() repo.create_snapshot()
@ -122,7 +128,7 @@ class TestRepositoryPostCreate(unittest.TestCase):
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_len(self, l): def test_len(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
@ -135,7 +141,7 @@ class TestRepositoryPostCreate(unittest.TestCase):
@given(lists(builds(Snapshot, text()), unique=True)) @given(lists(builds(Snapshot, text()), unique=True))
def test_current_snapshot(self, l): def test_current_snapshot(self, l):
self.mocked_pickle.load.return_value = { self.mocked_r_metadata.return_value = {
"snapshots": l.copy(), "snapshots": l.copy(),
"current_snapshot": l[-1] if l else None, "current_snapshot": l[-1] if l else None,
} }
@ -148,5 +154,6 @@ class TestRepositoryPostCreate(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.patched_path.stop() self.patched_path.stop()
self.patched_r_metadata.stop()
self.patched_w_metadata.stop()
self.patched_snapshot.stop() self.patched_snapshot.stop()
self.patched_pickle.stop()