예제 #1
0
def _from_kwargs(
    system: System = None,
    instance: Instance = None,
    system_id: str = None,
    instance_name: str = None,
    instance_id: str = None,
    **_,
) -> Tuple[System, Instance]:

    if system and instance:
        return system, instance

    if not system:
        if system_id:
            system = db.query_unique(System, raise_missing=True, id=system_id)
        elif instance:
            system = db.query_unique(System,
                                     raise_missing=True,
                                     instances__contains=instance)
        elif instance_id:
            system = db.query_unique(System,
                                     raise_missing=True,
                                     instances__id=instance_id)
        else:
            raise NotFoundException("Unable to find System")

    if not instance:
        if instance_name:
            instance = system.get_instance_by_name(instance_name)
        elif instance_id:
            instance = system.get_instance_by_id(instance_id)
        else:
            raise NotFoundException("Unable to find Instance")

    return system, instance
예제 #2
0
def _determine_target_garden(operation: Operation) -> str:
    """Determine the system the operation is targeting"""

    # Certain operations are ASSUMED to be targeted at the local garden
    if ("READ" in operation.operation_type or "JOB" in operation.operation_type
            or "FILE" in operation.operation_type or operation.operation_type
            in ("PLUGIN_LOG_RELOAD", "SYSTEM_CREATE", "SYSTEM_RESCAN")
            or "PUBLISH_EVENT" in operation.operation_type
            or "RUNNER" in operation.operation_type or operation.operation_type
            in ("PLUGIN_LOG_RELOAD", "SYSTEM_CREATE")):
        return config.get("garden.name")

    # Otherwise, each operation needs to be "parsed"
    if operation.operation_type in ("SYSTEM_RELOAD", "SYSTEM_UPDATE"):
        return _system_id_lookup(operation.args[0])

    if operation.operation_type == "SYSTEM_DELETE":
        # Force deletes get routed to local garden
        if operation.kwargs.get("force"):
            return config.get("garden.name")

        return _system_id_lookup(operation.args[0])

    if "INSTANCE" in operation.operation_type:
        if "system_id" in operation.kwargs and "instance_name" in operation.kwargs:
            return _system_id_lookup(operation.kwargs["system_id"])
        else:
            return _instance_id_lookup(operation.args[0])

    if operation.operation_type == "REQUEST_CREATE":
        target_system = System(
            namespace=operation.model.namespace,
            name=operation.model.system,
            version=operation.model.system_version,
        )
        return _system_name_lookup(target_system)

    if operation.operation_type.startswith("REQUEST"):
        request = db.query_unique(Request, id=operation.args[0])
        operation.kwargs["request"] = request

        return config.get("garden.name")

    if "GARDEN" in operation.operation_type:
        if operation.operation_type == "GARDEN_SYNC":
            sync_target = operation.kwargs.get("sync_target")
            if sync_target:
                return sync_target

        return config.get("garden.name")

    if operation.operation_type == "QUEUE_DELETE":
        # Need to deconstruct the queue name
        parts = operation.args[0].split(".")
        version = parts[2].replace("-", ".")

        return _system_name_lookup(
            System(namespace=parts[0], name=parts[1], version=version))

    raise Exception(f"Bad operation type {operation.operation_type}")
예제 #3
0
 def default_system(self, command1):
     return System(
         name="foo",
         version="1.0.0",
         instances=[Instance(name="foo")],
         commands=[command1],
     )
예제 #4
0
    def test_system_exists(self, plugin, bm_client, bg_system, bg_instance,
                           current_commands):
        bg_system.commands = [Command("test")]
        bm_client.update_system.return_value = bg_system

        existing_system = System(
            id="id",
            name="test_system",
            version="0.0.1",
            instances=[bg_instance],
            commands=current_commands,
            metadata={"foo": "bar"},
        )
        bm_client.find_unique_system.return_value = existing_system

        plugin._initialize()
        bm_client.initialize_instance.assert_called_once_with(bg_instance.id)
        bm_client.update_system.assert_called_once_with(
            existing_system.id,
            new_commands=bg_system.commands,
            metadata=bg_system.metadata,
            description=bg_system.description,
            icon_name=bg_system.icon_name,
            display_name=bg_system.display_name,
        )
        assert bm_client.create_system.called is False
        assert bm_client.create_system.return_value == plugin.system
        assert bm_client.initialize_instance.return_value == plugin.instance
예제 #5
0
def heartbeat(
    instance_id: str = None,
    instance: Instance = None,
    system: System = None,
    **_,
) -> Instance:
    """Instance heartbeat

    Args:
        instance_id: The Instance ID
        instance: The Instance
        system: The System

    Returns:
        The updated Instance
    """
    system, instance = _from_kwargs(system=system,
                                    instance=instance,
                                    instance_id=instance_id)

    system = db.modify(
        system,
        query={"instances__name": instance.name},
        set__instances__S__status_info__heartbeat=datetime.utcnow(),
    )

    return system.get_instance_by_name(instance.name)
예제 #6
0
    def _setup_system(self, client, inst_name, system, name, description, version, icon_name,
                      metadata, display_name, max_instances):
        if system:
            if name or description or version or icon_name or display_name or max_instances:
                raise ValidationError("Sorry, you can't specify a system as well as system "
                                      "creation helper keywords (name, description, version, "
                                      "max_instances, display_name, and icon_name)")

            if not system.instances:
                raise ValidationError("Explicit system definition requires explicit instance "
                                      "definition (use instances=[Instance(name='default')] "
                                      "for default behavior)")

            if not system.max_instances:
                system.max_instances = len(system.instances)

        else:
            name = name or os.environ.get('BG_NAME', None)
            version = version or os.environ.get('BG_VERSION', None)

            if client.__doc__ and not description:
                description = self.client.__doc__.split("\n")[0]

            system = System(name=name, description=description, version=version,
                            icon_name=icon_name, commands=client._commands,
                            max_instances=max_instances or 1,
                            instances=[Instance(name=inst_name)],
                            metadata=metadata, display_name=display_name)

        return system
예제 #7
0
def bg_system_2(system_dict, bg_instance, bg_command, bg_command_2):
    """A system with a different version."""
    dict_copy = copy.deepcopy(system_dict)
    dict_copy["version"] = "2.0.0"
    dict_copy["instances"] = [bg_instance]
    dict_copy["commands"] = [bg_command, bg_command_2]
    return System(**dict_copy)
예제 #8
0
    def test_new_instance(self, plugin, ez_client, bg_system, bg_instance):
        existing_system = System(
            id="id",
            name="test_system",
            version="0.0.1",
            instances=[bg_instance],
            max_instances=2,
            metadata={"foo": "bar"},
            template=None,
        )
        ez_client.find_unique_system.return_value = existing_system

        new_name = "foo_instance"
        plugin._config.instance_name = new_name

        plugin._initialize_system()
        assert ez_client.create_system.called is False
        ez_client.update_system.assert_called_once_with(
            existing_system.id,
            new_commands=bg_system.commands,
            metadata=bg_system.metadata,
            description=bg_system.description,
            icon_name=bg_system.icon_name,
            display_name=bg_system.display_name,
            template="<html>template</html>",
            add_instance=ANY,
        )
        assert ez_client.update_system.call_args[1][
            "add_instance"].name == new_name
예제 #9
0
    def test_system_exists(self, plugin, ez_client, bg_system, bg_instance,
                           current_commands):
        existing_system = System(
            id="id",
            name="test_system",
            version="0.0.1",
            instances=[bg_instance],
            commands=current_commands,
            metadata={"foo": "bar"},
            template=None,
        )
        ez_client.find_unique_system.return_value = existing_system

        bg_system.commands = [Command("test")]
        ez_client.update_system.return_value = bg_system

        plugin._initialize_system()
        assert ez_client.create_system.called is False
        ez_client.update_system.assert_called_once_with(
            existing_system.id,
            new_commands=bg_system.commands,
            metadata=bg_system.metadata,
            description=bg_system.description,
            icon_name=bg_system.icon_name,
            display_name=bg_system.display_name,
            template="<html>template</html>",
        )
예제 #10
0
    def test_remove_system(self, find_mock, remove_mock):
        find_mock.return_value = System(id='id')
        remove_mock.return_value = 'delete_response'

        self.assertEqual('delete_response',
                         self.client.remove_system(search='search params'))
        find_mock.assert_called_once_with(search='search params')
        remove_mock.assert_called_once_with('id')
예제 #11
0
    def test_remove_system(self, find_mock, remove_mock):
        find_mock.return_value = System(id="id")
        remove_mock.return_value = "delete_response"

        self.assertEqual("delete_response",
                         self.client.remove_system(search="search params"))
        find_mock.assert_called_once_with(search="search params")
        remove_mock.assert_called_once_with("id")
예제 #12
0
 def test_max_instances(self, plugin):
     system = System(
         name="name",
         version="1.0.0",
         instances=[Instance(name="1"),
                    Instance(name="2")],
     )
     new_system = plugin._setup_system(system, {})
     assert new_system.max_instances == 2
예제 #13
0
 def test_max_instances(self, plugin, client):
     system = System(
         name="name",
         version="1.0.0",
         instances=[Instance(name="1"),
                    Instance(name="2")],
     )
     new_system = plugin._setup_system(client, "default", system, "", "",
                                       "", "", {}, None, None)
     assert new_system.max_instances == 2
예제 #14
0
    def test_initialize_system_new_instance(self):
        self.plugin.instance_name = 'new_instance'

        existing_system = System(id='id', name='test_system', version='1.0.0',
                                 instances=[self.instance], max_instances=2,
                                 metadata={'foo': 'bar'})
        self.bm_client_mock.find_unique_system.return_value = existing_system

        self.plugin._initialize()
        self.assertTrue(self.bm_client_mock.create_system.called)
        self.assertTrue(self.bm_client_mock.update_system.called)
예제 #15
0
def update(
    instance_id: str = None,
    instance: Instance = None,
    system: System = None,
    new_status: str = None,
    metadata: dict = None,
    update_heartbeat: bool = True,
    **_,
) -> Instance:
    """Update an Instance status.

    Will also update the status_info heartbeat.

    Args:
        instance_id: The Instance ID
        instance: The Instance
        system: The System
        new_status: The new status
        metadata: New metadata
        update_heartbeat: Set the heartbeat to the current time

    Returns:
        The updated Instance
    """
    system, instance = _from_kwargs(system=system,
                                    instance=instance,
                                    instance_id=instance_id)

    logger.debug(f"Updating instance {system}[{instance}]")

    updates = {}

    if new_status:
        updates["set__instances__S__status"] = new_status

        if new_status == "STOPPED":
            lpm.update(instance_id=instance_id, restart=False, stopped=True)

    if update_heartbeat:
        updates["set__instances__S__status_info__heartbeat"] = datetime.utcnow(
        )

    if metadata:
        metadata_update = dict(instance.metadata)
        metadata_update.update(metadata)
        updates["set__instances__S__metadata"] = metadata_update

    system = db.modify(system,
                       query={"instances__name": instance.name},
                       **updates)

    return system.get_instance_by_name(instance.name)
예제 #16
0
    def _setup_system(self, system, plugin_kwargs):
        helper_keywords = {
            "name",
            "version",
            "description",
            "icon_name",
            "display_name",
            "max_instances",
            "metadata",
            "namespace",
            "template",
        }

        if system:
            if helper_keywords.intersection(plugin_kwargs.keys()):
                raise ValidationError(
                    "Sorry, you can't provide a complete system definition as well as "
                    "system creation helper kwargs %s" % helper_keywords
                )

            if not system.instances:
                raise ValidationError(
                    "Explicit system definition requires explicit instance "
                    "definition (use instances=[Instance(name='default')] for "
                    "default behavior)"
                )

            if not system.max_instances:
                system.max_instances = len(system.instances)

        else:
            # Commands are not defined here - they're set in the client property setter
            system = System(
                name=self._config.name,
                version=self._config.version,
                description=self._config.description,
                namespace=self._config.namespace,
                metadata=json.loads(self._config.metadata),
                instances=[Instance(name=self._config.instance_name)],
                max_instances=self._config.max_instances,
                icon_name=self._config.icon_name,
                display_name=self._config.display_name,
                template=self._config.template,
            )

        return system
예제 #17
0
    def setUp(self):
        self.app = brew_view.app.test_client()

        self.default_instance = Instance(name='default', status='RUNNING')
        self.default_command = Command(id='54ac18f778c4b57e963f3c18',
                                       name='command',
                                       description='foo')
        self.default_system = System(id='54ac18f778c4b57e963f3c18',
                                     name='default_system',
                                     version='1.0.0',
                                     instances=[self.default_instance],
                                     commands=[self.default_command])

        self.client_mock = Mock(name='client_mock')
        self.fake_context = MagicMock(
            __enter__=Mock(return_value=self.client_mock),
            __exit__=Mock(return_value=False))
예제 #18
0
    def test_new_instance(self, plugin, bm_client, bg_system, bg_instance):
        plugin.instance_name = "new_instance"

        existing_system = System(
            id="id",
            name="test_system",
            version="0.0.1",
            instances=[bg_instance],
            max_instances=2,
            metadata={"foo": "bar"},
        )
        bm_client.find_unique_system.return_value = existing_system

        plugin._initialize()
        assert 2 == len(existing_system.instances)
        assert bm_client.create_system.called is True
        assert bm_client.update_system.called is True
예제 #19
0
    def test_existing_multiple(self, tmp_path, loader, registry, plugin_1, bg_instance):
        """This is mainly to test that Instance IDs are correct

        We save a system with 2 instances:
         - instance1, 58542eb571afd47ead90beef
         - instance2, 58542eb571afd47ead90beee

        Then we load a plugin that defines instances [instance2, instance3].

        Correct behavior is:
         - instance1 removed from the database
         - instance3 created in the database
         - instance2 remains in the database, and the ID remains the same
        """

        instance1 = Instance(name="instance1", id="58542eb571afd47ead90beef")
        instance2 = Instance(name="instance2", id="58542eb571afd47ead90beee")
        create_system(
            System(name="foo", version="1.0", instances=[instance1, instance2])
        )

        plugin = tmp_path / "plugin"
        plugin.mkdir()

        write_file(
            plugin,
            textwrap.dedent(
                """
                NAME='foo'
                VERSION='1.0'
                PLUGIN_ENTRY='entry.py'
                INSTANCES=["instance2", "instance3"]
            """
            ),
        )

        plugin_runners = loader.load_plugin(plugin)
        assert len(plugin_runners) == 2

        assert db.query_unique(Instance, name="instance1") is None
        assert db.query_unique(Instance, name="instance3") is not None

        instance2_db = db.query_unique(Instance, name="instance2")
        assert instance2_db is not None
        assert instance2_db.id == instance2.id
예제 #20
0
    def test_existing(self, loader, registry, plugin_1):
        system_id = "58542eb571afd47ead90face"
        instance_id = "58542eb571afd47ead90beef"
        create_system(
            System(
                id=system_id,
                name="foo",
                version="1.0",
                instances=[Instance(id=instance_id)],
            )
        )

        plugin_runners = loader.load_plugin(plugin_1)

        assert len(plugin_runners) == 1
        assert str(plugin_runners[0].system.id) == system_id
        assert str(plugin_runners[0].instance.id) == instance_id
        assert plugin_runners[0].name == "foo[default]-1.0"
        assert plugin_runners[0].entry_point == "entry.py"
예제 #21
0
    def test_initialize_system_update_metadata(self):
        self.system.commands = [Command('test')]
        self.bm_client_mock.update_system.return_value = self.system

        existing_system = System(id='id', name='test_system', version='1.0.0',
                                 instances=[self.instance],
                                 metadata={})
        self.bm_client_mock.find_unique_system.return_value = existing_system

        self.plugin._initialize()
        self.assertFalse(self.bm_client_mock.create_system.called)
        self.bm_client_mock.update_system.assert_called_once_with(self.instance.id,
                                                                  new_commands=self.system.commands,
                                                                  description=None,
                                                                  display_name=None,
                                                                  icon_name=None,
                                                                  metadata={"foo": "bar"})
        self.bm_client_mock.initialize_instance.assert_called_once_with(self.instance.id)
        self.assertEqual(self.plugin.system, self.bm_client_mock.create_system.return_value)
        self.assertEqual(self.plugin.instance, self.bm_client_mock.initialize_instance.return_value)
예제 #22
0
    def setUp(self):
        self.app = brew_view.app.test_client()

        self.default_instance = Instance(name="default", status="RUNNING")
        self.default_command = Command(
            id="54ac18f778c4b57e963f3c18", name="command", description="foo"
        )
        self.default_system = System(
            id="54ac18f778c4b57e963f3c18",
            name="default_system",
            version="1.0.0",
            instances=[self.default_instance],
            commands=[self.default_command],
        )

        self.client_mock = Mock(name="client_mock")
        self.fake_context = MagicMock(
            __enter__=Mock(return_value=self.client_mock),
            __exit__=Mock(return_value=False),
        )
예제 #23
0
def initialize(
    instance_id: str = None,
    instance: Instance = None,
    system: System = None,
    runner_id: str = None,
    **_,
) -> Instance:
    """Initializes an instance.

    Args:
        instance_id: The Instance ID
        instance: The Instance
        system: The System
        runner_id: The runner id to associate with this plugin, if any

    Returns:
        The updated Instance
    """
    system, instance = _from_kwargs(system=system,
                                    instance=instance,
                                    instance_id=instance_id)

    logger.debug(f"Initializing instance {system}[{instance}]")

    queue_spec = queue.create(instance, system)

    system = db.modify(
        system,
        query={"instances__name": instance.name},
        **{
            "set__instances__S__status": "INITIALIZING",
            "set__instances__S__status_info__heartbeat": datetime.utcnow(),
            "set__instances__S__metadata__runner_id": runner_id,
            "set__instances__S__queue_type": queue_spec["queue_type"],
            "set__instances__S__queue_info": queue_spec["queue_info"],
        },
    )

    start(instance=instance, system=system)

    return system.get_instance_by_name(instance.name)
예제 #24
0
def create_system(system: System) -> System:
    """Create a new System

    Args:
        system: The System to create

    Returns:
        The created System

    """
    if system.namespace is None:
        system.namespace = config.get("garden.name")

    # Create in the database
    system = db.create(system)

    # Also need to let the routing module know
    from beer_garden.router import add_routing_system

    add_routing_system(system=system)

    return system
예제 #25
0
    def setUp(self):
        self.safe_copy = os.environ.copy()

        consumer_patcher = patch('brewtils.plugin.RequestConsumer')
        self.addCleanup(consumer_patcher.stop)
        self.consumer_patch = consumer_patcher.start()

        self.instance = Instance(id='id', name='default', queue_type='rabbitmq',
                                 queue_info={'url': 'url', 'admin': {'name': 'admin_queue'},
                                             'request': {'name': 'request_queue'}})
        self.system = System(name='test_system', version='1.0.0', instances=[self.instance],
                             metadata={'foo': 'bar'})
        self.client = MagicMock(name='client', spec='command', _commands=['command'])
        self.plugin = PluginBase(self.client, bg_host='localhost', system=self.system,
                                 metadata={'foo': 'bar'})
        self.plugin.instance = self.instance

        self.bm_client_mock = Mock(create_system=Mock(return_value=self.system),
                                   initialize_instance=Mock(return_value=self.instance))
        self.plugin.bm_client = self.bm_client_mock

        self.parser_mock = Mock()
        self.plugin.parser = self.parser_mock
예제 #26
0
 def test_no_instances(self, plugin, client):
     system = System(name="name", version="1.0.0")
     with pytest.raises(ValidationError,
                        match="explicit instance definition"):
         plugin._setup_system(client, "default", system, "", "", "", "", {},
                              None, None)
예제 #27
0
 def test_setup_system_no_max_instances(self):
     system = System(name='test_system', version='1.0.0', instances=[Instance(name='1'),
                                                                     Instance(name='2')])
     new_system = self.plugin._setup_system(self.client, 'default', system, '', '', '', '', {},
                                            None, None)
     self.assertEqual(2, new_system.max_instances)
예제 #28
0
 def test_setup_system_no_instances(self):
     system = System(name='test_system', version='1.0.0')
     self.assertRaises(ValidationError, self.plugin._setup_system, self.client,
                       'default', system,
                       '', '', '', '', {}, None, None)
예제 #29
0
def bg_system(system_dict, bg_instance, bg_command):
    """A system as a model."""
    dict_copy = copy.deepcopy(system_dict)
    dict_copy['instances'] = [bg_instance]
    dict_copy['commands'] = [bg_command]
    return System(**dict_copy)
예제 #30
0
def bg_command(command_dict, bg_parameter):
    """Use the bg_command fixture instead."""
    dict_copy = copy.deepcopy(command_dict)
    dict_copy['parameters'] = [bg_parameter]
    dict_copy['system'] = System(id=system_id)
    return Command(**dict_copy)