Contributing a new backend to ghost requires implementing a single class comprising, at the very least the class's constructor (__init__
) and the init
, put
, list
, get
and delete
base methods.
Each corresponding backend base method should return the same data structure.
If any dependencies are required for the backend, please add them under setup.py
in the extras_require
section using your backend's name:
extras_require={
'sqlalchemy': ['sqlalchemy>=1.0.15'],
...
'mystorage': ['mystorage'],
},
You can then conditionally import them like so:
try:
import mystorage
MYSTORAGE_EXISTS = True
except ImportError:
MYSTORAGE_EXISTS = False
The constructor should, at the very least, receive a db_path
argument representing the path, ip or url of the stash's backend.
class MyStorage(object):
def __init__(self, db_path=STORAGE_DEFAULT_PATH_MAPPING['mystorage'],
**backend_config):
if not MYSTORAGE_EXISTS:
raise ImportError('mystorage must be installed first')
self.client = self._get_client(db_path, backend_config)
...
The init
method should perform any actions related to initializing the backend like creating directories, indices, etc..
It should not return any value.
def init(self):
self.client.create()
self._configure_backend()
...
The put
method should insert a key into the stash.
It should return the id of the key whether as it is in the backend.
def put(self, key):
"""Insert the key and return its database id
"""
id = self.client.insert_key(key)
return id
The list
method should return a list of all key objects in the stash
def list(self):
"""Return a list of all keys (not just key names, but rather the keys
themselves).
e.g.
{u'created_at': u'2016-10-10 08:31:53',
u'description': None,
u'metadata': None,
u'modified_at': u'2016-10-10 08:31:53',
u'name': u'aws',
u'uid': u'459f12c0-f341-413e-9d7e-7410f912fb74',
u'value': u'the_value'},
{u'created_at': u'2016-10-10 08:32:29',
u'description': u'my gcp token',
u'metadata': {u'owner': u'nir'},
u'modified_at': u'2016-10-10 08:32:29',
u'name': u'gcp',
u'uid': u'a51a0043-f241-4d52-93c1-266a3c5de15e',
u'value': u'the_value'}]
"""
return self.client.list()
The get
method should return a single key
def get(self, key_name):
"""Return a dictionary consisting of the key itself
e.g.
{u'created_at': u'2016-10-10 08:31:53',
u'description': None,
u'metadata': None,
u'modified_at': u'2016-10-10 08:31:53',
u'name': u'aws',
u'uid': u'459f12c0-f341-413e-9d7e-7410f912fb74',
u'value': u'the_value'}
"""
key = self.client.get(key_name)
if not key:
return {}
return key
The delete
method should delete a key from the stash and return true if it was deleted or false if it wasn't
def delete(self, key_name):
"""Delete the key and return true if the key was deleted, else false
"""
self.client.delete(key_name)
key = self.get(key_name):
return key is {}
For your backend, you should add the default path to the global STORAGE_DEFAULT_PATH_MAPPING
:
{
'tinydb': os.path.join(GHOST_HOME, 'stash.json'),
...
'mystorage': https://localhost:1212,
}
For it to be available to the CLI, you should add a mapping to the global STORAGE_MAPPING
:
{
'tinydb': TinyDBStorage,
...
'mystorage': MyStorage,
}
Coverage is expected to be a 100% for every backend. The only part not required is the conditional import
part for its dependencies.