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

@ -19,6 +19,7 @@
#include "GRPCClient.h"
#include "GRPCUtils.h"
#include "../Exception/Exception.h"
#include "../ProcessMonitor.h"
using namespace google::protobuf;
@ -56,9 +57,10 @@ void GRPCClient::removeServiceConfigFile()
//****************************************************************************************************************************************************
/// \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.
//****************************************************************************************************************************************************
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs)
GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs, ProcessMonitor *serverProcess)
{
QString const path = grpcServerConfigPath();
QFile file(path);
@ -68,6 +70,9 @@ GRPCConfig GRPCClient::waitAndRetrieveServiceConfig(qint64 timeoutMs)
bool found = false;
while (true)
{
if (serverProcess && serverProcess->getStatus().ended)
throw Exception("Bridge application exited before providing a gRPC service configuration file.");
if (file.exists())
{
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[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.
//****************************************************************************************************************************************************
bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
bool GRPCClient::connectToServer(GRPCConfig const &config, ProcessMonitor *serverProcess, QString &outError)
{
try
{
@ -123,6 +129,9 @@ bool GRPCClient::connectToServer(GRPCConfig const &config, QString &outError)
int i = 0;
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));
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
public: // static member functions
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.
GRPCClient() = default; ///< Default constructor.
@ -60,7 +60,7 @@ public: // member functions.
GRPCClient &operator=(GRPCClient const &) = delete; ///< Disabled assignment operator.
GRPCClient &operator=(GRPCClient &&) = delete; ///< Disabled move assignment operator.
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 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)
, exePath_(exePath)
, args_(args)
, out_(stdout)
, err_(stderr)
{
QFileInfo fileInfo(exePath);
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
{
{
QMutexLocker locker(&statusMutex_);
status_.ended = false;
status_.pid = -1;
}
emit started();
QProcess p;
p.start(exePath_, args_);
p.waitForStarted();
status_.running = true;
status_.pid = p.processId();
QTextStream out(stdout), err(stderr);
QByteArray array;
while (!p.waitForFinished(100))
{
array = p.readAllStandardError();
if (!array.isEmpty())
{
err << array;
err.flush();
}
array = p.readAllStandardOutput();
if (!array.isEmpty())
{
out << array;
out.flush();
}
QMutexLocker locker(&statusMutex_);
status_.pid = p.processId();
}
status_.running = false;
while (!p.waitForFinished(100))
{
this->forwardProcessOutput(p);
}
this->forwardProcessOutput(p);
QMutexLocker locker(&statusMutex_);
status_.ended = true;
status_.returnCode = p.exitCode();
emit processExited(status_.returnCode);
@ -93,8 +111,9 @@ void ProcessMonitor::run()
//****************************************************************************************************************************************************
/// \return status of the monitored process
//****************************************************************************************************************************************************
const ProcessMonitor::MonitorStatus &ProcessMonitor::getStatus()
const ProcessMonitor::MonitorStatus ProcessMonitor::getStatus()
{
QMutexLocker locker(&statusMutex_);
return status_;
}

View File

@ -36,7 +36,7 @@ Q_OBJECT
public: // static member functions
struct MonitorStatus
{
bool running = false;
bool ended = false;
int returnCode = 0;
qint64 pid = 0;
};
@ -49,15 +49,21 @@ public: // member functions.
ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator.
ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator.
void run() override; ///< Run the worker.
MonitorStatus const &getStatus();
MonitorStatus const getStatus(); ///< Retrieve the current status of the process.
signals:
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
QMutex statusMutex_; ///< The status mutex.
QString const exePath_; ///< The path to the executable.
QStringList args_; ///< arguments to be passed to the brigde.
MonitorStatus status_; ///< Status of the monitoring.
QTextStream out_; ///< The standard output stream.
QTextStream err_; ///< The standard error stream.
};