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); }