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;
const auto unameOs = run({remoteRootPath.withNewPath("uname"), {"-s"}});
if (!unameOs)
const expected_str<QString> 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> 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<QString> 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> osArch = osArchFromString(unameArch.value());
if (!osArch)
return make_unexpected(osArch.error());
qCDebug(faLog) << "Remote host architecture:" << *unameArch;
const Utils::expected_str<Utils::FilePath> cmdBridgePath = Client::getCmdBridgePath(
osTypeFromString(unameOs.value()), osArchFromString(unameArch.value()), libExecPath);
const Utils::expected_str<Utils::FilePath> cmdBridgePath
= Client::getCmdBridgePath(*osType, *osArch, libExecPath);
if (!cmdBridgePath) {
return make_unexpected(

View File

@@ -259,15 +259,19 @@ expected_str<QFuture<Environment>> 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> 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<QFuture<Environment>> 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();

View File

@@ -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;
}

View File

@@ -3,6 +3,9 @@
#pragma once
#include "expected.h"
#include <QDebug>
#include <QString>
#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)
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<OsArch> 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 {

View File

@@ -313,7 +313,7 @@ public:
QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); }
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();
@@ -332,6 +332,8 @@ public:
void stopCurrentContainer();
expected_str<void> fetchSystemEnviroment();
expected_str<FilePath> getCmdBridgePath() const;
std::optional<FilePath> clangdExecutable() const
{
if (deviceSettings->clangdExecutable().isEmpty())
@@ -594,7 +596,22 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
: ProjectExplorer::IDevice(std::move(deviceSettings))
, 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())
return fileAccess;
@@ -603,15 +620,16 @@ DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings)
if (*fileAccess)
return fileAccess->get();
auto fAccess = std::make_unique<DockerDeviceFileAccess>(d);
expected_str<void> initResult = fAccess->init(
rootPath().withNewPath("/tmp/_qtc_cmdbridge"));
QTC_CHECK_EXPECTED(initResult);
if (initResult) {
*fileAccess = std::move(fAccess);
expected_str<std::unique_ptr<DeviceFileAccess>> 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<DockerFallbackFileAccess>(rootPath());
return fileAccess->get();
});
@@ -835,13 +853,18 @@ expected_str<void> isValidMountInfo(const DockerDevicePrivate::MountPair &mi)
return {};
}
QStringList DockerDevicePrivate::createMountArgs() const
expected_str<FilePath> 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<Utils::FilePath> cmdBridgePath = CmdBridge::Client::getCmdBridgePath(
osAndArch.first, osAndArch.second, Core::ICore::libexecPath());
QStringList DockerDevicePrivate::createMountArgs() const
{
const Utils::expected_str<Utils::FilePath> cmdBridgePath = getCmdBridgePath();
QTC_CHECK_EXPECTED(cmdBridgePath);
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;
proc.setCommand(
@@ -1333,13 +1356,21 @@ QPair<Utils::OsType, Utils::OsArch> 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<Environment> DockerDevicePrivate::environment()

View File

@@ -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<Origin>(map.value(OriginKey, ManuallyAdded).toInt());