From 8a7a4fce6cd8b99bf28f1661ba51dcfafc7b9c36 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 4 Jul 2022 11:09:11 +0200 Subject: [PATCH] docker: Add dockercli setting Change-Id: I46fada555d697007042d823ef1cad0658be98e22 Reviewed-by: hjk --- src/libs/utils/qtcprocess.cpp | 1 + src/plugins/docker/dockerapi.cpp | 17 +++-- src/plugins/docker/dockerapi.h | 8 ++- src/plugins/docker/dockerconstants.h | 2 + src/plugins/docker/dockerdevice.cpp | 46 +++++++------ src/plugins/docker/dockerdevice.h | 8 ++- src/plugins/docker/dockerplugin.cpp | 11 ++- src/plugins/docker/dockersettings.cpp | 98 ++++++++------------------- src/plugins/docker/dockersettings.h | 25 +++---- 9 files changed, 86 insertions(+), 130 deletions(-) diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index 62a2435cd1b..45362d1ed71 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -1027,6 +1027,7 @@ QtcProcess::QtcProcess(QObject *parent) : QObject(parent), d(new QtcProcessPrivate(this)) { + qRegisterMetaType("ProcessResultData"); static int qProcessExitStatusMeta = qRegisterMetaType(); static int qProcessProcessErrorMeta = qRegisterMetaType(); Q_UNUSED(qProcessExitStatusMeta) diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp index 77260ed158e..0acf60f3f97 100644 --- a/src/plugins/docker/dockerapi.cpp +++ b/src/plugins/docker/dockerapi.cpp @@ -43,7 +43,8 @@ using namespace Utils; DockerApi *s_instance{nullptr}; -DockerApi::DockerApi() +DockerApi::DockerApi(QSharedPointer settings) + : m_settings(settings) { s_instance = this; } @@ -56,7 +57,7 @@ DockerApi *DockerApi::instance() bool DockerApi::canConnect() { QtcProcess process; - FilePath dockerExe = findDockerClient(); + FilePath dockerExe = dockerClient(); if (dockerExe.isEmpty() || !dockerExe.isExecutableFile()) return false; @@ -83,11 +84,11 @@ void DockerApi::checkCanConnect(bool async) return; m_dockerDaemonAvailable = nullopt; - dockerDaemonAvailableChanged(); + emit dockerDaemonAvailableChanged(); auto future = Utils::runAsync([lk = std::move(lk), this] { m_dockerDaemonAvailable = canConnect(); - dockerDaemonAvailableChanged(); + emit dockerDaemonAvailableChanged(); }); Core::ProgressManager::addTask(future, tr("Checking docker daemon"), "DockerPlugin"); @@ -98,7 +99,7 @@ void DockerApi::checkCanConnect(bool async) bool isAvailable = canConnect(); if (!m_dockerDaemonAvailable.has_value() || isAvailable != m_dockerDaemonAvailable) { m_dockerDaemonAvailable = isAvailable; - dockerDaemonAvailableChanged(); + emit dockerDaemonAvailableChanged(); } } @@ -121,11 +122,9 @@ Utils::optional DockerApi::isDockerDaemonAvailable(bool async) return s_instance->dockerDaemonAvailable(async); } -FilePath DockerApi::findDockerClient() +FilePath DockerApi::dockerClient() { - if (m_dockerExecutable.isEmpty() || m_dockerExecutable.isExecutableFile()) - m_dockerExecutable = FilePath::fromString("docker").searchInPath(); - return m_dockerExecutable; + return FilePath::fromString(m_settings->dockerBinaryPath.value()); } } // namespace Internal diff --git a/src/plugins/docker/dockerapi.h b/src/plugins/docker/dockerapi.h index 0194099a711..96f5576871c 100644 --- a/src/plugins/docker/dockerapi.h +++ b/src/plugins/docker/dockerapi.h @@ -25,6 +25,8 @@ #pragma once +#include "dockersettings.h" + #include #include @@ -40,7 +42,7 @@ class DockerApi : public QObject Q_OBJECT public: - DockerApi(); + DockerApi(QSharedPointer settings); static DockerApi *instance(); @@ -56,12 +58,12 @@ public: static Utils::optional isDockerDaemonAvailable(bool async = true); private: - Utils::FilePath findDockerClient(); + Utils::FilePath dockerClient(); private: - Utils::FilePath m_dockerExecutable; Utils::optional m_dockerDaemonAvailable; QMutex m_daemonCheckGuard; + QSharedPointer m_settings; }; } // namespace Internal diff --git a/src/plugins/docker/dockerconstants.h b/src/plugins/docker/dockerconstants.h index 41a98ba65ea..8c52da4b6ae 100644 --- a/src/plugins/docker/dockerconstants.h +++ b/src/plugins/docker/dockerconstants.h @@ -29,6 +29,8 @@ namespace Docker { namespace Constants { +const char DOCKER[] = "docker"; + const char DOCKER_SETTINGS_ID[] = "Docker.Settings"; const char DOCKER_DEVICE_TYPE[] = "DockerDeviceType"; diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 2b7b671ef09..918a5054969 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -106,18 +106,20 @@ static Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg); class ContainerShell : public Utils::DeviceShell { public: - ContainerShell(const QString &containerId) - : m_containerId(containerId) + ContainerShell(QSharedPointer settings, const QString &containerId) + : m_settings(settings) + , m_containerId(containerId) { } private: void setupShellProcess(QtcProcess *shellProcess) final { - shellProcess->setCommand({"docker", {"container", "start", "-i", "-a", m_containerId}}); + shellProcess->setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "start", "-i", "-a", m_containerId}}); } private: + QSharedPointer m_settings; QString m_containerId; }; @@ -126,8 +128,9 @@ class DockerDevicePrivate : public QObject Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerDevice) public: - DockerDevicePrivate(DockerDevice *parent) + DockerDevicePrivate(DockerDevice *parent, QSharedPointer settings) : q(parent) + , m_settings(settings) {} ~DockerDevicePrivate() { stopCurrentContainer(); } @@ -144,6 +147,7 @@ public: DockerDevice *q; DockerDeviceData m_data; + QSharedPointer m_settings; // For local file access @@ -316,8 +320,8 @@ QString DockerDeviceData::repoAndTag() const // DockerDevice -DockerDevice::DockerDevice(const DockerDeviceData &data) - : d(new DockerDevicePrivate(this)) +DockerDevice::DockerDevice(QSharedPointer settings, const DockerDeviceData &data) + : d(new DockerDevicePrivate(this, settings)) { d->m_data = data; @@ -327,7 +331,7 @@ DockerDevice::DockerDevice(const DockerDeviceData &data) setDisplayName(tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId)); setAllowEmptyCommand(true); - setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { + setOpenTerminal([this, settings](const Environment &env, const FilePath &workingDir) { Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below. updateContainerAccess(); if (d->m_container.isEmpty()) { @@ -345,7 +349,7 @@ DockerDevice::DockerDevice(const DockerDeviceData &data) }); const QString wd = workingDir.isEmpty() ? "/" : workingDir.path(); - proc->setCommand({"docker", {"exec", "-it", "-w", wd, d->m_container, "/bin/sh"}}); + proc->setCommand({settings->dockerBinaryPath.filePath(), {"exec", "-it", "-w", wd, d->m_container, "/bin/sh"}}); proc->setEnvironment(Environment::systemEnvironment()); // The host system env. Intentional. proc->start(); }); @@ -385,7 +389,7 @@ void DockerDevicePrivate::stopCurrentContainer() m_shell.reset(); QtcProcess proc; - proc.setCommand({"docker", {"container", "stop", m_container}}); + proc.setCommand({m_settings->dockerBinaryPath.filePath(), {"container", "stop", m_container}}); m_container.clear(); @@ -410,7 +414,7 @@ void DockerDevicePrivate::startContainer() { const QString display = HostOsInfo::isLinuxHost() ? QString(":0") : QString(getLocalIPv4Address() + ":0.0"); - CommandLine dockerCreate{"docker", {"create", + CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(), {"create", "-i", "--rm", "-e", QString("DISPLAY=%1").arg(display), @@ -448,7 +452,7 @@ void DockerDevicePrivate::startContainer() return; LOG("Container via process: " << m_container); - m_shell = std::make_unique(m_container); + m_shell = std::make_unique(m_settings, m_container); connect(m_shell.get(), &DeviceShell::done, this, [this] (const ProcessResultData &resultData) { if (resultData.m_error != QProcess::UnknownError) return; @@ -497,7 +501,7 @@ CommandLine DockerDevice::withDockerExecCmd(const Utils::CommandLine &cmd, bool args << "-i"; args << d->m_container; - CommandLine dcmd{"docker", args}; + CommandLine dcmd{d->m_settings->dockerBinaryPath.filePath(), args}; dcmd.addCommandLineAsArgs(cmd); return dcmd; } @@ -1001,7 +1005,7 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const { if (!DockerApi::isDockerDaemonAvailable(false).value_or(false)) return false; - CommandLine dcmd{"docker", {"exec", m_container}}; + CommandLine dcmd{m_settings->dockerBinaryPath.filePath(), {"exec", m_container}}; dcmd.addCommandLineAsArgs(cmd); QtcProcess proc; @@ -1062,8 +1066,9 @@ public: class DockerDeviceSetupWizard final : public QDialog { public: - DockerDeviceSetupWizard() + DockerDeviceSetupWizard(QSharedPointer settings) : QDialog(ICore::dialogParent()) + , m_settings(settings) { setWindowTitle(DockerDevice::tr("Docker Image Selection")); resize(800, 600); @@ -1100,7 +1105,7 @@ public: connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); - CommandLine cmd{"docker", {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}}; + CommandLine cmd{m_settings->dockerBinaryPath.filePath(), {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}}; m_log->append(DockerDevice::tr("Running \"%1\"\n").arg(cmd.toUserOutput())); m_process = new QtcProcess(this); @@ -1150,7 +1155,7 @@ public: DockerImageItem *item = m_model.itemForIndex(selectedRows.front()); QTC_ASSERT(item, return {}); - auto device = DockerDevice::create(*item); + auto device = DockerDevice::create(m_settings, *item); device->setupId(IDevice::ManuallyAdded); device->setType(Constants::DOCKER_DEVICE_TYPE); device->setMachineType(IDevice::Hardware); @@ -1163,6 +1168,7 @@ public: TreeView *m_view = nullptr; QTextBrowser *m_log = nullptr; QDialogButtonBox *m_buttons; + QSharedPointer m_settings; QtcProcess *m_process = nullptr; QString m_selectedId; @@ -1170,18 +1176,18 @@ public: // Factory -DockerDeviceFactory::DockerDeviceFactory() +DockerDeviceFactory::DockerDeviceFactory(QSharedPointer settings) : IDeviceFactory(Constants::DOCKER_DEVICE_TYPE) { setDisplayName(DockerDevice::tr("Docker Device")); setIcon(QIcon()); - setCreator([] { - DockerDeviceSetupWizard wizard; + setCreator([settings] { + DockerDeviceSetupWizard wizard(settings); if (wizard.exec() != QDialog::Accepted) return IDevice::Ptr(); return wizard.device(); }); - setConstructionFunction([] { return DockerDevice::create({}); }); + setConstructionFunction([settings] { return DockerDevice::create(settings, {}); }); } } // Internal diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index d7a59aa38c0..9ddef267e51 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -25,6 +25,8 @@ #pragma once +#include "dockersettings.h" + #include #include #include @@ -56,10 +58,10 @@ public: using Ptr = QSharedPointer; using ConstPtr = QSharedPointer; - explicit DockerDevice(const DockerDeviceData &data); + explicit DockerDevice(QSharedPointer settings, const DockerDeviceData &data); ~DockerDevice(); - static Ptr create(const DockerDeviceData &data) { return Ptr(new DockerDevice(data)); } + static Ptr create(QSharedPointer settings, const DockerDeviceData &data) { return Ptr(new DockerDevice(settings, data)); } ProjectExplorer::IDeviceWidget *createWidget() override; QList validate() const override; @@ -133,7 +135,7 @@ private: class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory { public: - DockerDeviceFactory(); + DockerDeviceFactory(QSharedPointer settings); }; } // Internal diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp index fb5d410d05c..fb3720358f2 100644 --- a/src/plugins/docker/dockerplugin.cpp +++ b/src/plugins/docker/dockerplugin.cpp @@ -25,8 +25,6 @@ #include "dockerplugin.h" -#include "dockerconstants.h" - #include "dockerapi.h" #include "dockerdevice.h" #include "dockersettings.h" @@ -45,15 +43,14 @@ namespace Internal { class DockerPluginPrivate { public: - // DockerSettings settings; - // DockerOptionsPage optionsPage{&settings}; - - DockerDeviceFactory deviceFactory; + QSharedPointer m_settings{new DockerSettings}; + DockerDeviceFactory deviceFactory{m_settings}; + DockerSettingsPage m_settingPage{m_settings}; // DockerBuildStepFactory buildStepFactory; Utils::optional daemonRunning; - DockerApi dockerApi; + DockerApi dockerApi{m_settings}; }; static DockerPlugin *s_instance = nullptr; diff --git a/src/plugins/docker/dockersettings.cpp b/src/plugins/docker/dockersettings.cpp index f9c615ed0ea..a752bc0aea2 100644 --- a/src/plugins/docker/dockersettings.cpp +++ b/src/plugins/docker/dockersettings.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -23,107 +23,63 @@ ** ****************************************************************************/ -#include "dockerconstants.h" #include "dockersettings.h" +#include "dockerconstants.h" + #include #include +#include #include -#include -#include using namespace Utils; namespace Docker { namespace Internal { -// DockerSettings - -const char SETTINGS_KEY[] = "Docker"; - -static DockerSettings *theSettings = nullptr; - DockerSettings::DockerSettings() { - theSettings = this; + setSettingsGroup(Constants::DOCKER); setAutoApply(false); + + registerAspect(&dockerBinaryPath); + dockerBinaryPath.setDisplayStyle(StringAspect::PathChooserDisplay); + dockerBinaryPath.setExpectedKind(PathChooser::ExistingCommand); + dockerBinaryPath.setDefaultValue(FilePath::fromString("docker").searchInPath().toString()); + dockerBinaryPath.setDisplayName(tr("Docker CLI")); + dockerBinaryPath.setHistoryCompleter("Docker.Command.History"); + dockerBinaryPath.setLabelText(tr("Command:")); + dockerBinaryPath.setSettingsKey("cli"); + readSettings(Core::ICore::settings()); - - imageListFilter.setSettingsKey("DockerListFilter"); - imageListFilter.setPlaceHolderText(tr("")); - imageListFilter.setDisplayStyle(StringAspect::LineEditDisplay); - imageListFilter.setLabelText(tr("Filter:")); - - imageList.setDisplayStyle(StringAspect::TextEditDisplay); - imageList.setLabelText(tr("Images:")); - - connect(&imageListFilter, &BaseAspect::changed, this, &DockerSettings::updateImageList); } -DockerSettings *DockerSettings::instance() -{ - return theSettings; -} +// DockerSettingsPage -void DockerSettings::writeSettings(QSettings *settings) const +DockerSettingsPage::DockerSettingsPage(QSharedPointer settings) { - settings->remove(SETTINGS_KEY); - settings->beginGroup(SETTINGS_KEY); - forEachAspect([settings](BaseAspect *aspect) { - QtcSettings::setValueWithDefault(settings, aspect->settingsKey(), - aspect->value(), aspect->defaultValue()); - }); - settings->endGroup(); -} - -void DockerSettings::updateImageList() -{ - QtcProcess process; - process.setCommand({"docker", {"search", imageListFilter.value()}}); - process.start(); - process.waitForFinished(); - imageList.setValue(process.cleanedStdOut()); -} - -void DockerSettings::readSettings(const QSettings *settings) -{ - const QString keyRoot = QString(SETTINGS_KEY) + '/'; - forEachAspect([settings, keyRoot](BaseAspect *aspect) { - QString key = aspect->settingsKey(); - const QVariant value = settings->value(keyRoot + key, aspect->defaultValue()); - aspect->setValue(value); - }); -} - -// DockerOptionsPage - -DockerOptionsPage::DockerOptionsPage(DockerSettings *settings) -{ - setId(Constants::DOCKER_SETTINGS_ID); + setId(Docker::Constants::DOCKER_SETTINGS_ID); setDisplayName(DockerSettings::tr("Docker")); setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); - setDisplayCategory(QCoreApplication::translate("ProjectExplorer", "Devices")); - setCategoryIconPath(":/projectexplorer/images/settingscategory_devices.png"); - setSettings(settings); + setSettings(settings.get()); - setLayouter([settings](QWidget *widget) { - using namespace Layouting; + setLayouter([settings = settings.get()](QWidget *widget) { DockerSettings &s = *settings; + using namespace Layouting; + // clang-format off Column { Group { - Title(DockerSettings::tr("Search Images on Docker Hub")), - Form { - s.imageListFilter, - s.imageList - }, + Title(DockerSettings::tr("Configuration")), + Row { s.dockerBinaryPath } }, Stretch() }.attachTo(widget); + // clang-format on }); } -} // Internal -} // Docker +} // namespace Internal +} // namespace Docker diff --git a/src/plugins/docker/dockersettings.h b/src/plugins/docker/dockersettings.h index 71854a3ce85..88d590d00ce 100644 --- a/src/plugins/docker/dockersettings.h +++ b/src/plugins/docker/dockersettings.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -25,36 +25,27 @@ #pragma once +#include "coreplugin/dialogs/ioptionspage.h" #include -#include - -#include namespace Docker { namespace Internal { -class DockerSettings : public Utils::AspectContainer +class DockerSettings final : public Utils::AspectContainer { Q_DECLARE_TR_FUNCTIONS(Docker::Internal::DockerSettings) public: DockerSettings(); - static DockerSettings *instance(); - void readSettings(const QSettings *settings); - void writeSettings(QSettings *settings) const; - - void updateImageList(); - - Utils::StringAspect imageListFilter; - Utils::StringAspect imageList; + Utils::StringAspect dockerBinaryPath; }; -class DockerOptionsPage final : public Core::IOptionsPage +class DockerSettingsPage final : public Core::IOptionsPage { public: - explicit DockerOptionsPage(DockerSettings *settings); + explicit DockerSettingsPage(QSharedPointer settings); }; -} // Internal -} // Docker +} // namespace Internal +} // namespace Docker