GODT-2038: interrupt gRPC initialisation of bridge process terminates.

This commit is contained in:
Xavier Michelon
2022-11-07 14:34:13 +01:00
committed by Romain LE JEUNE
parent a949a113cf
commit f5148074fd
6 changed files with 64 additions and 30 deletions

View File

@ -49,7 +49,7 @@ void QMLBackend::init(GRPCConfig const &serviceConfig)
this->connectGrpcEvents(); this->connectGrpcEvents();
QString error; QString error;
if (app().grpc().connectToServer(serviceConfig, error)) if (app().grpc().connectToServer(serviceConfig, app().bridgeMonitor(), error))
app().log().info("Connected to backend via gRPC service."); app().log().info("Connected to backend via gRPC service.");
else else
throw Exception(QString("Cannot connectToServer to go backend via gRPC: %1").arg(error)); throw Exception(QString("Cannot connectToServer to go backend via gRPC: %1").arg(error));

View File

@ -288,7 +288,7 @@ int main(int argc, char *argv[])
} }
log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath()))); log.info(QString("Retrieving gRPC service configuration from '%1'").arg(QDir::toNativeSeparators(grpcServerConfigPath())));
app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(attach ? 0 : grpcServiceConfigWaitDelayMs)); app().backend().init(GRPCClient::waitAndRetrieveServiceConfig(attach ? 0 : grpcServiceConfigWaitDelayMs, app().bridgeMonitor()));
if (!attach) if (!attach)
GRPCClient::removeServiceConfigFile(); GRPCClient::removeServiceConfigFile();
@ -312,7 +312,7 @@ int main(int argc, char *argv[])
if (bridgeMonitor) if (bridgeMonitor)
{ {
const ProcessMonitor::MonitorStatus& status = bridgeMonitor->getStatus(); const ProcessMonitor::MonitorStatus& status = bridgeMonitor->getStatus();
if (!status.running && !attach) if (status.ended && !attach)
{ {
// ProcessMonitor already stopped meaning we are attached to an orphan Bridge. // ProcessMonitor already stopped meaning we are attached to an orphan Bridge.
// Restart the full process to be sure there is no more bridge orphans // Restart the full process to be sure there is no more bridge orphans

View File

@ -19,6 +19,7 @@
#include "GRPCClient.h" #include "GRPCClient.h"
#include "GRPCUtils.h" #include "GRPCUtils.h"
#include "../Exception/Exception.h" #include "../Exception/Exception.h"
#include "../ProcessMonitor.h"
using namespace google::protobuf; using namespace google::protobuf;
@ -56,9 +57,10 @@ void GRPCClient::removeServiceConfigFile()
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[in] timeoutMs The timeout in milliseconds /// \param[in] timeoutMs The timeout in milliseconds
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
/// \return The service config. /// \return The service config.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs) GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs, ProcessMonitor *serverProcess)
{ {
QString const path = grpcServerConfigPath(); QString const path = grpcServerConfigPath();
QFile file(path); QFile file(path);
@ -68,6 +70,9 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs)
bool found = false; bool found = false;
while (true) while (true)
{ {
if (serverProcess && serverProcess->getStatus().ended)
throw Exception("Bridge application exited before providing a gRPC service configuration file.");
if (file.exists()) if (file.exists())
{ {
found = true; found = true;
@ -100,9 +105,10 @@ void GRPCClient::setLog(Log *log)
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \param[out] outError If the function returns false, this variable contains a description of the error. /// \param[out] outError If the function returns false, this variable contains a description of the error.
/// \param[in] serverProcess An optional server process to monitor. If the process it, no need and retry, as connexion cannot be established. Ignored if null.
/// \return true iff the connection was successful. /// \return true iff the connection was successful.
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError) bool GRPCClient::connectToServer(GRPCConfig const &config, ProcessMonitor *serverProcess, QString &outError)
{ {
try try
{ {
@ -123,6 +129,9 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
int i = 0; int i = 0;
while (true) while (true)
{ {
if (serverProcess && serverProcess->getStatus().ended)
throw Exception("Bridge application ended before gRPC connexion could be established.");
this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i)); this->logInfo(QString("Connection to gRPC server at %1. attempt #%2").arg(address).arg(++i));
if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(grpcConnectionRetryDelayMs, GPR_TIMESPAN)))) if (channel_->WaitForConnected(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(grpcConnectionRetryDelayMs, GPR_TIMESPAN))))

View File

@ -50,7 +50,7 @@ class GRPCClient : public QObject
Q_OBJECT Q_OBJECT
public: // static member functions public: // static member functions
static void removeServiceConfigFile(); ///< Delete the service config file. static void removeServiceConfigFile(); ///< Delete the service config file.
static GRPCConfig waitAndRetrieveServiceConfig(qint64 timeoutMs); ///< Wait and retrieve the service configuration. static GRPCConfig waitAndRetrieveServiceConfig(qint64 timeoutMs, class ProcessMonitor *serverProcess); ///< Wait and retrieve the service configuration.
public: // member functions. public: // member functions.
GRPCClient() = default; ///< Default constructor. GRPCClient() = default; ///< Default constructor.
@ -60,7 +60,7 @@ public: // member functions.
GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator. GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator.
GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator. GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator.
void setLog(Log *log); ///< Set the log for the client. void setLog(Log *log); ///< Set the log for the client.
bool connectToServer(GRPCConfig const &config, QString &outError); ///< Establish connection to the gRPC server. bool connectToServer(GRPCConfig const &config, class ProcessMonitor *serverProcess, QString &outError); ///< Establish connection to the gRPC server.
grpc::Status checkTokens(QString const &clientConfigPath, QString &outReturnedClientToken); ///< Performs a token check. grpc::Status checkTokens(QString const &clientConfigPath, QString &outReturnedClientToken); ///< Performs a token check.
grpc::Status addLogEntry(Log::Level level, QString const &package, QString const &message); ///< Performs the "AddLogEntry" gRPC call. grpc::Status addLogEntry(Log::Level level, QString const &package, QString const &message); ///< Performs the "AddLogEntry" gRPC call.

View File

@ -33,6 +33,8 @@ ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args,
: Worker(parent) : Worker(parent)
, exePath_(exePath) , exePath_(exePath)
, args_(args) , args_(args)
, out_(stdout)
, err_(stderr)
{ {
QFileInfo fileInfo(exePath); QFileInfo fileInfo(exePath);
if (!fileInfo.exists()) if (!fileInfo.exists())
@ -42,6 +44,26 @@ ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args,
} }
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void ProcessMonitor::forwardProcessOutput(QProcess &p) {
QByteArray array = p.readAllStandardError();
if (!array.isEmpty())
{
err_ << array;
err_.flush();
}
array = p.readAllStandardOutput();
if (!array.isEmpty())
{
out_ << array;
out_.flush();
}
}
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
// //
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
@ -49,35 +71,31 @@ void ProcessMonitor::run()
{ {
try try
{ {
{
QMutexLocker locker(&statusMutex_);
status_.ended = false;
status_.pid = -1;
}
emit started(); emit started();
QProcess p; QProcess p;
p.start(exePath_, args_); p.start(exePath_, args_);
p.waitForStarted(); p.waitForStarted();
status_.running = true;
status_.pid = p.processId();
QTextStream out(stdout), err(stderr);
QByteArray array;
while (!p.waitForFinished(100))
{ {
array = p.readAllStandardError(); QMutexLocker locker(&statusMutex_);
if (!array.isEmpty()) status_.pid = p.processId();
{
err << array;
err.flush();
}
array = p.readAllStandardOutput();
if (!array.isEmpty())
{
out << array;
out.flush();
}
} }
status_.running = false; while (!p.waitForFinished(100))
{
this->forwardProcessOutput(p);
}
this->forwardProcessOutput(p);
QMutexLocker locker(&statusMutex_);
status_.ended = true;
status_.returnCode = p.exitCode(); status_.returnCode = p.exitCode();
emit processExited(status_.returnCode); emit processExited(status_.returnCode);
@ -93,8 +111,9 @@ void ProcessMonitor::run()
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
/// \return status of the monitored process /// \return status of the monitored process
//**************************************************************************************************************************************************** //****************************************************************************************************************************************************
const ProcessMonitor::MonitorStatus &ProcessMonitor::getStatus() const ProcessMonitor::MonitorStatus ProcessMonitor::getStatus()
{ {
QMutexLocker locker(&statusMutex_);
return status_; return status_;
} }

View File

@ -36,7 +36,7 @@ Q_OBJECT
public: // static member functions public: // static member functions
struct MonitorStatus struct MonitorStatus
{ {
bool running = false; bool ended = false;
int returnCode = 0; int returnCode = 0;
qint64 pid = 0; qint64 pid = 0;
}; };
@ -49,15 +49,21 @@ public: // member functions.
ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator. ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator.
ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator. ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator.
void run() override; ///< Run the worker. void run() override; ///< Run the worker.
MonitorStatus const &getStatus(); MonitorStatus const getStatus(); ///< Retrieve the current status of the process.
signals: signals:
void processExited(int code); ///< Slot for the exiting of the process. void processExited(int code); ///< Slot for the exiting of the process.
private: // member functions
void forwardProcessOutput(QProcess &p); ///< Forward the standard output and error from the process to this application standard output and error.
private: // data members private: // data members
QMutex statusMutex_; ///< The status mutex.
QString const exePath_; ///< The path to the executable. QString const exePath_; ///< The path to the executable.
QStringList args_; ///< arguments to be passed to the brigde. QStringList args_; ///< arguments to be passed to the brigde.
MonitorStatus status_; ///< Status of the monitoring. MonitorStatus status_; ///< Status of the monitoring.
QTextStream out_; ///< The standard output stream.
QTextStream err_; ///< The standard error stream.
}; };