aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcus Tillmanns <[email protected]>2023-09-14 15:13:43 +0200
committerMarcus Tillmanns <[email protected]>2023-09-15 13:00:39 +0000
commitb81026488ca2da8d13ae7816107c1beee0825eb1 (patch)
tree673ca4d3280526f24b770869da06bd157335ce7f
parentaab39532f30331451db292b9bab787705c081180 (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.cpp5
-rw-r--r--src/libs/utils/deviceshell.cpp89
-rw-r--r--src/libs/utils/deviceshell.h11
-rw-r--r--src/plugins/docker/dockerdevice.cpp48
-rw-r--r--src/plugins/docker/dockerdevice.h2
-rw-r--r--src/plugins/docker/dockerdevicewidget.cpp1
-rw-r--r--src/plugins/remotelinux/linuxdevice.cpp7
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