Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15416089
D10396.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D10396.id.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2445,6 +2445,7 @@
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
'PhabricatorStandardCustomFieldDate' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php',
+ 'PhabricatorStandardCustomFieldDecimal' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDecimal.php',
'PhabricatorStandardCustomFieldHeader' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php',
'PhabricatorStandardCustomFieldInt' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php',
'PhabricatorStandardCustomFieldInterface' => 'infrastructure/customfield/interface/PhabricatorStandardCustomFieldInterface.php',
@@ -5735,6 +5736,7 @@
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldDate' => 'PhabricatorStandardCustomField',
+ 'PhabricatorStandardCustomFieldDecimal' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldHeader' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldInt' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldPHIDs' => 'PhabricatorStandardCustomField',
diff --git a/src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php
@@ -90,8 +90,6 @@
$settings = array(
'ImageId' => $this->getDetail('ami'),
- 'MinCount' => 1,
- 'MaxCount' => 1,
'KeyName' => $this->getAWSKeyPairName(),
'InstanceType' => $this->getDetail('size'),
'SubnetId' => $this->getDetail('subnet-id'),
@@ -111,14 +109,78 @@
}
}
- $result = $this->getAWSEC2Future()
- ->setRawAWSQuery(
- 'RunInstances',
- $settings)
- ->resolve();
+ if ($this->getDetail('spot-enabled') &&
+ $this->getDetail('spot-price') !== null) {
+
+ $spot_settings = array(
+ 'SpotPrice' => $this->getDetail('spot-price'),
+ 'InstanceCount' => 1,
+ 'Type' => 'one-time',);
+
+ foreach ($settings as $key => $value) {
+ $spot_settings['LaunchSpecification.'.$key] = $value;
+ }
+
+ $result = $this->getAWSEC2Future()
+ ->setRawAWSQuery(
+ 'RequestSpotInstances',
+ $spot_settings)
+ ->resolve();
- $instance = $result->instancesSet->item[0];
- $instance_id = (string)$instance->instanceId;
+ $spot_request = $result->spotInstanceRequestSet->item[0];
+ $spot_request_id = (string)$spot_request->spotInstanceRequestId;
+
+ // Wait until the spot instance request is fulfilled.
+ while (true) {
+ try {
+ $result = $this->getAWSEC2Future()
+ ->setRawAWSQuery(
+ 'DescribeSpotInstanceRequests',
+ array(
+ 'SpotInstanceRequestId.0' => $spot_request_id,))
+ ->resolve();
+ } catch (PhutilAWSException $ex) {
+ // AWS does not provide immediate consistency, so this may throw
+ // "spot request does not exist" right after requesting spot
+ // instances.
+ continue;
+ }
+
+ $spot_request = $result->spotInstanceRequestSet->item[0];
+
+ $spot_state = (string)$spot_request->state;
+
+ if ($spot_state == 'open') {
+ // We are waiting for the request to be fulfilled.
+ sleep(5);
+ continue;
+ } else if ($spot_state == 'active') {
+ // The request has been fulfilled and we now have an instance ID.
+ $instance_id = (string)$spot_request->instanceId;
+ break;
+ } else {
+ // The spot request is closed, cancelled or failed.
+ throw new Exception(
+ 'Requested a spot instance, but the request is in state '.
+ '"'.$spot_state.'". This may occur when the current bid '.
+ 'price exceeds your maximum bid price ('.
+ $this->getDetail('spot-price').
+ ').');
+ }
+ }
+ } else {
+ $settings['MinCount'] = 1;
+ $settings['MaxCount'] = 1;
+
+ $result = $this->getAWSEC2Future()
+ ->setRawAWSQuery(
+ 'RunInstances',
+ $settings)
+ ->resolve();
+
+ $instance = $result->instancesSet->item[0];
+ $instance_id = (string)$instance->instanceId;
+ }
// Allocate the resource and place it into Pending status while
// we wait for the instance to start.
@@ -531,6 +593,28 @@
'By default, Phabricator will automatically install and configure '.
'SSH on the Windows image.'),
),
+ 'spot' => array(
+ 'name' => pht('Spot Instances'),
+ 'type' => 'header',
+ ),
+ 'spot-enabled' => array(
+ 'name' => pht('Use Spot Instances'),
+ 'type' => 'bool',
+ 'caption' => pht(
+ 'Use spot instances when allocating EC2 instances. Spot instances '.
+ 'are cheaper, but can be terminated at any time (for example, in '.
+ 'the middle of a Harbormaster build)'),
+ ),
+ 'spot-price' => array(
+ 'name' => pht('Maximum Bid'),
+ 'type' => 'decimal',
+ 'caption' => pht(
+ 'The maximum bid to pay per hour when running spot instances. If '.
+ 'the current bid price exceeds this amount, then the instance will '.
+ 'be terminated. WARNING: You should not set this higher '.
+ 'than the On Demand price for this instance type, or you could end '.
+ 'up paying more than the non-spot instance price.'),
+ ),
) + parent::getFieldSpecifications();
}
diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDecimal.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDecimal.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDecimal.php
@@ -0,0 +1,71 @@
+<?php
+
+final class PhabricatorStandardCustomFieldDecimal
+ extends PhabricatorStandardCustomField {
+
+ public function getFieldType() {
+ return 'decimal';
+ }
+
+ public function getValueForStorage() {
+ $value = $this->getFieldValue();
+ if (strlen($value)) {
+ return $value;
+ } else {
+ return null;
+ }
+ }
+
+ public function setValueFromStorage($value) {
+ if (strlen($value)) {
+ $value = (double)$value;
+ } else {
+ $value = null;
+ }
+ return $this->setFieldValue($value);
+ }
+
+
+ public function validateApplicationTransactions(
+ PhabricatorApplicationTransactionEditor $editor,
+ $type,
+ array $xactions) {
+
+ $errors = parent::validateApplicationTransactions(
+ $editor,
+ $type,
+ $xactions);
+
+ foreach ($xactions as $xaction) {
+ $value = $xaction->getNewValue();
+ if (strlen($value)) {
+ if (!preg_match('/^\d*\.?\d*$/', $value)) {
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht('%s must be an decimal.', $this->getFieldName()),
+ $xaction);
+ $this->setFieldError(pht('Invalid'));
+ }
+ }
+ }
+
+ return $errors;
+ }
+
+ public function getApplicationTransactionHasEffect(
+ PhabricatorApplicationTransaction $xaction) {
+
+ $old = $xaction->getOldValue();
+ $new = $xaction->getNewValue();
+ if (!strlen($old) && strlen($new)) {
+ return true;
+ } else if (strlen($old) && !strlen($new)) {
+ return true;
+ } else {
+ return ((double)$old !== (double)$new);
+ }
+ }
+
+
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 21, 9:13 AM (22 h, 43 m ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7224954
Default Alt Text
D10396.id.diff (8 KB)
Attached To
Mode
D10396: Implement support for launching spot instances in Amazon EC2
Attached
Detach File
Event Timeline
Log In to Comment