From 9e015dbaf7d47d64abdd852dfde49fb9391b7c0e Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 20 Jul 2021 15:23:26 +0200 Subject: [PATCH] Docker: Let some commands run inside shell If there is no local access for the docker container we create lots of explicit docker calls which take time to be created and executed as this always fires up another connection to the container. Currently we expect the container to have a shell and we already have it in place, so re-use the shell to execute at least a couple of commands. This heavily increases the performance of these commands. Change-Id: Ic778a250a2b3b8c5ce2a8dd6b7fa8c532bc6d4bf Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 73 +++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 6abe4f16257..dfcf7687468 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -307,6 +308,8 @@ public: ~DockerDevicePrivate() { stopCurrentContainer(); } bool runInContainer(const CommandLine &cmd) const; + bool runInShell(const CommandLine &cmd) const; + QString outputForRunInShell(const CommandLine &cmd) const; void tryCreateLocalFileAccess(); @@ -318,6 +321,7 @@ public: // For local file access QPointer m_shell; + mutable QMutex m_shellMutex; QString m_container; QString m_mergedDir; QFileSystemWatcher m_mergedDirWatcher; @@ -746,6 +750,7 @@ void DockerDevicePrivate::stopCurrentContainer() return; if (m_shell) { + QMutexLocker l(&m_shellMutex); m_shell->write("exit\n"); m_shell->waitForFinished(2000); if (m_shell->state() == QProcess::NotRunning) { @@ -1032,7 +1037,7 @@ bool DockerDevice::isExecutableFile(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-x", path}}); + return d->runInShell({"test", {"-x", path}}); } bool DockerDevice::isReadableFile(const FilePath &filePath) const @@ -1046,7 +1051,7 @@ bool DockerDevice::isReadableFile(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-r", path, "-a", "-f", path}}); + return d->runInShell({"test", {"-r", path, "-a", "-f", path}}); } bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const @@ -1060,7 +1065,7 @@ bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-w", path, "-a", "-f", path}}); + return d->runInShell({"test", {"-w", path, "-a", "-f", path}}); } bool DockerDevice::isReadableDirectory(const FilePath &filePath) const @@ -1074,7 +1079,7 @@ bool DockerDevice::isReadableDirectory(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-r", path, "-a", "-d", path}}); + return d->runInShell({"test", {"-r", path, "-a", "-d", path}}); } bool DockerDevice::isWritableDirectory(const FilePath &filePath) const @@ -1088,7 +1093,7 @@ bool DockerDevice::isWritableDirectory(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-w", path, "-a", "-d", path}}); + return d->runInShell({"test", {"-w", path, "-a", "-d", path}}); } bool DockerDevice::isFile(const FilePath &filePath) const @@ -1102,7 +1107,7 @@ bool DockerDevice::isFile(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-f", path}}); + return d->runInShell({"test", {"-f", path}}); } bool DockerDevice::isDirectory(const FilePath &filePath) const @@ -1116,7 +1121,7 @@ bool DockerDevice::isDirectory(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-d", path}}); + return d->runInShell({"test", {"-d", path}}); } bool DockerDevice::createDirectory(const FilePath &filePath) const @@ -1144,7 +1149,7 @@ bool DockerDevice::exists(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"test", {"-e", path}}); + return d->runInShell({"test", {"-e", path}}); } bool DockerDevice::ensureExistingFile(const FilePath &filePath) const @@ -1158,7 +1163,7 @@ bool DockerDevice::ensureExistingFile(const FilePath &filePath) const return res; } const QString path = filePath.path(); - return d->runInContainer({"touch", {path}}); + return d->runInShell({"touch", {path}}); } bool DockerDevice::removeFile(const FilePath &filePath) const @@ -1271,12 +1276,8 @@ FilePaths DockerDevice::directoryEntries(const FilePath &filePath, }); } - QtcProcess proc; - proc.setCommand({"ls", {"-1", "-b", "--", filePath.path()}}); - runProcess(proc); - proc.waitForFinished(); - - QStringList entries = proc.stdOut().split('\n', Qt::SkipEmptyParts); + const QString output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}}); + QStringList entries = output.split('\n', Qt::SkipEmptyParts); return FilePath::filterEntriesHelper(filePath, entries, nameFilters, filters, sort); } @@ -1388,6 +1389,48 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const return exitCode == 0; } +bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const +{ + if (m_accessible == NoDaemon) + return false; + QTC_ASSERT(m_shell, return false); + QMutexLocker l(&m_shellMutex); + m_shell->readAllStandardOutput(); // clean possible left-overs + m_shell->write(cmd.toUserOutput().toUtf8() + "\necho $?\n"); + m_shell->waitForReadyRead(); + QByteArray output = m_shell->readAllStandardOutput(); + int result = output.toInt(); + LOG("Run command in shell:" << cmd.toUserOutput() << "result: " << output << " ==>" << result); + return result == 0; +} + +// generate hex value +static QByteArray randomHex() +{ + quint32 val = QRandomGenerator::global()->generate(); + return QString::number(val, 16).toUtf8(); +} + +QString DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const +{ + if (m_accessible == NoDaemon) + return {}; + QTC_ASSERT(m_shell, return {}); + QMutexLocker l(&m_shellMutex); + m_shell->readAllStandardOutput(); // clean possible left-overs + const QByteArray markerWithNewLine("___QC_DOCKER_" + randomHex() + "_OUTPUT_MARKER___\n"); + m_shell->write(cmd.toUserOutput().toUtf8() + "\necho -n \"" + markerWithNewLine + "\"\n"); + QByteArray output; + while (!output.endsWith(markerWithNewLine)) { + m_shell->waitForReadyRead(); + output.append(m_shell->readAllStandardOutput()); + } + LOG("Run command in shell:" << cmd.toUserOutput() << "output size:" << output.size()); + if (QTC_GUARD(output.endsWith(markerWithNewLine))) + output.chop(markerWithNewLine.size()); + return QString::fromUtf8(output); +} + // Factory DockerDeviceFactory::DockerDeviceFactory()