diff options
author | Marcus Tillmanns <[email protected]> | 2023-09-14 15:13:43 +0200 |
---|---|---|
committer | Marcus Tillmanns <[email protected]> | 2023-09-15 13:00:39 +0000 |
commit | b81026488ca2da8d13ae7816107c1beee0825eb1 (patch) | |
tree | 673ca4d3280526f24b770869da06bd157335ce7f | |
parent | aab39532f30331451db292b9bab787705c081180 (diff) |
Docker: Improve error reporting
Change-Id: I1a1ad824b4084ce7203357acf9ec19ccfe91b5cd
Reviewed-by: Leena Miettinen <[email protected]>
Reviewed-by: hjk <[email protected]>
-rw-r--r-- | src/libs/utils/aspects.cpp | 5 | ||||
-rw-r--r-- | src/libs/utils/deviceshell.cpp | 89 | ||||
-rw-r--r-- | src/libs/utils/deviceshell.h | 11 | ||||
-rw-r--r-- | src/plugins/docker/dockerdevice.cpp | 48 | ||||
-rw-r--r-- | src/plugins/docker/dockerdevice.h | 2 | ||||
-rw-r--r-- | src/plugins/docker/dockerdevicewidget.cpp | 1 | ||||
-rw-r--r-- | src/plugins/remotelinux/linuxdevice.cpp | 7 |
7 files changed, 86 insertions, 77 deletions
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 42bd40badf3..129c454f988 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -2611,6 +2611,10 @@ void TextDisplay::addToLayout(LayoutItem &parent) // have a QWidget parent yet when used in a LayoutBuilder. if (!isVisible()) d->m_label->setVisible(false); + + connect(this, &TextDisplay::changed, d->m_label, [this] { + d->m_label->setText(d->m_message); + }); } parent.addItem(d->m_label.data()); } @@ -2629,6 +2633,7 @@ void TextDisplay::setIconType(InfoLabel::InfoType t) void TextDisplay::setText(const QString &message) { d->m_message = message; + emit changed(); } /*! diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 724a7ce33b0..33461f128f3 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -6,6 +6,7 @@ #include "process.h" #include "processinterface.h" #include "qtcassert.h" +#include "utilstr.h" #include <QLoggingCategory> @@ -154,22 +155,12 @@ CommandLine DeviceShell::createFallbackCommand(const CommandLine &cmd) } /*! - * \brief DeviceShell::startupFailed - * - * Override to display custom error messages - */ -void DeviceShell::startupFailed(const CommandLine &cmdLine) -{ - qCWarning(deviceShellLog) << "Failed to start shell via:" << cmdLine.toUserOutput(); -} - -/*! * \brief DeviceShell::start * \return Returns true if starting the Shell process succeeded * * \note You have to call this function when deriving from DeviceShell. Current implementations call the function from their constructor. */ -bool DeviceShell::start() +expected_str<void> DeviceShell::start() { m_shellProcess = std::make_unique<Process>(); connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), @@ -185,19 +176,21 @@ bool DeviceShell::start() // Moving the process into its own thread ... m_shellProcess->moveToThread(&m_thread); - bool result = false; + expected_str<void> result; QMetaObject::invokeMethod( m_shellProcess.get(), - [this] { - qCDebug(deviceShellLog) << "Starting shell process:" << m_shellProcess->commandLine().toUserOutput(); + [this]() -> expected_str<void> { + qCDebug(deviceShellLog) + << "Starting shell process:" << m_shellProcess->commandLine().toUserOutput(); m_shellProcess->start(); if (!m_shellProcess->waitForStarted()) { closeShellProcess(); - return false; + return make_unexpected(Tr::tr("The process failed to start.")); } - if (installShellScript()) { + auto installResult = installShellScript(); + if (installResult) { connect(m_shellProcess.get(), &Process::readyReadStandardOutput, m_shellProcess.get(), @@ -210,67 +203,64 @@ bool DeviceShell::start() qCWarning(deviceShellLog) << "Received unexpected output on stderr:" << stdErr; }); + + connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), [this] { + if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS + || m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) { + qCWarning(deviceShellLog) << "Shell exited with error code:" + << m_shellProcess->resultData().m_exitCode << "(" + << m_shellProcess->exitMessage() << ")"; + } + }); + + return {}; } else if (m_shellProcess->isRunning()) { m_shellProcess->kill(); m_shellProcess.reset(); - return false; - } else - return false; - - connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), [this] { - if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS - || m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) { - qCWarning(deviceShellLog) << "Shell exited with error code:" - << m_shellProcess->resultData().m_exitCode << "(" - << m_shellProcess->exitMessage() << ")"; - } - }); - - return true; + } + return make_unexpected(Tr::tr("Failed to install shell script: %1\n%2") + .arg(installResult.error()) + .arg(m_shellProcess->readAllStandardError())); }, Qt::BlockingQueuedConnection, &result); - if (!result) { - startupFailed(cmdLine); - } - return result; } -bool DeviceShell::checkCommand(const QByteArray &command) +expected_str<QByteArray> DeviceShell::checkCommand(const QByteArray &command) { const QByteArray checkCmd = "(which " + command + " || echo '<missing>')\n"; m_shellProcess->writeRaw(checkCmd); if (!m_shellProcess->waitForReadyRead()) { - qCWarning(deviceShellLog) << "Timeout while trying to check for" << command; - return false; + return make_unexpected( + Tr::tr("Timeout while trying to check for %1.").arg(QString::fromUtf8(command))); } QByteArray out = m_shellProcess->readAllRawStandardOutput(); if (out.contains("<missing>")) { m_shellScriptState = State::Failed; - qCWarning(deviceShellLog) << "Command" << command << "was not found"; m_missingFeatures.append(QString::fromUtf8(command)); - return false; + return make_unexpected(Tr::tr("Command %1 was not found.").arg(QString::fromUtf8(command))); } - return true; + return out; } -bool DeviceShell::installShellScript() +expected_str<void> DeviceShell::installShellScript() { if (m_forceFailScriptInstallation) { m_shellScriptState = State::Failed; - return false; + return make_unexpected(Tr::tr("Script installation was forced to fail.")); } static const QList<QByteArray> requiredCommands = {"base64", "cat", "echo", "kill", "mkfifo", "mktemp", "rm"}; for (const QByteArray &command : requiredCommands) { - if (!checkCommand(command)) - return false; + auto checkResult = checkCommand(command); + if (!checkResult) + return make_unexpected(checkResult.error()); } const static QByteArray shellScriptBase64 = FilePath(":/utils/scripts/deviceshell.sh") @@ -287,19 +277,18 @@ bool DeviceShell::installShellScript() while (m_shellScriptState == State::Unknown) { if (!m_shellProcess->waitForReadyRead(5000)) { - qCWarning(deviceShellLog) << "Timeout while waiting for shell script installation"; - return false; + return make_unexpected(Tr::tr("Timeout while waiting for shell script installation.")); } QByteArray out = m_shellProcess->readAllRawStandardError(); if (out.contains("SCRIPT_INSTALLED") && !out.contains("ERROR_INSTALL_SCRIPT")) { m_shellScriptState = State::Succeeded; - return true; + return {}; } if (out.contains("ERROR_INSTALL_SCRIPT")) { m_shellScriptState = State::Failed; - qCWarning(deviceShellLog) << "Failed installing device shell script"; - return false; + return make_unexpected( + Tr::tr("Failed to install shell script: %1").arg(QString::fromUtf8(out))); } if (!out.isEmpty()) { qCWarning(deviceShellLog) @@ -307,7 +296,7 @@ bool DeviceShell::installShellScript() } } - return true; + return {}; } void DeviceShell::closeShellProcess() diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index e5bc4ad7afe..73d9805e104 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -3,9 +3,9 @@ #pragma once -#include "utils_global.h" - +#include "expected.h" #include "fileutils.h" +#include "utils_global.h" #include <QHash> #include <QMutex> @@ -39,7 +39,7 @@ public: DeviceShell(bool forceFailScriptInstallation = false); virtual ~DeviceShell(); - bool start(); + expected_str<void> start(); RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); @@ -51,7 +51,6 @@ signals: void done(const ProcessResultData &resultData); protected: - virtual void startupFailed(const CommandLine &cmdLine); RunResult run(const CommandLine &cmd, const QByteArray &stdInData = {}); void close(); @@ -60,12 +59,12 @@ private: virtual void setupShellProcess(Process *shellProcess); virtual CommandLine createFallbackCommand(const CommandLine &cmdLine); - bool installShellScript(); + expected_str<void> installShellScript(); void closeShellProcess(); void onReadyRead(); - bool checkCommand(const QByteArray &command); + expected_str<QByteArray> checkCommand(const QByteArray &command); private: struct CommandRun : public RunResult diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index bcef8da84bc..ea6f27e3529 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -190,6 +190,8 @@ DockerDeviceSettings::DockerDeviceSettings() return newValue; }); }); + + containerStatus.setText(Tr::tr("stopped")); } // Used for "docker run" @@ -251,8 +253,8 @@ public: bool prepareForBuild(const Target *target); Tasks validateMounts() const; - bool createContainer(); - bool startContainer(); + expected_str<QString> createContainer(); + expected_str<void> startContainer(); void stopCurrentContainer(); void fetchSystemEnviroment(); @@ -733,10 +735,10 @@ bool DockerDevicePrivate::isImageAvailable() const return false; } -bool DockerDevicePrivate::createContainer() +expected_str<QString> DockerDevicePrivate::createContainer() { if (!isImageAvailable()) - return false; + return make_unexpected(Tr::tr("Image \"%1\" is not available.").arg(repoAndTag())); const QString display = HostOsInfo::isLinuxHost() ? QString(":0") : QString("host.docker.internal:0"); @@ -773,24 +775,25 @@ bool DockerDevicePrivate::createContainer() createProcess.runBlocking(); if (createProcess.result() != ProcessResult::FinishedWithSuccess) { - qCWarning(dockerDeviceLog) << "Failed creating docker container:"; - qCWarning(dockerDeviceLog) << "Exit Code:" << createProcess.exitCode(); - qCWarning(dockerDeviceLog) << createProcess.allOutput(); - return false; + return make_unexpected(Tr::tr("Failed creating Docker container. Exit code: %1, output: %2") + .arg(createProcess.exitCode()) + .arg(createProcess.allOutput())); } m_container = createProcess.cleanedStdOut().trimmed(); if (m_container.isEmpty()) - return false; + return make_unexpected( + Tr::tr("Failed creating Docker container. No container ID received.")); qCDebug(dockerDeviceLog) << "ContainerId:" << m_container; - return true; + return m_container; } -bool DockerDevicePrivate::startContainer() +expected_str<void> DockerDevicePrivate::startContainer() { - if (!createContainer()) - return false; + auto createResult = createContainer(); + if (!createResult) + return make_unexpected(createResult.error()); m_shell = std::make_unique<ContainerShell>(m_container, q->rootPath()); @@ -811,13 +814,10 @@ bool DockerDevicePrivate::startContainer() "or restart Qt Creator.")); }); - QTC_ASSERT(m_shell, return false); + QTC_ASSERT(m_shell, + return make_unexpected(Tr::tr("Failed to create container shell (Out of memory)."))); - if (m_shell->start()) - return true; - - qCWarning(dockerDeviceLog) << "Container shell failed to start"; - return false; + return m_shell->start(); } bool DockerDevicePrivate::updateContainerAccess() @@ -831,7 +831,15 @@ bool DockerDevicePrivate::updateContainerAccess() if (m_shell) return true; - return startContainer(); + auto result = startContainer(); + if (result) { + deviceSettings->containerStatus.setText(Tr::tr("Running")); + return true; + } + + qCWarning(dockerDeviceLog) << "Failed to start container:" << result.error(); + deviceSettings->containerStatus.setText(result.error()); + return false; } void DockerDevice::setMounts(const QStringList &mounts) const diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index d44a676bfd5..e910031c619 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -30,6 +30,8 @@ public: Utils::BoolAspect keepEntryPoint{this}; Utils::BoolAspect enableLldbFlags{this}; Utils::FilePathAspect clangdExecutable{this}; + + Utils::TextDisplay containerStatus{this}; }; class DockerDevice : public ProjectExplorer::IDevice diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index 7450297c158..018cabb07f6 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -162,6 +162,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) deviceSettings->tag, br, deviceSettings->imageId, br, daemonStateLabel, m_daemonReset, m_daemonState, br, + Tr::tr("Container State:"), deviceSettings->containerStatus, br, deviceSettings->useLocalUidGid, br, deviceSettings->keepEntryPoint, br, deviceSettings->enableLldbFlags, br, diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index d32a4edda18..3707f660052 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -847,7 +847,12 @@ public: connect(m_shell.get(), &DeviceShell::done, this, [this] { m_shell.release()->deleteLater(); }); - return m_shell->start(); + auto result = m_shell->start(); + if (!result) { + qCWarning(linuxDeviceLog) << "Failed to start shell for:" << parameters.userAtHost() + << ", " << result.error(); + } + return result.has_value(); } // Call me with shell mutex locked |