1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-20 13:52:40 +01:00

Implement support for leasing from Drydock hosts in Harbormaster

Summary:
This adds LeaseHostBuildStepImplementation for getting leases on hosts in Drydock via Harbormaster.  It stores the resulting lease in an artifact.

There is also a few bug fixes as well.

Test Plan: Created a build plan with a "Lease Host" build step.  Ran the build plan and saw the build pass and the artifact in the database.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T1049, T4111

Differential Revision: https://secure.phabricator.com/D7706
This commit is contained in:
James Rhodes 2013-12-05 12:46:23 +11:00
parent 53250d84df
commit d8d1173f52
8 changed files with 190 additions and 9 deletions

View file

@ -775,6 +775,7 @@ phutil_register_library_map(array(
'JavelinUIExample' => 'applications/uiexample/examples/JavelinUIExample.php',
'JavelinViewExample' => 'applications/uiexample/examples/JavelinViewExample.php',
'JavelinViewExampleServerView' => 'applications/uiexample/examples/JavelinViewExampleServerView.php',
'LeaseHostBuildStepImplementation' => 'applications/harbormaster/step/LeaseHostBuildStepImplementation.php',
'LegalpadConstants' => 'applications/legalpad/constants/LegalpadConstants.php',
'LegalpadController' => 'applications/legalpad/controller/LegalpadController.php',
'LegalpadDAO' => 'applications/legalpad/storage/LegalpadDAO.php',
@ -3154,6 +3155,7 @@ phutil_register_library_map(array(
'JavelinUIExample' => 'PhabricatorUIExample',
'JavelinViewExample' => 'PhabricatorUIExample',
'JavelinViewExampleServerView' => 'AphrontView',
'LeaseHostBuildStepImplementation' => 'BuildStepImplementation',
'LegalpadController' => 'PhabricatorController',
'LegalpadDAO' => 'PhabricatorLiskDAO',
'LegalpadDocument' =>

View file

@ -57,13 +57,14 @@ abstract class BuildStepImplementation {
}
/**
* Loads the settings for this build step implementation from a build target.
* Loads the settings for this build step implementation from a build
* step or target.
*/
public final function loadSettings(HarbormasterBuildTarget $build_target) {
public final function loadSettings($build_object) {
$this->settings = array();
$this->validateSettingDefinitions();
foreach ($this->getSettingDefinitions() as $name => $opt) {
$this->settings[$name] = $build_target->getDetail($name);
$this->settings[$name] = $build_object->getDetail($name);
}
return $this->settings;
}
@ -94,4 +95,57 @@ abstract class BuildStepImplementation {
public function getSettingRemarkupInstructions() {
return null;
}
/**
* Return the name of artifacts produced by this command.
*
* Something like:
*
* return array(
* 'some_name_input_by_user' => 'host');
*
* Future steps will calculate all available artifact mappings
* before them and filter on the type.
*
* @return array The mappings of artifact names to their types.
*/
public function getArtifactMappings() {
return array();
}
/**
* Returns a list of all artifacts made available by previous build steps.
*/
public static function getAvailableArtifacts(
HarbormasterBuildStep $current_build_step,
$artifact_type) {
$build_plan_phid = $current_build_step->getBuildPlanPHID();
$build_plan = id(new HarbormasterBuildPlanQuery())
->withPHIDs(array($build_plan_phid))
->executeOne();
$build_steps = $build_plan->loadOrderedBuildSteps();
$previous_implementations = array();
for ($i = 0; $i < count($build_steps); $i++) {
if ($build_steps[$i]->getPHID() === $current_build_step->getPHID()) {
break;
}
$previous_implementations[$i] = $build_steps[$i]->getStepImplementation();
}
$artifact_arrays = mpull($previous_implementations, 'getArtifactMappings');
$artifacts = array();
foreach ($artifact_arrays as $array) {
foreach ($array as $name => $type) {
if ($type !== $artifact_type) {
continue;
}
$artifacts[$name] = $type;
}
}
return $artifacts;
}
}

View file

@ -0,0 +1,76 @@
<?php
final class LeaseHostBuildStepImplementation
extends BuildStepImplementation {
public function getName() {
return pht('Lease Host');
}
public function getGenericDescription() {
return pht('Obtain a lease on a Drydock host for performing builds.');
}
public function getDescription() {
$settings = $this->getSettings();
return pht(
'Obtain a lease on a Drydock host whose platform is \'%s\'.',
$settings['platform']);
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$settings = $this->getSettings();
// Create the lease.
$lease = new DrydockLease();
$lease->setResourceType('host');
$lease->setAttributes(
array('platform' => $settings['platform']));
$lease->queueForActivation();
// Wait until the lease is fulfilled.
// TODO: This will throw an exception if the lease can't be fulfilled;
// we should treat that as build failure not build error.
$lease->waitUntilActive();
// Create the associated artifact.
$artifact = $build->createArtifact(
$build_target,
$settings['name'],
'host');
$artifact->setArtifactData(array(
'drydock-lease' => $lease->getID()));
$artifact->save();
}
public function validateSettings() {
$settings = $this->getSettings();
if ($settings['name'] === null || !is_string($settings['name'])) {
return false;
}
if ($settings['platform'] === null || !is_string($settings['platform'])) {
return false;
}
return true;
}
public function getSettingDefinitions() {
return array(
'name' => array(
'name' => 'Artifact Name',
'description' =>
'The name of the artifact to reference in future build steps.',
'type' => BuildStepImplementation::SETTING_TYPE_STRING),
'platform' => array(
'name' => 'Platform',
'description' => 'The platform of the host.',
'type' => BuildStepImplementation::SETTING_TYPE_STRING));
}
}

View file

@ -106,6 +106,19 @@ final class HarbormasterBuild extends HarbormasterDAO
return $log;
}
public function createArtifact(
HarbormasterBuildTarget $build_target,
$artifact_key,
$artifact_type) {
$artifact =
HarbormasterBuildArtifact::initializeNewBuildArtifact($build_target);
$artifact->setArtifactKey($artifact_key);
$artifact->setArtifactType($artifact_type);
$artifact->save();
return $artifact;
}
/**
* Checks for and handles build cancellation. If this method returns
* true, the caller should stop any current operations and return control

View file

@ -3,12 +3,18 @@
final class HarbormasterBuildArtifact extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $buildablePHID;
protected $buildTargetPHID;
protected $artifactType;
protected $artifactIndex;
protected $artifactKey;
protected $artifactData = array();
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildArtifact())
->setBuildTargetPHID($build_target->getPHID());
}
public function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
@ -27,7 +33,8 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
}
public function setArtifactKey($key) {
$this->artifactIndex = PhabricatorHash::digestForIndex($key);
$this->artifactIndex =
PhabricatorHash::digestForIndex($this->buildTargetPHID.$key);
$this->artifactKey = $key;
return $this;
}

View file

@ -36,6 +36,19 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
return $this->assertAttached($this->buildSteps);
}
/**
* Returns a standard, ordered list of build steps for this build plan.
*
* This method should be used to load build steps for a given build plan
* so that the ordering is consistent.
*/
public function loadOrderedBuildSteps() {
return id(new HarbormasterBuildStepQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildPlanPHIDs(array($this->getPHID()))
->execute();
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */

View file

@ -41,6 +41,25 @@ final class HarbormasterBuildStep extends HarbormasterDAO
return $this;
}
public function getStepImplementation() {
if ($this->className === null) {
throw new Exception("No implementation set for the given step.");
}
static $implementations = null;
if ($implementations === null) {
$implementations = BuildStepImplementation::getImplementations();
}
$class = $this->className;
if (!in_array($class, $implementations)) {
throw new Exception(
"Class name '".$class."' does not extend BuildStepImplementation.");
}
$implementation = newv($class, array());
$implementation->loadSettings($this);
return $implementation;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -38,10 +38,7 @@ final class HarbormasterBuildWorker extends PhabricatorWorker {
$buildable = $build->getBuildable();
$plan = $build->getBuildPlan();
$steps = id(new HarbormasterBuildStepQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withBuildPlanPHIDs(array($plan->getPHID()))
->execute();
$steps = $plan->loadOrderedBuildSteps();
// Perform the build.
foreach ($steps as $step) {