def handle(self, *args, **options): if not settings.CENTRAL_SERVER: raise CommandError("Disabled for distributed servers, until we can figure out what to do with ") options['platform'] = options['platform'].lower() # normalize if options['platform'] not in ["all", "linux", "macos", "darwin", "windows"]: raise CommandError("Unrecognized platform: %s; will include ALL files." % options['platform']) # Step 0: refresh all resources get_dubbed_video_map(force=True) # force a remote download # Step 1: recursively add all static files kalite_base = os.path.realpath(settings.PROJECT_PATH + "/../") files_dict = recursively_add_files(dirpath=kalite_base, **options) # Step 2: Add a local_settings.py file. # For distributed servers, this is a copy of the local local_settings.py, # with a few properties (specified as command-line options) overridden ls_file = create_local_settings_file(location=os.path.realpath(kalite_base+"/kalite/local_settings.py"), server_type=options['server_type'], locale=options['locale'], central_server=options["central_server"]) files_dict[ls_file] = { "dest_path": "kalite/local_settings.py" } # Step 3: select output file. if not options['file']: options['file'] = create_default_archive_filename(options) # Step 4: package into a zip file ensure_dir(os.path.realpath(os.path.dirname(options["file"]))) # allows relative paths to be passed.=== system_specific_zipping( files_dict = dict([(v["dest_path"], src_path) for src_path, v in files_dict.iteritems()]), zip_file = options["file"], compression=ZIP_DEFLATED if options['compress'] else ZIP_STORED, callback=_default_callback_zip if options["verbosity"] else None, )
def handle(self, *args, **options): options['platform'] = options['platform'].lower() # normalize if options['platform'] not in [ "all", "linux", "macos", "darwin", "windows" ]: raise CommandError( "Unrecognized platform: %s; will include ALL files." % options['platform']) # Step 0: refresh all resources if not settings.DEBUG: get_dubbed_video_map(force=True) # force a remote download # Step 1: recursively add all static files kalite_base = os.path.realpath(settings.KALITE_PATH) files_dict = recursively_add_files(dirpath=kalite_base, **options) # Step 2: Add a local_settings.py file. # For distributed servers, this is a copy of the local local_settings.py, # with a few properties (specified as command-line options) overridden ls_file = create_local_settings_file( location=os.path.realpath(kalite_base + "/kalite/local_settings.py"), server_type=options['server_type'], locale=options['locale'], central_server=options["central_server"]) files_dict[ls_file] = {"dest_path": "kalite/local_settings.py"} # Step 3: select output file. if not options['file']: options['file'] = create_default_archive_filename(options) # Step 4: package into a zip file ensure_dir(os.path.realpath(os.path.dirname( options["file"]))) # allows relative paths to be passed.=== system_specific_zipping( files_dict=dict([(v["dest_path"], src_path) for src_path, v in files_dict.iteritems()]), zip_file=options["file"], compression=ZIP_DEFLATED if options['compress'] else ZIP_STORED, callback=_default_callback_zip if options["verbosity"] else None, )
def handle(self, *args, **options): # Get the zone, remove so we can pass the rest of the options # to the zip procedure zone_id = options["zone_id"] del options["zone_id"] wrapper_filename = options["file"] or os.path.join(tempfile.mkdtemp(), "package_" + create_default_archive_filename(options)) # Pre-zip prep #1: # Create the json data file def create_json_file(include_data): central_server = Device.get_central_server() if not zone_id: models = [central_server] if central_server else [] else: # Get a chain of trust to the zone owner. # Because we're on the central server, this will # simply be the central server, but in the future # this would return an actual chain. logging.debug("Generating a zone invitation...") zone = Zone.objects.get(id=zone_id) chain = ChainOfTrust(zone=zone) assert chain.validate() new_invitation = ZoneInvitation.generate(zone=zone, invited_by=Device.get_own_device()) new_invitation.save() # keep a record of the invitation, for future revocation. Also, signs the thing # This ordering of objects is a bit be hokey, but OK--invitation usually must be # inserted before devicezones--but because it's not pointing to any devices, # it's OK to be at the end. # Note that the central server will always be at the front of the chain of trust, # so no need to explicitly include. models = chain.objects() + [new_invitation] # if include_data: logging.debug("Serializing entire dataset...") devices = Device.objects.by_zone(zone) devicezones = DeviceZone.objects.filter(zone=zone) models += list(devices) + list(devicezones) models += engine.get_models(zone=zone, limit=None) # get all models on this zone models_file = tempfile.mkstemp()[1] with open(models_file, "w") as fp: fp.write(engine.serialize(models)) return models_file models_file = create_json_file(options["include_data"]) # Pre-zip prep #2: # Generate the INNER zip def create_inner_zip_file(): zip_file = os.path.join(settings.MEDIA_ROOT, "zip", os.path.basename(create_default_archive_filename(options))) options["file"] = zip_file if settings.DEBUG or not os.path.exists(zip_file): # always regenerate in debug mode call_command("zip_kalite", **options) if not os.path.exists(zip_file): raise CommandError("Failed to create kalite zip file.") return zip_file inner_zip_file = create_inner_zip_file() # Pre-zip prep #3: # Create a file with the inner zip file signature. def create_signature_file(inner_zip_file): signature_file = os.path.splitext(inner_zip_file)[0] + "_signature.txt" logging.debug("Generating signature; saving to %s" % signature_file) if settings.DEBUG or not os.path.exists(signature_file): # always regenerate in debug mode key = Device.get_own_device().get_key() chunk_size = int(2E5) #200kb chunks signature = key.sign_large_file(inner_zip_file, chunk_size=chunk_size) with open(signature_file, "w") as fp: fp.write("%d\n" % chunk_size) fp.write(signature) return signature_file signature_file = create_signature_file(inner_zip_file) # Gather files together files_dict = { UpdateCommand.inner_zip_filename: inner_zip_file, UpdateCommand.signature_filename: signature_file, InitCommand.data_json_filename: models_file, } # Pre-zip prep #4: # Create the install scripts. def create_install_files(files_dict={}): install_files = {} # need to keep track of, for later cleanup # Create clickable scripts: unix install_sh_file = self.install_py_file[:-3] + "_linux.sh" install_files[install_sh_file] = tempfile.mkstemp()[1] with open(install_files[install_sh_file], "w") as fp: fp.write("echo 'Searching for python path...'\n") with open(os.path.realpath(os.path.join(settings.KALITE_PATH, "scripts/python.sh")), "r") as pythonfp: fp.write(pythonfp.read()) fp.write('\ncurrent_dir=`dirname "${BASH_SOURCE[0]}"`') fp.write('\n$PYEXEC "$current_dir/%s"' % self.install_py_file) # Create clickable scripts: mac install_command_file = self.install_py_file[:-3] + "_mac.command" install_files[install_command_file] = tempfile.mkstemp()[1] with open(install_files[install_command_file], "w") as fp: fp.write('\ncurrent_dir=`dirname "${BASH_SOURCE[0]}"`') fp.write('\nsource "$current_dir/%s"' % install_sh_file) # Create clickable scripts: windows install_bat_file = self.install_py_file[:-3] + "_windows.bat" install_files[install_bat_file] = tempfile.mkstemp()[1] with open(install_files[install_bat_file], "w") as fp: fp.write("start /b /wait python.exe %s" % self.install_py_file) # Dump readme file readme_file = "README" install_files[readme_file] = tempfile.mkstemp()[1] with open(install_files[readme_file], "w") as fp: # First line of docstring is a message that the docstring will # be the readme. Remove that, dump the rest raw! fp.write(inspect.getdoc(install_from_package).split("\n", 2)[1]) # NOTE: do this last, so we can pass the set of install files in the # function call, for effective cleanup. # Create the install python script, # by outputting the install_from_package function (extracting here # through inspection and dumping line-by-line), and its dependencies # (utils.platforms, grabbed here through force). # # Also output a call to the function. install_files[self.install_py_file] = tempfile.mkstemp()[1] with open(install_files[self.install_py_file], "w") as fp: for srcline in inspect.getsourcelines(install_from_package)[0]: fp.write(srcline) fp.write("\n%s\n" % open(get_module_source_file("fle_utils.platforms"), "r").read()) fp.write("\ninstall_from_package(\n") fp.write(" zip_file='%s',\n" % UpdateCommand.inner_zip_filename) fp.write(" signature_file='%s',\n" % UpdateCommand.signature_filename) fp.write(" data_json_file='%s',\n" % InitCommand.data_json_filename) fp.write(" install_files=%s,\n" % (files_dict.keys() + install_files.keys())) fp.write(")\n") fp.write("print 'Installation completed!'") return install_files install_files = create_install_files(files_dict=files_dict) # FINAL: Create the outer (wrapper) zip files_dict.update(install_files) system_specific_zipping( files_dict = files_dict, zip_file=wrapper_filename, compression=ZIP_STORED if settings.DEBUG else ZIP_DEFLATED, # callback=None, ) # cleanup os.remove(models_file) for fil in install_files.values(): os.remove(fil) sys.stdout.write("Successfully packaged KA Lite to %s.\n" % wrapper_filename) # To test: # Unpack to a temporary folder, then run install if options["auto_unpack"]: def simulate_enduser_experience(): # unpack the inner zip to the destination dest_dir = tempfile.mkdtemp() sys.stdout.write("TEST: Unpacking and installing to %s\n" % dest_dir) # Simulate a user unpacking the zip zip = ZipFile(wrapper_filename, "r") for afile in zip.namelist(): zip.extract(afile, path=dest_dir) # Simulate the user running the script raise NotImplementedError() simulate_enduser_experience()
def handle(self, *args, **options): # Get the zone, remove so we can pass the rest of the options # to the zip procedure zone_id = options["zone_id"] del options["zone_id"] wrapper_filename = options["file"] or os.path.join( tempfile.mkdtemp(), "package_" + create_default_archive_filename(options)) # Pre-zip prep #1: # Create the json data file def create_json_file(include_data): central_server = Device.get_central_server() if not zone_id: models = [central_server] if central_server else [] else: # Get a chain of trust to the zone owner. # Because we're on the central server, this will # simply be the central server, but in the future # this would return an actual chain. logging.debug("Generating a zone invitation...") zone = Zone.objects.get(id=zone_id) chain = ChainOfTrust(zone=zone) assert chain.validate() new_invitation = ZoneInvitation.generate( zone=zone, invited_by=Device.get_own_device()) new_invitation.save( ) # keep a record of the invitation, for future revocation. Also, signs the thing # This ordering of objects is a bit be hokey, but OK--invitation usually must be # inserted before devicezones--but because it's not pointing to any devices, # it's OK to be at the end. # Note that the central server will always be at the front of the chain of trust, # so no need to explicitly include. models = chain.objects() + [new_invitation] # if include_data: logging.debug("Serializing entire dataset...") devices = Device.objects.by_zone(zone) devicezones = DeviceZone.objects.filter(zone=zone) models += list(devices) + list(devicezones) models += engine.get_models( zone=zone, limit=None) # get all models on this zone models_file = tempfile.mkstemp()[1] with open(models_file, "w") as fp: fp.write(engine.serialize(models)) return models_file models_file = create_json_file(options["include_data"]) # Pre-zip prep #2: # Generate the INNER zip def create_inner_zip_file(): zip_file = os.path.join( settings.MEDIA_ROOT, "zip", os.path.basename(create_default_archive_filename(options))) options["file"] = zip_file if settings.DEBUG or not os.path.exists( zip_file): # always regenerate in debug mode call_command("zip_kalite", **options) if not os.path.exists(zip_file): raise CommandError("Failed to create kalite zip file.") return zip_file inner_zip_file = create_inner_zip_file() # Pre-zip prep #3: # Create a file with the inner zip file signature. def create_signature_file(inner_zip_file): signature_file = os.path.splitext( inner_zip_file)[0] + "_signature.txt" logging.debug("Generating signature; saving to %s" % signature_file) if settings.DEBUG or not os.path.exists( signature_file): # always regenerate in debug mode key = Device.get_own_device().get_key() chunk_size = int(2E5) #200kb chunks signature = key.sign_large_file(inner_zip_file, chunk_size=chunk_size) with open(signature_file, "w") as fp: fp.write("%d\n" % chunk_size) fp.write(signature) return signature_file signature_file = create_signature_file(inner_zip_file) # Gather files together files_dict = { UpdateCommand.inner_zip_filename: inner_zip_file, UpdateCommand.signature_filename: signature_file, InitCommand.data_json_filename: models_file, } # Pre-zip prep #4: # Create the install scripts. def create_install_files(files_dict={}): install_files = {} # need to keep track of, for later cleanup # Create clickable scripts: unix install_sh_file = self.install_py_file[:-3] + "_linux.sh" install_files[install_sh_file] = tempfile.mkstemp()[1] with open(install_files[install_sh_file], "w") as fp: fp.write("echo 'Searching for python path...'\n") with open( os.path.realpath( os.path.join(settings.KALITE_PATH, "scripts/python.sh")), "r") as pythonfp: fp.write(pythonfp.read()) fp.write('\ncurrent_dir=`dirname "${BASH_SOURCE[0]}"`') fp.write('\n$PYEXEC "$current_dir/%s"' % self.install_py_file) # Create clickable scripts: mac install_command_file = self.install_py_file[:-3] + "_mac.command" install_files[install_command_file] = tempfile.mkstemp()[1] with open(install_files[install_command_file], "w") as fp: fp.write('\ncurrent_dir=`dirname "${BASH_SOURCE[0]}"`') fp.write('\nsource "$current_dir/%s"' % install_sh_file) # Create clickable scripts: windows install_bat_file = self.install_py_file[:-3] + "_windows.bat" install_files[install_bat_file] = tempfile.mkstemp()[1] with open(install_files[install_bat_file], "w") as fp: fp.write("start /b /wait python.exe %s" % self.install_py_file) # Dump readme file readme_file = "README" install_files[readme_file] = tempfile.mkstemp()[1] with open(install_files[readme_file], "w") as fp: # First line of docstring is a message that the docstring will # be the readme. Remove that, dump the rest raw! fp.write( inspect.getdoc(install_from_package).split("\n", 2)[1]) # NOTE: do this last, so we can pass the set of install files in the # function call, for effective cleanup. # Create the install python script, # by outputting the install_from_package function (extracting here # through inspection and dumping line-by-line), and its dependencies # (utils.platforms, grabbed here through force). # # Also output a call to the function. install_files[self.install_py_file] = tempfile.mkstemp()[1] with open(install_files[self.install_py_file], "w") as fp: for srcline in inspect.getsourcelines(install_from_package)[0]: fp.write(srcline) fp.write("\n%s\n" % open( get_module_source_file("fle_utils.platforms"), "r").read()) fp.write("\ninstall_from_package(\n") fp.write(" zip_file='%s',\n" % UpdateCommand.inner_zip_filename) fp.write(" signature_file='%s',\n" % UpdateCommand.signature_filename) fp.write(" data_json_file='%s',\n" % InitCommand.data_json_filename) fp.write(" install_files=%s,\n" % (files_dict.keys() + install_files.keys())) fp.write(")\n") fp.write("print 'Installation completed!'") return install_files install_files = create_install_files(files_dict=files_dict) # FINAL: Create the outer (wrapper) zip files_dict.update(install_files) system_specific_zipping( files_dict=files_dict, zip_file=wrapper_filename, compression=ZIP_STORED if settings.DEBUG else ZIP_DEFLATED, # callback=None, ) # cleanup os.remove(models_file) for fil in install_files.values(): os.remove(fil) sys.stdout.write("Successfully packaged KA Lite to %s.\n" % wrapper_filename) # To test: # Unpack to a temporary folder, then run install if options["auto_unpack"]: def simulate_enduser_experience(): # unpack the inner zip to the destination dest_dir = tempfile.mkdtemp() sys.stdout.write("TEST: Unpacking and installing to %s\n" % dest_dir) # Simulate a user unpacking the zip zip = ZipFile(wrapper_filename, "r") for afile in zip.namelist(): zip.extract(afile, path=dest_dir) # Simulate the user running the script raise NotImplementedError() simulate_enduser_experience()