예제 #1
0
def _datasets(user, application_template):
    # The interface must:
    # - show the datasets that the application already has access to,
    # - show the datasets that the user has access to,
    # - not duplicate datasets that are both of the above, and
    # - allow selection or unselection of only the datasets the user has
    #   access to that are also REQUIRES_AUTHORIZATION.
    # This means the user won't be able to unselect datasets that the
    # application already has access to, but the users doesn't. This is
    # deliberate: if they could unselect such datasets, they wouldn't be able
    # to reverse the change, which may need urgent contact with support to
    # restore access

    source_tables_user = source_tables_for_user(user)
    source_tables_app = source_tables_for_app(application_template)

    selectable_dataset_ids = set(
        source_table['dataset']['id']
        for source_table in source_tables_user
        if source_table['dataset']['user_access_type'] == 'REQUIRES_AUTHORIZATION'
    )

    selected_dataset_ids = set(
        source_table['dataset']['id'] for source_table in source_tables_app
    )

    source_tables_without_select_info = _without_duplicates_preserve_order(
        source_tables_user + source_tables_app,
        key=lambda x: (x['dataset']['id'], x['schema'], x['table']),
    )

    source_tables = [
        {
            'dataset': {
                'selectable': source_table['dataset']['id'] in selectable_dataset_ids,
                'selected': source_table['dataset']['id'] in selected_dataset_ids,
                **source_table['dataset'],
            },
            **{key: value for key, value in source_table.items() if key != 'dataset'},
        }
        for source_table in source_tables_without_select_info
    ]

    tables_sorted_by_dataset = sorted(
        [table for table in source_tables],
        key=lambda x: (
            x['dataset']['name'],
            x['dataset']['id'],
            x['schema'],
            x['table'],
        ),
    )

    return [
        (dataset, list(tables))
        for dataset, tables in itertools.groupby(
            tables_sorted_by_dataset, lambda x: x['dataset']
        )
    ]
예제 #2
0
def spawn(
    name,
    user_id,
    tag,
    application_instance_id,
    spawner_options,
):
    user = get_user_model().objects.get(pk=user_id)
    application_instance = ApplicationInstance.objects.get(
        id=application_instance_id)

    (source_tables, db_role_schema_suffix, db_user) = (
        (
            source_tables_for_user(user),
            db_role_schema_suffix_for_user(user),
            postgres_user(user.email),
        ) if application_instance.application_template.application_type
        == 'TOOL' else (
            source_tables_for_app(application_instance.application_template),
            db_role_schema_suffix_for_app(
                application_instance.application_template),
            postgres_user(
                application_instance.application_template.host_basename),
        ))

    credentials = new_private_database_credentials(
        db_role_schema_suffix,
        source_tables,
        db_user,
        user,
        valid_for=datetime.timedelta(days=31),
    )

    if application_instance.application_template.application_type == 'TOOL':
        # For AppStream to access credentials
        write_credentials_to_bucket(user, credentials)

    app_schema = f'{USER_SCHEMA_STEM}{db_role_schema_suffix}'

    get_spawner(name).spawn(
        user,
        tag,
        application_instance,
        spawner_options,
        credentials,
        app_schema,
    )
예제 #3
0
def application_api_PUT(request, public_host):
    # A transaction is unnecessary: the single_running_or_spawning_integrity
    # key prevents duplicate spawning/running applications at the same
    # public host
    try:
        application_instance = get_api_visible_application_instance_by_public_host(
            public_host)
    except ApplicationInstance.DoesNotExist:
        pass
    else:
        return JsonResponse({'message': 'Application instance already exists'},
                            status=409)

    try:
        (
            application_template,
            tag,
            _,
            commit_id,
        ) = application_template_tag_user_commit_from_host(public_host)
    except ApplicationTemplate.DoesNotExist:
        return JsonResponse({'message': 'Application template does not exist'},
                            status=400)

    app_type = application_template.application_type

    (source_tables, db_role_schema_suffix, db_user) = ((
        source_tables_for_user(request.user),
        db_role_schema_suffix_for_user(request.user),
        postgres_user(request.user.email),
    ) if app_type == 'TOOL' else (
        source_tables_for_app(application_template),
        db_role_schema_suffix_for_app(application_template),
        postgres_user(application_template.host_basename),
    ))

    credentials = new_private_database_credentials(db_role_schema_suffix,
                                                   source_tables, db_user)

    if app_type == 'TOOL':
        # For AppStream to access credentials
        write_credentials_to_bucket(request.user, credentials)

    try:
        memory, cpu = request.GET['__memory_cpu'].split('_')
    except KeyError:
        memory = None
        cpu = None

    spawner_options = json.dumps(application_options(application_template))

    try:
        application_instance = ApplicationInstance.objects.create(
            owner=request.user,
            application_template=application_template,
            spawner=application_template.spawner,
            spawner_application_template_options=spawner_options,
            spawner_application_instance_id=json.dumps({}),
            public_host=public_host,
            state='SPAWNING',
            single_running_or_spawning_integrity=public_host,
            cpu=cpu,
            memory=memory,
            commit_id=commit_id,
        )
    except IntegrityError:
        application_instance = get_api_visible_application_instance_by_public_host(
            public_host)
    else:
        # The database users are stored so when the database users are cleaned up,
        # we know _not_ to delete any users used by running or spawning apps
        for creds in credentials:
            ApplicationInstanceDbUsers.objects.create(
                application_instance=application_instance,
                db_id=creds['db_id'],
                db_username=creds['db_user'],
            )

        app_schema = f'{USER_SCHEMA_STEM}{db_role_schema_suffix}'

        spawn.delay(
            application_template.spawner,
            request.user.email,
            str(request.user.profile.sso_id),
            tag,
            application_instance.id,
            spawner_options,
            credentials,
            app_schema,
        )

    return JsonResponse(api_application_dict(application_instance), status=200)