Ejemplo n.º 1
0
    def parse_system(cls, system, from_string=False, **kwargs):
        """Convert raw JSON string or dictionary to a system model object

        :param system: The raw input
        :param from_string: True if input is a JSON string, False if a dictionary
        :param kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
        :return: A System object
        """
        return cls._do_parse(system, SystemSchema(**kwargs), from_string=from_string)
Ejemplo n.º 2
0
    def serialize_system(cls, system, to_string=True, include_commands=True, **kwargs):
        """Convert a system model into serialized form

        :param system: The system object(s) to be serialized
        :param to_string: True to generate a JSON-formatted string, False to generate a dictionary
        :param include_commands: True if the system's command list should be included
        :param kwargs: Additional parameters to be passed to the Schema (e.g. many=True)
        :return: Serialized representation of system
        """
        if not include_commands:
            if 'exclude' in kwargs:
                kwargs['exclude'] += ('commands', )
            else:
                kwargs['exclude'] = ('commands', )

        return cls._do_serialize(SystemSchema(**kwargs), system, to_string)
Ejemplo n.º 3
0
class SystemListAPI(BaseHandler):

    parser = BeerGardenSchemaParser()
    logger = logging.getLogger(__name__)

    REQUEST_FIELDS = set(SystemSchema.get_attribute_names())

    # Need to ensure that Systems are updated atomically
    system_lock = Lock()

    @authenticated(permissions=[Permissions.SYSTEM_READ])
    def get(self):
        """
        ---
        summary: Retrieve all Systems
        description: |
          This endpoint allows for querying Systems.

          There are several parameters that control which fields are returned
          and what information is available. Things to be aware of:

          * The `include_commands` parameter is __deprecated__. Don't use it.
            Use `exclude_fields=commands` instead.

          * It's possible to specify `include_fields` _and_ `exclude_fields`.
            This doesn't make a lot of sense, but you can do it. If the same
            field is in both `exclude_fields` takes priority (the field will
            NOT be included in the response).

          Systems matching specific criteria can be filtered using additional
          query parameters. This is a very basic capability:

          * ?name=foo&version=1.0.0
            This will return the system named 'foo' with version '1.0.0'
          * ?name=foo&name=bar
            This will not do what you expect: only return the system named
            'bar' will be returned.
        parameters:
          - name: include_fields
            in: query
            required: false
            description: Specify fields to include in the response. All other
              fields will be excluded.
            type: array
            collectionFormat: csv
            items:
              type: string
          - name: exclude_fields
            in: query
            required: false
            description: Specify fields to exclude from the response
            type: array
            collectionFormat: csv
            items:
              type: string
          - name: dereference_nested
            in: query
            required: false
            description: Commands and instances will be an object id
            type: boolean
            default: true
          - name: include_commands
            in: query
            required: false
            description: __DEPRECATED__ Include commands in the response.
              Use `exclude_fields=commands` instead.
            type: boolean
            default: true
        responses:
          200:
            description: All Systems
            schema:
              type: array
              items:
                $ref: '#/definitions/System'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Systems
        """
        query_set = System.objects.order_by(
            self.request.headers.get('order_by', 'name'))
        serialize_params = {'to_string': True, 'many': True}

        include_fields = self.get_query_argument('include_fields', None)
        exclude_fields = self.get_query_argument('exclude_fields', None)
        dereference_nested = self.get_query_argument('dereference_nested',
                                                     None)
        include_commands = self.get_query_argument('include_commands', None)

        if include_fields:
            include_fields = set(
                include_fields.split(',')) & self.REQUEST_FIELDS
            query_set = query_set.only(*include_fields)
            serialize_params['only'] = include_fields

        if exclude_fields:
            exclude_fields = set(
                exclude_fields.split(',')) & self.REQUEST_FIELDS
            query_set = query_set.exclude(*exclude_fields)
            serialize_params['exclude'] = exclude_fields

        if include_commands and include_commands.lower() == 'false':
            query_set = query_set.exclude('commands')

            if 'exclude' not in serialize_params:
                serialize_params['exclude'] = set()
            serialize_params['exclude'].add('commands')

        if dereference_nested and dereference_nested.lower() == 'false':
            query_set = query_set.no_dereference()

        # TODO - Handle multiple query arguments with the same key
        # for example: (?name=foo&name=bar) ... what should that mean?
        filter_params = {}

        # Need to use self.request.query_arguments to get all the query args
        for key in self.request.query_arguments:
            if key in self.REQUEST_FIELDS:
                # Now use get_query_argument to get the decoded value
                filter_params[key] = self.get_query_argument(key)

        result_set = query_set.filter(**filter_params)

        self.set_header('Content-Type', 'application/json; charset=UTF-8')
        self.write(self.parser.serialize_system(result_set,
                                                **serialize_params))

    @coroutine
    @authenticated(permissions=[Permissions.SYSTEM_CREATE])
    def post(self):
        """
        ---
        summary: Create a new System or update an existing System
        description: If the System does not exist it will be created. If the System already exists
            it will be updated (assuming it passes validation).
        parameters:
          - name: system
            in: body
            description: The System definition to create / update
            schema:
              $ref: '#/definitions/System'
        responses:
          200:
            description: An existing System has been updated
            schema:
              $ref: '#/definitions/System'
          201:
            description: A new System has been created
            schema:
              $ref: '#/definitions/System'
          400:
            $ref: '#/definitions/400Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Systems
        """
        self.request.event.name = Events.SYSTEM_CREATED.name

        system_model = self.parser.parse_system(self.request.decoded_body,
                                                from_string=True)

        with (yield self.system_lock.acquire()):
            # See if we already have a system with this name + version
            existing_system = System.find_unique(system_model.name,
                                                 system_model.version)

            if not existing_system:
                self.logger.debug("Creating a new system: %s" %
                                  system_model.name)
                saved_system, status_code = self._create_new_system(
                    system_model)
            else:
                self.logger.debug("System %s already exists. Updating it." %
                                  system_model.name)
                self.request.event.name = Events.SYSTEM_UPDATED.name
                saved_system, status_code = self._update_existing_system(
                    existing_system, system_model)

            saved_system.deep_save()

        self.request.event_extras = {'system': saved_system}

        self.set_status(status_code)
        self.write(
            self.parser.serialize_system(saved_system,
                                         to_string=False,
                                         include_commands=True))

    @staticmethod
    def _create_new_system(system_model):
        new_system = system_model

        # Assign a default 'main' instance if there aren't any instances and there can only be one
        if not new_system.instances or len(new_system.instances) == 0:
            if new_system.max_instances is None or new_system.max_instances == 1:
                new_system.instances = [Instance(name='default')]
                new_system.max_instances = 1
            else:
                raise ModelValidationError(
                    'Could not create system %s-%s: Systems with '
                    'max_instances > 1 must also define their instances' %
                    (system_model.name, system_model.version))
        else:
            if not new_system.max_instances:
                new_system.max_instances = len(new_system.instances)

        return new_system, 201

    @staticmethod
    def _update_existing_system(existing_system, system_model):
        # Raise an exception if commands already exist for this system and they differ from what's
        # already in the database in a significant way
        if existing_system.commands and 'dev' not in existing_system.version and \
                existing_system.has_different_commands(system_model.commands):
            raise ModelValidationError(
                'System %s-%s already exists with different commands' %
                (system_model.name, system_model.version))
        else:
            existing_system.upsert_commands(system_model.commands)

        # Update instances
        if not system_model.instances or len(system_model.instances) == 0:
            system_model.instances = [Instance(name='default')]

        for attr in ['description', 'icon_name', 'display_name']:
            value = getattr(system_model, attr)

            # If we set an attribute on the model as None, mongoengine marks the attribute for
            # deletion. We want to prevent this, so we set it to an emtpy string.
            if value is None:
                setattr(existing_system, attr, "")
            else:
                setattr(existing_system, attr, value)

        # Update metadata
        new_metadata = system_model.metadata or {}
        existing_system.metadata.update(new_metadata)

        old_instances = [
            inst for inst in existing_system.instances
            if system_model.has_instance(inst.name)
        ]
        new_instances = [
            inst for inst in system_model.instances
            if not existing_system.has_instance(inst.name)
        ]
        existing_system.instances = old_instances + new_instances

        return existing_system, 200
Ejemplo n.º 4
0
class SystemListAPI(AuthorizationHandler):
    REQUEST_FIELDS = set(SystemSchema.get_attribute_names())

    async def get(self):
        """
        ---
        summary: Retrieve all Systems
        description: |
          This endpoint allows for querying Systems.

          There are several parameters that control which fields are returned
          and what information is available. Things to be aware of:

          * The `include_commands` parameter is __deprecated__. Don't use it.
            Use `exclude_fields=commands` instead.

          * It's possible to specify `include_fields` _and_ `exclude_fields`.
            This doesn't make a lot of sense, but you can do it. If the same
            field is in both `exclude_fields` takes priority (the field will
            NOT be included in the response).

          Systems matching specific criteria can be filtered using additional
          query parameters. This is a very basic capability:

          * ?name=foo&version=1.0.0
            This will return the system named 'foo' with version '1.0.0'
          * ?name=foo&name=bar
            This will not do what you expect: only return the system named
            'bar' will be returned.
        parameters:
          - name: include_fields
            in: query
            required: false
            description: Specify fields to include in the response. All other
              fields will be excluded.
            type: array
            collectionFormat: csv
            items:
              type: string
          - name: exclude_fields
            in: query
            required: false
            description: Specify fields to exclude from the response
            type: array
            collectionFormat: csv
            items:
              type: string
          - name: dereference_nested
            in: query
            required: false
            description: Commands and instances will be an object id
            type: boolean
            default: true
        responses:
          200:
            description: All Systems
            schema:
              type: array
              items:
                $ref: '#/definitions/System'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Systems
        """
        permitted_objects_filter = self.permitted_objects_filter(
            System, SYSTEM_READ)

        order_by = self.get_query_argument("order_by", None)

        dereference_nested = self.get_query_argument("dereference_nested",
                                                     None)
        if dereference_nested is None:
            dereference_nested = True
        else:
            dereference_nested = bool(dereference_nested.lower() == "true")

        include_fields = self.get_query_argument("include_fields", None)
        if include_fields:
            include_fields = set(
                include_fields.split(",")) & self.REQUEST_FIELDS

        exclude_fields = self.get_query_argument("exclude_fields", None)
        if exclude_fields:
            exclude_fields = set(
                exclude_fields.split(",")) & self.REQUEST_FIELDS

        # TODO - Handle multiple query arguments with the same key
        # for example: (?name=foo&name=bar) ... what should that mean?
        # Need to use self.request.query_arguments to get all the query args
        filter_params = {}
        for key in self.request.query_arguments:
            if key in self.REQUEST_FIELDS:
                filter_params[key] = self.get_query_argument(key)

        serialize_kwargs = {"to_string": True, "many": True}
        if include_fields:
            serialize_kwargs["only"] = include_fields
        if exclude_fields:
            serialize_kwargs["exclude"] = exclude_fields

        response = await self.client(
            Operation(
                operation_type="SYSTEM_READ_ALL",
                kwargs={
                    "serialize_kwargs": serialize_kwargs,
                    "q_filter": permitted_objects_filter,
                    "filter_params": filter_params,
                    "order_by": order_by,
                    "include_fields": include_fields,
                    "exclude_fields": exclude_fields,
                    "dereference_nested": dereference_nested,
                },
            ))

        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)

    async def post(self):
        """
        ---
        summary: Create a new System or update an existing System
        description: |
            If the System does not exist it will be created. If the System
            already exists it will be updated (assuming it passes validation).
        parameters:
          - name: system
            in: body
            description: The System definition to create / update
            schema:
              $ref: '#/definitions/System'
        responses:
          200:
            description: An existing System has been updated
            schema:
              $ref: '#/definitions/System'
          201:
            description: A new System has been created
            schema:
              $ref: '#/definitions/System'
          400:
            $ref: '#/definitions/400Error'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Systems
        """
        system = SchemaParser.parse_system(self.request.decoded_body,
                                           from_string=True)

        self.verify_user_permission_for_object(SYSTEM_CREATE, system)

        response = await self.client(
            Operation(
                operation_type="SYSTEM_CREATE",
                args=[system],
            ))
        self.set_status(201)
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.write(response)
Ejemplo n.º 5
0
 def test_get_attributes(self):
     attributes = SystemSchema.get_attribute_names()
     assert "id" in attributes
     assert "name" in attributes
     assert "__model__" not in attributes
Ejemplo n.º 6
0
 def test_make_object_with_model(self):
     schema = SystemSchema(context={"models": {"SystemSchema": System}})
     value = schema.make_object({"name": "name"})
     assert isinstance(value, System)
Ejemplo n.º 7
0
    ModelValidationError,
    PluginError,
    RequestPublishException,
)
from brewtils.models import Command, Event, Events, Instance, System
from brewtils.schemas import SystemSchema

import beer_garden.config as config
import beer_garden.db.api as db
import beer_garden.local_plugins.manager as lpm
import beer_garden.queue.api as queue
from beer_garden.errors import NotFoundException, NotUniqueException
from beer_garden.events import publish_event
from beer_garden.plugin import publish_stop

REQUEST_FIELDS = set(SystemSchema.get_attribute_names())

logger = logging.getLogger(__name__)


def get_system(system_id: str) -> System:
    """Retrieve an individual System

    Args:
        system_id: The System ID

    Returns:
        The System

    """
    return db.query_unique(System, id=system_id)
Ejemplo n.º 8
0
 def test_get_attributes(self):
     attributes = SystemSchema.get_attribute_names()
     assert 'id' in attributes
     assert 'name' in attributes
     assert '__model__' not in attributes
Ejemplo n.º 9
0
 def test_make_object_with_model(self):
     schema = SystemSchema(context={'models': {'SystemSchema': System}})
     value = schema.make_object({'name': 'name'})
     assert isinstance(value, System)