@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Make upstream callers respect "active bindings" when querying Almanac

Summary: Ref T13641. Make "active bindings" a real query and make callers that only care about active bindings only query for active bindings.

Test Plan:
- Queried for "bindings" and "activeBindings" via Conduit.
- Disabled/enabled devices, saw binding status update in UI.
- Loaded Diffusion cluster layout.
- Grepped for `needBindings()`, `getActiveBindings()`, etc.

Subscribers: yelirekim, PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13641

Differential Revision: https://secure.phabricator.com/D21628

+188 -59
+21
src/applications/almanac/constants/AlmanacDeviceStatus.php
··· 62 62 return $result; 63 63 } 64 64 65 + public static function getActiveStatusList() { 66 + $results = array(); 67 + foreach (self::newDeviceStatusMap() as $status_value => $status) { 68 + if (empty($status['disabled'])) { 69 + $results[] = $status_value; 70 + } 71 + } 72 + return $results; 73 + } 74 + 75 + public static function getDisabledStatusList() { 76 + $results = array(); 77 + foreach (self::newDeviceStatusMap() as $status_value => $status) { 78 + if (!empty($status['disabled'])) { 79 + $results[] = $status_value; 80 + } 81 + } 82 + return $results; 83 + } 84 + 65 85 private function getDeviceStatusProperty($key, $default = null) { 66 86 $map = self::newDeviceStatusMap(); 67 87 $properties = idx($map, $this->getValue(), array()); ··· 81 101 'icon.color' => 'grey', 82 102 'status-tag.icon' => 'fa-times', 83 103 'status-tag.color' => 'indigo', 104 + 'disabled' => true, 84 105 ), 85 106 ); 86 107 }
+25 -2
src/applications/almanac/engineextension/AlmanacBindingsSearchEngineAttachment.php
··· 3 3 final class AlmanacBindingsSearchEngineAttachment 4 4 extends AlmanacSearchEngineAttachment { 5 5 6 + private $isActive; 7 + 8 + public function setIsActive($is_active) { 9 + $this->isActive = $is_active; 10 + return $this; 11 + } 12 + 13 + public function getIsActive() { 14 + return $this->isActive; 15 + } 16 + 6 17 public function getAttachmentName() { 7 18 return pht('Almanac Bindings'); 8 19 } ··· 13 24 14 25 public function willLoadAttachmentData($query, $spec) { 15 26 $query->needProperties(true); 16 - $query->needBindings(true); 27 + 28 + if ($this->getIsActive()) { 29 + $query->needBindings(true); 30 + } else { 31 + $query->needActiveBindings(true); 32 + } 17 33 } 18 34 19 35 public function getAttachmentForObject($object, $data, $spec) { 20 36 $bindings = array(); 21 - foreach ($object->getBindings() as $binding) { 37 + 38 + if ($this->getIsActive()) { 39 + $service_bindings = $object->getActiveBindings(); 40 + } else { 41 + $service_bindings = $object->getBindings(); 42 + } 43 + 44 + foreach ($service_bindings as $binding) { 22 45 $bindings[] = $this->getAlmanacBindingDictionary($binding); 23 46 } 24 47
+2
src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php
··· 51 51 'phid' => $device->getPHID(), 52 52 'name' => $device->getName(), 53 53 'properties' => $this->getAlmanacPropertyList($device), 54 + 'status' => $device->getStatus(), 55 + 'disabled' => $device->isDisabled(), 54 56 ); 55 57 } 56 58
+51 -5
src/applications/almanac/query/AlmanacBindingQuery.php
··· 8 8 private $servicePHIDs; 9 9 private $devicePHIDs; 10 10 private $interfacePHIDs; 11 + private $isActive; 11 12 12 13 public function withIDs(array $ids) { 13 14 $this->ids = $ids; ··· 31 32 32 33 public function withInterfacePHIDs(array $phids) { 33 34 $this->interfacePHIDs = $phids; 35 + return $this; 36 + } 37 + 38 + public function withIsActive($active) { 39 + $this->isActive = $active; 34 40 return $this; 35 41 } 36 42 ··· 95 101 if ($this->ids !== null) { 96 102 $where[] = qsprintf( 97 103 $conn, 98 - 'id IN (%Ld)', 104 + 'binding.id IN (%Ld)', 99 105 $this->ids); 100 106 } 101 107 102 108 if ($this->phids !== null) { 103 109 $where[] = qsprintf( 104 110 $conn, 105 - 'phid IN (%Ls)', 111 + 'binding.phid IN (%Ls)', 106 112 $this->phids); 107 113 } 108 114 109 115 if ($this->servicePHIDs !== null) { 110 116 $where[] = qsprintf( 111 117 $conn, 112 - 'servicePHID IN (%Ls)', 118 + 'binding.servicePHID IN (%Ls)', 113 119 $this->servicePHIDs); 114 120 } 115 121 116 122 if ($this->devicePHIDs !== null) { 117 123 $where[] = qsprintf( 118 124 $conn, 119 - 'devicePHID IN (%Ls)', 125 + 'binding.devicePHID IN (%Ls)', 120 126 $this->devicePHIDs); 121 127 } 122 128 123 129 if ($this->interfacePHIDs !== null) { 124 130 $where[] = qsprintf( 125 131 $conn, 126 - 'interfacePHID IN (%Ls)', 132 + 'binding.interfacePHID IN (%Ls)', 127 133 $this->interfacePHIDs); 128 134 } 129 135 136 + if ($this->isActive !== null) { 137 + if ($this->isActive) { 138 + $where[] = qsprintf( 139 + $conn, 140 + '(binding.isDisabled = 0) AND (device.status IN (%Ls))', 141 + AlmanacDeviceStatus::getActiveStatusList()); 142 + } else { 143 + $where[] = qsprintf( 144 + $conn, 145 + '(binding.isDisabled = 1) OR (device.status IN (%Ls))', 146 + AlmanacDeviceStatus::getDisabledStatusList()); 147 + } 148 + } 149 + 130 150 return $where; 151 + } 152 + 153 + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { 154 + $joins = parent::buildJoinClauseParts($conn); 155 + 156 + if ($this->shouldJoinDeviceTable()) { 157 + $device_table = new AlmanacDevice(); 158 + $joins[] = qsprintf( 159 + $conn, 160 + 'JOIN %R device ON binding.devicePHID = device.phid', 161 + $device_table); 162 + } 163 + 164 + return $joins; 165 + } 166 + 167 + private function shouldJoinDeviceTable() { 168 + if ($this->isActive !== null) { 169 + return true; 170 + } 171 + 172 + return false; 173 + } 174 + 175 + protected function getPrimaryTableAlias() { 176 + return 'binding'; 131 177 } 132 178 133 179 }
+28 -5
src/applications/almanac/query/AlmanacServiceQuery.php
··· 12 12 private $nameSuffix; 13 13 14 14 private $needBindings; 15 + private $needActiveBindings; 15 16 16 17 public function withIDs(array $ids) { 17 18 $this->ids = $ids; ··· 56 57 57 58 public function needBindings($need_bindings) { 58 59 $this->needBindings = $need_bindings; 60 + return $this; 61 + } 62 + 63 + public function needActiveBindings($need_active) { 64 + $this->needActiveBindings = $need_active; 59 65 return $this; 60 66 } 61 67 ··· 160 166 } 161 167 162 168 protected function didFilterPage(array $services) { 163 - if ($this->needBindings) { 169 + $need_all = $this->needBindings; 170 + $need_active = $this->needActiveBindings; 171 + 172 + $need_any = ($need_all || $need_active); 173 + $only_active = ($need_active && !$need_all); 174 + 175 + if ($need_any) { 164 176 $service_phids = mpull($services, 'getPHID'); 165 - $bindings = id(new AlmanacBindingQuery()) 177 + 178 + $bindings_query = id(new AlmanacBindingQuery()) 166 179 ->setViewer($this->getViewer()) 167 180 ->withServicePHIDs($service_phids) 168 - ->needProperties($this->getNeedProperties()) 169 - ->execute(); 181 + ->needProperties($this->getNeedProperties()); 182 + 183 + if ($only_active) { 184 + $bindings_query->withIsActive(true); 185 + } 186 + 187 + $bindings = $bindings_query->execute(); 170 188 $bindings = mgroup($bindings, 'getServicePHID'); 171 189 172 190 foreach ($services as $service) { 173 191 $service_bindings = idx($bindings, $service->getPHID(), array()); 174 - $service->attachBindings($service_bindings); 192 + 193 + if ($only_active) { 194 + $service->attachActiveBindings($service_bindings); 195 + } else { 196 + $service->attachBindings($service_bindings); 197 + } 175 198 } 176 199 } 177 200
+5
src/applications/almanac/storage/AlmanacDevice.php
··· 282 282 ->setKey('status') 283 283 ->setType('map<string, wild>') 284 284 ->setDescription(pht('Device status information.')), 285 + id(new PhabricatorConduitSearchFieldSpecification()) 286 + ->setKey('disabled') 287 + ->setType('bool') 288 + ->setDescription(pht('True if device is disabled.')), 285 289 ); 286 290 } 287 291 ··· 294 298 'value' => $status->getValue(), 295 299 'name' => $status->getName(), 296 300 ), 301 + 'disabled' => $this->isDisabled(), 297 302 ); 298 303 } 299 304
+23 -6
src/applications/almanac/storage/AlmanacService.php
··· 21 21 22 22 private $almanacProperties = self::ATTACHABLE; 23 23 private $bindings = self::ATTACHABLE; 24 + private $activeBindings = self::ATTACHABLE; 24 25 private $serviceImplementation = self::ATTACHABLE; 25 26 26 27 public static function initializeNewService($type) { ··· 91 92 } 92 93 93 94 public function getActiveBindings() { 94 - $bindings = $this->getBindings(); 95 + return $this->assertAttached($this->activeBindings); 96 + } 95 97 96 - // Filter out disabled bindings. 98 + public function attachBindings(array $bindings) { 99 + $active_bindings = array(); 97 100 foreach ($bindings as $key => $binding) { 101 + // Filter out disabled bindings. 98 102 if ($binding->getIsDisabled()) { 99 - unset($bindings[$key]); 103 + continue; 104 + } 105 + 106 + // Filter out bindings to disabled devices. 107 + if ($binding->getDevice()->isDisabled()) { 108 + continue; 100 109 } 110 + 111 + $active_bindings[$key] = $binding; 101 112 } 102 113 103 - return $bindings; 104 - } 114 + $this->attachActiveBindings($active_bindings); 105 115 106 - public function attachBindings(array $bindings) { 107 116 $this->bindings = $bindings; 117 + return $this; 118 + } 119 + 120 + public function attachActiveBindings(array $bindings) { 121 + $this->activeBindings = $bindings; 108 122 return $this; 109 123 } 110 124 ··· 289 303 ->setAttachmentKey('properties'), 290 304 id(new AlmanacBindingsSearchEngineAttachment()) 291 305 ->setAttachmentKey('bindings'), 306 + id(new AlmanacBindingsSearchEngineAttachment()) 307 + ->setIsActive(true) 308 + ->setAttachmentKey('activeBindings'), 292 309 ); 293 310 } 294 311
+21 -1
src/applications/almanac/view/AlmanacBindingTableView.php
··· 56 56 57 57 $icon_active = id(new PHUIIconView()) 58 58 ->setIcon('fa-check') 59 + ->setColor('green') 59 60 ->addSigil('has-tooltip') 60 61 ->setMetadata( 61 62 array( 62 63 'tip' => pht('Active'), 63 64 )); 64 65 66 + $icon_device_disabled = id(new PHUIIconView()) 67 + ->setIcon('fa-times') 68 + ->setColor('grey') 69 + ->addSigil('has-tooltip') 70 + ->setMetadata( 71 + array( 72 + 'tip' => pht('Device Disabled'), 73 + )); 74 + 65 75 $rows = array(); 66 76 foreach ($bindings as $binding) { 67 77 $addr = $binding->getInterface()->getAddress(); 68 78 $port = $binding->getInterface()->getPort(); 69 79 80 + $device = $binding->getDevice(); 81 + if ($device->isDisabled()) { 82 + $binding_icon = $icon_device_disabled; 83 + } else if ($binding->getIsDisabled()) { 84 + $binding_icon = $icon_disabled; 85 + } else { 86 + $binding_icon = $icon_active; 87 + } 88 + 70 89 $rows[] = array( 71 90 $binding->getID(), 72 - ($binding->getIsDisabled() ? $icon_disabled : $icon_active), 91 + $binding_icon, 73 92 $handles->renderHandle($binding->getServicePHID()), 93 + 74 94 $handles->renderHandle($binding->getDevicePHID()), 75 95 $handles->renderHandle($binding->getInterface()->getNetworkPHID()), 76 96 $binding->getInterface()->renderDisplayAddress(),
+4 -27
src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php
··· 89 89 AlmanacClusterRepositoryServiceType::SERVICETYPE, 90 90 )) 91 91 ->withPHIDs(array($service_phid)) 92 - ->needBindings(true) 92 + ->needActiveBindings(true) 93 93 ->executeOne(); 94 94 if (!$service) { 95 95 // TODO: Viewer may not have permission to see the service, or it may ··· 104 104 105 105 $rows = array(); 106 106 if ($service) { 107 - $bindings = $service->getBindings(); 107 + $bindings = $service->getActiveBindings(); 108 108 $bindings = mgroup($bindings, 'getDevicePHID'); 109 109 110 110 // This is an unusual read which always comes from the master. ··· 117 117 118 118 $versions = mpull($versions, null, 'getDevicePHID'); 119 119 120 - // List enabled devices first, then sort devices in each group by name. 121 120 $sort = array(); 122 121 foreach ($bindings as $key => $binding_group) { 123 - $all_disabled = $this->isDisabledGroup($binding_group); 124 - 125 122 $sort[$key] = id(new PhutilSortVector()) 126 - ->addInt($all_disabled ? 1 : 0) 127 123 ->addString(head($binding_group)->getDevice()->getName()); 128 124 } 129 125 $sort = msortv($sort, 'getSelf'); 130 126 $bindings = array_select_keys($bindings, array_keys($sort)) + $bindings; 131 127 132 128 foreach ($bindings as $binding_group) { 133 - $all_disabled = $this->isDisabledGroup($binding_group); 134 129 $any_binding = head($binding_group); 135 130 136 - if ($all_disabled) { 137 - $binding_icon = 'fa-times grey'; 138 - $binding_tip = pht('Disabled'); 139 - } else { 140 - $binding_icon = 'fa-folder-open green'; 141 - $binding_tip = pht('Active'); 142 - } 131 + $binding_icon = 'fa-folder-open green'; 132 + $binding_tip = pht('Active'); 143 133 144 134 $binding_icon = id(new PHUIIconView()) 145 135 ->setIcon($binding_icon) ··· 374 364 } 375 365 376 366 return $box_view; 377 - } 378 - 379 - 380 - private function isDisabledGroup(array $binding_group) { 381 - assert_instances_of($binding_group, 'AlmanacBinding'); 382 - 383 - foreach ($binding_group as $binding) { 384 - if (!$binding->getIsDisabled()) { 385 - return false; 386 - } 387 - } 388 - 389 - return true; 390 367 } 391 368 392 369 }
+5 -10
src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
··· 34 34 DrydockBlueprint $blueprint, 35 35 DrydockLease $lease) { 36 36 $services = $this->loadServices($blueprint); 37 - $bindings = $this->loadAllBindings($services); 37 + $bindings = $this->getActiveBindings($services); 38 38 39 39 if (!$bindings) { 40 40 // If there are no devices bound to the services for this blueprint, ··· 222 222 ->setViewer($viewer) 223 223 ->withPHIDs($service_phids) 224 224 ->withServiceTypes($this->getAlmanacServiceTypes()) 225 - ->needBindings(true) 225 + ->needActiveBindings(true) 226 226 ->execute(); 227 227 $services = mpull($services, null, 'getPHID'); 228 228 ··· 242 242 return $this->services; 243 243 } 244 244 245 - private function loadAllBindings(array $services) { 245 + private function getActive(array $services) { 246 246 assert_instances_of($services, 'AlmanacService'); 247 - $bindings = array_mergev(mpull($services, 'getBindings')); 247 + $bindings = array_mergev(mpull($services, 'getActiveBindings')); 248 248 return mpull($bindings, null, 'getPHID'); 249 249 } 250 250 ··· 271 271 $allocated_phids = array_fuse($allocated_phids); 272 272 273 273 $services = $this->loadServices($blueprint); 274 - $bindings = $this->loadAllBindings($services); 274 + $bindings = $this->getActiveBindings($services); 275 275 276 276 $free = array(); 277 277 foreach ($bindings as $binding) { 278 - // Don't consider disabled bindings to be available. 279 - if ($binding->getIsDisabled()) { 280 - continue; 281 - } 282 - 283 278 if (empty($allocated_phids[$binding->getPHID()])) { 284 279 $free[] = $binding; 285 280 }
+1 -1
src/applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php
··· 61 61 array( 62 62 AlmanacClusterRepositoryServiceType::SERVICETYPE, 63 63 )) 64 - ->needBindings(true) 64 + ->needActiveBindings(true) 65 65 ->executeOne(); 66 66 if (!$service) { 67 67 throw new PhutilArgumentUsageException(
+2 -2
src/applications/repository/storage/PhabricatorRepository.php
··· 2109 2109 throw new Exception( 2110 2110 pht( 2111 2111 'The Almanac service for this repository is not bound to any '. 2112 - 'interfaces.')); 2112 + 'active interfaces.')); 2113 2113 } 2114 2114 2115 2115 $uris = array(); ··· 2531 2531 $service = id(new AlmanacServiceQuery()) 2532 2532 ->setViewer(PhabricatorUser::getOmnipotentUser()) 2533 2533 ->withPHIDs(array($service_phid)) 2534 - ->needBindings(true) 2534 + ->needActiveBindings(true) 2535 2535 ->needProperties(true) 2536 2536 ->executeOne(); 2537 2537 if (!$service) {