Page MenuHomePhabricator

D10396.id25016.diff
No OneTemporary

D10396.id25016.diff

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
@@ -2227,6 +2227,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',
@@ -5130,6 +5131,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,71 @@
}
}
- $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();
+
+ $spot_request = $result->spotInstanceRequestSet->item[0];
+ $spot_request_id = (string)$spot_request->spotInstanceRequestId;
+
+ // Wait until the spot instance request is fulfilled.
+ while (true) {
+ $result = $this->getAWSEC2Future()
+ ->setRawAWSQuery(
+ 'DescribeSpotInstanceRequests',
+ array(
+ 'SpotInstanceRequestId.0' => $spot_request_id))
+ ->resolve();
+
+ $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;
- $instance = $result->instancesSet->item[0];
- $instance_id = (string)$instance->instanceId;
+ $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.
@@ -493,6 +548,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

Mime Type
text/plain
Expires
Mon, Mar 17, 8:36 PM (4 d, 18 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7708835
Default Alt Text
D10396.id25016.diff (7 KB)

Event Timeline