mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	export: Add option to upload exports to S3.
This should make it more convenient to operationalize providing exports from Zulip Cloud. Fixes #11178.
This commit is contained in:
		@@ -2,15 +2,18 @@
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
from argparse import ArgumentParser, RawTextHelpFormatter
 | 
					from argparse import ArgumentParser, RawTextHelpFormatter
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
from django.core.management.base import CommandError
 | 
					from django.core.management.base import CommandError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from zerver.lib.export import do_export_realm, \
 | 
					from zerver.lib.export import do_export_realm, \
 | 
				
			||||||
    do_write_stats_file_for_realm_export
 | 
					    do_write_stats_file_for_realm_export
 | 
				
			||||||
from zerver.lib.management import ZulipBaseCommand
 | 
					from zerver.lib.management import ZulipBaseCommand
 | 
				
			||||||
 | 
					from zerver.lib.utils import generate_random_token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Command(ZulipBaseCommand):
 | 
					class Command(ZulipBaseCommand):
 | 
				
			||||||
    help = """Exports all data from a Zulip realm
 | 
					    help = """Exports all data from a Zulip realm
 | 
				
			||||||
@@ -97,6 +100,9 @@ class Command(ZulipBaseCommand):
 | 
				
			|||||||
                            action="store",
 | 
					                            action="store",
 | 
				
			||||||
                            default=6,
 | 
					                            default=6,
 | 
				
			||||||
                            help='Threads to use in exporting UserMessage objects in parallel')
 | 
					                            help='Threads to use in exporting UserMessage objects in parallel')
 | 
				
			||||||
 | 
					        parser.add_argument('--upload-to-s3',
 | 
				
			||||||
 | 
					                            action="store_true",
 | 
				
			||||||
 | 
					                            help="Whether to upload resulting tarball to s3")
 | 
				
			||||||
        self.add_realm_args(parser, True)
 | 
					        self.add_realm_args(parser, True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def handle(self, *args: Any, **options: Any) -> None:
 | 
					    def handle(self, *args: Any, **options: Any) -> None:
 | 
				
			||||||
@@ -125,3 +131,29 @@ class Command(ZulipBaseCommand):
 | 
				
			|||||||
        os.chdir(os.path.dirname(output_dir))
 | 
					        os.chdir(os.path.dirname(output_dir))
 | 
				
			||||||
        subprocess.check_call(["tar", "-czf", tarball_path, os.path.basename(output_dir)])
 | 
					        subprocess.check_call(["tar", "-czf", tarball_path, os.path.basename(output_dir)])
 | 
				
			||||||
        print("Tarball written to %s" % (tarball_path,))
 | 
					        print("Tarball written to %s" % (tarball_path,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not options["upload_to_s3"]:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def percent_callback(complete: Any, total: Any) -> None:
 | 
				
			||||||
 | 
					            sys.stdout.write('.')
 | 
				
			||||||
 | 
					            sys.stdout.flush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if settings.LOCAL_UPLOADS_DIR is not None:
 | 
				
			||||||
 | 
					            raise CommandError("S3 backend must be configured to upload to S3")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print("Uploading export tarball to S3")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from zerver.lib.upload import S3Connection, get_bucket, Key
 | 
				
			||||||
 | 
					        conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY)
 | 
				
			||||||
 | 
					        # We use the avatar bucket, because it's world-readable.
 | 
				
			||||||
 | 
					        bucket = get_bucket(conn, settings.S3_AVATAR_BUCKET)
 | 
				
			||||||
 | 
					        key = Key(bucket)
 | 
				
			||||||
 | 
					        key.key = os.path.join("exports", generate_random_token(32), os.path.basename(tarball_path))
 | 
				
			||||||
 | 
					        key.set_contents_from_filename(tarball_path, cb=percent_callback, num_cb=40)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public_url = 'https://{bucket}.{host}/{key}'.format(
 | 
				
			||||||
 | 
					            host=conn.server_name(),
 | 
				
			||||||
 | 
					            bucket=bucket.name,
 | 
				
			||||||
 | 
					            key=key.key)
 | 
				
			||||||
 | 
					        print("Uploaded to %s" % (public_url,))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user