# Copyright (C) 2017-2018 Lars Wirzenius # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import base64 import glob import os import yaml import ick2 class ControllerState: def __init__(self): self._statedir = None def get_state_directory(self): return self._statedir def set_state_directory(self, dirname): self._statedir = dirname if not os.path.exists(self._statedir): os.makedirs(self._statedir) def get_resource_directory(self, type_name): return os.path.join(self.get_state_directory(), type_name) def get_resource_filename(self, type_name, resource_name): dirname = self.get_resource_directory(type_name) basename = base64.urlsafe_b64encode( resource_name.encode()).decode('ascii') return os.path.join(dirname, basename + '.yaml') def load_resources(self, type_name): assert self._statedir is not None resources = [] dirname = self.get_resource_directory(type_name) for filename in glob.glob(dirname + '/*.yaml'): obj = self.load_resource(filename) resources.append(obj) return resources def load_resource(self, filename): with open(filename, 'r') as f: return yaml.safe_load(f) def get_resources(self, type_name): return self.load_resources(type_name) def get_resource(self, type_name, resource_name): filename = self.get_resource_filename(type_name, resource_name) if os.path.exists(filename): result = self.load_resource(filename) ick2.log.log( 'debug', msg_text='get_resource called', type_name=type_name, resource_name=resource_name, filename=filename, result=result) return result ick2.log.log( 'warning', msg_text='Cannot find resource', type_name=type_name, resource_name=resource_name) raise NotFound() def add_resource(self, type_name, resource_name, resource): filename = self.get_resource_filename(type_name, resource_name) dirname = os.path.dirname(filename) if not os.path.exists(dirname): os.makedirs(dirname) with open(filename, 'w') as f: yaml.safe_dump(resource, stream=f) return resource def update_resource(self, type_name, resource_name, body): filename = self.get_resource_filename(type_name, resource_name) with open(filename, 'w') as f: yaml.safe_dump(body, stream=f) return body def remove_resource(self, type_name, resource_name): filename = self.get_resource_filename(type_name, resource_name) if not os.path.exists(filename): raise NotFound() os.remove(filename) class NotFound(Exception): def __init__(self): super().__init__('Resource not found') class WrongProjectStatus(Exception): # pragma: no cover def __init__(self, new_state): super().__init__('Cannot set project state to {}'.format(new_state)) class ResourceStore: # pragma: no cover def __init__(self, state, category, name_field): self._state = state self._category = category self._name_field = name_field def get_resource_name(self, name): return str(name) def list(self): return self._state.get_resources(self._category) def get(self, name): name = self.get_resource_name(name) try: return self._state.get_resource(self._category, name) except ick2.NotFound: return { self._name_field: name, } def add(self, name, resource): name = self.get_resource_name(name) self._state.add_resource(self._category, name, resource) def update(self, resource): name = self.get_resource_name(resource[self._name_field]) try: self._state.get_resource(self._category, name) except ick2.NotFound: raise ick2.NotFound() else: self._state.update_resource(self._category, name, resource) class Workers(ResourceStore): # pragma: no cover def __init__(self, state): super().__init__(state, 'workers', 'worker') def get_worker(self, name): return self.get(name) def update_worker(self, worker): self.update(worker) class Projects(ResourceStore): # pragma: no cover def __init__(self, state): super().__init__(state, 'projects', 'project') def get_projects(self): return self.list() def update_project(self, project): self.update(project) class Builds(ResourceStore): # pragma: no cover def __init__(self, state): super().__init__(state, 'builds', 'build_id') def get_builds(self): return self.list() def update_build(self, build): self.update(build) class ProjectStatus(ResourceStore): # pragma: no cover def __init__(self, state): super().__init__(state, 'project_status', 'project') def _name(self, project_name): return project_name def get_instance(self, project_name): name = self._name(project_name) return self.get(name) def add_instance(self, project_name, instance): name = self._name(project_name) self.add(name, instance) def update_instance(self, project_name, instance): name = self._name(project_name) self.add(name, instance)