Exemple #1
0
    async def _backend_factory(populated=True, config={}, event_bus=None):
        config = BackendConfig(
            **{
                "administration_token": "s3cr3t",
                "db_drop_deleted_data": False,
                "db_min_connections": 1,
                "db_max_connections": 5,
                "debug": False,
                "db_url": backend_store,
                "blockstore_config": blockstore,
                **config,
            })
        if not event_bus:
            event_bus = event_bus_factory()
        # TODO: backend connection to postgresql will timeout if we use a trio
        # mock clock with autothreshold. We should detect this and do something here...
        async with backend_app_factory(config, event_bus=event_bus) as backend:
            if populated:
                with freeze_time("2000-01-01"):
                    binder = backend_data_binder_factory(backend)
                    await binder.bind_organization(coolorg, alice)
                    await binder.bind_organization(
                        expiredorg,
                        expiredalice,
                        expiration_date=pendulum.now())
                    await binder.bind_organization(otherorg, otheralice)
                    await binder.bind_device(alice2)
                    await binder.bind_device(adam)
                    await binder.bind_device(bob)

            yield backend
Exemple #2
0
    async def _backend_factory(populated=True, config={}, event_bus=None):
        ssl_context = fixtures_customization.get("backend_over_ssl", False)
        nonlocal backend_store, blockstore
        if fixtures_customization.get("backend_force_mocked"):
            backend_store = "MOCKED"
            assert fixtures_customization.get("blockstore_mode",
                                              "NO_RAID") == "NO_RAID"
            blockstore = MockedBlockStoreConfig()

        config = BackendConfig(
            **{
                "administration_token": "s3cr3t",
                "db_min_connections": 1,
                "db_max_connections": 5,
                "debug": False,
                "db_url": backend_store,
                "blockstore_config": blockstore,
                "email_config": None,
                "backend_addr": None,
                "forward_proto_enforce_https": None,
                "ssl_context": ssl_context if ssl_context else False,
                "organization_bootstrap_webhook_url": None,
                "organization_spontaneous_bootstrap": False,
                **config,
            })

        if not event_bus:
            event_bus = event_bus_factory()
        async with backend_app_factory(config, event_bus=event_bus) as backend:
            if populated:
                with freeze_time("2000-01-01"):
                    binder = backend_data_binder_factory(backend)
                    await binder.bind_organization(
                        coolorg,
                        alice,
                        initial_user_manifest=fixtures_customization.get(
                            "alice_initial_remote_user_manifest", "v1"),
                    )
                    await binder.bind_organization(expiredorg, expiredorgalice)
                    await backend.organization.update(
                        expiredorg.organization_id, is_expired=True)
                    await binder.bind_organization(otherorg, otheralice)
                    await binder.bind_device(alice2, certifier=alice)
                    await binder.bind_device(
                        adam,
                        certifier=alice2,
                        initial_user_manifest=fixtures_customization.get(
                            "adam_initial_remote_user_manifest", "v1"),
                    )
                    await binder.bind_device(
                        bob,
                        certifier=adam,
                        initial_user_manifest=fixtures_customization.get(
                            "bob_initial_remote_user_manifest", "v1"),
                    )

            yield backend
Exemple #3
0
async def test_retry_policy_allow_retry(postgresql_url, unused_tcp_port,
                                        asyncio_loop):
    host = "localhost"
    port = unused_tcp_port
    app_config = BackendConfig(
        administration_token="s3cr3t",
        db_min_connections=1,
        db_max_connections=5,
        debug=False,
        blockstore_config=PostgreSQLBlockStoreConfig(),
        email_config=None,
        backend_addr=None,
        forward_proto_enforce_https=None,
        ssl_context=False,
        spontaneous_organization_bootstrap=False,
        organization_bootstrap_webhook_url=None,
        db_url=postgresql_url,
    )
    # Allow to retry once
    retry_policy = RetryPolicy(maximum_attempts=1, pause_before_retry=0)
    async with trio.open_nursery() as nursery:
        # Run backend in the background
        nursery.start_soon(lambda: _run_backend(host,
                                                port,
                                                ssl_context=False,
                                                retry_policy=retry_policy,
                                                app_config=app_config))
        # Connect to PostgreSQL database
        async with triopg.connect(postgresql_url) as conn:

            # Test for 10 cycles
            pid = None
            for _ in range(10):
                # Wait for the backend to be connected
                new_pid, = await wait_for_listeners(conn)
                # Make sure a new connection has been created
                assert new_pid != pid
                pid = new_pid
                # Terminate the backend listener connection
                value, = await conn.fetchrow("SELECT pg_terminate_backend($1)",
                                             pid)
                assert value
                # Wait for the listener to terminate
                await wait_for_listeners(conn, to_terminate=True)

            # Cancel the backend nursery
            nursery.cancel_scope.cancel()
Exemple #4
0
async def test_retry_policy_no_retry(postgresql_url, unused_tcp_port,
                                     asyncio_loop):
    host = "localhost"
    port = unused_tcp_port
    app_config = BackendConfig(
        administration_token="s3cr3t",
        db_min_connections=1,
        db_max_connections=5,
        debug=False,
        blockstore_config=PostgreSQLBlockStoreConfig(),
        email_config=None,
        backend_addr=None,
        forward_proto_enforce_https=None,
        ssl_context=False,
        spontaneous_organization_bootstrap=False,
        organization_bootstrap_webhook_url=None,
        db_url=postgresql_url,
    )

    # No retry
    retry_policy = RetryPolicy(maximum_attempts=0, pause_before_retry=0)

    # Expect a connection error
    with pytest.raises(ConnectionError):
        async with trio.open_nursery() as nursery:
            # Run backend in the background
            nursery.start_soon(lambda: _run_backend(host,
                                                    port,
                                                    ssl_context=False,
                                                    retry_policy=retry_policy,
                                                    app_config=app_config))
            # Connect to PostgreSQL database
            async with triopg.connect(postgresql_url) as conn:
                # Wait for the backend to be connected
                pid, = await wait_for_listeners(conn)
                # Terminate the backend listener connection
                value, = await conn.fetchrow("SELECT pg_terminate_backend($1)",
                                             pid)
                assert value
                # Wait to get cancelled by the connection error `_run_backend`
                with trio.fail_after(3):
                    await trio.sleep_forever()
Exemple #5
0
    async def _backend_factory(populated=True, config={}, event_bus=None):
        ssl_context = fixtures_customization.get("backend_over_ssl", False)
        config = BackendConfig(
            **{
                "administration_token": "s3cr3t",
                "db_min_connections": 1,
                "db_max_connections": 5,
                "debug": False,
                "db_url": backend_store,
                "blockstore_config": blockstore,
                "email_config": None,
                "backend_addr": None,
                "forward_proto_enforce_https": None,
                "ssl_context": ssl_context if ssl_context else False,
                "spontaneous_organization_bootstrap": False,
                "organization_bootstrap_webhook_url": None,
                **config,
            })

        if not event_bus:
            event_bus = event_bus_factory()
        # TODO: backend connection to postgresql will timeout if we use a trio
        # mock clock with autothreshold. We should detect this and do something here...
        async with backend_app_factory(config, event_bus=event_bus) as backend:
            if populated:
                with freeze_time("2000-01-01"):
                    binder = backend_data_binder_factory(backend)
                    await binder.bind_organization(coolorg, alice)
                    await binder.bind_organization(
                        expiredorg,
                        expiredorgalice,
                        expiration_date=pendulum.now())
                    await binder.bind_organization(otherorg, otheralice)
                    await binder.bind_device(alice2, certifier=alice)
                    await binder.bind_device(adam, certifier=alice2)
                    await binder.bind_device(bob, certifier=adam)

            yield backend
Exemple #6
0
def run_cmd(
    host,
    port,
    db,
    db_drop_deleted_data,
    db_min_connections,
    db_max_connections,
    blockstore,
    administration_token,
    ssl_keyfile,
    ssl_certfile,
    log_level,
    log_format,
    log_file,
    log_filter,
    sentry_url,
    debug,
    dev,
):
    configure_logging(log_level, log_format, log_file, log_filter)
    if sentry_url:
        configure_sentry_logging(sentry_url)

    with cli_exception_handler(debug):

        config = BackendConfig(
            administration_token=administration_token,
            db_url=db,
            db_drop_deleted_data=db_drop_deleted_data,
            db_min_connections=db_min_connections,
            db_max_connections=db_max_connections,
            blockstore_config=blockstore,
            debug=debug,
        )

        if ssl_certfile or ssl_keyfile:
            ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            if ssl_certfile:
                ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
            else:
                ssl_context.load_default_certs()
        else:
            ssl_context = None

        async def _run_backend():
            async with backend_app_factory(config=config) as backend:

                async def _serve_client(stream):
                    if ssl_context:
                        stream = trio.SSLStream(stream, ssl_context, server_side=True)

                    try:
                        await backend.handle_client(stream)

                    except Exception:
                        # If we are here, something unexpected happened...
                        logger.exception("Unexpected crash")
                        await stream.aclose()

                await trio.serve_tcp(_serve_client, port, host=host)

        click.echo(
            f"Starting Parsec Backend on {host}:{port} (db={config.db_type}, "
            f"blockstore={config.blockstore_config.type})"
        )
        try:
            trio_run(_run_backend, use_asyncio=True)
        except KeyboardInterrupt:
            click.echo("bye ;-)")
Exemple #7
0
def run_cmd(
    host,
    port,
    db,
    db_min_connections,
    db_max_connections,
    db_first_tries_number,
    db_first_tries_sleep,
    blockstore,
    administration_token,
    spontaneous_organization_bootstrap,
    organization_bootstrap_webhook,
    backend_addr,
    email_host,
    email_port,
    email_host_user,
    email_host_password,
    email_use_ssl,
    email_use_tls,
    email_sender,
    forward_proto_enforce_https,
    ssl_keyfile,
    ssl_certfile,
    log_level,
    log_format,
    log_file,
    log_filter,
    sentry_url,
    debug,
    dev,
):

    # Start a local backend
    configure_logging(log_level, log_format, log_file, log_filter)
    if sentry_url:
        configure_sentry_logging(sentry_url)

    with cli_exception_handler(debug):

        if ssl_certfile or ssl_keyfile:
            ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            if ssl_certfile:
                ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
            else:
                ssl_context.load_default_certs()
        else:
            ssl_context = None

        if email_host == "MOCKED":
            tmpdir = tempfile.mkdtemp(prefix="tmp-email-folder-")
            if email_sender:
                email_config = MockedEmailConfig(sender=email_sender,
                                                 tmpdir=tmpdir)
            else:
                email_config = MockedEmailConfig(sender=DEFAULT_EMAIL_SENDER,
                                                 tmpdir=tmpdir)
        else:
            if not email_sender:
                raise ValueError(
                    "--email-sender is required when --email-host is provided")
            email_config = SmtpEmailConfig(
                host=email_host,
                port=email_port,
                host_user=email_host_user,
                host_password=email_host_password,
                use_ssl=email_use_ssl,
                use_tls=email_use_tls,
                sender=email_sender,
            )

        config = BackendConfig(
            administration_token=administration_token,
            db_url=db,
            db_min_connections=db_min_connections,
            db_max_connections=db_max_connections,
            db_first_tries_number=db_first_tries_number,
            db_first_tries_sleep=db_first_tries_sleep,
            spontaneous_organization_bootstrap=
            spontaneous_organization_bootstrap,
            organization_bootstrap_webhook_url=organization_bootstrap_webhook,
            blockstore_config=blockstore,
            email_config=email_config,
            ssl_context=True if ssl_context else False,
            forward_proto_enforce_https=forward_proto_enforce_https,
            backend_addr=backend_addr,
            debug=debug,
        )

        async def _run_backend():
            async with backend_app_factory(config=config) as backend:

                async def _serve_client(stream):
                    if ssl_context:
                        stream = trio.SSLStream(stream,
                                                ssl_context,
                                                server_side=True)

                    try:
                        await backend.handle_client(stream)

                    except Exception:
                        # If we are here, something unexpected happened...
                        logger.exception("Unexpected crash")
                        await stream.aclose()

                await trio.serve_tcp(_serve_client, port, host=host)

        click.echo(f"Starting Parsec Backend on {host}:{port}"
                   f" (db={config.db_type}"
                   f" blockstore={config.blockstore_config.type}"
                   f" backend_addr={config.backend_addr}"
                   f" email_config={str(email_config)})")
        try:
            trio_run(_run_backend, use_asyncio=True)
        except KeyboardInterrupt:
            click.echo("bye ;-)")
Exemple #8
0
def run_cmd(
    host: str,
    port: int,
    db: str,
    db_min_connections: int,
    db_max_connections: int,
    maximum_database_connection_attempts: int,
    pause_before_retry_database_connection: float,
    blockstore: BaseBlockStoreConfig,
    administration_token: str,
    spontaneous_organization_bootstrap: bool,
    organization_bootstrap_webhook: str,
    organization_initial_active_users_limit: int,
    organization_initial_user_profile_outsider_allowed: bool,
    backend_addr: BackendAddr,
    email_host: str,
    email_port: int,
    email_host_user: Optional[str],
    email_host_password: Optional[str],
    email_use_ssl: bool,
    email_use_tls: bool,
    email_sender: str,
    forward_proto_enforce_https: Optional[Tuple[bytes, bytes]],
    ssl_keyfile: Optional[Path],
    ssl_certfile: Optional[Path],
    log_level: str,
    log_format: str,
    log_file: Optional[str],
    sentry_dsn: Optional[str],
    sentry_environment: str,
    debug: bool,
    dev: bool,
) -> None:
    # Start a local backend

    with cli_exception_handler(debug):
        ssl_context: Optional[ssl.SSLContext]
        if ssl_certfile or ssl_keyfile:
            ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            if ssl_certfile:
                ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
            else:
                ssl_context.load_default_certs()
        else:
            ssl_context = None

        email_config: EmailConfig
        if email_host == "MOCKED":
            tmpdir = tempfile.mkdtemp(prefix="tmp-email-folder-")
            if email_sender:
                email_config = MockedEmailConfig(sender=email_sender,
                                                 tmpdir=tmpdir)
            else:
                email_config = MockedEmailConfig(sender=DEFAULT_EMAIL_SENDER,
                                                 tmpdir=tmpdir)
        else:
            if not email_sender:
                raise ValueError(
                    "--email-sender is required when --email-host is provided")
            email_config = SmtpEmailConfig(
                host=email_host,
                port=email_port,
                host_user=email_host_user,
                host_password=email_host_password,
                use_ssl=email_use_ssl,
                use_tls=email_use_tls,
                sender=email_sender,
            )

        app_config = BackendConfig(
            administration_token=administration_token,
            db_url=db,
            db_min_connections=db_min_connections,
            db_max_connections=db_max_connections,
            blockstore_config=blockstore,
            email_config=email_config,
            ssl_context=True if ssl_context else False,
            forward_proto_enforce_https=forward_proto_enforce_https,
            backend_addr=backend_addr,
            debug=debug,
            organization_bootstrap_webhook_url=organization_bootstrap_webhook,
            organization_spontaneous_bootstrap=
            spontaneous_organization_bootstrap,
            organization_initial_active_users_limit=
            organization_initial_active_users_limit,
            organization_initial_user_profile_outsider_allowed=
            organization_initial_user_profile_outsider_allowed,
        )

        click.echo(f"Starting Parsec Backend on {host}:{port}"
                   f" (db={app_config.db_type}"
                   f" blockstore={app_config.blockstore_config.type}"
                   f" email={email_config.type}"
                   f" telemetry={'on' if sentry_dsn else 'off'}"
                   f" backend_addr={app_config.backend_addr}"
                   ")")
        try:
            retry_policy = RetryPolicy(maximum_database_connection_attempts,
                                       pause_before_retry_database_connection)
            trio_run(_run_backend,
                     host,
                     port,
                     ssl_context,
                     retry_policy,
                     app_config,
                     use_asyncio=True)
        except KeyboardInterrupt:
            click.echo("bye ;-)")
Exemple #9
0
def run_cmd(
    host,
    port,
    db,
    db_min_connections,
    db_max_connections,
    maximum_database_connection_attempts,
    pause_before_retry_database_connection,
    blockstore,
    administration_token,
    spontaneous_organization_bootstrap,
    organization_bootstrap_webhook,
    backend_addr,
    email_host,
    email_port,
    email_host_user,
    email_host_password,
    email_use_ssl,
    email_use_tls,
    email_sender,
    forward_proto_enforce_https,
    ssl_keyfile,
    ssl_certfile,
    log_level,
    log_format,
    log_file,
    sentry_url,
    debug,
    dev,
):

    # Start a local backend
    configure_logging(log_level=log_level,
                      log_format=log_format,
                      log_file=log_file)
    if sentry_url:
        configure_sentry_logging(sentry_url)

    with cli_exception_handler(debug):

        if ssl_certfile or ssl_keyfile:
            ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
            if ssl_certfile:
                ssl_context.load_cert_chain(ssl_certfile, ssl_keyfile)
            else:
                ssl_context.load_default_certs()
        else:
            ssl_context = None

        if email_host == "MOCKED":
            tmpdir = tempfile.mkdtemp(prefix="tmp-email-folder-")
            if email_sender:
                email_config = MockedEmailConfig(sender=email_sender,
                                                 tmpdir=tmpdir)
            else:
                email_config = MockedEmailConfig(sender=DEFAULT_EMAIL_SENDER,
                                                 tmpdir=tmpdir)
        else:
            if not email_sender:
                raise ValueError(
                    "--email-sender is required when --email-host is provided")
            email_config = SmtpEmailConfig(
                host=email_host,
                port=email_port,
                host_user=email_host_user,
                host_password=email_host_password,
                use_ssl=email_use_ssl,
                use_tls=email_use_tls,
                sender=email_sender,
            )

        app_config = BackendConfig(
            administration_token=administration_token,
            db_url=db,
            db_min_connections=db_min_connections,
            db_max_connections=db_max_connections,
            spontaneous_organization_bootstrap=
            spontaneous_organization_bootstrap,
            organization_bootstrap_webhook_url=organization_bootstrap_webhook,
            blockstore_config=blockstore,
            email_config=email_config,
            ssl_context=True if ssl_context else False,
            forward_proto_enforce_https=forward_proto_enforce_https,
            backend_addr=backend_addr,
            debug=debug,
        )

        click.echo(f"Starting Parsec Backend on {host}:{port}"
                   f" (db={app_config.db_type}"
                   f" blockstore={app_config.blockstore_config.type}"
                   f" backend_addr={app_config.backend_addr}"
                   f" email_config={str(email_config)})")
        try:
            retry_policy = RetryPolicy(maximum_database_connection_attempts,
                                       pause_before_retry_database_connection)
            trio_run(_run_backend,
                     host,
                     port,
                     ssl_context,
                     retry_policy,
                     app_config,
                     use_asyncio=True)
        except KeyboardInterrupt:
            click.echo("bye ;-)")