Docker: Make GoCmdBridge optional

Change-Id: I29dbaafca3878b8130ae00eefc57881e35fd31e4
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Marcus Tillmanns
2024-06-24 06:49:57 +02:00
parent cd0d02f248
commit 9dc0a77503
4 changed files with 82 additions and 13 deletions

View File

@@ -1,5 +1,4 @@
add_qtc_library(CmdBridgeClient add_qtc_library(CmdBridgeClient
CONDITION TARGET CmdBridge
PUBLIC_DEPENDS Utils PUBLIC_DEPENDS Utils
DEFINES GOBRIDGE_MAGIC_PACKET_MARKER=\"${GOBRIDGE_MAGIC_PACKET_MARKER}\" DEFINES GOBRIDGE_MAGIC_PACKET_MARKER=\"${GOBRIDGE_MAGIC_PACKET_MARKER}\"
SOURCES SOURCES

View File

@@ -294,7 +294,10 @@ expected_str<QFuture<Environment>> Client::start()
auto func = it.value(); auto func = it.value();
auto id = it.key(); auto id = it.key();
it = j->map.erase(it); it = j->map.erase(it);
func(QVariantMap{{"Type", "error"}, {"Id", id}, {"Error", "Process exited"}}); func(QVariantMap{
{"Type", "error"},
{"Id", id},
{"Error", QString("Process exited: %1").arg(d->process->errorString())}});
} }
emit done(d->process->resultData()); emit done(d->process->resultData());

View File

@@ -1,5 +1,4 @@
add_qtc_plugin(Docker add_qtc_plugin(Docker
CONDITION TARGET CmdBridgeClient
DEPENDS Utils CmdBridgeClient DEPENDS Utils CmdBridgeClient
PLUGIN_DEPENDS Core ProjectExplorer QtSupport PLUGIN_DEPENDS Core ProjectExplorer QtSupport
SOURCES SOURCES

View File

@@ -78,7 +78,6 @@
#include <QThread> #include <QThread>
#include <QToolButton> #include <QToolButton>
#include <numeric>
#include <optional> #include <optional>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
@@ -116,6 +115,31 @@ public:
DockerDevicePrivate *m_dev = nullptr; DockerDevicePrivate *m_dev = nullptr;
}; };
class DockerFallbackFileAccess : public UnixDeviceFileAccess
{
const FilePath m_rootPath;
public:
DockerFallbackFileAccess(const FilePath &rootPath)
: m_rootPath(rootPath)
{}
RunResult runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const override
{
Process proc;
proc.setWriteData(stdInData);
proc.setCommand(
{m_rootPath.withNewPath(cmdLine.executable().path()), cmdLine.splitArguments()});
proc.runBlocking();
return {
proc.resultData().m_exitCode,
proc.readAllRawStandardOutput(),
proc.readAllRawStandardError(),
};
}
};
void DockerDeviceSettings::fromMap(const Store &map) void DockerDeviceSettings::fromMap(const Store &map)
{ {
DeviceSettings::fromMap(map); DeviceSettings::fromMap(map);
@@ -333,7 +357,7 @@ public:
std::optional<Environment> m_cachedEnviroment; std::optional<Environment> m_cachedEnviroment;
bool m_isShutdown = false; bool m_isShutdown = false;
std::unique_ptr<DockerDeviceFileAccess> m_fileAccess; std::unique_ptr<DeviceFileAccess> m_fileAccess;
}; };
class DockerProcessImpl : public ProcessInterface class DockerProcessImpl : public ProcessInterface
@@ -373,8 +397,10 @@ DockerProcessImpl::DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePriva
}); });
connect(&m_process, &Process::readyReadStandardOutput, this, [this] { connect(&m_process, &Process::readyReadStandardOutput, this, [this] {
if (m_hasReceivedFirstOutput) if (m_hasReceivedFirstOutput) {
emit readyRead(m_process.readAllRawStandardOutput(), {}); emit readyRead(m_process.readAllRawStandardOutput(), {});
return;
}
QByteArray output = m_process.readAllRawStandardOutput(); QByteArray output = m_process.readAllRawStandardOutput();
qsizetype idx = output.indexOf('\n'); qsizetype idx = output.indexOf('\n');
@@ -383,14 +409,30 @@ DockerProcessImpl::DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePriva
qCDebug(dockerDeviceLog) << "Process first line received:" << m_process.commandLine() qCDebug(dockerDeviceLog) << "Process first line received:" << m_process.commandLine()
<< firstLine; << firstLine;
if (!firstLine.startsWith("__qtc")) if (!firstLine.startsWith("__qtc")) {
emit done(ProcessResultData{
-1,
QProcess::ExitStatus::CrashExit,
QProcess::ProcessError::FailedToStart,
QString::fromUtf8(firstLine),
});
return; return;
}
bool ok = false; bool ok = false;
m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok); m_remotePID = firstLine.mid(5, firstLine.size() - 5 - 5).toLongLong(&ok);
if (ok) if (ok)
emit started(m_remotePID); emit started(m_remotePID);
else {
emit done(ProcessResultData{
-1,
QProcess::ExitStatus::CrashExit,
QProcess::ProcessError::FailedToStart,
QString::fromUtf8(firstLine),
});
return;
}
// In case we already received some error output, send it now. // In case we already received some error output, send it now.
const QByteArray stdErr = m_process.readAllRawStandardError(); const QByteArray stdErr = m_process.readAllRawStandardError();
@@ -479,8 +521,18 @@ void DockerProcessImpl::sendControlSignal(ControlSignal controlSignal)
m_process.closeWriteChannel(); m_process.closeWriteChannel();
return; return;
} }
static_cast<DockerDeviceFileAccess *>(m_device->fileAccess()) auto dfa = dynamic_cast<DockerDeviceFileAccess *>(m_device->fileAccess());
->signalProcess(m_remotePID, controlSignal); if (dfa) {
static_cast<DockerDeviceFileAccess *>(m_device->fileAccess())
->signalProcess(m_remotePID, controlSignal);
} else {
const int signal = controlSignalToInt(controlSignal);
Process p;
p.setCommand(
{m_device->rootPath().withNewPath("kill"),
{QString("-%1").arg(signal), QString("%2").arg(m_remotePID)}});
p.runBlocking();
}
} else { } else {
// clang-format off // clang-format off
switch (controlSignal) { switch (controlSignal) {
@@ -544,9 +596,14 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
setFileAccess([this]() -> DeviceFileAccess * { setFileAccess([this]() -> DeviceFileAccess * {
if (!d->m_fileAccess) { if (!d->m_fileAccess) {
auto fileAccess = std::make_unique<DockerDeviceFileAccess>(d); auto fileAccess = std::make_unique<DockerDeviceFileAccess>(d);
QTC_ASSERT_EXPECTED( auto initResult = fileAccess->init(rootPath().withNewPath("/tmp/_qtc_cmdbridge"));
fileAccess->init(rootPath().withNewPath("/tmp/_qtc_cmdbridge")), return nullptr); QTC_CHECK(initResult);
d->m_fileAccess = std::move(fileAccess); if (initResult) {
d->m_fileAccess = std::move(fileAccess);
return d->m_fileAccess.get();
}
d->m_fileAccess = std::make_unique<DockerFallbackFileAccess>(rootPath());
} }
return d->m_fileAccess.get(); return d->m_fileAccess.get();
}); });
@@ -649,11 +706,20 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
exec.addCommandLineAsArgs(cmd, CommandLine::Raw); exec.addCommandLineAsArgs(cmd, CommandLine::Raw);
if (withMarker) { if (withMarker) {
// Check the executable for existence.
CommandLine testType({"type", {}});
testType.addArg(cmd.executable().path());
testType.addArgs(">/dev/null", CommandLine::Raw);
// Send PID only if existence was confirmed, so we can correctly notify
// a failed start.
CommandLine echo("echo"); CommandLine echo("echo");
echo.addArgs("__qtc$$qtc__", CommandLine::Raw); echo.addArgs("__qtc$$qtc__", CommandLine::Raw);
echo.addCommandLineWithAnd(exec); echo.addCommandLineWithAnd(exec);
dockerCmd.addCommandLineAsSingleArg(echo); testType.addCommandLineWithAnd(echo);
dockerCmd.addCommandLineAsSingleArg(testType);
} else { } else {
dockerCmd.addCommandLineAsSingleArg(exec); dockerCmd.addCommandLineAsSingleArg(exec);
} }
@@ -763,6 +829,8 @@ QStringList DockerDevicePrivate::createMountArgs() const
const Utils::expected_str<Utils::FilePath> cmdBridgePath = CmdBridge::Client::getCmdBridgePath( const Utils::expected_str<Utils::FilePath> cmdBridgePath = CmdBridge::Client::getCmdBridgePath(
osAndArch.first, osAndArch.second, Core::ICore::libexecPath()); osAndArch.first, osAndArch.second, Core::ICore::libexecPath());
QTC_CHECK_EXPECTED(cmdBridgePath);
QStringList cmds; QStringList cmds;
QList<MountPair> mounts; QList<MountPair> mounts;
for (const FilePath &m : deviceSettings->mounts()) for (const FilePath &m : deviceSettings->mounts())