Path: blob/master/src/applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php
13418 views
<?php12final class PhabricatorFilesManagementMigrateWorkflow3extends PhabricatorFilesManagementWorkflow {45protected function didConstruct() {6$arguments = $this->newIteratorArguments();78$arguments[] = array(9'name' => 'engine',10'param' => 'storage-engine',11'help' => pht('Migrate to the named storage engine.'),12);1314$arguments[] = array(15'name' => 'dry-run',16'help' => pht('Show what would be migrated.'),17);1819$arguments[] = array(20'name' => 'min-size',21'param' => 'bytes',22'help' => pht(23'Do not migrate data for files which are smaller than a given '.24'filesize.'),25);2627$arguments[] = array(28'name' => 'max-size',29'param' => 'bytes',30'help' => pht(31'Do not migrate data for files which are larger than a given '.32'filesize.'),33);3435$arguments[] = array(36'name' => 'copy',37'help' => pht(38'Copy file data instead of moving it: after migrating, do not '.39'remove the old data even if it is no longer referenced.'),40);4142$arguments[] = array(43'name' => 'local-disk-source',44'param' => 'path',45'help' => pht(46'When migrating from a local disk source, use the specified '.47'path as the root directory.'),48);4950$this51->setName('migrate')52->setSynopsis(pht('Migrate files between storage engines.'))53->setArguments($arguments);54}5556public function execute(PhutilArgumentParser $args) {5758// See T13306. This flag allows you to import files from a backup of59// local disk storage into some other engine. When the caller provides60// the flag, we override the local disk engine configuration and treat61// it as though it is configured to use the specified location.6263$local_disk_source = $args->getArg('local-disk-source');64if (strlen($local_disk_source)) {65$path = Filesystem::resolvePath($local_disk_source);66try {67Filesystem::assertIsDirectory($path);68} catch (FilesystemException $ex) {69throw new PhutilArgumentUsageException(70pht(71'The "--local-disk-source" argument must point to a valid, '.72'readable directory on local disk.'));73}7475$env = PhabricatorEnv::beginScopedEnv();76$env->overrideEnvConfig('storage.local-disk.path', $path);77}7879$target_key = $args->getArg('engine');80if (!$target_key) {81throw new PhutilArgumentUsageException(82pht(83'Specify an engine to migrate to with `%s`. '.84'Use `%s` to get a list of engines.',85'--engine',86'files engines'));87}8889$target_engine = PhabricatorFile::buildEngine($target_key);9091$iterator = $this->buildIterator($args);92$is_dry_run = $args->getArg('dry-run');9394$min_size = (int)$args->getArg('min-size');95$max_size = (int)$args->getArg('max-size');9697$is_copy = $args->getArg('copy');9899$failed = array();100$engines = PhabricatorFileStorageEngine::loadAllEngines();101$total_bytes = 0;102$total_files = 0;103foreach ($iterator as $file) {104$monogram = $file->getMonogram();105106// See T7148. When we export data for an instance, we copy all the data107// for Files from S3 into the database dump so that the database dump is108// a complete, standalone archive of all the data. In the general case,109// installs may have a similar process using "--copy" to create a more110// complete backup.111112// When doing this, we may run into temporary files which have been113// deleted between the time we took the original dump and the current114// timestamp. These files can't be copied since the data no longer115// exists: the daemons on the live install already deleted it.116117// Simply avoid this whole mess by declining to migrate expired temporary118// files. They're as good as dead anyway.119120$ttl = $file->getTTL();121if ($ttl) {122if ($ttl < PhabricatorTime::getNow()) {123echo tsprintf(124"%s\n",125pht(126'%s: Skipping expired temporary file.',127$monogram));128continue;129}130}131132$engine_key = $file->getStorageEngine();133$engine = idx($engines, $engine_key);134135if (!$engine) {136echo tsprintf(137"%s\n",138pht(139'%s: Uses unknown storage engine "%s".',140$monogram,141$engine_key));142$failed[] = $file;143continue;144}145146if ($engine->isChunkEngine()) {147echo tsprintf(148"%s\n",149pht(150'%s: Stored as chunks, no data to migrate directly.',151$monogram));152continue;153}154155if ($engine_key === $target_key) {156echo tsprintf(157"%s\n",158pht(159'%s: Already stored in engine "%s".',160$monogram,161$target_key));162continue;163}164165$byte_size = $file->getByteSize();166167if ($min_size && ($byte_size < $min_size)) {168echo tsprintf(169"%s\n",170pht(171'%s: File size (%s) is smaller than minimum size (%s).',172$monogram,173phutil_format_bytes($byte_size),174phutil_format_bytes($min_size)));175continue;176}177178if ($max_size && ($byte_size > $max_size)) {179echo tsprintf(180"%s\n",181pht(182'%s: File size (%s) is larger than maximum size (%s).',183$monogram,184phutil_format_bytes($byte_size),185phutil_format_bytes($max_size)));186continue;187}188189if ($is_dry_run) {190echo tsprintf(191"%s\n",192pht(193'%s: (%s) Would migrate from "%s" to "%s" (dry run)...',194$monogram,195phutil_format_bytes($byte_size),196$engine_key,197$target_key));198} else {199echo tsprintf(200"%s\n",201pht(202'%s: (%s) Migrating from "%s" to "%s"...',203$monogram,204phutil_format_bytes($byte_size),205$engine_key,206$target_key));207}208209try {210if ($is_dry_run) {211// Do nothing, this is a dry run.212} else {213$file->migrateToEngine($target_engine, $is_copy);214}215216$total_files += 1;217$total_bytes += $byte_size;218219echo tsprintf(220"%s\n",221pht('Done.'));222223} catch (Exception $ex) {224echo tsprintf(225"%s\n",226pht('Failed! %s', (string)$ex));227$failed[] = $file;228229throw $ex;230}231}232233echo tsprintf(234"%s\n",235pht(236'Total Migrated Files: %s',237new PhutilNumber($total_files)));238239echo tsprintf(240"%s\n",241pht(242'Total Migrated Bytes: %s',243phutil_format_bytes($total_bytes)));244245if ($is_dry_run) {246echo tsprintf(247"%s\n",248pht(249'This was a dry run, so no real migrations were performed.'));250}251252if ($failed) {253$monograms = mpull($failed, 'getMonogram');254255echo tsprintf(256"%s\n",257pht('Failures: %s.', implode(', ', $monograms)));258259return 1;260}261262return 0;263}264265}266267268