Index: src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php =================================================================== --- src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php +++ src/applications/drydock/blueprint/DrydockAmazonEC2HostBlueprintImplementation.php @@ -814,6 +814,128 @@ } } + if ($resource->getAttribute('platform') === 'windows') { + $drive_path = $this->getDetail('storage-path'); + if ($drive_path[1] === ':') { + $drive_path = $drive_path[0].':\\'; + + $resource->setAttribute( + 'aws-status', + pht( + 'Waiting for lease drive "%s" to appear', + $drive_path)); + $resource->save(); + + $last_attempt = PhabricatorTime::getNow(); + + while (true) { + try { + $last_attempt = PhabricatorTime::getNow(); + + $this->log(pht( + 'Attempting to check lease drive \'%s\' exists on \'%s\'', + $drive_path, + $instance_id)); + + $ssh_future = $ssh->getExecFuture( + 'ls %s', + $drive_path); + $ssh_future->resolvex(); + if ($ssh_future->getWasKilledByTimeout()) { + throw new Exception( + pht('%s execution timed out.', $protocol_name)); + } + + break; + } catch (Exception $ex) { + + $this->log(pht('%s', (string)$ex)); + + // Make sure the instance hasn't been terminated or shutdown while + // we've been trying to connect. + $result = $this->getAWSEC2Future() + ->setRawAWSQuery( + 'DescribeInstances', + array( + 'InstanceId.0' => $instance_id, + )) + ->resolve(); + + $reservation = $result->reservationSet->item[0]; + $instance = $reservation->instancesSet->item[0]; + $instance_state = (string)$instance->instanceState->name; + + $this->log(pht( + 'Lease storage drive "%s" not yet ready; instance is in '. + 'state \'%s\'', + $drive_path, + $instance_state)); + + if ($instance_state === 'shutting-down' || + $instance_state === 'terminated') { + + $this->log(pht( + 'Instance has ended up in state \'%s\' while waiting '. + 'for lease storage drive to appear', + $instance_state)); + + // Deallocate and release the public IP address if we + // allocated one. + if ($resource->getAttribute('eip-allocated')) { + try { + $this->getAWSEC2Future() + ->setRawAWSQuery( + 'DisassociateAddress', + array( + 'AssociationId' => + $resource->getAttribute('eip-association-id'), + )) + ->resolve(); + } catch (PhutilAWSException $ex) {} + + try { + $this->getAWSEC2Future() + ->setRawAWSQuery( + 'ReleaseAddress', + array( + 'AllocationId' => + $resource->getAttribute('eip-allocation-id'), + )) + ->resolve(); + } catch (PhutilAWSException $ex) {} + + $resource->setAttribute( + 'eip-status', + 'Released'); + $resource->save(); + } + + $resource->setAttribute( + 'aws-status', + 'Terminated'); + $resource->save(); + + throw new Exception( + 'Allocated instance, but ended up in unexpected state \''. + $instance_state.'\'!'); + } else { + $check_time = PhabricatorTime::getNow() - $last_attempt; + if ($check_time < 60) { + $this->log(pht( + '%s connection returned too quickly, manually sleeping '. + 'for %d seconds to avoid a large number of AWS calls.', + $protocol_name, + $check_time)); + sleep($check_time); + } + } + + continue; + } + } + } + } + // Update the resource into open status. $resource->setStatus(DrydockResourceStatus::STATUS_OPEN); $resource->setAttribute(