added the KeyValueStore component
This commit is contained in:
parent
52220bffbd
commit
3e2b7d9014
|
@ -0,0 +1,3 @@
|
|||
from .kvs import KeyValueStore
|
||||
|
||||
components = {KeyValueStore.component_type: KeyValueStore}
|
38
bunker/backends/component.py
Normal file
38
bunker/backends/component.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from abc import abstractmethod, abstractclassmethod, ABCMeta
|
||||
class AbstractComponent(metaclass=ABCMeta):
|
||||
component_type = "DO NOT INSTANTIATE abstract component"
|
||||
def __init__(self, bunker, name, password):
|
||||
self._bunker = bunker
|
||||
self._name = name
|
||||
self._password = password
|
||||
|
||||
@abstractclassmethod
|
||||
def new(cls, bunker, name, password):
|
||||
"""
|
||||
Create a new component in the given bunker
|
||||
using the given name and password.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def close(self, notify_bunker=True):
|
||||
"""
|
||||
This method should "close" the given component,
|
||||
meaning it should write back the changes and bring
|
||||
it in a state where it cannot be used anymore.
|
||||
|
||||
Such a state needs two important protections:
|
||||
|
||||
- It is protected against accidental usage, such that
|
||||
a regular user that forgets that the component is already
|
||||
closed cannot damage the data
|
||||
- All sensitive data (i.e. the password) is destroyed.
|
||||
|
||||
If notify_bunker is True the parenting bunker will be
|
||||
notified that the current component is already closed.
|
||||
|
||||
"""
|
||||
pass
|
||||
@abstractmethod
|
||||
def write_back(self):
|
||||
pass
|
54
bunker/backends/kvs.py
Normal file
54
bunker/backends/kvs.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from ljson.slapdash.mem import Table
|
||||
from ljson.slapdash import SlapdashHeader
|
||||
|
||||
from .component import AbstractComponent
|
||||
|
||||
class KeyValueStore(AbstractComponent):
|
||||
component_type = "kvs"
|
||||
def __init__(self, bunker, name, password):
|
||||
AbstractComponent.__init__(self, bunker, name, password)
|
||||
|
||||
file_, type_ = bunker._load_component(name, password)
|
||||
self._content = Table.from_file(file_)
|
||||
self._open = True
|
||||
|
||||
@classmethod
|
||||
def new(cls, bunker, name, password):
|
||||
bunker._add_component(name, password, KeyValueStore.component_type)
|
||||
|
||||
file_, type_ = bunker._load_component(name, password)
|
||||
table = Table(SlapdashHeader({}), [])
|
||||
table.save(file_)
|
||||
bunker._save_component(name, password, file_)
|
||||
|
||||
return cls(bunker, name, password)
|
||||
|
||||
def additem(self, key, value):
|
||||
if(not self._open):
|
||||
raise IOError("KeyValueStore is closed")
|
||||
self._content.additem({"key": key, "value": value})
|
||||
def getitem(self, key):
|
||||
if(not self._open):
|
||||
raise IOError("KeyValueStore is closed")
|
||||
return self._content[{"key": key}]["value"][0]
|
||||
|
||||
def write_back(self):
|
||||
if(not self._open):
|
||||
raise IOError("KeyValueStore is closed")
|
||||
file_, type_ = self._bunker._load_component(self._name, self._password)
|
||||
self._content.save(file_)
|
||||
self._bunker._save_component(self._name, self._password, file_)
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
return not self._open
|
||||
|
||||
def close(self, notify_bunker=True):
|
||||
self.write_back()
|
||||
self._open = False
|
||||
self._name = ""
|
||||
self._password = b""
|
||||
del(self._content)
|
||||
if(notify_bunker):
|
||||
self._bunker.notify_component_is_closed(self)
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
import uuid
|
||||
from collections import deque
|
||||
import ljson
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from .files.tarfile import RewriteableTarFile
|
||||
from .files.bunkeredfile import BunkeredFile
|
||||
from .backends import components
|
||||
|
||||
class Bunker(object):
|
||||
def __init__(self, path):
|
||||
self._tarfile = RewriteableTarFile.open(path)
|
||||
self._components = ljson.Table.from_file(self._tarfile.get_file("__bunker_main__"))
|
||||
|
||||
self._stack_of_open_components = deque()
|
||||
self._stack_of_open_components.appendleft(deque())
|
||||
|
||||
@classmethod
|
||||
def open(cls, path):
|
||||
|
@ -52,6 +55,8 @@ class Bunker(object):
|
|||
while(True):
|
||||
chunk = file_.read(chunk_size)
|
||||
length_read += len(chunk)
|
||||
if(not chunk):
|
||||
break
|
||||
if(length_read == length):
|
||||
decrypted.write(key.decrypt(chunk))
|
||||
break
|
||||
|
@ -87,7 +92,7 @@ class Bunker(object):
|
|||
self._components.save(self._tarfile.get_file("__bunker_main__"))
|
||||
self._tarfile.writeback_file("__bunker_main__")
|
||||
|
||||
def _save_compontent(self, name, password, file_):
|
||||
def _save_component(self, name, password, file_):
|
||||
chunk_size = 16
|
||||
if(not {"component": name} in self._components):
|
||||
raise ValueError("unknown component: {}".format(name))
|
||||
|
@ -102,6 +107,7 @@ class Bunker(object):
|
|||
|
||||
file_out = self._tarfile.get_file(file_name)
|
||||
file_out.truncate(0)
|
||||
file_out.seek(0, 0)
|
||||
|
||||
file_.seek(0, 0)
|
||||
chunk = file_.read(chunk_size)
|
||||
|
@ -117,4 +123,33 @@ class Bunker(object):
|
|||
file_out.close()
|
||||
|
||||
|
||||
def __enter__(self):
|
||||
self._stack_of_open_components.appendleft(deque())
|
||||
return self
|
||||
|
||||
def __exit__(self):
|
||||
open_components = self._stack_of_open_components.popleft()
|
||||
for component in open_components:
|
||||
component.close(notify_bunker=False)
|
||||
return False
|
||||
|
||||
def add_component(self, type_, name, password):
|
||||
if(not type_ in components):
|
||||
raise ValueError("unknown component type: '{}'".format(type_))
|
||||
component = components[type_].new(self, name, password)
|
||||
self._stack_of_open_components[0].append(component)
|
||||
return component
|
||||
|
||||
def get_component(self, name, password):
|
||||
type_ = self._components[{"component": name}]["type"][0]
|
||||
component = components[type_](self, name, password)
|
||||
self._stack_of_open_components[0].append(component)
|
||||
|
||||
return component
|
||||
|
||||
def notify_component_is_closed(self, component):
|
||||
for context in self._stack_of_open_components:
|
||||
for open_component in context:
|
||||
if(open_component == component):
|
||||
context.remove(open_component)
|
||||
break
|
||||
|
|
Loading…
Reference in New Issue
Block a user