3 final class PhabricatorFilesManagementMigrateWorkflow
4 extends PhabricatorFilesManagementWorkflow
{
6 protected function didConstruct() {
7 $arguments = $this->newIteratorArguments();
11 'param' => 'storage-engine',
12 'help' => pht('Migrate to the named storage engine.'),
17 'help' => pht('Show what would be migrated.'),
24 'Do not migrate data for files which are smaller than a given '.
32 'Do not migrate data for files which are larger than a given '.
39 'Copy file data instead of moving it: after migrating, do not '.
40 'remove the old data even if it is no longer referenced.'),
44 'name' => 'local-disk-source',
47 'When migrating from a local disk source, use the specified '.
48 'path as the root directory.'),
53 ->setSynopsis(pht('Migrate files between storage engines.'))
54 ->setArguments($arguments);
57 public function execute(PhutilArgumentParser
$args) {
59 // See T13306. This flag allows you to import files from a backup of
60 // local disk storage into some other engine. When the caller provides
61 // the flag, we override the local disk engine configuration and treat
62 // it as though it is configured to use the specified location.
64 $local_disk_source = $args->getArg('local-disk-source');
65 if (strlen($local_disk_source)) {
66 $path = Filesystem
::resolvePath($local_disk_source);
68 Filesystem
::assertIsDirectory($path);
69 } catch (FilesystemException
$ex) {
70 throw new PhutilArgumentUsageException(
72 'The "--local-disk-source" argument must point to a valid, '.
73 'readable directory on local disk.'));
76 $env = PhabricatorEnv
::beginScopedEnv();
77 $env->overrideEnvConfig('storage.local-disk.path', $path);
80 $target_key = $args->getArg('engine');
82 throw new PhutilArgumentUsageException(
84 'Specify an engine to migrate to with `%s`. '.
85 'Use `%s` to get a list of engines.',
90 $target_engine = PhabricatorFile
::buildEngine($target_key);
92 $iterator = $this->buildIterator($args);
93 $is_dry_run = $args->getArg('dry-run');
95 $min_size = (int)$args->getArg('min-size');
96 $max_size = (int)$args->getArg('max-size');
98 $is_copy = $args->getArg('copy');
101 $engines = PhabricatorFileStorageEngine
::loadAllEngines();
104 foreach ($iterator as $file) {
105 $monogram = $file->getMonogram();
107 // See T7148. When we export data for an instance, we copy all the data
108 // for Files from S3 into the database dump so that the database dump is
109 // a complete, standalone archive of all the data. In the general case,
110 // installs may have a similar process using "--copy" to create a more
113 // When doing this, we may run into temporary files which have been
114 // deleted between the time we took the original dump and the current
115 // timestamp. These files can't be copied since the data no longer
116 // exists: the daemons on the live install already deleted it.
118 // Simply avoid this whole mess by declining to migrate expired temporary
119 // files. They're as good as dead anyway.
121 $ttl = $file->getTTL();
123 if ($ttl < PhabricatorTime
::getNow()) {
127 '%s: Skipping expired temporary file.',
133 $engine_key = $file->getStorageEngine();
134 $engine = idx($engines, $engine_key);
140 '%s: Uses unknown storage engine "%s".',
147 if ($engine->isChunkEngine()) {
151 '%s: Stored as chunks, no data to migrate directly.',
156 if ($engine_key === $target_key) {
160 '%s: Already stored in engine "%s".',
166 $byte_size = $file->getByteSize();
168 if ($min_size && ($byte_size < $min_size)) {
172 '%s: File size (%s) is smaller than minimum size (%s).',
174 phutil_format_bytes($byte_size),
175 phutil_format_bytes($min_size)));
179 if ($max_size && ($byte_size > $max_size)) {
183 '%s: File size (%s) is larger than maximum size (%s).',
185 phutil_format_bytes($byte_size),
186 phutil_format_bytes($max_size)));
194 '%s: (%s) Would migrate from "%s" to "%s" (dry run)...',
196 phutil_format_bytes($byte_size),
203 '%s: (%s) Migrating from "%s" to "%s"...',
205 phutil_format_bytes($byte_size),
212 // Do nothing, this is a dry run.
214 $file->migrateToEngine($target_engine, $is_copy);
218 $total_bytes +
= $byte_size;
224 } catch (Exception
$ex) {
227 pht('Failed! %s', (string)$ex));
237 'Total Migrated Files: %s',
238 new PhutilNumber($total_files)));
243 'Total Migrated Bytes: %s',
244 phutil_format_bytes($total_bytes)));
250 'This was a dry run, so no real migrations were performed.'));
254 $monograms = mpull($failed, 'getMonogram');
258 pht('Failures: %s.', implode(', ', $monograms)));