From 3af260b7826fe0509723e12d29d71ca407cc50b5 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 5 Apr 2022 11:43:56 +0200 Subject: 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 --- .clang-format | 1 + src/plugins/docker/CMakeLists.txt | 1 + src/plugins/docker/docker.qbs | 2 + src/plugins/docker/dockerapi.cpp | 121 ++++++++++++++++++++++++++++++ src/plugins/docker/dockerapi.h | 68 +++++++++++++++++ src/plugins/docker/dockerdevice.cpp | 18 ++--- src/plugins/docker/dockerdevicewidget.cpp | 14 ++-- src/plugins/docker/dockerplugin.cpp | 21 +++--- src/plugins/docker/dockerplugin.h | 6 +- 9 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 src/plugins/docker/dockerapi.cpp create mode 100644 src/plugins/docker/dockerapi.h diff --git a/.clang-format b/.clang-format index 97f7f2b2347..70777652915 100644 --- a/.clang-format +++ b/.clang-format @@ -99,6 +99,7 @@ PenaltyExcessCharacter: 50 PenaltyReturnTypeOnItsOwnLine: 300 PointerAlignment: Right ReflowComments: false +RemoveBracesLLVM: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: true diff --git a/src/plugins/docker/CMakeLists.txt b/src/plugins/docker/CMakeLists.txt index 3153b10bb1c..68903c630b5 100644 --- a/src/plugins/docker/CMakeLists.txt +++ b/src/plugins/docker/CMakeLists.txt @@ -2,6 +2,7 @@ add_qtc_plugin(Docker PLUGIN_DEPENDS Core ProjectExplorer QtSupport SOURCES docker_global.h + dockerapi.cpp dockerapi.h dockerbuildstep.cpp dockerbuildstep.h dockerconstants.h dockerdevice.cpp dockerdevice.h diff --git a/src/plugins/docker/docker.qbs b/src/plugins/docker/docker.qbs index 3c6680604a1..1e79fa8f5b9 100644 --- a/src/plugins/docker/docker.qbs +++ b/src/plugins/docker/docker.qbs @@ -12,6 +12,8 @@ QtcPlugin { files: [ "docker_global.h", + "dockerapi.cpp", + "dockerapi.h", "dockerbuildstep.cpp", "dockerbuildstep.h", "dockerconstants.h", diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp new file mode 100644 index 00000000000..b733b18320d --- /dev/null +++ b/src/plugins/docker/dockerapi.cpp @@ -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 +#include + +#include +#include + +#include + +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 DockerApi::dockerDaemonAvailable() +{ + if (!m_dockerDaemonAvailable.has_value()) + checkCanConnect(); + return m_dockerDaemonAvailable; +} + +Utils::optional 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 diff --git a/src/plugins/docker/dockerapi.h b/src/plugins/docker/dockerapi.h new file mode 100644 index 00000000000..186a5ccfd97 --- /dev/null +++ b/src/plugins/docker/dockerapi.h @@ -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 +#include + +#include +#include +#include + +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 dockerDaemonAvailable(); + static Utils::optional isDockerDaemonAvailable(); + +private: + Utils::FilePath findDockerClient(); + +private: + Utils::FilePath m_dockerExecutable; + Utils::optional m_dockerDaemonAvailable; + QMutex m_daemonCheckGuard; +}; + +} // namespace Internal +} // namespace Docker diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 314119ead30..772d9c541ac 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -317,7 +317,7 @@ void DockerDevice::updateContainerAccess() const void DockerDevicePrivate::stopCurrentContainer() { - if (m_container.isEmpty() || !DockerPlugin::isDaemonRunning().value_or(false)) + if (m_container.isEmpty() || !DockerApi::isDockerDaemonAvailable().value_or(false)) return; if (m_shell) { @@ -412,7 +412,7 @@ void DockerDevicePrivate::startContainer() // negative exit codes indicate problems like no docker daemon, missing permissions, // no shell and seem to result in exit codes 125+ if (exitCode > 120) { - DockerPlugin::setGlobalDaemonState(false); + DockerApi::recheckDockerDaemon(); LOG("DOCKER DAEMON NOT RUNNING?"); MessageManager::writeFlashing(tr("Docker daemon appears to be not running. " "Verify daemon is up and running and reset the " @@ -428,12 +428,10 @@ void DockerDevicePrivate::startContainer() m_shell->waitForStarted(); if (!m_shell->isRunning()) { - DockerPlugin::setGlobalDaemonState(false); + DockerApi::recheckDockerDaemon(); LOG("DOCKER SHELL FAILED"); return; } - - DockerPlugin::setGlobalDaemonState(true); } void DockerDevicePrivate::updateContainerAccess() @@ -441,7 +439,7 @@ void DockerDevicePrivate::updateContainerAccess() if (!m_container.isEmpty()) return; - if (DockerPlugin::isDaemonRunning().value_or(true) == false) + if (DockerApi::isDockerDaemonAvailable().value_or(true) == false) return; if (m_shell) @@ -899,7 +897,7 @@ bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray void DockerDevice::runProcess(QtcProcess &process) const { updateContainerAccess(); - if (!DockerPlugin::isDaemonRunning().value_or(false)) + if (!DockerApi::isDockerDaemonAvailable().value_or(false)) return; if (d->m_container.isEmpty()) { LOG("No container set to run " << process.commandLine().toUserOutput()); @@ -979,7 +977,7 @@ void DockerDevicePrivate::fetchSystemEnviroment() bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const { - if (!DockerPlugin::isDaemonRunning().value_or(false)) + if (!DockerApi::isDockerDaemonAvailable().value_or(false)) return false; CommandLine dcmd{"docker", {"exec", m_container}}; dcmd.addCommandLineAsArgs(cmd); @@ -997,7 +995,7 @@ bool DockerDevicePrivate::runInContainer(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()); return false; } @@ -1023,7 +1021,7 @@ static QByteArray randomHex() QByteArray DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const { - if (!DockerPlugin::isDaemonRunning().value_or(false)) + if (!DockerApi::isDockerDaemonAvailable().value_or(false)) return {}; QTC_ASSERT(m_shell && m_shell->isRunning(), return {}); QMutexLocker l(&m_shellMutex); diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index 5409bc31d12..a652cc27c37 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -71,13 +71,17 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) "It will be automatically re-evaluated next time access is needed.")); m_daemonState = new QLabel; - updateDaemonStateTexts(); - connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] { - DockerPlugin::setGlobalDaemonState(Utils::nullopt); + connect(DockerApi::instance(), &DockerApi::dockerDaemonAvailableChanged, this, [this]{ updateDaemonStateTexts(); }); + updateDaemonStateTexts(); + + connect(m_daemonReset, &QToolButton::clicked, this, [] { + DockerApi::recheckDockerDaemon(); + }); + m_runAsOutsideUser = new QCheckBox(tr("Run as outside user")); m_runAsOutsideUser->setToolTip(tr("Uses user ID and group ID of the user running Qt Creator " "in the docker container.")); @@ -150,7 +154,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) 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.")); else logView->append(tr("Docker daemon appears to be running.")); @@ -208,7 +212,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) void DockerDeviceWidget::updateDaemonStateTexts() { - Utils::optional daemonState = DockerPlugin::isDaemonRunning(); + Utils::optional daemonState = DockerApi::instance()->dockerDaemonAvailable(); if (!daemonState.has_value()) { m_daemonReset->setIcon(Icons::INFO.icon()); m_daemonState->setText(tr("Daemon state not evaluated.")); diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp index d195c81d6b3..3c07eb29857 100644 --- a/src/plugins/docker/dockerplugin.cpp +++ b/src/plugins/docker/dockerplugin.cpp @@ -27,6 +27,7 @@ #include "dockerconstants.h" +#include "dockerapi.h" #include "dockerbuildstep.h" #include "dockerdevice.h" #include "dockersettings.h" @@ -43,13 +44,15 @@ namespace Internal { class DockerPluginPrivate { public: -// DockerSettings settings; -// DockerOptionsPage optionsPage{&settings}; + // DockerSettings settings; + // DockerOptionsPage optionsPage{&settings}; DockerDeviceFactory deviceFactory; -// DockerBuildStepFactory buildStepFactory; + // DockerBuildStepFactory buildStepFactory; Utils::optional daemonRunning; + + DockerApi dockerApi; }; static DockerPlugin *s_instance = nullptr; @@ -59,16 +62,10 @@ DockerPlugin::DockerPlugin() s_instance = this; } -// Utils::null_opt for not evaluated, true or false if it had been evaluated already -Utils::optional DockerPlugin::isDaemonRunning() -{ - return s_instance ? s_instance->d->daemonRunning : Utils::nullopt; -} - -void DockerPlugin::setGlobalDaemonState(Utils::optional state) +DockerApi *DockerPlugin::dockerApi() { - QTC_ASSERT(s_instance, return); - s_instance->d->daemonRunning = state; + QTC_ASSERT(s_instance, return nullptr); + return &s_instance->d->dockerApi; } DockerPlugin::~DockerPlugin() diff --git a/src/plugins/docker/dockerplugin.h b/src/plugins/docker/dockerplugin.h index aa9c093556a..7eb1d3b10b4 100644 --- a/src/plugins/docker/dockerplugin.h +++ b/src/plugins/docker/dockerplugin.h @@ -25,6 +25,8 @@ #pragma once +#include "dockerapi.h" + #include #include @@ -36,11 +38,11 @@ class DockerPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Docker.json") + public: DockerPlugin(); - static Utils::optional isDaemonRunning(); - static void setGlobalDaemonState(Utils::optional state); + static DockerApi *dockerApi(); private: ~DockerPlugin() final; -- cgit v1.2.3