mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Make various Drydock improvements
Summary: Tightens up a bunch of stuff: - In `drydock lease`, pull and print logs so the user can see what's happening. - Remove `DrydockAllocator`, which was a dumb class that did nothing. Move the tiny amount of logic it held directly to `DrydockLease`. - Move `resourceType` from worker task metadata directly to `DrydockLease`. Other things (like the web UI) can be more informative with this information available. - Pass leases to `allocateResource()`. We always allocate in response to a lease activation request, and the lease often has vital information. This also allows us to associate logs with leases correctly. Test Plan: Ran `drydock lease --type host` and saw it perform a host allocation in EC2. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2015 Differential Revision: https://secure.phabricator.com/D3870
This commit is contained in:
parent
3844be3f83
commit
89b37f0357
12 changed files with 116 additions and 110 deletions
2
resources/sql/patches/drydockresourcetype.sql
Normal file
2
resources/sql/patches/drydockresourcetype.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_drydock.drydock_lease
|
||||||
|
ADD resourceType VARCHAR(128) NOT NULL COLLATE utf8_bin;
|
|
@ -411,8 +411,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionURITestCase' => 'applications/diffusion/request/__tests__/DiffusionURITestCase.php',
|
'DiffusionURITestCase' => 'applications/diffusion/request/__tests__/DiffusionURITestCase.php',
|
||||||
'DiffusionView' => 'applications/diffusion/view/DiffusionView.php',
|
'DiffusionView' => 'applications/diffusion/view/DiffusionView.php',
|
||||||
'DivinerListController' => 'applications/diviner/controller/DivinerListController.php',
|
'DivinerListController' => 'applications/diviner/controller/DivinerListController.php',
|
||||||
'DrydockAllocator' => 'applications/drydock/allocator/DrydockAllocator.php',
|
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
|
||||||
'DrydockAllocatorWorker' => 'applications/drydock/allocator/DrydockAllocatorWorker.php',
|
|
||||||
'DrydockApacheWebrootBlueprint' => 'applications/drydock/blueprint/webroot/DrydockApacheWebrootBlueprint.php',
|
'DrydockApacheWebrootBlueprint' => 'applications/drydock/blueprint/webroot/DrydockApacheWebrootBlueprint.php',
|
||||||
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
|
||||||
'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php',
|
'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php',
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2012 Facebook, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
final class DrydockAllocator {
|
|
||||||
|
|
||||||
private $resourceType;
|
|
||||||
private $lease;
|
|
||||||
|
|
||||||
public function setResourceType($resource_type) {
|
|
||||||
$this->resourceType = $resource_type;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResourceType() {
|
|
||||||
return $this->resourceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPendingLease() {
|
|
||||||
if (!$this->lease) {
|
|
||||||
$lease = new DrydockLease();
|
|
||||||
$lease->setStatus(DrydockLeaseStatus::STATUS_PENDING);
|
|
||||||
$lease->save();
|
|
||||||
|
|
||||||
$this->lease = $lease;
|
|
||||||
}
|
|
||||||
return $lease;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function allocate() {
|
|
||||||
$lease = $this->getPendingLease();
|
|
||||||
|
|
||||||
$data = array(
|
|
||||||
'type' => $this->getResourceType(),
|
|
||||||
'lease' => $lease->getID(),
|
|
||||||
);
|
|
||||||
|
|
||||||
$task = PhabricatorWorker::scheduleTask('DrydockAllocatorWorker', $data);
|
|
||||||
|
|
||||||
$lease->setTaskID($task->getID());
|
|
||||||
|
|
||||||
return $lease;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -33,13 +33,6 @@ abstract class DrydockBlueprint {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAllocator($type) {
|
|
||||||
$allocator = new DrydockAllocator();
|
|
||||||
$allocator->setResourceType($type);
|
|
||||||
|
|
||||||
return $allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
final public function acquireLease(
|
final public function acquireLease(
|
||||||
DrydockResource $resource,
|
DrydockResource $resource,
|
||||||
DrydockLease $lease) {
|
DrydockLease $lease) {
|
||||||
|
@ -101,13 +94,18 @@ abstract class DrydockBlueprint {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function executeAllocateResource() {
|
protected function executeAllocateResource(DrydockLease $lease) {
|
||||||
throw new Exception("This blueprint can not allocate resources!");
|
throw new Exception("This blueprint can not allocate resources!");
|
||||||
}
|
}
|
||||||
|
|
||||||
final public function allocateResource() {
|
final public function allocateResource(DrydockLease $lease) {
|
||||||
|
$this->activeLease = $lease;
|
||||||
|
$this->activeResource = null;
|
||||||
|
|
||||||
|
$this->log('Allocating Resource');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$resource = $this->executeAllocateResource();
|
$resource = $this->executeAllocateResource($lease);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$this->logException($ex);
|
$this->logException($ex);
|
||||||
$this->activeResource = null;
|
$this->activeResource = null;
|
||||||
|
|
|
@ -22,7 +22,7 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function executeAllocateResource() {
|
public function executeAllocateResource(DrydockLease $lease) {
|
||||||
$resource = $this->newResourceTemplate('EC2 Host');
|
$resource = $this->newResourceTemplate('EC2 Host');
|
||||||
|
|
||||||
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
|
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
|
||||||
|
@ -41,7 +41,7 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
|
||||||
|
|
||||||
$instance_id = (string)$xml->instancesSet[0]->item[0]->instanceId[0];
|
$instance_id = (string)$xml->instancesSet[0]->item[0]->instanceId[0];
|
||||||
|
|
||||||
$this->log('Started Instance: {$instance_id}');
|
$this->log("Started Instance: {$instance_id}");
|
||||||
$resource->setAttribute('instance.id', $instance_id);
|
$resource->setAttribute('instance.id', $instance_id);
|
||||||
$resource->save();
|
$resource->save();
|
||||||
|
|
||||||
|
|
|
@ -27,15 +27,16 @@ abstract class DrydockPhabricatorApplicationBlueprint
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function executeAllocateResource() {
|
public function executeAllocateResource(DrydockLease $lease) {
|
||||||
|
|
||||||
$resource = $this->newResourceTemplate('Phabricator');
|
$resource = $this->newResourceTemplate('Phabricator');
|
||||||
|
|
||||||
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
|
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
|
||||||
$resource->save();
|
$resource->save();
|
||||||
|
|
||||||
$allocator = $this->getAllocator('host');
|
$host = id(new DrydockLease())
|
||||||
$host = $allocator->allocate();
|
->setResourceType('host')
|
||||||
|
->queueForActivation();
|
||||||
|
|
||||||
$cmd = $host->waitUntilActive()->getInterface('command');
|
$cmd = $host->waitUntilActive()->getInterface('command');
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ EOSETUP
|
||||||
$lease->save();
|
$lease->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function executeAllocateResource() {
|
public function executeAllocateResource(DrydockLease $lease) {
|
||||||
|
|
||||||
$resource = $this->newResourceTemplate('Apache');
|
$resource = $this->newResourceTemplate('Apache');
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ final class DrydockManagementLeaseWorkflow
|
||||||
'help' => 'Resource type.',
|
'help' => 'Resource type.',
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'spec',
|
'name' => 'attributes',
|
||||||
'param' => 'name=value,...',
|
'param' => 'name=value,...',
|
||||||
'help' => 'Resource specficiation.',
|
'help' => 'Resource specficiation.',
|
||||||
),
|
),
|
||||||
|
@ -47,19 +47,18 @@ final class DrydockManagementLeaseWorkflow
|
||||||
"Specify a resource type with `--type`.");
|
"Specify a resource type with `--type`.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$spec = $args->getArg('spec');
|
$attributes = $args->getArg('attributes');
|
||||||
if ($spec) {
|
if ($attributes) {
|
||||||
$options = new PhutilSimpleOptions();
|
$options = new PhutilSimpleOptions();
|
||||||
$spec = $options->parse($spec);
|
$attributes = $options->parse($attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
$allocator = new DrydockAllocator();
|
$lease = new DrydockLease();
|
||||||
$allocator->setResourceType($resource_type);
|
$lease->setResourceType($resource_type);
|
||||||
if ($spec) {
|
if ($attributes) {
|
||||||
// TODO: Shove this in there.
|
$lease->setAttributes($attributes);
|
||||||
}
|
}
|
||||||
|
$lease->queueForActivation();
|
||||||
$lease = $allocator->allocate();
|
|
||||||
|
|
||||||
$root = dirname(phutil_get_library_root('phabricator'));
|
$root = dirname(phutil_get_library_root('phabricator'));
|
||||||
$wait = new ExecFuture(
|
$wait = new ExecFuture(
|
||||||
|
@ -67,14 +66,25 @@ final class DrydockManagementLeaseWorkflow
|
||||||
$root.'/scripts/drydock/drydock_control.php',
|
$root.'/scripts/drydock/drydock_control.php',
|
||||||
$lease->getID());
|
$lease->getID());
|
||||||
|
|
||||||
|
$cursor = 0;
|
||||||
foreach (Futures(array($wait))->setUpdateInterval(1) as $key => $future) {
|
foreach (Futures(array($wait))->setUpdateInterval(1) as $key => $future) {
|
||||||
if ($future) {
|
if ($future) {
|
||||||
$future->resolvex();
|
$future->resolvex();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Pull logs.
|
$logs = id(new DrydockLogQuery())
|
||||||
$console->writeErr("Working...\n");
|
->withLeaseIDs(array($lease->getID()))
|
||||||
|
->withAfterID($cursor)
|
||||||
|
->setOrder(DrydockLogQuery::ORDER_ID)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if ($logs) {
|
||||||
|
foreach ($logs as $log) {
|
||||||
|
$console->writeErr("%s\n", $log->getMessage());
|
||||||
|
}
|
||||||
|
$cursor = max(mpull($logs, 'getID'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$console->writeOut("Acquired Lease %s\n", $lease->getID());
|
$console->writeOut("Acquired Lease %s\n", $lease->getID());
|
||||||
|
|
|
@ -18,8 +18,13 @@
|
||||||
|
|
||||||
final class DrydockLogQuery extends PhabricatorOffsetPagedQuery {
|
final class DrydockLogQuery extends PhabricatorOffsetPagedQuery {
|
||||||
|
|
||||||
|
const ORDER_EPOCH = 'order-epoch';
|
||||||
|
const ORDER_ID = 'order-id';
|
||||||
|
|
||||||
private $resourceIDs;
|
private $resourceIDs;
|
||||||
private $leaseIDs;
|
private $leaseIDs;
|
||||||
|
private $afterID;
|
||||||
|
private $order = self::ORDER_EPOCH;
|
||||||
|
|
||||||
public function withResourceIDs(array $ids) {
|
public function withResourceIDs(array $ids) {
|
||||||
$this->resourceIDs = $ids;
|
$this->resourceIDs = $ids;
|
||||||
|
@ -31,26 +36,32 @@ final class DrydockLogQuery extends PhabricatorOffsetPagedQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setOrder($order) {
|
||||||
|
$this->order = $order;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withAfterID($id) {
|
||||||
|
$this->afterID = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
$table = new DrydockLog();
|
$table = new DrydockLog();
|
||||||
$conn_r = $table->establishConnection('r');
|
$conn_r = $table->establishConnection('r');
|
||||||
|
|
||||||
$where = $this->buildWhereClause($conn_r);
|
|
||||||
$order = $this->buildOrderClause($conn_r);
|
|
||||||
$limit = $this->buildLimitClause($conn_r);
|
|
||||||
|
|
||||||
$data = queryfx_all(
|
$data = queryfx_all(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'SELECT log.* FROM %T log %Q %Q %Q',
|
'SELECT log.* FROM %T log %Q %Q %Q',
|
||||||
$table->getTableName(),
|
$table->getTableName(),
|
||||||
$where,
|
$this->buildWhereClause($conn_r),
|
||||||
$order,
|
$this->buildOrderClause($conn_r),
|
||||||
$limit);
|
$this->buildLimitClause($conn_r));
|
||||||
|
|
||||||
return $table->loadAllFromArray($data);
|
return $table->loadAllFromArray($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildWhereClause($conn_r) {
|
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
if ($this->resourceIDs) {
|
if ($this->resourceIDs) {
|
||||||
|
@ -67,11 +78,25 @@ final class DrydockLogQuery extends PhabricatorOffsetPagedQuery {
|
||||||
$this->leaseIDs);
|
$this->leaseIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->afterID) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'id > %d',
|
||||||
|
$this->afterID);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildOrderClause($conn_r) {
|
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
||||||
return 'ORDER BY log.epoch DESC';
|
switch ($this->order) {
|
||||||
|
case self::ORDER_EPOCH:
|
||||||
|
return 'ORDER BY log.epoch DESC';
|
||||||
|
case self::ORDER_ID:
|
||||||
|
return 'ORDER BY id ASC';
|
||||||
|
default:
|
||||||
|
throw new Exception("Unknown order '{$this->order}'!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,11 @@ final class DrydockLease extends DrydockDAO {
|
||||||
|
|
||||||
protected $phid;
|
protected $phid;
|
||||||
protected $resourceID;
|
protected $resourceID;
|
||||||
|
protected $resourceType;
|
||||||
protected $until;
|
protected $until;
|
||||||
protected $ownerPHID;
|
protected $ownerPHID;
|
||||||
protected $attributes = array();
|
protected $attributes = array();
|
||||||
protected $status;
|
protected $status = DrydockLeaseStatus::STATUS_PENDING;
|
||||||
protected $taskID;
|
protected $taskID;
|
||||||
|
|
||||||
private $resource;
|
private $resource;
|
||||||
|
@ -76,10 +77,37 @@ final class DrydockLease extends DrydockDAO {
|
||||||
$this->getResourceID());
|
$this->getResourceID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function queueForActivation() {
|
||||||
|
if ($this->getID()) {
|
||||||
|
throw new Exception(
|
||||||
|
"Only new leases may be queued for activation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setStatus(DrydockLeaseStatus::STATUS_PENDING);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
// NOTE: Prevent a race where some eager worker quickly grabs the task
|
||||||
|
// before we can save the Task ID.
|
||||||
|
|
||||||
|
$this->openTransaction();
|
||||||
|
$this->beginReadLocking();
|
||||||
|
|
||||||
|
$this->reload();
|
||||||
|
|
||||||
|
$task = PhabricatorWorker::scheduleTask(
|
||||||
|
'DrydockAllocatorWorker',
|
||||||
|
$this->getID());
|
||||||
|
|
||||||
|
$this->setTaskID($task->getID());
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$this->endReadLocking();
|
||||||
|
$this->saveTransaction();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function release() {
|
public function release() {
|
||||||
|
|
||||||
// TODO: Insert a cleanup task into the taskmaster queue.
|
|
||||||
|
|
||||||
$this->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
|
$this->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
|
|
|
@ -19,20 +19,18 @@
|
||||||
final class DrydockAllocatorWorker extends PhabricatorWorker {
|
final class DrydockAllocatorWorker extends PhabricatorWorker {
|
||||||
|
|
||||||
protected function doWork() {
|
protected function doWork() {
|
||||||
$data = $this->getTaskData();
|
$lease_id = $this->getTaskData();
|
||||||
|
|
||||||
$lease = id(new DrydockLease())->loadOneWhere(
|
$lease = id(new DrydockLease())->load($lease_id);
|
||||||
'id = %d',
|
|
||||||
$data['lease']);
|
|
||||||
if (!$lease) {
|
if (!$lease) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$type = $data['type'];
|
$type = $lease->getResourceType();
|
||||||
|
|
||||||
$candidates = id(new DrydockResource())->loadAllWhere(
|
$candidates = id(new DrydockResource())->loadAllWhere(
|
||||||
'type = %s AND status = %s',
|
'type = %s AND status = %s',
|
||||||
$type,
|
$lease->getResourceType(),
|
||||||
DrydockResourceStatus::STATUS_OPEN);
|
DrydockResourceStatus::STATUS_OPEN);
|
||||||
|
|
||||||
if ($candidates) {
|
if ($candidates) {
|
||||||
|
@ -64,7 +62,7 @@ final class DrydockAllocatorWorker extends PhabricatorWorker {
|
||||||
shuffle($blueprints);
|
shuffle($blueprints);
|
||||||
|
|
||||||
$blueprint = head($blueprints);
|
$blueprint = head($blueprints);
|
||||||
$resource = $blueprint->allocateResource();
|
$resource = $blueprint->allocateResource($lease);
|
||||||
}
|
}
|
||||||
|
|
||||||
$blueprint = $resource->getBlueprint();
|
$blueprint = $resource->getBlueprint();
|
|
@ -1024,6 +1024,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
||||||
'type' => 'sql',
|
'type' => 'sql',
|
||||||
'name' => $this->getPatchPath('drydocktaskid.sql'),
|
'name' => $this->getPatchPath('drydocktaskid.sql'),
|
||||||
),
|
),
|
||||||
|
'drydockresoucetype.sql' => array(
|
||||||
|
'type' => 'sql',
|
||||||
|
'name' => $this->getPatchPath('drydockresourcetype.sql'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue