@@ -85,3 +85,18 @@ def delete_collection(env, bucket_id, collection_id):
     current_transaction.commit()
 
     return 0
+
+
+def purge_deleted(env, timestamp):
+    if timestamp is None:
+        logger.warning('Purge all tombstones.')
+    else:
+        logger.info('Purge tombstones before %r.' % timestamp)
+    registry = env['registry']
+
+    count = registry.storage.purge_deleted(parent_id='*', collection_id=None, before=timestamp)
+    current_transaction.commit()
+
+    logger.info('%s tombstone(s) deleted.' % count)
+
+    return 0
"""
kinto.core.scripts: utilities to build admin scripts for kinto-based services
"""

import transaction as current_transaction
from pyramid.settings import asbool

from kinto import logger
from kinto.core.storage import exceptions as storage_exceptions


def migrate(env, dry_run=False):
    """
    User-friendly frontend to run database migrations.
    """
    registry = env['registry']
    settings = registry.settings
    readonly_backends = ('storage', 'permission')
    readonly_mode = asbool(settings.get('readonly', False))

    for backend in ('cache', 'storage', 'permission'):
        if hasattr(registry, backend):
            if readonly_mode and backend in readonly_backends:
                message = ('Cannot migrate the %s backend while '
                           'in readonly mode.' % backend)
                logger.error(message)
            else:
                getattr(registry, backend).initialize_schema(dry_run=dry_run)


def delete_collection(env, bucket_id, collection_id):
    registry = env['registry']
    settings = registry.settings
    readonly_mode = asbool(settings.get('readonly', False))

    if readonly_mode:
        message = ('Cannot delete the collection while in readonly mode.')
        logger.error(message)
        return 31

    bucket = '/buckets/%s' % bucket_id
    collection = '/buckets/%s/collections/%s' % (bucket_id, collection_id)

    try:
        registry.storage.get(collection_id='bucket',
                             parent_id='',
                             object_id=bucket_id)
    except storage_exceptions.RecordNotFoundError:
        logger.error("Bucket %r does not exist." % bucket)
        return 32

    try:
        registry.storage.get(collection_id='collection',
                             parent_id=bucket,
                             object_id=collection_id)
    except storage_exceptions.RecordNotFoundError:
        logger.error("Collection %r does not exist." % collection)
        return 33

    deleted = registry.storage.delete_all(collection_id='record',
                                          parent_id=collection,
                                          with_deleted=False)
    if len(deleted) == 0:
        logger.info('No records found for %r.' % collection)
    else:
        logger.info('%d record(s) were deleted.' % len(deleted))

    registry.storage.delete(collection_id='collection',
                            parent_id=bucket,
                            object_id=collection_id,
                            with_deleted=False)
    logger.info("%r collection object was deleted." % collection)

    record = ('/buckets/{bucket_id}'
              '/collections/{collection_id}'
              '/records/{record_id}')

    registry.permission.delete_object_permissions(
        collection,
        *[record.format(bucket_id=bucket_id,
                        collection_id=collection_id,
                        record_id=r['id']) for r in deleted])
    logger.info('Related permissions were deleted.')

    current_transaction.commit()

    return 0

I think we should allow people to configure the bucket and collection they want to purge, don't you think?