diff --git a/blockade/api/manager.py b/blockade/api/manager.py index 49ec9e7..1649416 100644 --- a/blockade/api/manager.py +++ b/blockade/api/manager.py @@ -14,59 +14,95 @@ # limitations under the License. # +from blockade.config import BlockadeConfig from blockade.core import Blockade +from blockade.errors import BlockadeNotFound from blockade.errors import InvalidBlockadeName from blockade.net import BlockadeNetwork from blockade.state import BlockadeState -# TODO(pdmars): breaks if server restarts, refactor to be part of BlockadeState -BLOCKADE_CONFIGS = {} +import os +import yaml + + DATA_DIR = "/tmp" +BASE_BLOCKADE_DIR = os.path.join(DATA_DIR, ".blockade") +REST_STATE_FILE = os.path.join(BASE_BLOCKADE_DIR, "rest_state.yaml") class BlockadeManager: - """Simple helper for what should eventually be persisted via BlockadeState + """Simple helper to persist Blockade configurations managed via REST API """ @staticmethod def set_data_dir(data_dir): global DATA_DIR DATA_DIR = data_dir + BASE_BLOCKADE_DIR = os.path.join(DATA_DIR, ".blockade") + REST_STATE_FILE = os.path.join(BASE_BLOCKADE_DIR, "rest_state.yaml") + + + @staticmethod + def init_base_blockade_dir(): + if not os.path.isdir(BASE_BLOCKADE_DIR): + os.makedirs(BASE_BLOCKADE_DIR) + + @staticmethod + def read_rest_state(): + BlockadeManager.init_base_blockade_dir() + rest_state = {} + if os.path.exists(REST_STATE_FILE): + with open(REST_STATE_FILE, "r") as f: + rest_state = yaml.safe_load(f) or {} + return rest_state + + @staticmethod + def write_rest_state(rest_state): + BlockadeManager.init_base_blockade_dir() + with open(REST_STATE_FILE, "w") as f: + yaml.safe_dump(rest_state, f) @staticmethod def blockade_exists(name): - global BLOCKADE_CONFIGS - return name in BLOCKADE_CONFIGS + rest_state = BlockadeManager.read_rest_state() + return name in rest_state @staticmethod def store_config(name, config): - global BLOCKADE_CONFIGS - BLOCKADE_CONFIGS[name] = config + rest_state = BlockadeManager.read_rest_state() + rest_state[name] = config + BlockadeManager.write_rest_state(rest_state) @staticmethod def delete_config(name): - global BLOCKADE_CONFIGS - if name in BLOCKADE_CONFIGS: - del BLOCKADE_CONFIGS[name] - - @staticmethod - def load_state(name): - global DATA_DIR - try: - return BlockadeState(blockade_id=name, data_dir=DATA_DIR) - except InvalidBlockadeName: - raise + rest_state = BlockadeManager.read_rest_state() + if name in rest_state: + del rest_state[name] + else: + raise BlockadeNotFound() + BlockadeManager.write_rest_state(rest_state) @staticmethod def get_blockade(name): - global BLOCKADE_CONFIGS - config = BLOCKADE_CONFIGS[name] + rest_state = BlockadeManager.read_rest_state() + blockade_state = BlockadeManager.load_blockade_state(name) + if name not in rest_state: + raise BlockadeNotFound() + config = BlockadeConfig.from_dict(rest_state[name]) return Blockade(config, blockade_id=name, - state=BlockadeManager.load_state(name), + state=blockade_state, network=BlockadeNetwork(config)) @staticmethod def get_all_blockade_names(): - global BLOCKADE_CONFIGS - return list(BLOCKADE_CONFIGS.keys()) + rest_state = BlockadeManager.read_rest_state() + return list(rest_state.keys()) + + @staticmethod + def load_blockade_state(name): + global DATA_DIR + try: + return BlockadeState(blockade_id=name, data_dir=DATA_DIR) + except InvalidBlockadeName: + raise diff --git a/blockade/api/rest.py b/blockade/api/rest.py index 33652f6..94d4256 100644 --- a/blockade/api/rest.py +++ b/blockade/api/rest.py @@ -19,6 +19,7 @@ from blockade.api.manager import BlockadeManager from blockade.config import BlockadeConfig +from blockade.errors import BlockadeNotFound from blockade.errors import DockerContainerNotFound from blockade.errors import InvalidBlockadeName @@ -44,8 +45,9 @@ def unsupported_media_type(error): @app.errorhandler(404) -def blockade_name_not_found(error): - return 'Blockade name not found', 404 +@app.errorhandler(BlockadeNotFound) +def blockade_not_found(error): + return 'Blockade not found', 404 @app.errorhandler(InvalidBlockadeName) @@ -78,7 +80,7 @@ def create(name): # This will abort with a 400 if the JSON is bad data = request.get_json() config = BlockadeConfig.from_dict(data) - BlockadeManager.store_config(name, config) + BlockadeManager.store_config(name, data) b = BlockadeManager.get_blockade(name) containers = b.create() diff --git a/blockade/errors.py b/blockade/errors.py index f9c8193..3073738 100644 --- a/blockade/errors.py +++ b/blockade/errors.py @@ -57,3 +57,7 @@ class InvalidBlockadeName(BlockadeError): class DockerContainerNotFound(BlockadeError): """Docker container not found """ + +class BlockadeNotFound(BlockadeError): + """Blockade not found + """