From 4505724cc4f16c91183ccdf368a4a59d6c23f050 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 12 Dec 2014 12:07:11 -0800 Subject: [PATCH] Allow repositories to be bound to an AlmanacService Summary: Ref T2783. This is primarily exploratory and just figuring out what we're blocked on: - Allow a Repository to be bound to a Service. The Service may eventually define multiple read/write nodes, etc. - There's no UI to do this binding yet, you have to touch the database manually. - If a repository is bound to a Service, effect Conduit calls via calls to the remote service instead of executing them in-process. - These don't actually work yet since there's no authentication (see T5955). Test Plan: - Made a nice Service with a nice Binding to a nice Interface on a nice Device. - Force-associated a repository with the service using a raw MySQL query. - Saw Phabricator try to make a remote call to the service (on localhost) and fail because of missing auth stuff. - Also ran `almanac.queryservices`. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T2783 Differential Revision: https://secure.phabricator.com/D10982 --- .../sql/autopatches/20141210.reposervice.sql | 2 + .../AlmanacQueryServicesConduitAPIMethod.php | 15 +---- .../almanac/query/AlmanacServiceQuery.php | 24 ++++++++ .../almanac/storage/AlmanacService.php | 10 +++ .../diffusion/query/DiffusionQuery.php | 61 +++++++++++++++++-- .../storage/PhabricatorRepository.php | 2 + 6 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 resources/sql/autopatches/20141210.reposervice.sql diff --git a/resources/sql/autopatches/20141210.reposervice.sql b/resources/sql/autopatches/20141210.reposervice.sql new file mode 100644 index 0000000000..554bc77b89 --- /dev/null +++ b/resources/sql/autopatches/20141210.reposervice.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_repository.repository + ADD almanacServicePHID VARBINARY(64); diff --git a/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php b/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php index dd7bef415b..b1587150af 100644 --- a/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php +++ b/src/applications/almanac/conduit/AlmanacQueryServicesConduitAPIMethod.php @@ -31,7 +31,8 @@ final class AlmanacQueryServicesConduitAPIMethod $viewer = $request->getUser(); $query = id(new AlmanacServiceQuery()) - ->setViewer($viewer); + ->setViewer($viewer) + ->needBindings(true); $ids = $request->getValue('ids'); if ($ids !== null) { @@ -52,16 +53,6 @@ final class AlmanacQueryServicesConduitAPIMethod $services = $query->executeWithCursorPager($pager); - if ($services) { - $bindings = id(new AlmanacBindingQuery()) - ->setViewer($viewer) - ->withServicePHIDs(mpull($services, 'getPHID')) - ->execute(); - $bindings = mgroup($bindings, 'getServicePHID'); - } else { - $bindings = array(); - } - $data = array(); foreach ($services as $service) { $phid = $service->getPHID(); @@ -69,7 +60,7 @@ final class AlmanacQueryServicesConduitAPIMethod $properties = $service->getAlmanacProperties(); $properties = mpull($properties, 'getFieldValue', 'getFieldName'); - $service_bindings = idx($bindings, $phid, array()); + $service_bindings = $service->getBindings(); $service_bindings = array_values($service_bindings); foreach ($service_bindings as $key => $service_binding) { $service_bindings[$key] = $this->getBindingDictionary($service_binding); diff --git a/src/applications/almanac/query/AlmanacServiceQuery.php b/src/applications/almanac/query/AlmanacServiceQuery.php index 84e1250cbd..f3d4ea0ae2 100644 --- a/src/applications/almanac/query/AlmanacServiceQuery.php +++ b/src/applications/almanac/query/AlmanacServiceQuery.php @@ -6,6 +6,7 @@ final class AlmanacServiceQuery private $ids; private $phids; private $names; + private $needBindings; public function withIDs(array $ids) { $this->ids = $ids; @@ -22,6 +23,11 @@ final class AlmanacServiceQuery return $this; } + public function needBindings($need_bindings) { + $this->needBindings = $need_bindings; + return $this; + } + protected function loadPage() { $table = new AlmanacService(); $conn_r = $table->establishConnection('r'); @@ -71,4 +77,22 @@ final class AlmanacServiceQuery return $this->formatWhereClause($where); } + protected function didFilterPage(array $services) { + if ($this->needBindings) { + $service_phids = mpull($services, 'getPHID'); + $bindings = id(new AlmanacBindingQuery()) + ->setViewer($this->getViewer()) + ->withServicePHIDs($service_phids) + ->execute(); + $bindings = mgroup($bindings, 'getServicePHID'); + + foreach ($services as $service) { + $service_bindings = idx($bindings, $service->getPHID(), array()); + $service->attachBindings($service_bindings); + } + } + + return parent::didFilterPage($services); + } + } diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacService.php index b058c116be..64a14c053d 100644 --- a/src/applications/almanac/storage/AlmanacService.php +++ b/src/applications/almanac/storage/AlmanacService.php @@ -17,6 +17,7 @@ final class AlmanacService private $customFields = self::ATTACHABLE; private $almanacProperties = self::ATTACHABLE; + private $bindings = self::ATTACHABLE; public static function initializeNewService() { return id(new AlmanacService()) @@ -65,6 +66,15 @@ final class AlmanacService return '/almanac/service/view/'.$this->getName().'/'; } + public function getBindings() { + return $this->assertAttached($this->bindings); + } + + public function attachBindings(array $bindings) { + $this->bindings = $bindings; + return $this; + } + /* -( AlmanacPropertyInterface )------------------------------------------- */ diff --git a/src/applications/diffusion/query/DiffusionQuery.php b/src/applications/diffusion/query/DiffusionQuery.php index f54a7a0ebd..87714249fe 100644 --- a/src/applications/diffusion/query/DiffusionQuery.php +++ b/src/applications/diffusion/query/DiffusionQuery.php @@ -62,12 +62,61 @@ abstract class DiffusionQuery extends PhabricatorQuery { $params = $params + $core_params; - return id(new ConduitCall( - $method, - $params - )) - ->setUser($user) - ->execute(); + $service_phid = $repository->getAlmanacServicePHID(); + if ($service_phid === null) { + return id(new ConduitCall($method, $params)) + ->setUser($user) + ->execute(); + } + + $service = id(new AlmanacServiceQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPHIDs(array($service_phid)) + ->needBindings(true) + ->executeOne(); + if (!$service) { + throw new Exception( + pht( + 'The Alamnac service for this repository is invalid or could not '. + 'be loaded.')); + } + + $bindings = $service->getBindings(); + if (!$bindings) { + throw new Exception( + pht( + 'The Alamanc service for this repository is not bound to any '. + 'interfaces.')); + } + + $uris = array(); + foreach ($bindings as $binding) { + $iface = $binding->getInterface(); + + $protocol = $binding->getAlmanacPropertyValue('protocol'); + if ($protocol === 'http') { + $uris[] = 'http://'.$iface->renderDisplayAddress().'/'; + } else if ($protocol === 'https' || $protocol === null) { + $uris[] = 'https://'.$iface->renderDisplayAddress().'/'; + } else { + throw new Exception( + pht( + 'The Almanac service for this repository has a binding to an '. + 'invalid interface with an unknown protocol ("%s").', + $protocol)); + } + } + + shuffle($uris); + $uri = head($uris); + + $domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain(); + + // TODO: This call needs authentication, which is blocked by T5955. + + return id(new ConduitClient($uri)) + ->setHost($domain) + ->callMethodSynchronous($method, $params); } public function execute() { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index bf60744b55..72ba11c987 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -52,6 +52,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO protected $versionControlSystem; protected $details = array(); protected $credentialPHID; + protected $almanacServicePHID; private $commitCount = self::ATTACHABLE; private $mostRecentCommit = self::ATTACHABLE; @@ -86,6 +87,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO 'uuid' => 'text64?', 'pushPolicy' => 'policy', 'credentialPHID' => 'phid?', + 'almanacServicePHID' => 'phid?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null,