Docker: Improve error output when bridge fails to start

Change-Id: I86d9d3972c483d21cd2ff64d3581b518e6b0a819
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2024-07-09 09:52:21 +02:00
parent cba0f779a9
commit 30e44ae15f
6 changed files with 86 additions and 34 deletions

View File

@@ -68,22 +68,31 @@ expected_str<void> FileAccess::deployAndInit(
qCDebug(faLog) << "Found dd on remote host:" << *whichDD; qCDebug(faLog) << "Found dd on remote host:" << *whichDD;
const auto unameOs = run({remoteRootPath.withNewPath("uname"), {"-s"}}); const expected_str<QString> unameOs = run({remoteRootPath.withNewPath("uname"), {"-s"}});
if (!unameOs) if (!unameOs) {
return make_unexpected( return make_unexpected(
QString("Could not determine OS on remote host: %1").arg(unameOs.error())); QString("Could not determine OS on remote host: %1").arg(unameOs.error()));
}
Utils::expected_str<OsType> osType = osTypeFromString(unameOs.value());
if (!osType)
return make_unexpected(osType.error());
qCDebug(faLog) << "Remote host OS:" << *unameOs; qCDebug(faLog) << "Remote host OS:" << *unameOs;
const auto unameArch = run({remoteRootPath.withNewPath("uname"), {"-m"}}); const expected_str<QString> unameArch = run({remoteRootPath.withNewPath("uname"), {"-m"}});
if (!unameArch) if (!unameArch) {
return make_unexpected( return make_unexpected(
QString("Could not determine architecture on remote host: %1").arg(unameArch.error())); QString("Could not determine architecture on remote host: %1").arg(unameArch.error()));
}
const Utils::expected_str<OsArch> osArch = osArchFromString(unameArch.value());
if (!osArch)
return make_unexpected(osArch.error());
qCDebug(faLog) << "Remote host architecture:" << *unameArch; qCDebug(faLog) << "Remote host architecture:" << *unameArch;
const Utils::expected_str<Utils::FilePath> cmdBridgePath = Client::getCmdBridgePath( const Utils::expected_str<Utils::FilePath> cmdBridgePath
osTypeFromString(unameOs.value()), osArchFromString(unameArch.value()), libExecPath); = Client::getCmdBridgePath(*osType, *osArch, libExecPath);
if (!cmdBridgePath) { if (!cmdBridgePath) {
return make_unexpected( return make_unexpected(

View File

@@ -259,15 +259,19 @@ expected_str<QFuture<Environment>> Client::start()
d->jobs.writeLocked()->map.insert(-1, [envPromise](QVariantMap map) { d->jobs.writeLocked()->map.insert(-1, [envPromise](QVariantMap map) {
envPromise->start(); envPromise->start();
QString type = map.value("Type").toString(); QString type = map.value("Type").toString();
QTC_CHECK(type == "environment");
if (type == "environment") { if (type == "environment") {
OsType osType = osTypeFromString(map.value("OsType").toString()); expected_str<OsType> osType = osTypeFromString(map.value("OsType").toString());
Environment env(map.value("Env").toStringList(), osType); QTC_CHECK_EXPECTED(osType);
Environment env(map.value("Env").toStringList(), osType.value_or(OsTypeLinux));
envPromise->addResult(env); envPromise->addResult(env);
} else if (type == "error") { } else if (type == "error") {
QString err = map.value("Error", QString{}).toString(); QString err = map.value("Error", QString{}).toString();
qCWarning(clientLog) << "Error: " << err; qCWarning(clientLog) << "Error: " << err;
envPromise->setException(std::make_exception_ptr(std::runtime_error(err.toStdString()))); envPromise->setException(std::make_exception_ptr(std::runtime_error(err.toStdString())));
} else {
qCWarning(clientLog) << "Unknown initial response type: " << type;
envPromise->setException(
std::make_exception_ptr(std::runtime_error("Unknown response type")));
} }
envPromise->finish(); envPromise->finish();
@@ -284,9 +288,11 @@ expected_str<QFuture<Environment>> Client::start()
connect(d->process, &Process::done, d->process, [this] { connect(d->process, &Process::done, d->process, [this] {
if (d->process->resultData().m_exitCode != 0) { if (d->process->resultData().m_exitCode != 0) {
qCWarning(clientLog).noquote() << d->process->resultData().m_errorString; qCWarning(clientLog)
qCWarning(clientLog).noquote() << d->process->readAllStandardError(); << "Process exited with error code:" << d->process->resultData().m_exitCode
qCWarning(clientLog).noquote() << d->process->readAllStandardOutput(); << "Error:" << d->process->errorString()
<< "StandardError:" << d->process->readAllStandardError()
<< "StandardOutput:" << d->process->readAllStandardOutput();
} }
auto j = d->jobs.writeLocked(); auto j = d->jobs.writeLocked();

View File

@@ -31,7 +31,8 @@ bool HostOsInfo::m_useOverrideFileNameCaseSensitivity = false;
OsArch HostOsInfo::hostArchitecture() OsArch HostOsInfo::hostArchitecture()
{ {
static const OsArch arch = osArchFromString(QSysInfo::currentCpuArchitecture()); static const OsArch arch
= osArchFromString(QSysInfo::currentCpuArchitecture()).value_or(OsArchUnknown);
return arch; return arch;
} }

View File

@@ -3,6 +3,9 @@
#pragma once #pragma once
#include "expected.h"
#include <QDebug>
#include <QString> #include <QString>
#include <algorithm> #include <algorithm>
@@ -33,7 +36,7 @@ inline QString osTypeToString(OsType osType)
} }
} }
inline OsType osTypeFromString(const QString &string) inline Utils::expected_str<OsType> osTypeFromString(const QString &string)
{ {
if (string.compare("windows", Qt::CaseInsensitive) == 0) if (string.compare("windows", Qt::CaseInsensitive) == 0)
return OsTypeWindows; return OsTypeWindows;
@@ -44,10 +47,11 @@ inline OsType osTypeFromString(const QString &string)
return OsTypeMac; return OsTypeMac;
if (string.compare("other unix", Qt::CaseInsensitive) == 0) if (string.compare("other unix", Qt::CaseInsensitive) == 0)
return OsTypeOtherUnix; return OsTypeOtherUnix;
return OsTypeOther;
return Utils::make_unexpected(QString::fromLatin1("Unknown os type: %1").arg(string));
} }
inline OsArch osArchFromString(const QString &architecture) inline Utils::expected_str<OsArch> osArchFromString(const QString &architecture)
{ {
if (architecture == QLatin1String("x86_64")) if (architecture == QLatin1String("x86_64"))
return OsArchAMD64; return OsArchAMD64;
@@ -59,7 +63,8 @@ inline OsArch osArchFromString(const QString &architecture)
return OsArchArm; return OsArchArm;
if (architecture == QLatin1String("arm64") || architecture == QLatin1String("aarch64")) if (architecture == QLatin1String("arm64") || architecture == QLatin1String("aarch64"))
return OsArchArm64; return OsArchArm64;
return OsArchUnknown;
return Utils::make_unexpected(QString::fromLatin1("Unknown architecture: %1").arg(architecture));
} }
namespace OsSpecificAspects { namespace OsSpecificAspects {

View File

@@ -313,7 +313,7 @@ public:
QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); } QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); }
QString dockerImageId() const { return deviceSettings->imageId(); } QString dockerImageId() const { return deviceSettings->imageId(); }
QPair<Utils::OsType, Utils::OsArch> osTypeAndArch() const; expected_str<QPair<Utils::OsType, Utils::OsArch>> osTypeAndArch() const;
expected_str<Environment> environment(); expected_str<Environment> environment();
@@ -332,6 +332,8 @@ public:
void stopCurrentContainer(); void stopCurrentContainer();
expected_str<void> fetchSystemEnviroment(); expected_str<void> fetchSystemEnviroment();
expected_str<FilePath> getCmdBridgePath() const;
std::optional<FilePath> clangdExecutable() const std::optional<FilePath> clangdExecutable() const
{ {
if (deviceSettings->clangdExecutable().isEmpty()) if (deviceSettings->clangdExecutable().isEmpty())
@@ -594,7 +596,22 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
: ProjectExplorer::IDevice(std::move(deviceSettings)) : ProjectExplorer::IDevice(std::move(deviceSettings))
, d(new DockerDevicePrivate(this)) , d(new DockerDevicePrivate(this))
{ {
setFileAccess([this]() -> DeviceFileAccess * { auto createBridgeFileAccess = [this]() -> expected_str<std::unique_ptr<DeviceFileAccess>> {
expected_str<FilePath> cmdBridgePath = d->getCmdBridgePath();
if (!cmdBridgePath)
return make_unexpected(cmdBridgePath.error());
auto fAccess = std::make_unique<DockerDeviceFileAccess>(d);
expected_str<void> initResult = fAccess->init(
rootPath().withNewPath("/tmp/_qtc_cmdbridge"));
if (!initResult)
return make_unexpected(initResult.error());
return fAccess;
};
setFileAccess([this, createBridgeFileAccess]() -> DeviceFileAccess * {
if (DeviceFileAccess *fileAccess = d->m_fileAccess.readLocked()->get()) if (DeviceFileAccess *fileAccess = d->m_fileAccess.readLocked()->get())
return fileAccess; return fileAccess;
@@ -603,15 +620,16 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
if (*fileAccess) if (*fileAccess)
return fileAccess->get(); return fileAccess->get();
auto fAccess = std::make_unique<DockerDeviceFileAccess>(d); expected_str<std::unique_ptr<DeviceFileAccess>> fAccess = createBridgeFileAccess();
expected_str<void> initResult = fAccess->init(
rootPath().withNewPath("/tmp/_qtc_cmdbridge")); if (fAccess) {
QTC_CHECK_EXPECTED(initResult); *fileAccess = std::move(*fAccess);
if (initResult) {
*fileAccess = std::move(fAccess);
return fileAccess->get(); return fileAccess->get();
} }
qCWarning(dockerDeviceLog) << "Failed to start CmdBridge:" << fAccess.error()
<< ", falling back to slow direct access";
*fileAccess = std::make_unique<DockerFallbackFileAccess>(rootPath()); *fileAccess = std::make_unique<DockerFallbackFileAccess>(rootPath());
return fileAccess->get(); return fileAccess->get();
}); });
@@ -835,13 +853,18 @@ expected_str<void> isValidMountInfo(const DockerDevicePrivate::MountPair &mi)
return {}; return {};
} }
QStringList DockerDevicePrivate::createMountArgs() const expected_str<FilePath> DockerDevicePrivate::getCmdBridgePath() const
{ {
auto osAndArch = osTypeAndArch(); auto osAndArch = osTypeAndArch();
if (!osAndArch)
return make_unexpected(osAndArch.error());
return CmdBridge::Client::getCmdBridgePath(
osAndArch->first, osAndArch->second, Core::ICore::libexecPath());
};
const Utils::expected_str<Utils::FilePath> cmdBridgePath = CmdBridge::Client::getCmdBridgePath( QStringList DockerDevicePrivate::createMountArgs() const
osAndArch.first, osAndArch.second, Core::ICore::libexecPath()); {
const Utils::expected_str<Utils::FilePath> cmdBridgePath = getCmdBridgePath();
QTC_CHECK_EXPECTED(cmdBridgePath); QTC_CHECK_EXPECTED(cmdBridgePath);
QStringList cmds; QStringList cmds;
@@ -1325,7 +1348,7 @@ void DockerDeviceFactory::shutdownExistingDevices()
} }
} }
QPair<Utils::OsType, Utils::OsArch> DockerDevicePrivate::osTypeAndArch() const expected_str<QPair<Utils::OsType, Utils::OsArch>> DockerDevicePrivate::osTypeAndArch() const
{ {
Process proc; Process proc;
proc.setCommand( proc.setCommand(
@@ -1333,13 +1356,21 @@ QPair<Utils::OsType, Utils::OsArch> DockerDevicePrivate::osTypeAndArch() const
{"image", "inspect", repoAndTag(), "--format", "{{.Os}}\t{{.Architecture}}"}}); {"image", "inspect", repoAndTag(), "--format", "{{.Os}}\t{{.Architecture}}"}});
proc.runBlocking(); proc.runBlocking();
if (proc.result() != ProcessResult::FinishedWithSuccess) if (proc.result() != ProcessResult::FinishedWithSuccess)
return {Utils::OsType::OsTypeOther, Utils::OsArch::OsArchUnknown}; return make_unexpected(Tr::tr("Failed to inspect image: %1").arg(proc.allOutput()));
const QString out = proc.cleanedStdOut().trimmed(); const QString out = proc.cleanedStdOut().trimmed();
const QStringList parts = out.split('\t'); const QStringList parts = out.split('\t');
if (parts.size() != 2) if (parts.size() != 2)
return {Utils::OsType::OsTypeOther, Utils::OsArch::OsArchUnknown}; return make_unexpected(Tr::tr("Could not parse image inspect output: %1").arg(out));
return {osTypeFromString(parts.at(0)), osArchFromString(parts.at(1))};
auto os = osTypeFromString(parts.at(0));
auto arch = osArchFromString(parts.at(1));
if (!os)
return make_unexpected(os.error());
if (!arch)
return make_unexpected(arch.error());
return qMakePair(os.value(), arch.value());
} }
expected_str<Environment> DockerDevicePrivate::environment() expected_str<Environment> DockerDevicePrivate::environment()

View File

@@ -500,7 +500,7 @@ void IDevice::fromMap(const Store &map)
settings()->fromMap(map); settings()->fromMap(map);
d->id = Id::fromSetting(map.value(IdKey)); d->id = Id::fromSetting(map.value(IdKey));
d->osType = osTypeFromString(map.value(ClientOsTypeKey, osTypeToString(OsTypeLinux)).toString()); d->osType = osTypeFromString(map.value(ClientOsTypeKey).toString()).value_or(OsTypeLinux);
if (!d->id.isValid()) if (!d->id.isValid())
d->id = newId(); d->id = newId();
d->origin = static_cast<Origin>(map.value(OriginKey, ManuallyAdded).toInt()); d->origin = static_cast<Origin>(map.value(OriginKey, ManuallyAdded).toInt());