Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php
index 7838808f48..a324a20637 100644
--- a/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php
+++ b/src/applications/search/management/PhabricatorSearchManagementIndexWorkflow.php
@@ -1,208 +1,210 @@
<?php
final class PhabricatorSearchManagementIndexWorkflow
extends PhabricatorSearchManagementWorkflow {
protected function didConstruct() {
$this
->setName('index')
->setSynopsis(pht('Build or rebuild search indexes.'))
->setExamples(
"**index** D123\n".
"**index** --type task\n".
"**index** --all")
->setArguments(
array(
array(
'name' => 'all',
'help' => pht('Reindex all documents.'),
),
array(
'name' => 'type',
'param' => 'type',
'help' => pht(
'Object types to reindex, like "task", "commit" or "revision".'),
),
array(
'name' => 'background',
'help' => pht(
'Instead of indexing in this process, queue tasks for '.
'the daemons. This can improve performance, but makes '.
'it more difficult to debug search indexing.'),
),
array(
'name' => 'force',
'short' => 'f',
'help' => pht(
'Force a complete rebuild of the entire index instead of an '.
'incremental update.'),
),
array(
'name' => 'objects',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
+ $this->validateClusterSearchConfig();
+
$console = PhutilConsole::getConsole();
$is_all = $args->getArg('all');
$is_type = $args->getArg('type');
$is_force = $args->getArg('force');
$obj_names = $args->getArg('objects');
if ($obj_names && ($is_all || $is_type)) {
throw new PhutilArgumentUsageException(
pht(
"You can not name objects to index alongside the '%s' or '%s' flags.",
'--all',
'--type'));
} else if (!$obj_names && !($is_all || $is_type)) {
throw new PhutilArgumentUsageException(
pht(
"Provide one of '%s', '%s' or a list of object names.",
'--all',
'--type'));
}
if ($obj_names) {
$phids = $this->loadPHIDsByNames($obj_names);
} else {
$phids = $this->loadPHIDsByTypes($is_type);
}
if (!$phids) {
throw new PhutilArgumentUsageException(pht('Nothing to index!'));
}
if ($args->getArg('background')) {
$is_background = true;
} else {
PhabricatorWorker::setRunAllTasksInProcess(true);
$is_background = false;
}
if (!$is_background) {
$console->writeOut(
"%s\n",
pht(
'Run this workflow with "%s" to queue tasks for the daemon workers.',
'--background'));
}
$groups = phid_group_by_type($phids);
foreach ($groups as $group_type => $group) {
$console->writeOut(
"%s\n",
pht('Indexing %d object(s) of type %s.', count($group), $group_type));
}
$bar = id(new PhutilConsoleProgressBar())
->setTotal(count($phids));
$parameters = array(
'force' => $is_force,
);
$any_success = false;
foreach ($phids as $phid) {
try {
PhabricatorSearchWorker::queueDocumentForIndexing($phid, $parameters);
$any_success = true;
} catch (Exception $ex) {
phlog($ex);
}
$bar->update(1);
}
$bar->done();
if (!$any_success) {
throw new Exception(
pht('Failed to rebuild search index for any documents.'));
}
}
private function loadPHIDsByNames(array $names) {
$query = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->withNames($names);
$query->execute();
$objects = $query->getNamedResults();
foreach ($names as $name) {
if (empty($objects[$name])) {
throw new PhutilArgumentUsageException(
pht(
"'%s' is not the name of a known object.",
$name));
}
}
return mpull($objects, 'getPHID');
}
private function loadPHIDsByTypes($type) {
$objects = id(new PhutilClassMapQuery())
->setAncestorClass('PhabricatorFulltextInterface')
->execute();
$normalized_type = phutil_utf8_strtolower($type);
$matches = array();
foreach ($objects as $object) {
$object_class = get_class($object);
$normalized_class = phutil_utf8_strtolower($object_class);
if ($normalized_class === $normalized_type) {
$matches = array($object_class => $object);
break;
}
if (!strlen($type) ||
strpos($normalized_class, $normalized_type) !== false) {
$matches[$object_class] = $object;
}
}
if (!$matches) {
$all_types = array();
foreach ($objects as $object) {
$all_types[] = get_class($object);
}
sort($all_types);
throw new PhutilArgumentUsageException(
pht(
'Type "%s" matches no indexable objects. Supported types are: %s.',
$type,
implode(', ', $all_types)));
}
if ((count($matches) > 1) && strlen($type)) {
throw new PhutilArgumentUsageException(
pht(
'Type "%s" matches multiple indexable objects. Use a more '.
'specific string. Matching object types are: %s.',
$type,
implode(', ', array_keys($matches))));
}
$phids = array();
foreach ($matches as $match) {
$iterator = new LiskMigrationIterator($match);
foreach ($iterator as $object) {
$phids[] = $object->getPHID();
}
}
return $phids;
}
}
diff --git a/src/applications/search/management/PhabricatorSearchManagementInitWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementInitWorkflow.php
index c984b2ddbf..8728b72ee5 100644
--- a/src/applications/search/management/PhabricatorSearchManagementInitWorkflow.php
+++ b/src/applications/search/management/PhabricatorSearchManagementInitWorkflow.php
@@ -1,70 +1,71 @@
<?php
final class PhabricatorSearchManagementInitWorkflow
extends PhabricatorSearchManagementWorkflow {
protected function didConstruct() {
$this
->setName('init')
->setSynopsis(pht('Initialize or repair a search service.'))
->setExamples('**init**');
}
public function execute(PhutilArgumentParser $args) {
+ $this->validateClusterSearchConfig();
$work_done = false;
foreach (PhabricatorSearchService::getAllServices() as $service) {
echo tsprintf(
"%s\n",
pht(
'Initializing search service "%s".',
$service->getDisplayName()));
if (!$service->isWritable()) {
echo tsprintf(
"%s\n",
pht(
'Skipping service "%s" because it is not writable.',
$service->getDisplayName()));
continue;
}
$engine = $service->getEngine();
if (!$engine->indexExists()) {
echo tsprintf(
"%s\n",
pht('Service index does not exist, creating...'));
$engine->initIndex();
$work_done = true;
} else if (!$engine->indexIsSane()) {
echo tsprintf(
"%s\n",
pht('Service index is out of date, repairing...'));
$engine->initIndex();
$work_done = true;
} else {
echo tsprintf(
"%s\n",
pht('Service index is already up to date.'));
}
echo tsprintf(
"%s\n",
pht('Done.'));
}
if (!$work_done) {
echo tsprintf(
"%s\n",
pht('No services need initialization.'));
return 0;
}
echo tsprintf(
"%s\n",
pht('Service initialization complete.'));
}
}
diff --git a/src/applications/search/management/PhabricatorSearchManagementWorkflow.php b/src/applications/search/management/PhabricatorSearchManagementWorkflow.php
index 23333665e3..86d8c104cb 100644
--- a/src/applications/search/management/PhabricatorSearchManagementWorkflow.php
+++ b/src/applications/search/management/PhabricatorSearchManagementWorkflow.php
@@ -1,4 +1,26 @@
<?php
abstract class PhabricatorSearchManagementWorkflow
- extends PhabricatorManagementWorkflow {}
+ extends PhabricatorManagementWorkflow {
+
+ protected function validateClusterSearchConfig() {
+ // Configuration is normally validated by setup self-checks on the web
+ // workflow, but users may reasonsably run `bin/search` commands after
+ // making manual edits to "local.json". Re-verify configuration here before
+ // continuing.
+
+ $config_key = 'cluster.search';
+ $config_value = PhabricatorEnv::getEnvConfig($config_key);
+
+ try {
+ PhabricatorClusterSearchConfigOptionType::validateValue($config_value);
+ } catch (Exception $ex) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Setting "%s" is misconfigured: %s',
+ $config_key,
+ $ex->getMessage()));
+ }
+ }
+
+}
diff --git a/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php b/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php
index 4a5f7ea6c5..3c099a0548 100644
--- a/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php
+++ b/src/infrastructure/cluster/config/PhabricatorClusterSearchConfigOptionType.php
@@ -1,79 +1,84 @@
<?php
final class PhabricatorClusterSearchConfigOptionType
extends PhabricatorConfigJSONOptionType {
public function validateOption(PhabricatorConfigOption $option, $value) {
+ self::validateClusterSearchConfigValue($value);
+ }
+
+ public static function validateValue($value) {
if (!is_array($value)) {
throw new Exception(
pht(
'Search cluster configuration is not valid: value must be a '.
'list of search hosts.'));
}
$engines = PhabricatorSearchService::loadAllFulltextStorageEngines();
foreach ($value as $index => $spec) {
if (!is_array($spec)) {
throw new Exception(
pht(
'Search cluster configuration is not valid: each entry in the '.
'list must be a dictionary describing a search service, but '.
'the value with index "%s" is not a dictionary.',
$index));
}
try {
PhutilTypeSpec::checkMap(
$spec,
array(
'type' => 'string',
'hosts' => 'optional list<map<string, wild>>',
'roles' => 'optional map<string, wild>',
'port' => 'optional int',
'protocol' => 'optional string',
'path' => 'optional string',
'version' => 'optional int',
));
} catch (Exception $ex) {
throw new Exception(
pht(
'Search engine configuration has an invalid service '.
'specification (at index "%s"): %s.',
$index,
$ex->getMessage()));
}
if (!array_key_exists($spec['type'], $engines)) {
throw new Exception(
- pht('Invalid search engine type: %s. Valid types include: %s',
+ pht(
+ 'Invalid search engine type: %s. Valid types are: %s.',
$spec['type'],
implode(', ', array_keys($engines))));
}
if (isset($spec['hosts'])) {
foreach ($spec['hosts'] as $hostindex => $host) {
try {
PhutilTypeSpec::checkMap(
$host,
array(
'host' => 'string',
'roles' => 'optional map<string, wild>',
'port' => 'optional int',
'protocol' => 'optional string',
'path' => 'optional string',
'version' => 'optional int',
));
} catch (Exception $ex) {
throw new Exception(
pht(
'Search cluster configuration has an invalid host '.
'specification (at index "%s"): %s.',
$hostindex,
$ex->getMessage()));
}
}
}
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 27, 9:13 PM (1 w, 9 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
183518
Default Alt Text
(12 KB)

Event Timeline