docker-plugin: fix daemon state

Changed daemon state to actually check the docker runtime to determine
if its available or not.

Change-Id: I9e183658dfc7c34e229aec2a332cf303793284e5
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2022-04-05 11:43:56 +02:00
parent 1d63eaee23
commit 3af260b782
9 changed files with 223 additions and 29 deletions

View File

@@ -99,6 +99,7 @@ PenaltyExcessCharacter: 50
PenaltyReturnTypeOnItsOwnLine: 300 PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right PointerAlignment: Right
ReflowComments: false ReflowComments: false
RemoveBracesLLVM: true
SortIncludes: true SortIncludes: true
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: true SpaceAfterCStyleCast: true

View File

@@ -2,6 +2,7 @@ add_qtc_plugin(Docker
PLUGIN_DEPENDS Core ProjectExplorer QtSupport PLUGIN_DEPENDS Core ProjectExplorer QtSupport
SOURCES SOURCES
docker_global.h docker_global.h
dockerapi.cpp dockerapi.h
dockerbuildstep.cpp dockerbuildstep.h dockerbuildstep.cpp dockerbuildstep.h
dockerconstants.h dockerconstants.h
dockerdevice.cpp dockerdevice.h dockerdevice.cpp dockerdevice.h

View File

@@ -12,6 +12,8 @@ QtcPlugin {
files: [ files: [
"docker_global.h", "docker_global.h",
"dockerapi.cpp",
"dockerapi.h",
"dockerbuildstep.cpp", "dockerbuildstep.cpp",
"dockerbuildstep.h", "dockerbuildstep.h",
"dockerconstants.h", "dockerconstants.h",

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "dockerapi.h"
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/qtcprocess.h>
#include <QLoggingCategory>
#include <QtConcurrent>
#include <thread>
Q_LOGGING_CATEGORY(dockerApiLog, "qtc.docker.api", QtDebugMsg);
namespace Docker {
namespace Internal {
using namespace Utils;
DockerApi *s_instance{nullptr};
DockerApi::DockerApi()
{
s_instance = this;
}
DockerApi *DockerApi::instance()
{
return s_instance;
}
bool DockerApi::canConnect()
{
QtcProcess process;
FilePath dockerExe = findDockerClient();
if (dockerExe.isEmpty() || !dockerExe.isExecutableFile())
return false;
bool result = false;
process.setCommand(CommandLine(dockerExe, QStringList{"info"}));
connect(&process, &QtcProcess::done, [&process, &result] {
qCInfo(dockerApiLog) << "'docker info' result:\n" << qPrintable(process.allOutput());
if (process.result() == ProcessResult::FinishedWithSuccess)
result = true;
});
process.start();
process.waitForFinished();
return result;
}
void DockerApi::checkCanConnect()
{
std::unique_lock lk(m_daemonCheckGuard, std::try_to_lock);
if (!lk.owns_lock())
return;
m_dockerDaemonAvailable = nullopt;
dockerDaemonAvailableChanged();
auto future = QtConcurrent::run(QThreadPool::globalInstance(), [lk = std::move(lk), this] {
m_dockerDaemonAvailable = canConnect();
dockerDaemonAvailableChanged();
});
Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin");
}
void DockerApi::recheckDockerDaemon()
{
QTC_ASSERT(s_instance, return );
s_instance->checkCanConnect();
}
Utils::optional<bool> DockerApi::dockerDaemonAvailable()
{
if (!m_dockerDaemonAvailable.has_value())
checkCanConnect();
return m_dockerDaemonAvailable;
}
Utils::optional<bool> DockerApi::isDockerDaemonAvailable()
{
QTC_ASSERT(s_instance, return nullopt);
return s_instance->dockerDaemonAvailable();
}
FilePath DockerApi::findDockerClient()
{
if (m_dockerExecutable.isEmpty() || m_dockerExecutable.isExecutableFile())
m_dockerExecutable = FilePath::fromString("docker").searchInPath();
return m_dockerExecutable;
}
} // namespace Internal
} // namespace Docker

View File

@@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QMutex>
#include <QObject>
#include <utils/filepath.h>
#include <utils/guard.h>
#include <utils/optional.h>
namespace Docker {
namespace Internal {
class DockerApi : public QObject
{
Q_OBJECT
public:
DockerApi();
static DockerApi *instance();
bool canConnect();
void checkCanConnect();
static void recheckDockerDaemon();
signals:
void dockerDaemonAvailableChanged();
public:
Utils::optional<bool> dockerDaemonAvailable();
static Utils::optional<bool> isDockerDaemonAvailable();
private:
Utils::FilePath findDockerClient();
private:
Utils::FilePath m_dockerExecutable;
Utils::optional<bool> m_dockerDaemonAvailable;
QMutex m_daemonCheckGuard;
};
} // namespace Internal
} // namespace Docker

View File

@@ -317,7 +317,7 @@ void DockerDevice::updateContainerAccess() const
void DockerDevicePrivate::stopCurrentContainer() void DockerDevicePrivate::stopCurrentContainer()
{ {
if (m_container.isEmpty() || !DockerPlugin::isDaemonRunning().value_or(false)) if (m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable().value_or(false))
return; return;
if (m_shell) { if (m_shell) {
@@ -412,7 +412,7 @@ void DockerDevicePrivate::startContainer()
// negative exit codes indicate problems like no docker daemon, missing permissions, // negative exit codes indicate problems like no docker daemon, missing permissions,
// no shell and seem to result in exit codes 125+ // no shell and seem to result in exit codes 125+
if (exitCode > 120) { if (exitCode > 120) {
DockerPlugin::setGlobalDaemonState(false); DockerApi::recheckDockerDaemon();
LOG("DOCKER DAEMON NOT RUNNING?"); LOG("DOCKER DAEMON NOT RUNNING?");
MessageManager::writeFlashing(tr("Docker daemon appears to be not running. " MessageManager::writeFlashing(tr("Docker daemon appears to be not running. "
"Verify daemon is up and running and reset the " "Verify daemon is up and running and reset the "
@@ -428,12 +428,10 @@ void DockerDevicePrivate::startContainer()
m_shell->waitForStarted(); m_shell->waitForStarted();
if (!m_shell->isRunning()) { if (!m_shell->isRunning()) {
DockerPlugin::setGlobalDaemonState(false); DockerApi::recheckDockerDaemon();
LOG("DOCKER SHELL FAILED"); LOG("DOCKER SHELL FAILED");
return; return;
} }
DockerPlugin::setGlobalDaemonState(true);
} }
void DockerDevicePrivate::updateContainerAccess() void DockerDevicePrivate::updateContainerAccess()
@@ -441,7 +439,7 @@ void DockerDevicePrivate::updateContainerAccess()
if (!m_container.isEmpty()) if (!m_container.isEmpty())
return; return;
if (DockerPlugin::isDaemonRunning().value_or(true) == false) if (DockerApi::isDockerDaemonAvailable().value_or(true) == false)
return; return;
if (m_shell) if (m_shell)
@@ -899,7 +897,7 @@ bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray
void DockerDevice::runProcess(QtcProcess &process) const void DockerDevice::runProcess(QtcProcess &process) const
{ {
updateContainerAccess(); updateContainerAccess();
if (!DockerPlugin::isDaemonRunning().value_or(false)) if (!DockerApi::isDockerDaemonAvailable().value_or(false))
return; return;
if (d->m_container.isEmpty()) { if (d->m_container.isEmpty()) {
LOG("No container set to run " << process.commandLine().toUserOutput()); LOG("No container set to run " << process.commandLine().toUserOutput());
@@ -979,7 +977,7 @@ void DockerDevicePrivate::fetchSystemEnviroment()
bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
{ {
if (!DockerPlugin::isDaemonRunning().value_or(false)) if (!DockerApi::isDockerDaemonAvailable().value_or(false))
return false; return false;
CommandLine dcmd{"docker", {"exec", m_container}}; CommandLine dcmd{"docker", {"exec", m_container}};
dcmd.addCommandLineAsArgs(cmd); dcmd.addCommandLineAsArgs(cmd);
@@ -997,7 +995,7 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const
{ {
if (!QTC_GUARD(DockerPlugin::isDaemonRunning().value_or(false))) { if (!QTC_GUARD(DockerApi::isDockerDaemonAvailable().value_or(false))) {
LOG("No daemon. Could not run " << cmd.toUserOutput()); LOG("No daemon. Could not run " << cmd.toUserOutput());
return false; return false;
} }
@@ -1023,7 +1021,7 @@ static QByteArray randomHex()
QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const
{ {
if (!DockerPlugin::isDaemonRunning().value_or(false)) if (!DockerApi::isDockerDaemonAvailable().value_or(false))
return {}; return {};
QTC_ASSERT(m_shell && m_shell->isRunning(), return {}); QTC_ASSERT(m_shell && m_shell->isRunning(), return {});
QMutexLocker l(&m_shellMutex); QMutexLocker l(&m_shellMutex);

View File

@@ -71,11 +71,15 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
"It will be automatically re-evaluated next time access is needed.")); "It will be automatically re-evaluated next time access is needed."));
m_daemonState = new QLabel; m_daemonState = new QLabel;
connect(DockerApi::instance(), &DockerApi::dockerDaemonAvailableChanged, this, [this]{
updateDaemonStateTexts();
});
updateDaemonStateTexts(); updateDaemonStateTexts();
connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] { connect(m_daemonReset, &QToolButton::clicked, this, [] {
DockerPlugin::setGlobalDaemonState(Utils::nullopt); DockerApi::recheckDockerDaemon();
updateDaemonStateTexts();
}); });
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user")); m_runAsOutsideUser = new QCheckBox(tr("Run as outside user"));
@@ -150,7 +154,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths()); m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths());
if (DockerPlugin::isDaemonRunning().value_or(false) == false) if (DockerApi::instance()->dockerDaemonAvailable().value_or(false) == false)
logView->append(tr("Docker daemon appears to be not running.")); logView->append(tr("Docker daemon appears to be not running."));
else else
logView->append(tr("Docker daemon appears to be running.")); logView->append(tr("Docker daemon appears to be running."));
@@ -208,7 +212,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device)
void DockerDeviceWidget::updateDaemonStateTexts() void DockerDeviceWidget::updateDaemonStateTexts()
{ {
Utils::optional<bool> daemonState = DockerPlugin::isDaemonRunning(); Utils::optional<bool> daemonState = DockerApi::instance()->dockerDaemonAvailable();
if (!daemonState.has_value()) { if (!daemonState.has_value()) {
m_daemonReset->setIcon(Icons::INFO.icon()); m_daemonReset->setIcon(Icons::INFO.icon());
m_daemonState->setText(tr("Daemon state not evaluated.")); m_daemonState->setText(tr("Daemon state not evaluated."));

View File

@@ -27,6 +27,7 @@
#include "dockerconstants.h" #include "dockerconstants.h"
#include "dockerapi.h"
#include "dockerbuildstep.h" #include "dockerbuildstep.h"
#include "dockerdevice.h" #include "dockerdevice.h"
#include "dockersettings.h" #include "dockersettings.h"
@@ -50,6 +51,8 @@ public:
// DockerBuildStepFactory buildStepFactory; // DockerBuildStepFactory buildStepFactory;
Utils::optional<bool> daemonRunning; Utils::optional<bool> daemonRunning;
DockerApi dockerApi;
}; };
static DockerPlugin *s_instance = nullptr; static DockerPlugin *s_instance = nullptr;
@@ -59,16 +62,10 @@ DockerPlugin::DockerPlugin()
s_instance = this; s_instance = this;
} }
// Utils::null_opt for not evaluated, true or false if it had been evaluated already DockerApi *DockerPlugin::dockerApi()
Utils::optional<bool> DockerPlugin::isDaemonRunning()
{ {
return s_instance ? s_instance->d->daemonRunning : Utils::nullopt; QTC_ASSERT(s_instance, return nullptr);
} return &s_instance->d->dockerApi;
void DockerPlugin::setGlobalDaemonState(Utils::optional<bool> state)
{
QTC_ASSERT(s_instance, return);
s_instance->d->daemonRunning = state;
} }
DockerPlugin::~DockerPlugin() DockerPlugin::~DockerPlugin()

View File

@@ -25,6 +25,8 @@
#pragma once #pragma once
#include "dockerapi.h"
#include <extensionsystem/iplugin.h> #include <extensionsystem/iplugin.h>
#include <utils/optional.h> #include <utils/optional.h>
@@ -36,11 +38,11 @@ class DockerPlugin final : public ExtensionSystem::IPlugin
{ {
Q_OBJECT Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Docker.json") Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Docker.json")
public: public:
DockerPlugin(); DockerPlugin();
static Utils::optional<bool> isDaemonRunning(); static DockerApi *dockerApi();
static void setGlobalDaemonState(Utils::optional<bool> state);
private: private:
~DockerPlugin() final; ~DockerPlugin() final;