private function buildPropertyListView(DrydockLease $lease, PhabricatorActionListView $actions)
 {
     $viewer = $this->getViewer();
     $view = new PHUIPropertyListView();
     $view->setActionList($actions);
     $view->addProperty(pht('Status'), DrydockLeaseStatus::getNameForStatus($lease->getStatus()));
     $view->addProperty(pht('Resource Type'), $lease->getResourceType());
     $resource_phid = $lease->getResourcePHID();
     if ($resource_phid) {
         $resource_display = $viewer->renderHandle($resource_phid);
     } else {
         $resource_display = phutil_tag('em', array(), pht('No Resource'));
     }
     $view->addProperty(pht('Resource'), $resource_display);
     $until = $lease->getUntil();
     if ($until) {
         $until_display = phabricator_datetime($until, $viewer);
     } else {
         $until_display = phutil_tag('em', array(), pht('Never'));
     }
     $view->addProperty(pht('Expires'), $until_display);
     $attributes = $lease->getAttributes();
     if ($attributes) {
         $view->addSectionHeader(pht('Attributes'), 'fa-list-ul');
         foreach ($attributes as $key => $value) {
             $view->addProperty($key, $value);
         }
     }
     return $view;
 }
 private function buildPropertyListView(DrydockLease $lease, PhabricatorActionListView $actions)
 {
     $view = new PHUIPropertyListView();
     $view->setActionList($actions);
     switch ($lease->getStatus()) {
         case DrydockLeaseStatus::STATUS_ACTIVE:
             $status = pht('Active');
             break;
         case DrydockLeaseStatus::STATUS_RELEASED:
             $status = pht('Released');
             break;
         case DrydockLeaseStatus::STATUS_EXPIRED:
             $status = pht('Expired');
             break;
         case DrydockLeaseStatus::STATUS_PENDING:
             $status = pht('Pending');
             break;
         case DrydockLeaseStatus::STATUS_BROKEN:
             $status = pht('Broken');
             break;
         default:
             $status = pht('Unknown');
             break;
     }
     $view->addProperty(pht('Status'), $status);
     $view->addProperty(pht('Resource Type'), $lease->getResourceType());
     $view->addProperty(pht('Resource'), $lease->getResourceID());
     $attributes = $lease->getAttributes();
     if ($attributes) {
         $view->addSectionHeader(pht('Attributes'));
         foreach ($attributes as $key => $value) {
             $view->addProperty($key, $value);
         }
     }
     return $view;
 }
 /**
  * Check that the resource a blueprint allocated is roughly the sort of
  * object we expect.
  *
  * @param DrydockBlueprint Blueprint which built the resource.
  * @param wild Thing which the blueprint claims is a valid resource.
  * @param DrydockLease Lease the resource was allocated for.
  * @return void
  * @task allocator
  */
 private function validateAllocatedResource(DrydockBlueprint $blueprint, $resource, DrydockLease $lease)
 {
     if (!$resource instanceof DrydockResource) {
         throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: %s must ' . 'return an object of type %s or throw, but returned something else.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'allocateResource()', 'DrydockResource'));
     }
     if (!$resource->isAllocatedResource()) {
         throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: %s ' . 'must actually allocate the resource it returns.', $blueprint->getBlueprintName(), $blueprint->getClassName(), 'allocateResource()'));
     }
     $resource_type = $resource->getType();
     $lease_type = $lease->getResourceType();
     if ($resource_type !== $lease_type) {
         throw new Exception(pht('Blueprint "%s" (of type "%s") is not properly implemented: it ' . 'built a resource of type "%s" to satisfy a lease requesting a ' . 'resource of type "%s".', $blueprint->getBlueprintName(), $blueprint->getClassName(), $resource_type, $lease_type));
     }
 }
 private function allocateLease(DrydockLease $lease)
 {
     $type = $lease->getResourceType();
     $blueprints = $this->loadAllBlueprints();
     // TODO: Policy stuff.
     $pool = id(new DrydockResource())->loadAllWhere('type = %s AND status = %s', $lease->getResourceType(), DrydockResourceStatus::STATUS_OPEN);
     $this->logToDrydock(pht('Found %d Open Resource(s)', count($pool)));
     $candidates = array();
     foreach ($pool as $key => $candidate) {
         if (!isset($blueprints[$candidate->getBlueprintPHID()])) {
             unset($pool[$key]);
             continue;
         }
         $blueprint = $blueprints[$candidate->getBlueprintPHID()];
         $implementation = $blueprint->getImplementation();
         if ($implementation->filterResource($candidate, $lease)) {
             $candidates[] = $candidate;
         }
     }
     $this->logToDrydock(pht('%d Open Resource(s) Remain', count($candidates)));
     $resource = null;
     if ($candidates) {
         shuffle($candidates);
         foreach ($candidates as $candidate_resource) {
             $blueprint = $blueprints[$candidate_resource->getBlueprintPHID()]->getImplementation();
             if ($blueprint->allocateLease($candidate_resource, $lease)) {
                 $resource = $candidate_resource;
                 break;
             }
         }
     }
     if (!$resource) {
         $blueprints = DrydockBlueprintImplementation::getAllBlueprintImplementationsForResource($type);
         $this->logToDrydock(pht('Found %d Blueprints', count($blueprints)));
         foreach ($blueprints as $key => $candidate_blueprint) {
             if (!$candidate_blueprint->isEnabled()) {
                 unset($blueprints[$key]);
                 continue;
             }
         }
         $this->logToDrydock(pht('%d Blueprints Enabled', count($blueprints)));
         foreach ($blueprints as $key => $candidate_blueprint) {
             if (!$candidate_blueprint->canAllocateMoreResources($pool)) {
                 unset($blueprints[$key]);
                 continue;
             }
         }
         $this->logToDrydock(pht('%d Blueprints Can Allocate', count($blueprints)));
         if (!$blueprints) {
             $lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN);
             $lease->save();
             $this->logToDrydock(pht("There are no resources of type '%s' available, and no " . "blueprints which can allocate new ones.", $type));
             return;
         }
         // TODO: Rank intelligently.
         shuffle($blueprints);
         $blueprint = head($blueprints);
         $resource = $blueprint->allocateResource($lease);
         if (!$blueprint->allocateLease($resource, $lease)) {
             // TODO: This "should" happen only if we lost a race with another lease,
             // which happened to acquire this resource immediately after we
             // allocated it. In this case, the right behavior is to retry
             // immediately. However, other things like a blueprint allocating a
             // resource it can't actually allocate the lease on might be happening
             // too, in which case we'd just allocate infinite resources. Probably
             // what we should do is test for an active or allocated lease and retry
             // if we find one (although it might have already been released by now)
             // and fail really hard ("your configuration is a huge broken mess")
             // otherwise. But just throw for now since this stuff is all edge-casey.
             // Alternatively we could bring resources up in a "BESPOKE" status
             // and then switch them to "OPEN" only after the allocating lease gets
             // its grubby mitts on the resource. This might make more sense but
             // is a bit messy.
             throw new Exception(pht('Lost an allocation race?'));
         }
     }
     $blueprint = $resource->getBlueprint();
     $blueprint->acquireLease($resource, $lease);
 }