1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

More Drydock Stuff

Summary:
  - Still really really rough.
  - Adds a full synchronous mode for debugging.
  - Adds some logging.
  - It can now allocate EC2 machines and put webroots on them in a hacky, terrible way.
  - Adds a base query class.

Test Plan: oh hey look a test page? http://ec2-50-18-65-151.us-west-1.compute.amazonaws.com:2011/

Reviewers: btrahan

Reviewed By: btrahan

CC: aran, epriestley

Maniphest Tasks: T1049

Differential Revision: https://secure.phabricator.com/D2026
This commit is contained in:
epriestley 2012-03-26 20:54:26 -07:00
parent bf2422afc8
commit 914f044b62
36 changed files with 905 additions and 68 deletions

View file

@ -0,0 +1,10 @@
CREATE TABLE phabricator_drydock.drydock_log (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
resourceID INT UNSIGNED,
leaseID INT UNSIGNED,
epoch INT UNSIGNED NOT NULL,
message LONGTEXT COLLATE utf8_general_ci NOT NULL,
KEY (resourceID, epoch),
KEY (leaseID, epoch),
KEY (epoch)
) ENGINE=InnoDB, COLLATE utf8_general_ci;

View file

@ -26,11 +26,20 @@ phutil_require_module('phutil', 'future/exec');
PhutilServiceProfiler::installEchoListener();
$allocator = new DrydockAllocator();
$allocator->setResourceType('host');
$allocator->makeSynchronous();
$allocator->setResourceType('webroot');
$lease = $allocator->allocate();
$lease->waitUntilActive();
$cmd = $lease->getInterface('webroot');
echo "URI: ".$cmd->getURI()."\n";
$lease->release();
die("Done.\n");
$i_file = $lease->getInterface('command');
list($stdout) = $i_file->execx('ls / ; echo -- ; uptime ; echo -- ; uname -n');

View file

@ -354,6 +354,8 @@ phutil_register_library_map(array(
'DiffusionView' => 'applications/diffusion/view/base',
'DrydockAllocator' => 'applications/drydock/allocator/resource',
'DrydockAllocatorWorker' => 'applications/drydock/allocator/worker',
'DrydockApacheWebrootBlueprint' => 'applications/drydock/blueprint/webroot/apache',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/apache',
'DrydockBlueprint' => 'applications/drydock/blueprint/base',
'DrydockCommandInterface' => 'applications/drydock/interface/command/base',
'DrydockConstants' => 'applications/drydock/constants/base',
@ -366,12 +368,17 @@ phutil_register_library_map(array(
'DrydockLeaseStatus' => 'applications/drydock/constants/leasestatus',
'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/local',
'DrydockLocalHostBlueprint' => 'applications/drydock/blueprint/localhost',
'DrydockLog' => 'applications/drydock/storage/log',
'DrydockLogController' => 'applications/drydock/controller/log',
'DrydockLogQuery' => 'applications/drydock/query/log',
'DrydockPhabricatorApplicationBlueprint' => 'applications/drydock/blueprint/application/phabricator',
'DrydockRemoteHostBlueprint' => 'applications/drydock/blueprint/remotehost',
'DrydockResource' => 'applications/drydock/storage/resource',
'DrydockResourceAllocateController' => 'applications/drydock/controller/resourceallocate',
'DrydockResourceListController' => 'applications/drydock/controller/resourcelist',
'DrydockResourceStatus' => 'applications/drydock/constants/resourcestatus',
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/ssh',
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/base',
'HeraldAction' => 'applications/herald/storage/action',
'HeraldActionConfig' => 'applications/herald/config/action',
'HeraldAllRulesController' => 'applications/herald/controller/all',
@ -683,6 +690,7 @@ phutil_register_library_map(array(
'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
'PhabricatorObjectHandleStatus' => 'applications/phid/handle/const/status',
'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/offsetpaged',
'PhabricatorOwnerPathQuery' => 'applications/owners/query/path',
'PhabricatorOwnersController' => 'applications/owners/controller/base',
'PhabricatorOwnersDAO' => 'applications/owners/storage/base',
@ -727,6 +735,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransaction' => 'applications/project/storage/transaction',
'PhabricatorProjectTransactionType' => 'applications/project/constants/transaction',
'PhabricatorProjectUpdateController' => 'applications/project/controller/update',
'PhabricatorQuery' => 'infrastructure/query/base',
'PhabricatorRedirectController' => 'applications/base/controller/redirect',
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/refresh',
'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential',
@ -1167,6 +1176,7 @@ phutil_register_library_map(array(
'DiffusionDiffController' => 'DiffusionController',
'DiffusionEmptyResultView' => 'DiffusionView',
'DiffusionExternalController' => 'DiffusionController',
'DiffusionFileContentQuery' => 'DiffusionQuery',
'DiffusionGitBranchQuery' => 'DiffusionBranchQuery',
'DiffusionGitBranchQueryTestCase' => 'PhabricatorTestCase',
'DiffusionGitBrowseQuery' => 'DiffusionBrowseQuery',
@ -1212,6 +1222,8 @@ phutil_register_library_map(array(
'DiffusionURITestCase' => 'ArcanistPhutilTestCase',
'DiffusionView' => 'AphrontView',
'DrydockAllocatorWorker' => 'PhabricatorWorker',
'DrydockApacheWebrootBlueprint' => 'DrydockBlueprint',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
'DrydockCommandInterface' => 'DrydockInterface',
'DrydockController' => 'PhabricatorController',
'DrydockDAO' => 'PhabricatorLiskDAO',
@ -1221,12 +1233,17 @@ phutil_register_library_map(array(
'DrydockLeaseStatus' => 'DrydockConstants',
'DrydockLocalCommandInterface' => 'DrydockCommandInterface',
'DrydockLocalHostBlueprint' => 'DrydockBlueprint',
'DrydockLog' => 'DrydockDAO',
'DrydockLogController' => 'DrydockController',
'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery',
'DrydockPhabricatorApplicationBlueprint' => 'DrydockBlueprint',
'DrydockRemoteHostBlueprint' => 'DrydockBlueprint',
'DrydockResource' => 'DrydockDAO',
'DrydockResourceAllocateController' => 'DrydockController',
'DrydockResourceListController' => 'DrydockController',
'DrydockResourceStatus' => 'DrydockConstants',
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
'DrydockWebrootInterface' => 'DrydockInterface',
'HeraldAction' => 'HeraldDAO',
'HeraldAllRulesController' => 'HeraldController',
'HeraldApplyTranscript' => 'HeraldDAO',
@ -1468,6 +1485,7 @@ phutil_register_library_map(array(
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorObjectGraph' => 'AbstractDirectedGraph',
'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOwnersController' => 'PhabricatorController',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDeleteController' => 'PhabricatorOwnersController',

View file

@ -398,6 +398,7 @@ class AphrontDefaultApplicationConfiguration
'edit/(?P<id>\d+)/' => 'DrydockhostEditController',
),
'lease/' => 'DrydockLeaseListController',
'log/' => 'DrydockLogController',
),
'/chatlog/' => array(

View file

@ -20,6 +20,7 @@ final class DrydockAllocator {
private $resourceType;
private $lease;
private $synchronous;
public function setResourceType($resource_type) {
$this->resourceType = $resource_type;
@ -30,6 +31,10 @@ final class DrydockAllocator {
return $this->resourceType;
}
public function makeSynchronous() {
$this->synchronous = true;
}
public function getPendingLease() {
if (!$this->lease) {
$lease = new DrydockLease();
@ -44,13 +49,21 @@ final class DrydockAllocator {
public function allocate() {
$lease = $this->getPendingLease();
$task = new PhabricatorWorkerTask();
$task->setTaskClass('DrydockAllocatorWorker');
$task->setData(array(
'type' => $this->getResourceType(),
'lease' => $lease->getID(),
));
$task->save();
$data = array(
'type' => $this->getResourceType(),
'lease' => $lease->getID(),
);
if ($this->synchronous) {
$data['synchronous'] = true;
$worker = new DrydockAllocatorWorker($data);
$worker->executeTask();
} else {
$task = new PhabricatorWorkerTask();
$task->setTaskClass('DrydockAllocatorWorker');
$task->setData($data);
$task->save();
}
return $lease;
}

View file

@ -6,6 +6,7 @@
phutil_require_module('phabricator', 'applications/drydock/allocator/worker');
phutil_require_module('phabricator', 'applications/drydock/constants/leasestatus');
phutil_require_module('phabricator', 'applications/drydock/storage/lease');
phutil_require_module('phabricator', 'infrastructure/daemon/workers/storage/task');

View file

@ -40,7 +40,6 @@ final class DrydockAllocatorWorker extends PhabricatorWorker {
$resource = head($candidates);
} else {
$blueprints = DrydockBlueprint::getAllBlueprintsForResource($type);
foreach ($blueprints as $key => $blueprint) {
if (!$blueprint->canAllocateResources()) {
unset($blueprints[$key]);
@ -52,11 +51,11 @@ final class DrydockAllocatorWorker extends PhabricatorWorker {
$lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN);
$lease->save();
/* TODO
$lease->updateLog(
DrydockBlueprint::writeLog(
null,
$lease,
"There are no resources of type '{$type}' available, and no ".
"blueprints which can allocate new ones.");
*/
return;
}
@ -65,12 +64,16 @@ final class DrydockAllocatorWorker extends PhabricatorWorker {
shuffle($blueprints);
$blueprint = head($blueprints);
if (isset($data['synchronous'])) {
$blueprint->makeSynchronous();
}
$resource = $blueprint->allocateResource();
}
$lease->setResourceID($resource->getID());
$lease->setStatus(DrydockLeaseStatus::STATUS_ACTIVE);
$lease->save();
$blueprint = $resource->getBlueprint();
$blueprint->acquireLease($resource, $lease);
}
}

View file

@ -0,0 +1,67 @@
<?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.
*/
abstract class DrydockPhabricatorApplicationBlueprint
extends DrydockBlueprint {
public function getType() {
return 'application';
}
public function canAllocateResources() {
return true;
}
public function executeAllocateResource() {
$resource = $this->newResourceTemplate('Phabricator');
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
$resource->save();
$allocator = $this->getAllocator('host');
$host = $allocator->allocate();
$cmd = $host->waitUntilActive()->getInterface('command');
$cmd->execx(<<<EOINSTALL
yum install git &&
mkdir -p /opt/drydock &&
cd /opt/drydock &&
git clone git://github.com/facebook/libphutil.git &&
git clone git://github.com/facebook/arcanist.git &&
git clone git://github.com/facebook/phabricator.git
EOINSTALL
);
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
$resource->save();
return $resource;
}
public function getInterface(
DrydockResource $resource,
DrydockLease $lease,
$type) {
throw new Exception("No interface of type '{$type}'.");
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/blueprint/base');
phutil_require_module('phabricator', 'applications/drydock/constants/resourcestatus');
phutil_require_source('DrydockPhabricatorApplicationBlueprint.php');

View file

@ -18,20 +18,114 @@
abstract class DrydockBlueprint {
private $activeLease;
private $activeResource;
private $synchronous;
final public function makeSynchronous() {
$this->synchronous = true;
}
abstract public function getType();
abstract public function getInterface(
DrydockResource $resource,
DrydockLease $lease,
$type);
protected function executeAcquireLease(
DrydockResource $resource,
DrydockLease $lease) {
return;
}
protected function getAllocator($type) {
$allocator = new DrydockAllocator();
if ($this->synchronous) {
$allocator->makeSynchronous();
}
$allocator->setResourceType($type);
return $allocator;
}
final public function acquireLease(
DrydockResource $resource,
DrydockLease $lease) {
$this->activeResource = $resource;
$this->activeLease = $lease;
$this->log('Acquiring Lease');
try {
$this->executeAcquireLease($resource, $lease);
} catch (Exception $ex) {
$this->logException($ex);
$this->activeResource = null;
$this->activeLease = null;
throw $ex;
}
$lease->setResourceID($resource->getID());
$lease->setStatus(DrydockLeaseStatus::STATUS_ACTIVE);
$lease->save();
$this->activeResource = null;
$this->activeLease = null;
}
protected function logException(Exception $ex) {
$this->log($ex->getMessage());
}
protected function log($message) {
self::writeLog(
$this->activeResource,
$this->activeLease,
$message);
}
public static function writeLog(
DrydockResource $resource = null,
DrydockLease $lease = null,
$message) {
$log = id(new DrydockLog())
->setEpoch(time())
->setMessage($message);
if ($resource) {
$log->setResourceID($resource->getID());
}
if ($lease) {
$log->setLeaseID($lease->getID());
}
$log->save();
}
public function canAllocateResources() {
return false;
}
public function allocateResource() {
protected function executeAllocateResource() {
throw new Exception("This blueprint can not allocate resources!");
}
final public function allocateResource() {
try {
$resource = $this->executeAllocateResource();
} catch (Exception $ex) {
$this->logException($ex);
$this->activeResource = null;
throw $ex;
}
return $resource;
}
public static function getAllBlueprints() {
static $list = null;
@ -58,4 +152,19 @@ abstract class DrydockBlueprint {
return idx($groups, $type, array());
}
protected function newResourceTemplate($name) {
$resource = new DrydockResource();
$resource->setBlueprintClass(get_class($this));
$resource->setType($this->getType());
$resource->setStatus(DrydockResourceStatus::STATUS_PENDING);
$resource->setName($name);
$resource->save();
$this->activeResource = $resource;
$this->log('New Template');
return $resource;
}
}

View file

@ -6,6 +6,12 @@
phutil_require_module('phabricator', 'applications/drydock/allocator/resource');
phutil_require_module('phabricator', 'applications/drydock/constants/leasestatus');
phutil_require_module('phabricator', 'applications/drydock/constants/resourcestatus');
phutil_require_module('phabricator', 'applications/drydock/storage/log');
phutil_require_module('phabricator', 'applications/drydock/storage/resource');
phutil_require_module('phutil', 'symbols');
phutil_require_module('phutil', 'utils');

View file

@ -22,16 +22,8 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
return true;
}
public function allocateResource() {
echo "ALLOCATING EC2 HOST!\n";
$resource = new DrydockResource();
$resource->setBlueprintClass(get_class($this));
$resource->setType($this->getType());
$resource->setStatus(DrydockResourceStatus::STATUS_PENDING);
$resource->setName('EC2 Host');
$resource->save();
public function executeAllocateResource() {
$resource = $this->newResourceTemplate('EC2 Host');
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
$resource->save();
@ -49,7 +41,9 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
$instance_id = (string)$xml->instancesSet[0]->item[0]->instanceId[0];
echo "instance id: ".$instance_id."\n";
$this->log('Started Instance: {$instance_id}');
$resource->setAttribute('instance.id', $instance_id);
$resource->save();
$n = 1;
do {
@ -59,25 +53,23 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
'InstanceId.1' => $instance_id,
));
var_dump($xml);
$instance = $xml->reservationSet[0]->item[0]->instancesSet[0]->item[0];
$state = (string)$instance->instanceState[0]->name;
echo "State = {$state}\n";
if ($state == 'pending') {
sleep(min($n++, 15));
} else if ($state == 'running') {
break;
} else {
// TODO: Communicate this failure.
$this->log("EC2 host reported in unknown state '{$state}'.");
$resource->setStatus(DrydockResourceStatus::STATUS_BROKEN);
$resource->save();
}
} while (true);
$this->log('Waiting for Init');
$n = 1;
do {
@ -87,8 +79,6 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
'InstanceId' => $instance_id,
));
var_dump($xml);
$item = $xml->instanceStatusSet[0]->item[0];
$system_status = (string)$item->systemStatus->status[0];
@ -101,17 +91,15 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
($instance_status == 'ok')) {
break;
} else {
// TODO: Communicate this failure.
$this->log(
"EC2 system and instance status in bad states: ".
"'{$system_status}', '{$instance_status}'.");
$resource->setStatus(DrydockResourceStatus::STATUS_BROKEN);
$resource->save();
}
} while (true);
// TODO: This is a fuzz factor because sshd doesn't come up immediately
// once EC2 reports the machine reachable. Validate that SSH is actually
// responsive.
sleep(120);
$resource->setAttributes(
array(
'host' => (string)$instance->dnsName,
@ -119,6 +107,24 @@ final class DrydockEC2HostBlueprint extends DrydockRemoteHostBlueprint {
'ssh-keyfile' => '/Users/epriestley/.ssh/id_ec2w',
));
$resource->setName($resource->getName().' ('.$instance->dnsName.')');
$resource->save();
$this->log('Waiting for SSH');
// SSH isn't immediately responsive, so wait for it to actually come up.
$cmd = $this->getInterface($resource, new DrydockLease(), 'command');
$n = 1;
do {
list($err) = $cmd->exec('true');
if ($err) {
sleep(min($n++, 15));
} else {
break;
}
} while (true);
$this->log('SSH OK');
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
$resource->save();

View file

@ -9,7 +9,7 @@
phutil_require_module('phabricator', 'applications/drydock/blueprint/remotehost');
phutil_require_module('phabricator', 'applications/drydock/constants/resourcestatus');
phutil_require_module('phabricator', 'applications/drydock/interface/command/ssh');
phutil_require_module('phabricator', 'applications/drydock/storage/resource');
phutil_require_module('phabricator', 'applications/drydock/storage/lease');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'future/aws/ec2');

View file

@ -0,0 +1,129 @@
<?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 DrydockApacheWebrootBlueprint
extends DrydockBlueprint {
public function getType() {
return 'webroot';
}
public function canAllocateResources() {
return true;
}
public function executeAcquireLease(
DrydockResource $resource,
DrydockLease $lease) {
$key = Filesystem::readRandomCharacters(12);
$ports = $resource->getAttribute('ports', array());
for ($ii = 2000; ; $ii++) {
if (empty($ports[$ii])) {
$ports[$ii] = $lease->getID();
$port = $ii;
break;
}
}
$resource->setAttribute('ports', $ports);
$resource->save();
$host = $resource->getAttribute('host');
$lease->setAttribute('port', $port);
$lease->setAttribute('key', $key);
$lease->save();
$config = <<<EOCONFIG
Listen *:{$port}
<VirtualHost *:{$port}>
DocumentRoot /opt/drydock/webroot/{$key}/
ServerName {$host}
</VirtualHost>
EOCONFIG;
$cmd = $this->getInterface($resource, $lease, 'command');
$cmd->execx(<<<EOSETUP
sudo mkdir -p %s &&
sudo sh -c %s &&
sudo /etc/init.d/httpd restart
EOSETUP
,
"/opt/drydock/webroot/{$key}/",
csprintf(
'echo %s > %s',
$config,
"/etc/httpd/conf.d/drydock-{$key}.conf"));
$lease->setAttribute('uri', "http://{$host}:{$port}/");
$lease->save();
}
public function executeAllocateResource() {
$resource = $this->newResourceTemplate('Apache');
$resource->setStatus(DrydockResourceStatus::STATUS_ALLOCATING);
$resource->save();
$allocator = $this->getAllocator('host');
$host = $allocator->allocate();
$cmd = $host->waitUntilActive()->getInterface('command');
$cmd->execx(<<<EOINSTALL
(yes | sudo yum install httpd) && sudo mkdir -p /opt/drydock/webroot/
EOINSTALL
);
$resource->setAttribute('lease.host', $host->getID());
$resource->setAttribute('host', $host->getResource()->getAttribute('host'));
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
$resource->save();
return $resource;
}
public function getInterface(
DrydockResource $resource,
DrydockLease $lease,
$type) {
switch ($type) {
case 'webroot':
$iface = new DrydockApacheWebrootInterface();
$iface->setConfiguration(
array(
'uri' => $lease->getAttribute('uri'),
));
return $iface;
case 'command':
$host_lease_id = $resource->getAttribute('lease.host');
$host_lease = id(new DrydockLease())->load($host_lease_id);
$host_lease->attachResource($host_lease->loadResource());
return $host_lease->getInterface($type);
}
throw new Exception("No interface of type '{$type}'.");
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/blueprint/base');
phutil_require_module('phabricator', 'applications/drydock/constants/resourcestatus');
phutil_require_module('phabricator', 'applications/drydock/interface/webroot/apache');
phutil_require_module('phabricator', 'applications/drydock/storage/lease');
phutil_require_module('phutil', 'filesystem');
phutil_require_module('phutil', 'utils');
phutil_require_module('phutil', 'xsprintf/csprintf');
phutil_require_source('DrydockApacheWebrootBlueprint.php');

View file

@ -43,28 +43,14 @@ abstract class DrydockController extends PhabricatorController {
}
final protected function buildSideNav($selected) {
$items = array(
'resourcelist' => array(
'href' => '/drydock/resource/',
'name' => 'Resources',
),
'leaselist' => array(
'href' => '/drydock/lease/',
'name' => 'Leases',
),
);
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/drydock/'));
$nav->addFilter('resource', 'Resources');
$nav->addFilter('lease', 'Leases');
$nav->addSpacer();
$nav->addFilter('log', 'Logs');
$nav = new AphrontSideNavView();
foreach ($items as $key => $info) {
$nav->addNavItem(
phutil_render_tag(
'a',
array(
'href' => $info['href'],
'class' => ($key == $selected ? 'aphront-side-nav-selected' : null),
),
phutil_escape_html($info['name'])));
}
$nav->selectFilter($selected, 'resource');
return $nav;
}

View file

@ -9,9 +9,9 @@
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phabricator', 'view/layout/sidenav');
phutil_require_module('phabricator', 'view/layout/sidenavfilter');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'parser/uri');
phutil_require_module('phutil', 'utils');

View file

@ -22,7 +22,7 @@ final class DrydockLeaseListController extends DrydockController {
$request = $this->getRequest();
$user = $request->getUser();
$nav = $this->buildSideNav('leaselist');
$nav = $this->buildSideNav('lease');
$pager = new AphrontPagerView();
$pager->setURI(new PhutilURI('/drydock/lease/'), 'page');

View file

@ -0,0 +1,87 @@
<?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 DrydockLogController extends DrydockController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$nav = $this->buildSideNav('log');
$query = new DrydockLogQuery();
$resource_ids = $request->getStrList('resource');
if ($resource_ids) {
$query->withResourceIDs($resource_ids);
}
$lease_ids = $request->getStrList('lease');
if ($lease_ids) {
$query->withLeaseIDs($lease_ids);
}
$pager = new AphrontPagerView();
$pager->setPageSize(500);
$pager->setOffset($request->getInt('offset'));
$pager->setURI($request->getRequestURI(), 'offset');
$logs = $query->executeWithPager($pager);
$rows = array();
foreach ($logs as $log) {
$rows[] = array(
$log->getResourceID(),
$log->getLeaseID(),
phutil_escape_html($log->getMessage()),
phabricator_datetime($log->getEpoch(), $user),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Resource',
'Lease',
'Message',
'Date',
));
$table->setColumnClasses(
array(
'',
'',
'wide',
'',
));
$panel = new AphrontPanelView();
$panel->setHeader('Drydock Logs');
$panel->appendChild($table);
$panel->appendChild($pager);
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Logs',
));
}
}

View file

@ -0,0 +1,19 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/controller/base');
phutil_require_module('phabricator', 'applications/drydock/query/log');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_source('DrydockLogController.php');

View file

@ -22,7 +22,7 @@ final class DrydockResourceListController extends DrydockController {
$request = $this->getRequest();
$user = $request->getUser();
$nav = $this->buildSideNav('resourcelist');
$nav = $this->buildSideNav('resource');
$pager = new AphrontPagerView();
$pager->setURI(new PhutilURI('/drydock/resource/'), 'page');

View file

@ -22,8 +22,11 @@ final class DrydockSSHCommandInterface extends DrydockCommandInterface {
$argv = func_get_args();
$full_command = call_user_func_array('csprintf', $argv);
// NOTE: The "-t -t" is for psuedo-tty allocation so we can "sudo" on some
// systems, but maybe more trouble than it's worth?
return new ExecFuture(
'ssh -o StrictHostKeyChecking=no -i %s %s@%s -- %s',
'ssh -t -t -o StrictHostKeyChecking=no -i %s %s@%s -- %s',
$this->getConfig('ssh-keyfile'),
$this->getConfig('user'),
$this->getConfig('host'),

View file

@ -0,0 +1,25 @@
<?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 DrydockApacheWebrootInterface extends DrydockWebrootInterface {
public function getURI() {
return $this->getConfig('uri');
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/interface/webroot/base');
phutil_require_source('DrydockApacheWebrootInterface.php');

View file

@ -0,0 +1,27 @@
<?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.
*/
abstract class DrydockWebrootInterface extends DrydockInterface {
final public function getInterfaceType() {
return 'webroot';
}
abstract public function getURI();
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/interface/base');
phutil_require_source('DrydockWebrootInterface.php');

View file

@ -0,0 +1,77 @@
<?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 DrydockLogQuery extends PhabricatorOffsetPagedQuery {
private $resourceIDs;
private $leaseIDs;
public function withResourceIDs(array $ids) {
$this->resourceIDs = $ids;
return $this;
}
public function withLeaseIDs(array $ids) {
$this->leaseIDs = $ids;
return $this;
}
public function execute() {
$table = new DrydockLog();
$conn_r = $table->establishConnection('r');
$where = $this->buildWhereClause($conn_r);
$order = $this->buildOrderClause($conn_r);
$limit = $this->buildLimitClause($conn_r);
$data = queryfx_all(
$conn_r,
'SELECT log.* FROM %T log %Q %Q %Q',
$table->getTableName(),
$where,
$order,
$limit);
return $table->loadAllFromArray($data);
}
private function buildWhereClause($conn_r) {
$where = array();
if ($this->resourceIDs) {
$where[] = qsprintf(
$conn_r,
'resourceID IN (%Ld)',
$this->resourceIDs);
}
if ($this->leaseIDs) {
$where[] = qsprintf(
$conn_r,
'leaseID IN (%Ld)',
$this->leaseIDs);
}
return $this->formatWhereClause($where);
}
private function buildOrderClause($conn_r) {
return 'ORDER BY log.epoch DESC';
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/storage/log');
phutil_require_module('phabricator', 'infrastructure/query/offsetpaged');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_source('DrydockLogQuery.php');

View file

@ -36,6 +36,15 @@ final class DrydockLease extends DrydockDAO {
) + parent::getConfiguration();
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
return $this;
}
public function getAttribute($key, $default = null) {
return idx($this->attributes, $key, $default);
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_DRYL);
@ -87,6 +96,7 @@ final class DrydockLease extends DrydockDAO {
}
public function waitUntilActive() {
$this->reload();
while (true) {
switch ($this->status) {
case DrydockLeaseStatus::STATUS_ACTIVE:

View file

@ -0,0 +1,32 @@
<?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 DrydockLog extends DrydockDAO {
protected $resourceID;
protected $leaseID;
protected $epoch;
protected $message;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
) + parent::getConfiguration();
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/drydock/storage/base');
phutil_require_source('DrydockLog.php');

View file

@ -29,7 +29,6 @@ final class DrydockResource extends DrydockDAO {
protected $capabilities = array();
protected $ownerPHID;
private $blueprint;
public function getConfiguration() {
@ -51,6 +50,11 @@ final class DrydockResource extends DrydockDAO {
return idx($this->attributes, $key, $default);
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
return $this;
}
public function getCapability($key, $default = null) {
return idx($this->capbilities, $key, $default);
}

View file

@ -0,0 +1,30 @@
<?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.
*/
abstract class PhabricatorQuery {
abstract public function execute();
final protected function formatWhereClause(array $parts) {
if (!$parts) {
return '';
}
return 'WHERE ('.implode(') AND (', $parts).')';
}
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('PhabricatorQuery.php');

View file

@ -0,0 +1,59 @@
<?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.
*/
/**
* A query class which uses offset/limit paging. Provides logic and accessors
* for offsets and limits.
*/
abstract class PhabricatorOffsetPagedQuery extends PhabricatorQuery {
private $offset;
private $limit;
final public function setOffset($offset) {
$this->offset = $offset;
return $this;
}
final public function setLimit($limit) {
$this->limit = $limit;
return $this;
}
final protected function buildLimitClause(AphrontDatabaseConnection $conn_r) {
if ($this->limit && $this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, $this->limit);
} else if ($this->limit) {
return qsprintf($conn_r, 'LIMIT %d', $this->limit);
} else if ($this->offset) {
return qsprintf($conn_r, 'LIMIT %d, %d', $this->offset, PHP_INT_MAX);
} else {
return '';
}
}
final public function executeWithPager(AphrontPagerView $pager) {
$this->setLimit($pager->getPageSize() + 1);
$this->setOffset($pager->getOffset());
$results = $this->execute();
return $pager->sliceResults($results);
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'infrastructure/query/base');
phutil_require_module('phabricator', 'storage/qsprintf');
phutil_require_source('PhabricatorOffsetPagedQuery.php');