Scalr\System\Zmq\Cron\Task\ServerTerminate::worker PHP Method

worker() public method

See also: Scalr\System\Zmq\Cron\TaskInterface::worker()
public worker ( $request )
    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;
    }