public function worker($request)
{
$dtNow = new DateTime('now');
try {
$dbServer = DBServer::LoadByID($request->serverId);
} catch (ServerNotFoundException $e) {
$this->log('INFO', "Server:%s does not exist:%s", $request->serverId, $e->getMessage());
return false;
}
if (!in_array($dbServer->status, [SERVER_STATUS::PENDING_TERMINATE, SERVER_STATUS::PENDING_SUSPEND, SERVER_STATUS::TERMINATED, SERVER_STATUS::SUSPENDED])) {
return false;
}
// Check and skip locked instances
if ($dbServer->status == SERVER_STATUS::PENDING_TERMINATE && $dbServer->GetProperty(EC2_SERVER_PROPERTIES::IS_LOCKED) == 1) {
if (($checkDateTime = $dbServer->GetProperty(EC2_SERVER_PROPERTIES::IS_LOCKED_LAST_CHECK_TIME)) <= time()) {
if (!$dbServer->GetRealStatus(true)->isTerminated()) {
$isLocked = $dbServer->GetEnvironmentObject()->aws($dbServer->GetCloudLocation())->ec2->instance->describeAttribute($dbServer->GetCloudServerID(), InstanceAttributeType::disableApiTermination());
if ($isLocked) {
\Scalr::getContainer()->logger(LOG_CATEGORY::FARM)->warn(new FarmLogMessage($dbServer, sprintf("Server '%s' has disableAPITermination flag and can't be terminated (Platform: %s) (ServerTerminate).", !empty($dbServer->serverId) ? $dbServer->serverId : null, !empty($dbServer->platform) ? $dbServer->platform : null)));
$startTime = strtotime($dbServer->dateShutdownScheduled);
// 1, 2, 3, 4, 5, 6, 9, 14, ... 60
$diff = round((($checkDateTime < $startTime ? $startTime : $checkDateTime) - $startTime) / 60 * 0.5) * 60;
$diff = $diff == 0 ? 60 : ($diff > 3600 ? 3600 : $diff);
$dbServer->SetProperty(EC2_SERVER_PROPERTIES::IS_LOCKED_LAST_CHECK_TIME, time() + $diff);
return false;
} else {
$dbServer->SetProperty(EC2_SERVER_PROPERTIES::IS_LOCKED, $isLocked);
}
}
} else {
return false;
}
}
//Warming up static DI cache
\Scalr::getContainer()->warmup();
// Reconfigure observers
\Scalr::ReconfigureObservers();
if ($dbServer->status == SERVER_STATUS::TERMINATED || $dbServer->dateShutdownScheduled <= $dtNow->format('Y-m-d H:i:s')) {
try {
$p = PlatformFactory::NewPlatform($dbServer->platform);
$environment = $dbServer->GetEnvironmentObject();
if (!$environment->isPlatformEnabled($dbServer->platform)) {
throw new Exception(sprintf("%s platform is not enabled in the '%s' (%d) environment.", $dbServer->platform, $environment->name, $environment->id));
}
if ($dbServer->GetCloudServerID()) {
$serverHistory = $dbServer->getServerHistory();
$isTermination = in_array($dbServer->status, [SERVER_STATUS::TERMINATED, SERVER_STATUS::PENDING_TERMINATE]);
$isSuspension = in_array($dbServer->status, [SERVER_STATUS::SUSPENDED, SERVER_STATUS::PENDING_SUSPEND]);
/* @var $terminationData TerminationData */
$terminationData = null;
//NOTE: in any case, after call, be sure to set callback to null
$this->setupClientCallback($p, function ($request, $response) use($dbServer, &$terminationData) {
$terminationData = new TerminationData();
$terminationData->serverId = $dbServer->serverId;
if ($request instanceof \http\Client\Request) {
$terminationData->requestUrl = $request->getRequestUrl();
$terminationData->requestQuery = $request->getQuery();
$terminationData->request = $request->toString();
}
if ($response instanceof \http\Client\Response) {
$terminationData->response = $response->toString();
$terminationData->responseCode = $response->getResponseCode();
$terminationData->responseStatus = $response->getResponseStatus();
}
}, $dbServer);
try {
$status = $dbServer->GetRealStatus();
} catch (Exception $e) {
//eliminate callback
$this->setupClientCallback($p, null, $dbServer);
throw $e;
}
//eliminate callback
$this->setupClientCallback($p, null, $dbServer);
if ($dbServer->isCloudstack()) {
//Workaround for when expunge flag not working and servers stuck in Destroyed state.
$isTerminated = $status->isTerminated() && $status->getName() != 'Destroyed';
} else {
$isTerminated = $status->isTerminated();
}
if ($isTermination && !$isTerminated || $isSuspension && !$dbServer->GetRealStatus()->isSuspended()) {
try {
if ($dbServer->farmId != 0) {
// If farm role or/and farm was removed this will fail which is okay
$dbFarmRole = null;
try {
$dbFarmRole = $dbServer->GetFarmRoleObject();
$dbFarm = $dbServer->GetFarmObject();
} catch (\Exception $e) {
//do nothing
}
// We need to make sure that DISK node will be terminated after all other nodes only if farm was terminated
if (!empty($dbFarm) && !empty($dbFarmRole) && $dbFarm->Status == \FARM_STATUS::TERMINATED && $dbFarmRole->GetRoleObject()->hasBehavior(ROLE_BEHAVIORS::RABBITMQ)) {
$serverExists = \Scalr::getDb()->GetOne("\n SELECT EXISTS (\n SELECT 1 FROM servers\n WHERE farm_roleid = ?\n AND status NOT IN (?, ?)\n AND `index` != ?\n )\n ", [$dbServer->farmRoleId, SERVER_STATUS::TERMINATED, SERVER_STATUS::SUSPENDED, 1]);
if ($dbServer->index == 1 && $serverExists) {
\Scalr::getContainer()->logger(LOG_CATEGORY::FARM)->warn(new FarmLogMessage($dbServer, sprintf("RabbitMQ role. Main DISK node should be terminated after all other nodes. Waiting... (Platform: %s) (ServerTerminate).", $dbServer->platform)));
return false;
}
}
\Scalr::getContainer()->logger(LOG_CATEGORY::FARM)->warn(new FarmLogMessage($dbServer, sprintf("Terminating server '%s' (Platform: %s) (ServerTerminate).", !empty($dbServer->serverId) ? $dbServer->serverId : null, !empty($dbServer->platform) ? $dbServer->platform : null)));
}
} catch (Exception $e) {
$this->getLogger()->warn("Server: %s caused exception: %s", $request->serverId, $e->getMessage());
}
$terminationTime = $dbServer->GetProperty(SERVER_PROPERTIES::TERMINATION_REQUEST_UNIXTIME);
if (!$terminationTime || time() - $terminationTime > 180) {
if ($isTermination) {
$p->TerminateServer($dbServer);
} else {
$p->SuspendServer($dbServer);
}
$dbServer->SetProperty(SERVER_PROPERTIES::TERMINATION_REQUEST_UNIXTIME, time());
if ($dbServer->farmId) {
$wasHostDownFired = \Scalr::getDb()->GetOne("\n SELECT id FROM events WHERE event_server_id = ? AND type = ? AND is_suspend = '0'", array($request->serverId, 'HostDown'));
if (!$wasHostDownFired) {
$event = new HostDownEvent($dbServer);
$event->isSuspended = !$isTermination;
\Scalr::FireEvent($dbServer->farmId, $event);
}
}
}
} else {
if ($dbServer->status == SERVER_STATUS::TERMINATED) {
if (!$dbServer->dateShutdownScheduled || time() - strtotime($dbServer->dateShutdownScheduled) > 600) {
$errorResolution = true;
$serverHistory->setTerminated();
$dbServer->Remove();
if (isset($terminationData)) {
$terminationData->save();
}
}
} else {
if ($dbServer->status == SERVER_STATUS::PENDING_TERMINATE) {
$dbServer->updateStatus(SERVER_STATUS::TERMINATED);
$errorResolution = true;
} else {
if ($dbServer->status == SERVER_STATUS::PENDING_SUSPEND) {
$dbServer->update(['status' => SERVER_STATUS::SUSPENDED, 'remoteIp' => '', 'localIp' => '']);
$errorResolution = true;
}
}
}
if (!empty($errorResolution) && $request->attempts > 0 && ($ste = ServerTerminationError::findPk($request->serverId))) {
//Automatic error resolution
$ste->delete();
}
}
} else {
//If there is no cloudserverID we don't need to add this server into server history.
//$serverHistory->setTerminated();
$dbServer->Remove();
}
} catch (InstanceNotFound $e) {
if ($serverHistory) {
$serverHistory->setTerminated();
}
$dbServer->Remove();
if (isset($terminationData)) {
$terminationData->save();
}
} catch (Exception $e) {
if ($request->serverId && stripos($e->getMessage(), "modify its 'disableApiTermination' instance attribute and try again") !== false && $dbServer && $dbServer->platform == \SERVER_PLATFORMS::EC2) {
// server has disableApiTermination flag on cloud, update server properties
$dbServer->SetProperty(EC2_SERVER_PROPERTIES::IS_LOCKED, 1);
} else {
if ($request->serverId && ($e instanceof InvalidCloudCredentialsException || CloudPlatformSuspensionInfo::isSuspensionException($e) || stripos($e->getMessage(), "tenant is disabled") !== false || stripos($e->getMessage(), "was not able to validate the provided access credentials") !== false || stripos($e->getMessage(), "platform is not enabled") !== false || stripos($e->getMessage(), "neither api key nor password was provided for the openstack config") !== false || stripos($e->getMessage(), "refreshing the OAuth2 token") !== false || strpos($e->getMessage(), "Cannot obtain endpoint url. Unavailable service") !== false)) {
//Postpones unsuccessful task for 30 minutes.
$ste = new ServerTerminationError($request->serverId, isset($request->attempts) ? $request->attempts + 1 : 1, $e->getMessage());
$minutes = rand(30, 40);
$ste->retryAfter = new \DateTime('+' . $minutes . ' minutes');
if ($ste->attempts > self::MAX_ATTEMPTS && in_array($dbServer->status, [SERVER_STATUS::PENDING_TERMINATE, SERVER_STATUS::TERMINATED])) {
//We are going to remove those with Pending terminate status from Scalr after 1 month of unsuccessful attempts
$dbServer->Remove();
if (isset($terminationData)) {
$terminationData->save();
}
}
$ste->save();
} else {
$this->log('ERROR', "Server:%s, failed - Exeption:%s %s", $request->serverId, get_class($e), $e->getMessage());
throw $e;
}
}
}
}
return $request;
}