Example #1
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}")
Example #2
0
    def initialize(self):
        """Actually construct all the various component pieces"""

        self.scheduler = self._setup_scheduler()

        load_plugin_log_config()

        plugin_config = config.get("plugin")
        self.helper_threads = [
            HelperThread(
                StatusMonitor,
                timeout_seconds=plugin_config.status_timeout,
                heartbeat_interval=plugin_config.status_heartbeat,
            )
        ]

        # Only want to run the MongoPruner if it would do anything
        tasks, run_every = db.prune_tasks(**config.get("db.ttl"))
        if run_every:
            self.helper_threads.append(
                HelperThread(
                    db.get_pruner(), tasks=tasks, run_every=timedelta(minutes=run_every)
                )
            )

        metrics_config = config.get("metrics")
        if metrics_config.prometheus.enabled:
            self.helper_threads.append(
                HelperThread(
                    PrometheusServer,
                    metrics_config.prometheus.host,
                    metrics_config.prometheus.port,
                )
            )

        beer_garden.router.forward_processor = QueueListener(
            action=beer_garden.router.forward, name="forwarder"
        )

        self.mp_manager = self._setup_multiprocessing_manager()

        beer_garden.local_plugins.manager.lpm_proxy = self.mp_manager.PluginManager()

        self.entry_manager = beer_garden.api.entry_point.Manager()

        beer_garden.events.manager = self._setup_events_manager()

        file_event = Event(name=Events.PLUGIN_LOGGER_FILE_CHANGE.name)
        self.plugin_log_config_observer = MonitorFile(
            path=config.get("plugin.logging.config_file"),
            create_event=file_event,
            modify_event=file_event,
        )
        self.plugin_local_log_config_observer = MonitorFile(
            path=config.get("plugin.local.logging.config_file"),
            create_event=file_event,
            modify_event=file_event,
        )
Example #3
0
def run(ep_conn):
    conn_manager = StompManager(ep_conn)

    _setup_event_handling(conn_manager)
    _setup_operation_forwarding()

    entry_config = config.get("entry.stomp")
    parent_config = config.get("parent.stomp")
    garden_name = config.get("garden.name")

    if entry_config.get("enabled"):
        conn_manager.add_connection(stomp_config=entry_config,
                                    name=f"{garden_name}_entry",
                                    is_main=True)

    if parent_config.get("enabled"):
        conn_manager.add_connection(stomp_config=parent_config,
                                    name=f"{garden_name}_parent",
                                    is_main=True)

    for garden in get_gardens(include_local=False):
        if garden.name != garden_name and garden.connection_type:
            if garden.connection_type.casefold() == "stomp":
                connection_params = garden.connection_params.get("stomp", {})
                connection_params["send_destination"] = None
                conn_manager.add_connection(stomp_config=connection_params,
                                            name=garden.name)

    conn_manager.start()

    logger.info("Stomp entry point started")

    publish(
        Event(name=Events.ENTRY_STARTED.name,
              metadata={"entry_point_type": "STOMP"}))

    while not shutdown_event.wait(10):
        for name, info in conn_manager.conn_dict.items():
            connection = info.get("conn")

            if connection:
                logger.debug(f"{name}: Checking connection")
                if not connection.is_connected():
                    logger.debug(f"{name}: Attempting to reconnect")

                    if connection.connect():
                        logger.debug(f"{name}: Reconnect successful")
                    else:
                        logger.debug(f"{name}: Reconnect failed")

    conn_manager.shutdown()
    conn_manager.stop()
    conn_manager.join(5)

    logger.debug("Stopping forward processing")
    beer_garden.router.forward_processor.stop()
Example #4
0
def handle_event(event):
    # Whenever a request is completed check to see if this process is waiting for it
    if event.name == Events.REQUEST_COMPLETED.name:
        completion_event = request_map.pop(event.payload.id, None)
        if completion_event:
            completion_event.set()

    # Only care about local garden
    if event.garden == config.get("garden.name"):

        if event.name == Events.GARDEN_STOPPED.name:
            # When shutting down we need to close all handing connections/threads
            # waiting for a response. This will invoke each connection/thread to be
            # returned the current status of the Request.
            for request_event in request_map:
                request_map[request_event].set()

    # Only care about downstream garden
    elif event.garden != config.get("garden.name"):
        if event.name in (
                Events.REQUEST_CREATED.name,
                Events.REQUEST_STARTED.name,
                Events.REQUEST_COMPLETED.name,
        ):
            # When we send child requests to child gardens where the parent was on
            # the local garden we remove the parent before sending them. Only setting
            # the subset of fields that change "corrects" the parent
            existing_request = db.query_unique(Request, id=event.payload.id)

            if existing_request:
                for field in ("status", "output", "error_class"):
                    setattr(existing_request, field,
                            getattr(event.payload, field))

                try:
                    db.update(existing_request)
                except RequestStatusTransitionError:
                    pass
            else:
                # Attempt to create the request, if it already exists then continue on
                try:
                    db.create(event.payload)
                except NotUniqueException:
                    pass

    # Required if the main process spawns a wait Request
    if event.name == Events.REQUEST_COMPLETED.name:
        if str(event.payload.id) in request_map:
            request_map[str(event.payload.id)].set()
Example #5
0
def ensure_users():
    """Create the default admin user if necessary"""
    if User.objects.count() == 0:
        username = config.get("auth.default_admin.username")
        password = config.get("auth.default_admin.password")
        superuser_role = Role.objects.get(name="superuser")

        logger.info("Creating default admin user with username: %s", username)

        admin = User(username=username)
        admin.set_password(password)
        admin.role_assignments = [
            RoleAssignment(role=superuser_role, domain={"scope": "Global"})
        ]
        admin.save()
Example #6
0
def _setup_ssl_context() -> Tuple[Optional[ssl.SSLContext], Optional[ssl.SSLContext]]:
    http_config = config.get("entry.http")
    if http_config.ssl.enabled:
        server_ssl = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        server_ssl.load_cert_chain(
            certfile=http_config.ssl.public_key, keyfile=http_config.ssl.private_key
        )
        server_ssl.verify_mode = getattr(
            ssl, "CERT_" + http_config.ssl.client_cert_verify.upper()
        )

        client_ssl = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
        client_ssl.load_cert_chain(
            certfile=http_config.ssl.public_key, keyfile=http_config.ssl.private_key
        )

        if http_config.ssl.ca_cert or http_config.ssl.ca_path:
            server_ssl.load_verify_locations(
                cafile=http_config.ssl.ca_cert, capath=http_config.ssl.ca_path
            )
            client_ssl.load_verify_locations(
                cafile=http_config.ssl.ca_cert, capath=http_config.ssl.ca_path
            )
    else:
        server_ssl = None
        client_ssl = None

    return server_ssl, client_ssl
Example #7
0
def default() -> str:
    """Get the default namespace for this Garden

    Returns:
        The default namespace
    """
    return config.get("garden.name")
Example #8
0
def _pre_route(operation: Operation) -> Operation:
    """Called before any routing logic is applied"""
    # If no source garden is defined set it to the local garden
    if operation.source_garden_name is None:
        operation.source_garden_name = config.get("garden.name")

    if operation.operation_type == "REQUEST_CREATE":
        if operation.model.namespace is None:
            operation.model.namespace = config.get("garden.name")

    elif operation.operation_type == "SYSTEM_READ_ALL":
        if operation.kwargs.get("filter_params", {}).get("namespace") == "":
            operation.kwargs["filter_params"]["namespace"] = config.get(
                "garden.name")

    return operation
Example #9
0
    def clean(self):
        """Validate before saving to the database"""

        if self.status not in BrewtilsRequest.STATUS_LIST:
            raise ModelValidationError(
                f"Can not save Request {self}: Invalid status '{self.status}'")

        if (self.command_type is not None
                and self.command_type not in BrewtilsRequest.COMMAND_TYPES):
            raise ModelValidationError(
                f"Can not save Request {self}: Invalid command type"
                f" '{self.command_type}'")

        if (self.output_type is not None
                and self.output_type not in BrewtilsRequest.OUTPUT_TYPES):
            raise ModelValidationError(
                f"Can not save Request {self}: Invalid output type '{self.output_type}'"
            )

        # Deal with has_parent
        if self.has_parent is None:
            self.has_parent = bool(self.parent)
        elif self.has_parent != bool(self.parent):
            raise ModelValidationError(
                f"Cannot save Request {self}: parent value of {self.parent!r} is not "
                f"consistent with has_parent value of {self.has_parent}")

        if (self.namespace == config.get("garden.name")) and (
                "status" in self.changed_fields or self.created):
            self.status_updated_at = datetime.datetime.utcnow()
Example #10
0
def _setup_application():
    """Setup things that can be taken care of before io loop is started"""
    global io_loop, tornado_app, server, client_ssl

    io_loop = IOLoop.current()

    # Set up motor connection
    moto.create_connection(db_config=beer_garden.config.get("db"))

    auth_config = config.get("auth")
    if not auth_config.token_secret:
        auth_config.token_secret = os.urandom(20)
        if auth_config.enabled:
            logger.warning(
                "Brew-view was started with authentication enabled and no "
                "Secret. Generated tokens will not be valid across Brew-view "
                "restarts. To prevent this set the auth.token.secret config.")

    # This is only used for publishing events for external consumption (in v2 request
    # events were published with a link to the request, for example).
    # Commenting this out as it's not useful at the moment
    # from urllib3.util.url import Url
    #
    # http_config = config.get("entry.http")
    # public_url = Url(
    #     scheme="https" if http_config.ssl.enabled else "http",
    #     host=http_config.public_fqdn,
    #     port=http_config.port,
    #     path=http_config.url_prefix,
    # ).url

    tornado_app = _setup_tornado_app()
    server_ssl, client_ssl = _setup_ssl_context()

    server = HTTPServer(tornado_app, ssl_options=server_ssl)
Example #11
0
def _sync_roles_from_role_definition_file():
    """If auth.role_definition_file is set in the main application config, this will
    load that file and pass it over to sync_roles to make the Role definitions in the
    database match what is defined in the file.
    """
    role_definition_file: Union[str,
                                None] = config.get("auth.role_definition_file")

    if role_definition_file:
        logger.info(f"Syncing role definitions from {role_definition_file}")

        try:
            with open(role_definition_file, "r") as filestream:
                role_definitions = yaml.safe_load(filestream)
                sync_roles(role_definitions)
        except FileNotFoundError:
            raise ConfigurationError(
                f"Role definition file {role_definition_file} not found.")
        except SchemaValidationError:
            raise ConfigurationError(
                f"Error processing role definition file {role_definition_file}."
            )
        except ValidationError as validation_error:
            raise ConfigurationError(
                f"Invalid role definition in {role_definition_file}: {validation_error}"
            )
    else:
        logger.info(
            "auth.role_definition_file not defined. No roles will be synced.")
Example #12
0
def handle_event(event):
    # Only care about local garden
    if event.garden == config.get("garden.name"):
        if event.name == Events.INSTANCE_INITIALIZED.name:
            lpm_proxy.handle_initialize(event)
        elif event.name == Events.INSTANCE_STOPPED.name:
            lpm_proxy.handle_stopped(event)
Example #13
0
def setup_routing():
    """Initialize the routing subsystem

    This will load the cached child garden definitions and use them to populate the
    two dictionaries that matter, garden_lookup and garden_connections.

    It will then query the database for all local systems and add those to the
    dictionaries as well.
    """
    for system in db.query(System, filter_params={"local": True}):
        add_routing_system(system)

    # Don't add the local garden
    for garden in get_gardens(include_local=False):
        if garden.name != config.get("garden.name"):
            for system in garden.systems:
                add_routing_system(system=system, garden_name=garden.name)

            if (garden.connection_type is not None
                    and garden.connection_type.casefold() != "local"):
                with garden_lock:
                    gardens[garden.name] = garden
                    if garden.connection_type.casefold() == "stomp":
                        if garden.name not in stomp_garden_connections:
                            stomp_garden_connections[
                                garden.name] = create_stomp_connection(garden)

            else:
                logger.warning(
                    f"Garden with invalid connection info: {garden!r}")
Example #14
0
def route(operation: Operation):
    """Entry point into the routing subsystem

    Args:
        operation: The operation to route

    Returns:

    """
    operation = _pre_route(operation)

    logger.debug(f"Routing {operation!r}")

    if not operation.operation_type:
        raise RoutingRequestException("Missing operation type")

    if operation.operation_type not in route_functions.keys():
        raise RoutingRequestException(
            f"Unknown operation type '{operation.operation_type}'")

    # Determine which garden the operation is targeting
    if not operation.target_garden_name:
        operation.target_garden_name = _determine_target_garden(operation)

    if not operation.target_garden_name:
        raise UnknownGardenException(
            f"Could not determine the target garden for routing {operation!r}")

    # If it's targeted at THIS garden, execute
    if operation.target_garden_name == config.get("garden.name"):
        return execute_local(operation)
    else:
        return initiate_forward(operation)
Example #15
0
def _pre_forward(operation: Operation) -> Operation:
    """Called before forwarding an operation"""

    # Validate that the operation can be forwarded
    if operation.operation_type not in routable_operations:
        raise RoutingRequestException(
            f"Operation type '{operation.operation_type}' can not be forwarded"
        )

    if operation.operation_type == "REQUEST_CREATE":
        # Save the request so it'll have an ID and we'll have something to update
        local_request = create_request(operation.model)

        if operation.model.namespace == config.get("garden.name"):
            operation.model = local_request
        else:
            # When the target is a remote garden, just capture the id. We don't
            # want to replace the entire model, as we'd lose the base64 encoded file
            # parameter data.
            operation.model.id = local_request.id

        # Clear parent before forwarding so the child doesn't freak out about an
        # unknown request
        operation.model.parent = None
        operation.model.has_parent = False

        # Pull out and store the wait event, if it exists
        wait_event = operation.kwargs.pop("wait_event", None)
        if wait_event:
            beer_garden.requests.request_map[operation.model.id] = wait_event

    return operation
Example #16
0
def handle_event(event):
    """Handle events"""
    if event.name in (Events.SYSTEM_CREATED.name, Events.SYSTEM_UPDATED.name):
        add_routing_system(system=event.payload, garden_name=event.garden)
    elif event.name == Events.SYSTEM_REMOVED.name:
        remove_routing_system(system=event.payload)

    # Here we want to handle sync events from immediate children only
    if (not event.error and (event.name == Events.GARDEN_SYNC.name)
            and (event.garden != config.get("garden.name"))
            and (event.garden == event.payload.name)):
        with routing_lock:
            # First remove all current routes to this garden
            remove_routing_garden(garden_name=event.garden)

            # Then add routes to the new systems
            for system in event.payload.systems:
                add_routing_system(system=system,
                                   garden_name=event.payload.name)

    # This is a little unintuitive. We want to let the garden module deal with handling
    # any downstream garden changes since handling those changes is nontrivial.
    # It's *those* events we want to act on here, not the "raw" downstream ones.
    # This is also why we only handle GARDEN_UPDATED and not STARTED or STOPPED
    if event.garden == config.get("garden.name") and not event.error:
        if event.name == Events.GARDEN_UPDATED.name:
            gardens[event.payload.name] = event.payload

            if event.payload.connection_type:
                if event.payload.connection_type.casefold() == "stomp":
                    if (event.payload.name in stomp_garden_connections
                            and stomp_garden_connections[
                                event.payload.name].is_connected()):
                        stomp_garden_connections[
                            event.payload.name].disconnect()
                    stomp_garden_connections[
                        event.payload.name] = create_stomp_connection(
                            event.payload)

        elif event.name == Events.GARDEN_REMOVED.name:
            try:
                del gardens[event.payload.name]
                if event.payload.name in stomp_garden_connections:
                    stomp_garden_connections[event.payload.name].disconnect()
                    del stomp_garden_connections[event.payload.name]
            except KeyError:
                pass
Example #17
0
    def _startup(self):
        """Initializes core requirements for Application"""
        self.logger.debug("Starting Application...")

        self.logger.debug("Starting event manager...")
        beer_garden.events.manager.start()

        self.logger.debug("Setting up database...")
        db.create_connection(db_config=config.get("db"))
        db.initial_setup(config.get("auth.guest_login_enabled"))

        self.logger.debug("Setting up message queues...")
        queue.initial_setup()

        self.logger.debug("Starting helper threads...")
        for helper_thread in self.helper_threads:
            helper_thread.start()

        self.logger.debug("Setting up garden routing...")
        beer_garden.router.setup_routing()

        self.logger.debug("Starting forwarding processor...")
        beer_garden.router.forward_processor.start()

        self.logger.debug("Creating and starting entry points...")
        self.entry_manager.create_all()
        self.entry_manager.start()

        self.logger.debug("Starting local plugin process monitoring...")
        beer_garden.local_plugins.manager.lpm_proxy.start()

        self.logger.debug("Starting scheduler")
        self.scheduler.start()

        self.logger.debug("Publishing startup sync")
        beer_garden.garden.publish_garden()

        self.logger.debug("Starting plugin log config file monitors")
        if config.get("plugin.logging.config_file"):
            self.plugin_log_config_observer.start()
        if config.get("plugin.local.logging.config_file"):
            self.plugin_local_log_config_observer.start()

        self.logger.debug("Loading jobs from database")
        self.scheduler.initialize_from_db()

        self.logger.info("All set! Let me know if you need anything else!")
Example #18
0
    async def get(self):
        """Subset of configuration options that the frontend needs"""
        auth_config = config.get("auth")
        ui_config = config.get("ui")

        configs = {
            "application_name": ui_config.name,
            "auth_enabled": auth_config.enabled,
            "icon_default": ui_config.icon_default,
            "debug_mode": ui_config.debug_mode,
            "execute_javascript": ui_config.execute_javascript,
            "garden_name": config.get("garden.name"),
            "guest_login_enabled": auth_config.guest_login_enabled,
            "metrics_url": config.get("metrics.prometheus.url"),
            "url_prefix": config.get("entry.http.url_prefix"),
        }

        self.write(configs)
Example #19
0
    def set_default_headers(self):
        """Headers set here will be applied to all responses"""
        self.set_header("BG-Version", beer_garden.__version__)

        if config.get("ui.cors_enabled"):
            self.set_header("Access-Control-Allow-Origin", "*")
            self.set_header("Access-Control-Allow-Headers", "Content-Type")
            self.set_header("Access-Control-Allow-Methods",
                            "GET, POST, PATCH, DELETE, OPTIONS")
Example #20
0
    def stop_one(
            self,
            runner_id: Optional[str] = None,
            instance_id: Optional[str] = None,
            send_sigterm: bool = True,
            remove: bool = False,  # noqa
    ) -> Runner:
        """Stop the runner for a given Runner ID or Instance ID.

        The PluginManager has no ability to places messages on the message queue, so
        it's possible that a stop message will already have been sent to the plugin
        that's being asked to stop. If that's NOT the case then send_sigterm should be
        set to True to attempt to stop the runner gracefully.

        This will wait for the runner to stop for plugin.local.timeout.shutdown seconds.
        If the runner is not stopped after that time its process will be killed with
        SIGKILL.

        Args:
            runner_id: The runner ID to stop, optional.
            instance_id: The instance ID to stop, optional.
            send_sigterm: If true, send SIGTERM before waiting. Defaults to ``True``.
            remove: Flag controlling if the runner should be removed from runner list.
                Defaults to ``False``.

        Returns:
            The stopped runner.
        """
        the_runner = None

        if runner_id is not None:
            the_runner = self._from_runner_id(runner_id)
        elif instance_id is not None:
            the_runner = self._from_instance_id(instance_id)

        if the_runner is None:
            raise Exception(
                f"Could not determine runner using runner ID ({runner_id}) and "
                f"instance ID ({instance_id})")

        if send_sigterm:
            the_runner.term()

        the_runner.join(config.get("plugin.local.timeout.shutdown"))

        if the_runner.is_alive():
            the_runner.dead = True
            the_runner.kill()

        the_runner.stopped = True
        the_runner.restart = False

        if remove:
            self._runners.remove(the_runner)

        return the_runner.state()
Example #21
0
    def _environment(
        self,
        plugin_config: Dict[str, Any],
        instance_name: str,
        plugin_path: Path,
        runner_id: str,
    ) -> Dict[str, str]:
        env = {}

        # System info comes from config file
        for key in _SYSTEM_SPEC:
            key = key.upper()

            if key in plugin_config:
                env["BG_" + key] = plugin_config.get(key)

        env.update(
            {
                # Connection info comes from Beer-garden config
                "BG_HOST": self._connection_info.host,
                "BG_PORT": self._connection_info.port,
                "BG_URL_PREFIX": self._connection_info.url_prefix,
                "BG_SSL_ENABLED": self._connection_info.ssl.enabled,
                "BG_CA_CERT": self._connection_info.ssl.ca_cert,
                "BG_CA_VERIFY": False,  # TODO - Fix this
                # The rest
                "BG_INSTANCE_NAME": instance_name,
                "BG_RUNNER_ID": runner_id,
                "BG_PLUGIN_PATH": plugin_path.resolve(),
                "BG_USERNAME": self._username,
                "BG_PASSWORD": self._password,
            }
        )

        if "LOG_LEVEL" in plugin_config:
            env["BG_LOG_LEVEL"] = plugin_config["LOG_LEVEL"]

        # Ensure values are all strings
        for key, value in env.items():
            env[key] = json.dumps(value) if isinstance(value, dict) else str(value)

        # Allowed host env vars
        for env_var in config.get("plugin.local.host_env_vars"):
            if env_var in env:
                logger.warning(
                    f"Skipping host environment variable {env_var} for runner at "
                    f"{plugin_path} as it's already set in the process environment"
                )
            else:
                env[env_var] = os.environ.get(env_var, default="")

        # ENVIRONMENT from beer.conf
        for key, value in plugin_config.get("ENVIRONMENT", {}).items():
            env[key] = expand_string(str(value), env)

        return env
Example #22
0
    def _setup_events_manager(self):
        """Set up the event manager for the Main Processor"""
        event_manager = FanoutProcessor(name="event manager")

        # Forward all events down into the entry points
        event_manager.register(self.entry_manager, manage=False)

        # Register the callback processor
        event_manager.register(QueueListener(action=garden_callbacks, name="callbacks"))

        # Set up parent connection
        cfg = config.get("parent.http")
        if cfg.enabled:

            def reconnect_action():
                beer_garden.garden.publish_garden(status="RUNNING")

            easy_client = EasyClient(
                bg_host=cfg.host,
                bg_port=cfg.port,
                bg_url_prefix=cfg.url_prefix,
                access_token=cfg.access_token,
                api_version=cfg.api_version,
                client_timeout=cfg.client_timeout,
                password=cfg.password,
                refresh_token=cfg.password,
                username=cfg.username,
                ssl_enabled=cfg.ssl.enabled,
                ca_cert=cfg.ssl.ca_cert,
                ca_verify=cfg.ssl.ca_verify,
                client_cert=cfg.ssl.client_cert,
                client_key=cfg.ssl.client_key,
            )

            event_manager.register(
                HttpParentUpdater(
                    easy_client=easy_client,
                    black_list=config.get("parent.skip_events"),
                    reconnect_action=reconnect_action,
                )
            )

        return event_manager
Example #23
0
    def test_ensure_local_garden_updates_garden_from_config(self, monkeypatch):
        """ensure_local_garden should update the name of an existing Garden entry in the
        database with a connection type of LOCAL"""
        monkeypatch.setattr(config, "get", self.config_get)

        Garden(name="thisshouldchange", connection_type="LOCAL").save()
        ensure_local_garden()
        garden = Garden.objects.get(connection_type="LOCAL")

        assert garden.name == config.get("garden.name")
Example #24
0
def handle_event(event):
    """Handle events"""
    if event.name in (Events.SYSTEM_CREATED.name, Events.SYSTEM_UPDATED.name):
        add_routing_system(system=event.payload, garden_name=event.garden)
    elif event.name == Events.SYSTEM_REMOVED.name:
        remove_routing_system(system=event.payload)

    # Handle downstream events
    if event.garden != config.get("garden.name"):
        if event.name == Events.GARDEN_SYNC.name:
            # TODO - Do we also need to remove systems here?
            for system in event.payload.systems:
                add_routing_system(system=system,
                                   garden_name=event.payload.name)

    # This is a little unintuitive. We want to let the garden module deal with handling
    # any downstream garden changes since handling those changes is nontrivial.
    # It's *those* events we want to act on here, not the "raw" downstream ones.
    # This is also why we only handle GARDEN_UPDATED and not STARTED or STOPPED
    if event.garden == config.get("garden.name"):
        if event.name == Events.GARDEN_UPDATED.name:
            gardens[event.payload.name] = event.payload

            if event.payload.connection_type:
                if event.payload.connection_type.casefold() == "stomp":
                    if (event.payload.name in stomp_garden_connections
                            and stomp_garden_connections[
                                event.payload.name].is_connected()):
                        stomp_garden_connections[
                            event.payload.name].disconnect()
                    stomp_garden_connections[
                        event.payload.name] = create_stomp_connection(
                            event.payload)

        elif event.name == Events.GARDEN_REMOVED.name:
            try:
                del gardens[event.payload.name]
                if event.payload.name in stomp_garden_connections:
                    stomp_garden_connections[event.payload.name].disconnect()
                    del stomp_garden_connections[event.payload.name]
            except KeyError:
                pass
Example #25
0
    def test_ensure_local_garden_creates_new_garden_from_config(
            self, monkeypatch):
        """ensure_local_garden should create a Garden entry in the database with
        name derived from the "garden.name" config setting and a connection type of
        LOCAL"""
        monkeypatch.setattr(config, "get", self.config_get)

        ensure_local_garden()
        garden = Garden.objects.get(connection_type="LOCAL")

        assert garden.name == config.get("garden.name")
Example #26
0
    def __init__(self):
        handler_config = cast(
            Box, config.get("auth.authentication_handlers.trusted_header"))
        self.username_header = handler_config.get("username_header")
        self.user_groups_header = handler_config.get("user_groups_header")
        self.create_users = handler_config.get("create_users")
        self.group_definition_file = cast(
            str, config.get("auth.group_definition_file"))
        self.group_mapping = {}

        if self.group_definition_file:
            try:
                self.group_mapping = self._load_group_mapping(
                    self.group_definition_file)
            except ConfigurationError as exc:
                logger.error("Error loading group definitions: %s", exc)
        else:
            logger.error(
                "No group_definition_file defined. Users will not be assigned to any "
                "groups. To fix this, set the 'auth.group_definition_file' "
                "configuration parameter and restart beer garden.")
Example #27
0
    def post(self):
        """
        ---
        summary: Use credentials to generate access and refresh tokens
        responses:
          200:
            description: All Tokens
            schema:
              type: array
              items:
                $ref: '#/definitions/Command'
          50x:
            $ref: '#/definitions/50xError'
        tags:
          - Tokens
        """
        parsed_body = json.loads(self.request.decoded_body)

        try:
            principal = Principal.objects.get(username=parsed_body["username"])
            if (config.get("auth.guest_login_enabled") and principal.username
                    == beer_garden.api.http.anonymous_principal.username):
                verified = True
            else:
                verified = yield self.executor.submit(
                    verify, str(parsed_body["password"]), str(principal.hash))

            if verified:
                tokens = generate_tokens(principal, self.REFRESH_COOKIE_EXP)

                # This is a semi-done solution. To really do this, we cannot give them
                # a token, instead we should return an error, indicating they need to
                # update their password, and then login again. In the short term, this
                # will be enough. This is really meant only to work for our UI so
                # backwards compatibility is not a concern.
                if principal.metadata.get(
                        "auto_change"
                ) and not principal.metadata.get("changed"):
                    self.set_header("change_password_required", "true")

                if parsed_body.get("remember_me", False):
                    self.set_secure_cookie(
                        self.REFRESH_COOKIE_NAME,
                        tokens["refresh"],
                        expires_days=self.REFRESH_COOKIE_EXP,
                    )
                self.write(json.dumps(tokens))
                return
        except DoesNotExist:
            # Still attempt to verify something so the request takes a while
            custom_app_context.verify("", None)

        raise HTTPError(status_code=403, log_message="Bad credentials")
Example #28
0
    def _verify_db_connection(self):
        """Verify that that the application can connect to a database

        Returns:
            True: the verification was successful
            False: the app was stopped before a connection could be verified
        """
        self.logger.debug("Verifying database connection...")
        return self._progressive_backoff(
            partial(db.check_connection, config.get("db")),
            "Unable to connect to database, is it started?",
        )
Example #29
0
    def _setup_multiprocessing_manager():
        BaseManager.register(
            "PluginManager",
            callable=partial(
                PluginManager,
                plugin_dir=config.get("plugin.local.directory"),
                log_dir=config.get("plugin.local.log_directory"),
                connection_info=config.get("entry.http"),
                username=config.get("plugin.local.auth.username"),
                password=config.get("plugin.local.auth.password"),
            ),
        )

        def initializer():
            signal.signal(signal.SIGINT, signal.SIG_IGN)
            signal.signal(signal.SIGTERM, signal.SIG_IGN)

        data_manager = BaseManager()
        data_manager.start(initializer=initializer)

        return data_manager
Example #30
0
    def test_ensure_roles_creates_roles_defined_in_file(
            self, role_definition_yaml, config_mock_value):
        """ensure_roles should create the roles defined in the auth.role_definition_file
        if specified"""
        role_definition_file = config.get("auth.role_definition_file")

        with patch(
                "builtins.open",
                mock_open(read_data=role_definition_yaml)) as mock_file_read:
            ensure_roles()
            mock_file_read.assert_called_with(role_definition_file, "r")

        assert len(Role.objects.filter(name="testrole1")) == 1