Differential D21534 Diff 51255 src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php
Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/daemon/workers/management/PhabricatorWorkerManagementWorkflow.php
| <?php | <?php | ||||
| abstract class PhabricatorWorkerManagementWorkflow | abstract class PhabricatorWorkerManagementWorkflow | ||||
| extends PhabricatorManagementWorkflow { | extends PhabricatorManagementWorkflow { | ||||
| protected function getTaskSelectionArguments() { | protected function getTaskSelectionArguments() { | ||||
| return array( | return array( | ||||
| array( | array( | ||||
| 'name' => 'id', | 'name' => 'id', | ||||
| 'param' => 'id', | 'param' => 'id', | ||||
| 'repeat' => true, | 'repeat' => true, | ||||
| 'help' => pht('Select one or more tasks by ID.'), | 'help' => pht('Select one or more tasks by ID.'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'class', | 'name' => 'class', | ||||
| 'param' => 'name', | 'param' => 'name', | ||||
| 'help' => pht('Select all tasks of a given class.'), | 'help' => pht('Select tasks of a given class.'), | ||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'min-failure-count', | 'name' => 'min-failure-count', | ||||
| 'param' => 'int', | 'param' => 'int', | ||||
| 'help' => pht('Limit to tasks with at least this many failures.'), | 'help' => pht('Select tasks with a minimum failure count.'), | ||||
| ), | |||||
| array( | |||||
| 'name' => 'max-failure-count', | |||||
| 'param' => 'int', | |||||
| 'help' => pht('Select tasks with a maximum failure count.'), | |||||
| ), | ), | ||||
| array( | array( | ||||
| 'name' => 'active', | 'name' => 'active', | ||||
| 'help' => pht('Select all active tasks.'), | 'help' => pht('Select active tasks.'), | ||||
| ), | |||||
| array( | |||||
| 'name' => 'archived', | |||||
| 'help' => pht('Select archived tasks.'), | |||||
| ), | |||||
| array( | |||||
| 'name' => 'container', | |||||
| 'param' => 'name', | |||||
| 'help' => pht( | |||||
| 'Select tasks with the given container or containers.'), | |||||
| 'repeat' => true, | |||||
| ), | |||||
| array( | |||||
| 'name' => 'object', | |||||
| 'param' => 'name', | |||||
| 'repeat' => true, | |||||
| 'help' => pht( | |||||
| 'Select tasks affecting the given object or objects.'), | |||||
| ), | |||||
| array( | |||||
| 'name' => 'min-priority', | |||||
| 'param' => 'int', | |||||
| 'help' => pht('Select tasks with a minimum priority.'), | |||||
| ), | |||||
| array( | |||||
| 'name' => 'max-priority', | |||||
| 'param' => 'int', | |||||
| 'help' => pht('Select tasks with a maximum priority.'), | |||||
| ), | |||||
| array( | |||||
| 'name' => 'limit', | |||||
| 'param' => 'int', | |||||
| 'help' => pht('Limit selection to a maximum number of tasks.'), | |||||
| ), | ), | ||||
| ); | ); | ||||
| } | } | ||||
| protected function loadTasks(PhutilArgumentParser $args) { | protected function loadTasks(PhutilArgumentParser $args) { | ||||
| $ids = $args->getArg('id'); | $ids = $args->getArg('id'); | ||||
| $class = $args->getArg('class'); | $class = $args->getArg('class'); | ||||
| $min_failures = $args->getArg('min-failure-count'); | |||||
| $active = $args->getArg('active'); | $active = $args->getArg('active'); | ||||
| $archived = $args->getArg('archived'); | |||||
| $container_names = $args->getArg('container'); | |||||
| $object_names = $args->getArg('object'); | |||||
| $min_failures = $args->getArg('min-failure-count'); | |||||
| $max_failures = $args->getArg('max-failure-count'); | |||||
| $min_priority = $args->getArg('min-priority'); | |||||
| $max_priority = $args->getArg('max-priority'); | |||||
| $limit = $args->getArg('limit'); | |||||
| if (!$ids && !$class && !$min_failures && !$active) { | $any_constraints = false; | ||||
| if ($ids) { | |||||
| $any_constraints = true; | |||||
| } | |||||
| if ($class) { | |||||
| $any_constraints = true; | |||||
| } | |||||
| if ($active || $archived) { | |||||
| $any_constraints = true; | |||||
| if ($active && $archived) { | |||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht( | pht( | ||||
| 'Use "--id", "--class", "--active", and/or "--min-failure-count" '. | 'You can not specify both "--active" and "--archived" tasks: '. | ||||
| 'to select tasks.')); | 'no tasks can match both constraints.')); | ||||
| } | |||||
| } | |||||
| if ($container_names) { | |||||
| $any_constraints = true; | |||||
| $container_phids = $this->loadObjectPHIDsFromArguments($container_names); | |||||
| } else { | |||||
| $container_phids = array(); | |||||
| } | |||||
| if ($object_names) { | |||||
| $any_constraints = true; | |||||
| $object_phids = $this->loadObjectPHIDsFromArguments($object_names); | |||||
| } else { | |||||
| $object_phids = array(); | |||||
| } | |||||
| if (($min_failures !== null) || ($max_failures !== null)) { | |||||
| $any_constraints = true; | |||||
| if (($min_failures !== null) && ($max_failures !== null)) { | |||||
| if ($min_failures > $max_failures) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Specified "--min-failures" must not be larger than '. | |||||
| 'specified "--max-failures".')); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (($min_priority !== null) || ($max_priority !== null)) { | |||||
| $any_constraints = true; | |||||
| if (($min_priority !== null) && ($max_priority !== null)) { | |||||
| if ($min_priority > $max_priority) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Specified "--min-priority" may not be larger than '. | |||||
| 'specified "--max-priority".')); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (!$any_constraints) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Use constraint flags (like "--id" or "--class") to select which '. | |||||
| 'tasks to affect. Use "--help" for a list of supported constraint '. | |||||
| 'flags.')); | |||||
| } | |||||
| if ($limit !== null) { | |||||
| $limit = (int)$limit; | |||||
| if ($limit <= 0) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Specified "--limit" must be a positive integer.')); | |||||
| } | |||||
| } | } | ||||
| $active_query = new PhabricatorWorkerActiveTaskQuery(); | $active_query = new PhabricatorWorkerActiveTaskQuery(); | ||||
| $archive_query = new PhabricatorWorkerArchiveTaskQuery(); | $archive_query = new PhabricatorWorkerArchiveTaskQuery(); | ||||
| if ($ids) { | if ($ids) { | ||||
| $active_query = $active_query->withIDs($ids); | $active_query = $active_query->withIDs($ids); | ||||
| $archive_query = $archive_query->withIDs($ids); | $archive_query = $archive_query->withIDs($ids); | ||||
| } | } | ||||
| if ($class) { | if ($class) { | ||||
| $class_array = array($class); | $class_array = array($class); | ||||
| $active_query = $active_query->withClassNames($class_array); | $active_query = $active_query->withClassNames($class_array); | ||||
| $archive_query = $archive_query->withClassNames($class_array); | $archive_query = $archive_query->withClassNames($class_array); | ||||
| } | } | ||||
| if ($min_failures) { | if ($min_failures) { | ||||
| $active_query = $active_query->withFailureCountBetween( | $active_query->withFailureCountBetween($min_failures, $max_failures); | ||||
| $min_failures, null); | $archive_query->withFailureCountBetween($min_failures, $max_failures); | ||||
| $archive_query = $archive_query->withFailureCountBetween( | |||||
| $min_failures, null); | |||||
| } | } | ||||
| if ($container_phids) { | |||||
| $active_query->withContainerPHIDs($container_phids); | |||||
| $archive_query->withContainerPHIDs($container_phids); | |||||
| } | |||||
| if ($object_phids) { | |||||
| $active_query->withObjectPHIDs($object_phids); | |||||
| $archive_query->withObjectPHIDs($object_phids); | |||||
| } | |||||
| if ($min_priority || $max_priority) { | |||||
| $active_query->withPriorityBetween($min_priority, $max_priority); | |||||
| $archive_query->withPriorityBetween($min_priority, $max_priority); | |||||
| } | |||||
| if ($limit) { | |||||
| $active_query->setLimit($limit); | |||||
| $archive_query->setLimit($limit); | |||||
| } | |||||
| if ($archived) { | |||||
| $active_tasks = array(); | |||||
| } else { | |||||
| $active_tasks = $active_query->execute(); | $active_tasks = $active_query->execute(); | ||||
| } | |||||
| if ($active) { | if ($active) { | ||||
| $archive_tasks = array(); | $archive_tasks = array(); | ||||
| } else { | } else { | ||||
| $archive_tasks = $archive_query->execute(); | $archive_tasks = $archive_query->execute(); | ||||
| } | } | ||||
| $tasks = | $tasks = | ||||
| mpull($active_tasks, null, 'getID') + | mpull($active_tasks, null, 'getID') + | ||||
| mpull($archive_tasks, null, 'getID'); | mpull($archive_tasks, null, 'getID'); | ||||
| if ($limit) { | |||||
| $tasks = array_slice($tasks, 0, $limit, $preserve_keys = true); | |||||
| } | |||||
| if ($ids) { | if ($ids) { | ||||
| foreach ($ids as $id) { | foreach ($ids as $id) { | ||||
| if (empty($tasks[$id])) { | if (empty($tasks[$id])) { | ||||
| throw new PhutilArgumentUsageException( | throw new PhutilArgumentUsageException( | ||||
| pht('No task exists with id "%s"!', $id)); | pht('No task with ID "%s" matches the constraints!', $id)); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if ($class && $min_failures) { | |||||
| if (!$tasks) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht('No task exists with class "%s" and at least %d failures!', | |||||
| $class, | |||||
| $min_failures)); | |||||
| } | |||||
| } else if ($class) { | |||||
| if (!$tasks) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht('No task exists with class "%s"!', $class)); | |||||
| } | |||||
| } else if ($min_failures) { | |||||
| if (!$tasks) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht('No tasks exist with at least %d failures!', $min_failures)); | |||||
| } | |||||
| } | } | ||||
| // We check that IDs are valid, but for all other constraints it is | |||||
| // acceptable to select no tasks to act upon. | |||||
| // When we lock tasks properly, this gets populated as a side effect. Just | // When we lock tasks properly, this gets populated as a side effect. Just | ||||
| // fake it when doing manual CLI stuff. This makes sure CLI yields have | // fake it when doing manual CLI stuff. This makes sure CLI yields have | ||||
| // their expires times set properly. | // their expires times set properly. | ||||
| foreach ($tasks as $task) { | foreach ($tasks as $task) { | ||||
| if ($task instanceof PhabricatorWorkerActiveTask) { | if ($task instanceof PhabricatorWorkerActiveTask) { | ||||
| $task->setServerTime(PhabricatorTime::getNow()); | $task->setServerTime(PhabricatorTime::getNow()); | ||||
| } | } | ||||
| } | } | ||||
| // If the user specified one or more "--id" flags, process the tasks in | |||||
| // the given order. Otherwise, process them in FIFO order so the sequence | |||||
| // is somewhat consistent with natural execution order. | |||||
| // NOTE: When "--limit" is used, we end up selecting the newest tasks | |||||
| // first. At time of writing, there's no way to order the queries | |||||
| // correctly, so just accept it as reasonable behavior. | |||||
| if ($ids) { | |||||
| $tasks = array_select_keys($tasks, $ids); | |||||
| } else { | |||||
| $tasks = msort($tasks, 'getID'); | |||||
| } | |||||
| return $tasks; | return $tasks; | ||||
| } | } | ||||
| protected function describeTask(PhabricatorWorkerTask $task) { | protected function describeTask(PhabricatorWorkerTask $task) { | ||||
| return pht('Task %d (%s)', $task->getID(), $task->getTaskClass()); | return pht('Task %d (%s)', $task->getID(), $task->getTaskClass()); | ||||
| } | } | ||||
| private function loadObjectPHIDsFromArguments(array $names) { | |||||
| $viewer = $this->getViewer(); | |||||
| $seen_names = array(); | |||||
| foreach ($names as $name) { | |||||
| if (isset($seen_names[$name])) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Object "%s" is specified more than once. Specify only unique '. | |||||
| 'objects.', | |||||
| $name)); | |||||
| } | |||||
| $seen_names[$name] = true; | |||||
| } | |||||
| $object_query = id(new PhabricatorObjectQuery()) | |||||
| ->setViewer($viewer) | |||||
| ->withNames($names); | |||||
| $object_query->execute(); | |||||
| $name_map = $object_query->getNamedResults(); | |||||
| $phid_map = array(); | |||||
| foreach ($names as $name) { | |||||
| if (!isset($name_map[$name])) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'No object with name "%s" could be loaded.', | |||||
| $name)); | |||||
| } | |||||
| $phid = $name_map[$name]->getPHID(); | |||||
| if (isset($phid_map[$phid])) { | |||||
| throw new PhutilArgumentUsageException( | |||||
| pht( | |||||
| 'Names "%s" and "%s" identify the same object. Specify only '. | |||||
| 'unique objects.', | |||||
| $name, | |||||
| $phid_map[$phid])); | |||||
| } | |||||
| $phid_map[$phid] = $name; | |||||
| } | |||||
| return array_keys($phid_map); | |||||
| } | |||||
| } | } | ||||