Commit d0bb306d authored by Thomas's avatar Thomas

Added secrets domain

parent 09e7701f
...@@ -33,7 +33,7 @@ from strongr.cli import DeploySingleCommand, ListDeployedVmsCommand,\ ...@@ -33,7 +33,7 @@ from strongr.cli import DeploySingleCommand, ListDeployedVmsCommand,\
RunWorkerCommand, DestroySingleCommand,\ RunWorkerCommand, DestroySingleCommand,\
DestroyManyCommand, EnsureMinAmountOfNodesCommand, \ DestroyManyCommand, EnsureMinAmountOfNodesCommand, \
MakeDbCommand, RunTestsCommand, TestCommand, CleanupCommand,\ MakeDbCommand, RunTestsCommand, TestCommand, CleanupCommand,\
GetJobStdOut GetJobStdOut, GetSecret
application = Application() application = Application()
...@@ -56,6 +56,7 @@ application.add(RunTestsCommand()) ...@@ -56,6 +56,7 @@ application.add(RunTestsCommand())
application.add(TestCommand()) application.add(TestCommand())
application.add(CleanupCommand()) application.add(CleanupCommand())
application.add(GetJobStdOut()) application.add(GetJobStdOut())
application.add(GetSecret())
if __name__ == '__main__': if __name__ == '__main__':
application.run(input_=argvInputs) application.run(input_=argvInputs)
...@@ -16,3 +16,4 @@ from .runtestscommand import RunTestsCommand ...@@ -16,3 +16,4 @@ from .runtestscommand import RunTestsCommand
from .testcommand import TestCommand from .testcommand import TestCommand
from .cleanupcommand import CleanupCommand from .cleanupcommand import CleanupCommand
from .getjobstdout import GetJobStdOut from .getjobstdout import GetJobStdOut
from .getsecret import GetSecret
from strongr.core.domain.secretdomain import SecretDomain
from .wrapper import Command
class GetSecret(Command):
"""
Returns a jobs stdout
secret:get
{key : The key of the secret}
"""
def handle(self):
service = SecretDomain.secret_service()
query_bus = service.get_query_bus()
query_factory = SecretDomain.query_factory()
result = query_bus.handle(query_factory.new_get_secret(self.argument('key')))
print(result)
from strongr.core.gateways import Gateways from strongr.core.gateways import Gateways
from strongr.core.domain.schedulerdomain import SchedulerDomain from strongr.core.domain.schedulerdomain import SchedulerDomain
from strongr.core.domain.restdomain import RestDomain from strongr.core.domain.restdomain import RestDomain
from strongr.core.domain.secretdomain import SecretDomain
from .wrapper import Command from .wrapper import Command
...@@ -13,7 +14,8 @@ class MakeDbCommand(Command): ...@@ -13,7 +14,8 @@ class MakeDbCommand(Command):
def handle(self): def handle(self):
services = [ services = [
SchedulerDomain.schedulerService(), SchedulerDomain.schedulerService(),
RestDomain.oauth2Service() RestDomain.oauth2Service(),
SecretDomain.secret_service()
] ]
for service in services: for service in services:
......
...@@ -18,7 +18,7 @@ class DeployVmsHandler(AbstractDeployVmsHandler): ...@@ -18,7 +18,7 @@ class DeployVmsHandler(AbstractDeployVmsHandler):
client = salt.cloud.CloudClient(strongr.core.Core.config().clouddomain.OpenNebula.salt_config + '/cloud') client = salt.cloud.CloudClient(strongr.core.Core.config().clouddomain.OpenNebula.salt_config + '/cloud')
ret = [] ret = []
for chunked_names in self._chunk_list(command.names, 2): for chunked_names in self._chunk_list(command.names, 50):
for name in chunked_names: for name in chunked_names:
vmnew_event = strongr.clouddomain.model.gateways.Gateways.inter_domain_event_factory().newVmNewEvent(name, command.cores, command.ram) vmnew_event = strongr.clouddomain.model.gateways.Gateways.inter_domain_event_factory().newVmNewEvent(name, command.cores, command.ram)
strongr.core.Core.inter_domain_events_publisher().publish(vmnew_event) strongr.core.Core.inter_domain_events_publisher().publish(vmnew_event)
......
...@@ -5,6 +5,7 @@ import strongr.core ...@@ -5,6 +5,7 @@ import strongr.core
class LoadConfigHandler(): class LoadConfigHandler():
def __call__(self, command): def __call__(self, command):
config_dict = DefaultsLoader().getConfig() config_dict = DefaultsLoader().getConfig()
strongr.core.getCore().config.update(ConfigStruct(**config_dict))
loaders = { loaders = {
IniLoader.__name__: IniLoader(), IniLoader.__name__: IniLoader(),
......
...@@ -21,7 +21,8 @@ class DefaultsLoader: ...@@ -21,7 +21,8 @@ class DefaultsLoader:
}, },
'db': { 'db': {
'engine': { 'engine': {
'url': 'sqlite://' 'url': 'sqlite:////tmp/strongr.db',
'echo': True
} }
}, },
'stats': { 'stats': {
......
...@@ -11,6 +11,7 @@ import strongr.core ...@@ -11,6 +11,7 @@ import strongr.core
from strongr.core.middlewares.celery.celerymiddleware import CeleryMiddleware from strongr.core.middlewares.celery.celerymiddleware import CeleryMiddleware
from strongr.core.middlewares.logging.loggingmiddleware import LoggingMiddleware from strongr.core.middlewares.logging.loggingmiddleware import LoggingMiddleware
from strongr.core.middlewares.stats.statsmiddleware import StatsMiddleware from strongr.core.middlewares.stats.statsmiddleware import StatsMiddleware
from strongr.core.middlewares.transaction.transactionmiddleware import TransactionMiddleware
class AbstractService(): class AbstractService():
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
...@@ -37,6 +38,7 @@ class AbstractService(): ...@@ -37,6 +38,7 @@ class AbstractService():
def _default_middlewares(self, will_return_values, enable_stats=True): def _default_middlewares(self, will_return_values, enable_stats=True):
middlewares = [ middlewares = [
TransactionMiddleware(),
LoggingMiddleware(), LoggingMiddleware(),
CeleryMiddleware(will_return_values) CeleryMiddleware(will_return_values)
] ]
...@@ -45,9 +47,6 @@ class AbstractService(): ...@@ -45,9 +47,6 @@ class AbstractService():
import strongr.core.gateways import strongr.core.gateways
middlewares.append(StatsMiddleware(strongr.core.gateways.Gateways.stats())) middlewares.append(StatsMiddleware(strongr.core.gateways.Gateways.stats()))
import strongr.core.middlewares.transaction.transactionmiddleware
middlewares.append(strongr.core.middlewares.transaction.transactionmiddleware.TransactionMiddleware())
return middlewares return middlewares
def _make_default_querybus(self, mappings, middlewares=None, enable_stats=True): def _make_default_querybus(self, mappings, middlewares=None, enable_stats=True):
......
import dependency_injector.containers as containers
import dependency_injector.providers as providers
from strongr.secretsdomain.service import SecretService
import dependency_injector.providers as providers
from strongr.secretsdomain.service import SecretService
from strongr.secretsdomain.factory import CommandFactory, QueryFactory
import strongr.core.domain.clouddomain
class SecretDomain(containers.DeclarativeContainer):
"""IoC container of service providers."""
secret_service = providers.Singleton(SecretService)
command_factory = providers.Singleton(CommandFactory)
query_factory = providers.Singleton(QueryFactory)
from cmndr import Middleware from cmndr import Middleware
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
import strongr.core.gateways
import logging import logging
class TransactionMiddleware(Middleware): class TransactionMiddleware(Middleware):
def execute(self, command, next_callable): def execute(self, command, next_callable):
session = strongr.core.gateways.Gateways.sqlalchemy_session() import strongr.core
if hasattr(strongr.core.Core.config(), 'db') and hasattr(strongr.core.Core.config().db, 'engine'):
import strongr.core.gateways
try: session = strongr.core.gateways.Gateways.sqlalchemy_session()
ret = next_callable(command)
session.commit() try:
return ret ret = next_callable(command)
except SQLAlchemyError as e: session.commit()
session.rollback() return ret
logging.getLogger("Transaction Middleware").warning(e) except SQLAlchemyError as e:
session.rollback()
logging.getLogger("Transaction Middleware").warning(e)
#raise e
else:
# db not configured yet, ignore transactions for now
return next_callable(command)
from sqlalchemy import types, String
import uuid
class UUID(types.TypeDecorator):
impl = String()
def __init__(self, *args, **kwargs):
super(UUID, self).__init__(*args, **kwargs)
self.impl.length = 64
types.TypeDecorator.__init__(self,length=self.impl.length)
def process_bind_param(self,value,dialect=None):
if value and isinstance(value,uuid.UUID):
return value
elif value and not isinstance(value,uuid.UUID):
raise ValueError,'value %s is not a valid uuid.UUID' % value
else:
return None
def process_result_value(self,value,dialect=None):
if value:
return uuid.UUID(value)
else:
return None
def is_mutable(self):
return False
from sqlalchemy.types import TypeDecorator, CHAR
from sqlalchemy.dialects.postgresql import UUID as POSTGRESQL_UUID
from sqlalchemy.dialects.mysql import BINARY as MYSQL_BINARY
import uuid
# source: http://docs.sqlalchemy.org/en/latest/core/custom_types.html
class UUIDType(TypeDecorator):
"""Platform-independent GUID type.
Uses PostgreSQL's UUID type or mysql's binary type, otherwise uses
CHAR(32), storing as stringified hex values.
"""
impl = CHAR
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(POSTGRESQL_UUID())
elif dialect.name == 'mysql':
return dialect.type_descriptor(MYSQL_BINARY(16))
else:
return dialect.type_descriptor(CHAR(32))
def process_bind_param(self, value, dialect):
if value is None:
return value
elif dialect.name == 'postgresql':
return str(value)
elif dialect.name == 'mysql':
if not isinstance(value, uuid.UUID):
return uuid.UUID(value).bytes
else:
return value.bytes
else:
if not isinstance(value, uuid.UUID):
return "%.32x" % uuid.UUID(value).int
else:
# hexstring
return "%.32x" % value.int
def process_result_value(self, value, dialect):
if value is None:
return value
elif dialect.name == 'mysql':
if not isinstance(value, uuid.UUID):
value = uuid.UUID(bytes=value)
return value
else:
if not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value
...@@ -3,6 +3,7 @@ from flask_restplus import Api ...@@ -3,6 +3,7 @@ from flask_restplus import Api
from strongr.restdomain.api.v1.scheduler import ns as scheduler_namespace from strongr.restdomain.api.v1.scheduler import ns as scheduler_namespace
from strongr.restdomain.api.v1.oauth2 import ns as oauth2_namespace from strongr.restdomain.api.v1.oauth2 import ns as oauth2_namespace
from strongr.restdomain.api.v1.secrets import ns as secrets_namespace
blueprint = Blueprint('api_v1', __name__, url_prefix='/v1') blueprint = Blueprint('api_v1', __name__, url_prefix='/v1')
api = Api(blueprint, api = Api(blueprint,
...@@ -13,3 +14,4 @@ api = Api(blueprint, ...@@ -13,3 +14,4 @@ api = Api(blueprint,
api.add_namespace(scheduler_namespace) api.add_namespace(scheduler_namespace)
api.add_namespace(oauth2_namespace) api.add_namespace(oauth2_namespace)
api.add_namespace(secrets_namespace)
from flask_restplus import Namespace, Resource, fields
from flask import request, jsonify
import time
import uuid
from strongr.core.domain.secretdomain import SecretDomain
from strongr.restdomain.api.utils import namespace_require_oauth
ns = Namespace('secrets', description='Operations related to the secretsdomain')
@ns.route('/')
class ListSecrets(Resource):
def __init__(self, *args, **kwargs):
super(ListSecrets, self).__init__(*args, **kwargs)
@ns.response(200, '')
def get(self):
secret_service = SecretDomain.secret_service()
secret_query_factory = SecretDomain.query_factory()
secret_query_bus = secret_service.get_query_bus()
result = secret_query_bus.handle(secret_query_factory.new_list_secrets())
return result, 200
@ns.route('/<string:key>/<string:value>')
class PostSecret(Resource):
def __init__(self, *args, **kwargs):
super(PostSecret, self).__init__(*args, **kwargs)
@ns.response(201, 'Secret successfully created.')
def post(self, key, value):
secret_service = SecretDomain.secret_service()
secret_command_factory = SecretDomain.command_factory()
secret_command_bus = secret_service.get_command_bus()
secret_command_bus.handle(secret_command_factory.new_add_secret(key, value))
return {'key': key}, 200
@ns.route('/<string:key>')
class DeleteSecret(Resource):
def __init__(self, *args, **kwargs):
super(DeleteSecret, self).__init__(*args, **kwargs)
@ns.response(202, 'Secret successfully deleted.')
def delete(self, key):
secret_service = SecretDomain.secret_service()
secret_command_factory = SecretDomain.command_factory()
secret_command_bus = secret_service.get_command_bus()
secret_command_bus.handle(secret_command_factory.new_remove_secret(key))
return {'key': key}, 201
...@@ -5,7 +5,7 @@ from strongr.core import gateways ...@@ -5,7 +5,7 @@ from strongr.core import gateways
from sqlalchemy import Column, ForeignKey, Integer, String from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from strongr.core.sqlalchemydatatypes.uuid import UUID from strongr.core.sqlalchemydatatypes.uuidtype import UUIDType
Base = gateways.Gateways.sqlalchemy_base() Base = gateways.Gateways.sqlalchemy_base()
......
...@@ -5,7 +5,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String ...@@ -5,7 +5,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String
from authlib.flask.oauth2.sqla import OAuth2ClientMixin from authlib.flask.oauth2.sqla import OAuth2ClientMixin
from strongr.core.sqlalchemydatatypes.uuid import UUID from strongr.core.sqlalchemydatatypes.uuidtype import UUIDType
Base = gateways.Gateways.sqlalchemy_base() Base = gateways.Gateways.sqlalchemy_base()
......
...@@ -3,7 +3,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String ...@@ -3,7 +3,7 @@ from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from authlib.flask.oauth2.sqla import OAuth2TokenMixin from authlib.flask.oauth2.sqla import OAuth2TokenMixin
from strongr.core.sqlalchemydatatypes.uuid import UUID from strongr.core.sqlalchemydatatypes.uuidtype import UUIDType
Base = gateways.Gateways.sqlalchemy_base() Base = gateways.Gateways.sqlalchemy_base()
......
...@@ -5,7 +5,7 @@ from sqlalchemy.orm import synonym ...@@ -5,7 +5,7 @@ from sqlalchemy.orm import synonym
import strongr.core.gateways as gateways import strongr.core.gateways as gateways
from sqlalchemy import Column, String from sqlalchemy import Column, String
from strongr.core.sqlalchemydatatypes.uuid import UUID from strongr.core.sqlalchemydatatypes.uuidtype import UUIDType
import strongr.restdomain.model.gateways import strongr.restdomain.model.gateways
Base = gateways.Gateways.sqlalchemy_base() Base = gateways.Gateways.sqlalchemy_base()
......
from .addsecret import AddSecret
from .removesecret import RemoveSecret
class AddSecret():
def __init__(self, key, value):
self.key = key
self.value = value
class RemoveSecret():
def __init__(self, key):
self.key = key
from .commandfactory import CommandFactory
from .queryfactory import QueryFactory
from strongr.core.exception import InvalidParameterException
from strongr.secretsdomain.command import AddSecret, RemoveSecret
class CommandFactory:
def new_add_secret(self, key, value):
if len(key) == 0:
raise InvalidParameterException('key len must be > 0')
elif not isinstance(key, basestring):
raise InvalidParameterException('key must be string type')
if len(value) == 0:
raise InvalidParameterException('value len must be > 0')
elif not isinstance(value, basestring):
raise InvalidParameterException('value must be string type')
return AddSecret(key, value)
def new_remove_secret(self, key):
if len(key) == 0:
raise InvalidParameterException('key len must be > 0')
return RemoveSecret(key)
from strongr.secretsdomain.query import ListSecrets, GetSecret
class QueryFactory():
_list_secrets = None
def new_list_secrets(self):
# flyweight pattern
if self._list_secrets is None:
self._list_secrets = ListSecrets()
return self._list_secrets
def new_get_secret(self, key):
return GetSecret(key)
from .addsecrethandler import AddSecretHandler
from .getsecrethandler import GetSecretHandler
from .removesecrethandler import RemoveSecretHandler
from .listsecretshandler import ListSecretsHandler
from strongr.secretsdomain.model import Secret
import strongr.core.gateways
import uuid
class AddSecretHandler():
def __call__(self, command):
session = strongr.core.gateways.Gateways.sqlalchemy_session()
secret = session.query(Secret).filter(Secret.key == command.key).first()
if secret is None:
secret = Secret()
secret.key = command.key
secret.value = command.value
session.add(secret)
import strongr.core.gateways
from strongr.secretsdomain.model import Secret
import itertools
class GetSecretHandler():
def __call__(self, query):
session = strongr.core.gateways.Gateways.sqlalchemy_session()
result = session.query(Secret).filter(Secret.key == query.key).first()
if result is not None:
return result.value
return None
import strongr.core.gateways
from strongr.secretsdomain.model import Secret
import itertools
class ListSecretsHandler():
def __call__(self, query):
session = strongr.core.gateways.Gateways.sqlalchemy_session()
result = session.query(Secret.key).order_by(Secret.key).all()
return list(itertools.chain.from_iterable(result))
from strongr.secretsdomain.model import Secret
import strongr.core.gateways
class RemoveSecretHandler():
def __call__(self, command):
session = strongr.core.gateways.Gateways.sqlalchemy_session()
session.query(Secret).filter(Secret.key == command.key).delete()
import strongr.core.gateways as gateways
from sqlalchemy import Column, String
from strongr.core.sqlalchemydatatypes.uuidtype import UUIDType
import uuid
Base = gateways.Gateways.sqlalchemy_base()
class Secret(Base):
__tablename__ = 'secrets'
secret_id = Column(UUIDType, default=uuid.uuid4, primary_key=True)
key = Column(String(512), unique=True)
value = Column(String(2048))
from .getsecret import GetSecret
from .listsecrets import ListSecrets
class GetSecret():
def __init__(self, key):
self.key = key
from .secretservice import SecretService
from strongr.core.abstracts.abstractservice import AbstractService
from strongr.secretsdomain.command import AddSecret, RemoveSecret
from strongr.secretsdomain.handler import AddSecretHandler, RemoveSecretHandler
from strongr.secretsdomain.query import GetSecret, ListSecrets
from strongr.secretsdomain.handler import GetSecretHandler, ListSecretsHandler
class SecretService(AbstractService):
_command_bus = None
_query_bus = None
def register_models(self):
from strongr.secretsdomain.model import Secret
# importing alone is enough for registration
def get_command_bus(self):
if self._command_bus is None:
self._command_bus = self._make_default_commandbus({
AddSecretHandler: AddSecret,
RemoveSecretHandler: RemoveSecret
})
return self._command_bus
def get_query_bus(self):
if self._query_bus is None:
self._query_bus = self._make_default_querybus({
GetSecretHandler: GetSecret,
ListSecretsHandler: ListSecrets
})
return self._query_bus
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment