1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-02 09:58:24 +01:00

Add "bin/file migrate" options to support import of a local-disk backup for Phacility instances

Summary:
Ref T13306. Currently, there's no easy way to import a third-party local-disk file dump into a Phacility instance.

Add some more options to `bin/files migrate` to support this. In particular, this enables:

```
$ ./bin/files --from-engine local-disk --engine amazon-s3 --local-disk-source path/to/backup
```

...to import these files into S3 directly.

These are general-purpose options and theoretically useful in other use cases, although realistically those cases are probably very rare.

Test Plan: Used `bin/files` with the new options to move files in and out of local disk storage in an arbitrary backup directory. Got clean exports/imports.

Reviewers: amckinley

Maniphest Tasks: T13306

Differential Revision: https://secure.phabricator.com/D20571
This commit is contained in:
epriestley 2019-06-05 14:33:18 -07:00
parent f1a588c771
commit 64a9500078
3 changed files with 83 additions and 11 deletions

View file

@ -46,10 +46,44 @@ final class PhabricatorFilesManagementMigrateWorkflow
'name' => 'names', 'name' => 'names',
'wildcard' => true, 'wildcard' => true,
), ),
array(
'name' => 'from-engine',
'param' => 'engine',
'help' => pht('Migrate files from the named storage engine.'),
),
array(
'name' => 'local-disk-source',
'param' => 'path',
'help' => pht(
'When migrating from a local disk source, use the specified '.
'path as the root directory.'),
),
)); ));
} }
public function execute(PhutilArgumentParser $args) { public function execute(PhutilArgumentParser $args) {
// See T13306. This flag allows you to import files from a backup of
// local disk storage into some other engine. When the caller provides
// the flag, we override the local disk engine configuration and treat
// it as though it is configured to use the specified location.
$local_disk_source = $args->getArg('local-disk-source');
if (strlen($local_disk_source)) {
$path = Filesystem::resolvePath($local_disk_source);
try {
Filesystem::assertIsDirectory($path);
} catch (FilesystemException $ex) {
throw new PhutilArgumentUsageException(
pht(
'The "--local-disk-source" argument must point to a valid, '.
'readable directory on local disk.'));
}
$env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('storage.local-disk.path', $path);
}
$target_key = $args->getArg('engine'); $target_key = $args->getArg('engine');
if (!$target_key) { if (!$target_key) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(

View file

@ -4,23 +4,48 @@ abstract class PhabricatorFilesManagementWorkflow
extends PhabricatorManagementWorkflow { extends PhabricatorManagementWorkflow {
protected function buildIterator(PhutilArgumentParser $args) { protected function buildIterator(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$names = $args->getArg('names'); $names = $args->getArg('names');
if ($args->getArg('all')) { $is_all = $args->getArg('all');
if ($names) { $from_engine = $args->getArg('from-engine');
throw new PhutilArgumentUsageException(
pht( $any_constraint = ($from_engine || $names);
'Specify either a list of files or `%s`, but not both.',
'--all')); if (!$is_all && !$any_constraint) {
} throw new PhutilArgumentUsageException(
return new LiskMigrationIterator(new PhabricatorFile()); pht(
'Use "--all" to migrate all files, or choose files to migrate '.
'with "--names" or "--from-engine".'));
} }
if ($is_all && $any_constraint) {
throw new PhutilArgumentUsageException(
pht(
'You can not migrate all files with "--all" and also migrate only '.
'a subset of files with "--from-engine" or "--names".'));
}
// If we're migrating specific named files, convert the names into IDs
// first.
$ids = null;
if ($names) { if ($names) {
return $this->loadFilesWithNames($names); $files = $this->loadFilesWithNames($names);
$ids = mpull($files, 'getID');
} }
return null; $query = id(new PhabricatorFileQuery())
->setViewer($viewer);
if ($ids) {
$query->withIDs($ids);
}
if ($from_engine) {
$query->withStorageEngines(array($from_engine));
}
return new PhabricatorQueryIterator($query);
} }
protected function loadFilesWithNames(array $names) { protected function loadFilesWithNames(array $names) {
@ -36,7 +61,7 @@ abstract class PhabricatorFilesManagementWorkflow
if (empty($files[$name])) { if (empty($files[$name])) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
"No file '%s' exists!", 'No file "%s" exists.',
$name)); $name));
} }
} }

View file

@ -19,6 +19,7 @@ final class PhabricatorFileQuery
private $needTransforms; private $needTransforms;
private $builtinKeys; private $builtinKeys;
private $isBuiltin; private $isBuiltin;
private $storageEngines;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -137,6 +138,11 @@ final class PhabricatorFileQuery
$ngrams); $ngrams);
} }
public function withStorageEngines(array $engines) {
$this->storageEngines = $engines;
return $this;
}
public function showOnlyExplicitUploads($explicit_uploads) { public function showOnlyExplicitUploads($explicit_uploads) {
$this->explicitUploads = $explicit_uploads; $this->explicitUploads = $explicit_uploads;
return $this; return $this;
@ -469,6 +475,13 @@ final class PhabricatorFileQuery
} }
} }
if ($this->storageEngines !== null) {
$where[] = qsprintf(
$conn,
'storageEngine IN (%Ls)',
$this->storageEngines);
}
return $where; return $where;
} }