From 30e44ae15f1edd5d92b947df5cbe08c6c92c6bb3 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 9 Jul 2024 09:52:21 +0200 Subject: [PATCH] Docker: Improve error output when bridge fails to start Change-Id: I86d9d3972c483d21cd2ff64d3581b518e6b0a819 Reviewed-by: hjk --- .../gocmdbridge/client/bridgedfileaccess.cpp | 21 +++++-- .../gocmdbridge/client/cmdbridgeclient.cpp | 18 ++++-- src/libs/utils/hostosinfo.cpp | 3 +- src/libs/utils/osspecificaspects.h | 13 ++-- src/plugins/docker/dockerdevice.cpp | 63 ++++++++++++++----- .../projectexplorer/devicesupport/idevice.cpp | 2 +- 6 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/libs/gocmdbridge/client/bridgedfileaccess.cpp b/src/libs/gocmdbridge/client/bridgedfileaccess.cpp index c8242ba92bc..2c7b6140bbc 100644 --- a/src/libs/gocmdbridge/client/bridgedfileaccess.cpp +++ b/src/libs/gocmdbridge/client/bridgedfileaccess.cpp @@ -68,22 +68,31 @@ expected_str FileAccess::deployAndInit( qCDebug(faLog) << "Found dd on remote host:" << *whichDD; - const auto unameOs = run({remoteRootPath.withNewPath("uname"), {"-s"}}); - if (!unameOs) + const expected_str unameOs = run({remoteRootPath.withNewPath("uname"), {"-s"}}); + if (!unameOs) { return make_unexpected( QString("Could not determine OS on remote host: %1").arg(unameOs.error())); + } + Utils::expected_str osType = osTypeFromString(unameOs.value()); + if (!osType) + return make_unexpected(osType.error()); qCDebug(faLog) << "Remote host OS:" << *unameOs; - const auto unameArch = run({remoteRootPath.withNewPath("uname"), {"-m"}}); - if (!unameArch) + const expected_str unameArch = run({remoteRootPath.withNewPath("uname"), {"-m"}}); + if (!unameArch) { return make_unexpected( QString("Could not determine architecture on remote host: %1").arg(unameArch.error())); + } + + const Utils::expected_str osArch = osArchFromString(unameArch.value()); + if (!osArch) + return make_unexpected(osArch.error()); qCDebug(faLog) << "Remote host architecture:" << *unameArch; - const Utils::expected_str cmdBridgePath = Client::getCmdBridgePath( - osTypeFromString(unameOs.value()), osArchFromString(unameArch.value()), libExecPath); + const Utils::expected_str cmdBridgePath + = Client::getCmdBridgePath(*osType, *osArch, libExecPath); if (!cmdBridgePath) { return make_unexpected( diff --git a/src/libs/gocmdbridge/client/cmdbridgeclient.cpp b/src/libs/gocmdbridge/client/cmdbridgeclient.cpp index 1ba693a8c88..1dacdc1d4fb 100644 --- a/src/libs/gocmdbridge/client/cmdbridgeclient.cpp +++ b/src/libs/gocmdbridge/client/cmdbridgeclient.cpp @@ -259,15 +259,19 @@ expected_str> Client::start() d->jobs.writeLocked()->map.insert(-1, [envPromise](QVariantMap map) { envPromise->start(); QString type = map.value("Type").toString(); - QTC_CHECK(type == "environment"); if (type == "environment") { - OsType osType = osTypeFromString(map.value("OsType").toString()); - Environment env(map.value("Env").toStringList(), osType); + expected_str osType = osTypeFromString(map.value("OsType").toString()); + QTC_CHECK_EXPECTED(osType); + Environment env(map.value("Env").toStringList(), osType.value_or(OsTypeLinux)); envPromise->addResult(env); } else if (type == "error") { QString err = map.value("Error", QString{}).toString(); qCWarning(clientLog) << "Error: " << err; 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(); @@ -284,9 +288,11 @@ expected_str> Client::start() connect(d->process, &Process::done, d->process, [this] { if (d->process->resultData().m_exitCode != 0) { - qCWarning(clientLog).noquote() << d->process->resultData().m_errorString; - qCWarning(clientLog).noquote() << d->process->readAllStandardError(); - qCWarning(clientLog).noquote() << d->process->readAllStandardOutput(); + qCWarning(clientLog) + << "Process exited with error code:" << d->process->resultData().m_exitCode + << "Error:" << d->process->errorString() + << "StandardError:" << d->process->readAllStandardError() + << "StandardOutput:" << d->process->readAllStandardOutput(); } auto j = d->jobs.writeLocked(); diff --git a/src/libs/utils/hostosinfo.cpp b/src/libs/utils/hostosinfo.cpp index 717dc900cd8..cbdb3525ae1 100644 --- a/src/libs/utils/hostosinfo.cpp +++ b/src/libs/utils/hostosinfo.cpp @@ -31,7 +31,8 @@ bool HostOsInfo::m_useOverrideFileNameCaseSensitivity = false; OsArch HostOsInfo::hostArchitecture() { - static const OsArch arch = osArchFromString(QSysInfo::currentCpuArchitecture()); + static const OsArch arch + = osArchFromString(QSysInfo::currentCpuArchitecture()).value_or(OsArchUnknown); return arch; } diff --git a/src/libs/utils/osspecificaspects.h b/src/libs/utils/osspecificaspects.h index 0af8ad52102..43bc28447de 100644 --- a/src/libs/utils/osspecificaspects.h +++ b/src/libs/utils/osspecificaspects.h @@ -3,6 +3,9 @@ #pragma once +#include "expected.h" + +#include #include #include @@ -33,7 +36,7 @@ inline QString osTypeToString(OsType osType) } } -inline OsType osTypeFromString(const QString &string) +inline Utils::expected_str osTypeFromString(const QString &string) { if (string.compare("windows", Qt::CaseInsensitive) == 0) return OsTypeWindows; @@ -44,10 +47,11 @@ inline OsType osTypeFromString(const QString &string) return OsTypeMac; if (string.compare("other unix", Qt::CaseInsensitive) == 0) 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 osArchFromString(const QString &architecture) { if (architecture == QLatin1String("x86_64")) return OsArchAMD64; @@ -59,7 +63,8 @@ inline OsArch osArchFromString(const QString &architecture) return OsArchArm; if (architecture == QLatin1String("arm64") || architecture == QLatin1String("aarch64")) return OsArchArm64; - return OsArchUnknown; + + return Utils::make_unexpected(QString::fromLatin1("Unknown architecture: %1").arg(architecture)); } namespace OsSpecificAspects { diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 52f7c1bec93..27878672e8f 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -313,7 +313,7 @@ public: QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); } QString dockerImageId() const { return deviceSettings->imageId(); } - QPair osTypeAndArch() const; + expected_str> osTypeAndArch() const; expected_str environment(); @@ -332,6 +332,8 @@ public: void stopCurrentContainer(); expected_str fetchSystemEnviroment(); + expected_str getCmdBridgePath() const; + std::optional clangdExecutable() const { if (deviceSettings->clangdExecutable().isEmpty()) @@ -594,7 +596,22 @@ DockerDevice::DockerDevice(std::unique_ptr deviceSettings) : ProjectExplorer::IDevice(std::move(deviceSettings)) , d(new DockerDevicePrivate(this)) { - setFileAccess([this]() -> DeviceFileAccess * { + auto createBridgeFileAccess = [this]() -> expected_str> { + expected_str cmdBridgePath = d->getCmdBridgePath(); + + if (!cmdBridgePath) + return make_unexpected(cmdBridgePath.error()); + + auto fAccess = std::make_unique(d); + expected_str 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()) return fileAccess; @@ -603,15 +620,16 @@ DockerDevice::DockerDevice(std::unique_ptr deviceSettings) if (*fileAccess) return fileAccess->get(); - auto fAccess = std::make_unique(d); - expected_str initResult = fAccess->init( - rootPath().withNewPath("/tmp/_qtc_cmdbridge")); - QTC_CHECK_EXPECTED(initResult); - if (initResult) { - *fileAccess = std::move(fAccess); + expected_str> fAccess = createBridgeFileAccess(); + + if (fAccess) { + *fileAccess = std::move(*fAccess); return fileAccess->get(); } + qCWarning(dockerDeviceLog) << "Failed to start CmdBridge:" << fAccess.error() + << ", falling back to slow direct access"; + *fileAccess = std::make_unique(rootPath()); return fileAccess->get(); }); @@ -835,13 +853,18 @@ expected_str isValidMountInfo(const DockerDevicePrivate::MountPair &mi) return {}; } -QStringList DockerDevicePrivate::createMountArgs() const +expected_str DockerDevicePrivate::getCmdBridgePath() const { 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 cmdBridgePath = CmdBridge::Client::getCmdBridgePath( - osAndArch.first, osAndArch.second, Core::ICore::libexecPath()); - +QStringList DockerDevicePrivate::createMountArgs() const +{ + const Utils::expected_str cmdBridgePath = getCmdBridgePath(); QTC_CHECK_EXPECTED(cmdBridgePath); QStringList cmds; @@ -1325,7 +1348,7 @@ void DockerDeviceFactory::shutdownExistingDevices() } } -QPair DockerDevicePrivate::osTypeAndArch() const +expected_str> DockerDevicePrivate::osTypeAndArch() const { Process proc; proc.setCommand( @@ -1333,13 +1356,21 @@ QPair DockerDevicePrivate::osTypeAndArch() const {"image", "inspect", repoAndTag(), "--format", "{{.Os}}\t{{.Architecture}}"}}); proc.runBlocking(); 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 QStringList parts = out.split('\t'); if (parts.size() != 2) - return {Utils::OsType::OsTypeOther, Utils::OsArch::OsArchUnknown}; - return {osTypeFromString(parts.at(0)), osArchFromString(parts.at(1))}; + return make_unexpected(Tr::tr("Could not parse image inspect output: %1").arg(out)); + + 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 DockerDevicePrivate::environment() diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 90eca216fa7..7e290b2fd22 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -500,7 +500,7 @@ void IDevice::fromMap(const Store &map) settings()->fromMap(map); 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()) d->id = newId(); d->origin = static_cast(map.value(OriginKey, ManuallyAdded).toInt());