Docker: Check Image available

Docker will try to download images from the registry if an image is
not available locally. This takes a while, even if the image is not
available remotely. To circumvent the hangs resulting from this we
first check if the image is available locally and if it is not we do
not try to start it.

Fixes: QTCREATORBUG-28880
Change-Id: I6b9de8601b87e3050ae9ac5f1bbe3fa9701d4cc1
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-03-21 09:43:10 +01:00
parent 65814b124c
commit 97c1bb53a5
2 changed files with 47 additions and 19 deletions

View File

@@ -143,7 +143,7 @@ public:
RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {});
void updateContainerAccess(); bool updateContainerAccess();
void changeMounts(QStringList newMounts); void changeMounts(QStringList newMounts);
bool ensureReachable(const FilePath &other); bool ensureReachable(const FilePath &other);
void shutdown(); void shutdown();
@@ -171,7 +171,7 @@ public:
Tasks validateMounts() const; Tasks validateMounts() const;
bool createContainer(); bool createContainer();
void startContainer(); bool startContainer();
void stopCurrentContainer(); void stopCurrentContainer();
void fetchSystemEnviroment(); void fetchSystemEnviroment();
@@ -186,6 +186,8 @@ public:
QStringList createMountArgs() const; QStringList createMountArgs() const;
bool isImageAvailable() const;
DockerDevice *const q; DockerDevice *const q;
DockerDeviceData m_data; DockerDeviceData m_data;
DockerSettings *m_settings; DockerSettings *m_settings;
@@ -392,7 +394,9 @@ DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &dat
setOpenTerminal([this, settings](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. Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below.
updateContainerAccess(); if (!updateContainerAccess())
return;
if (d->containerId().isEmpty()) { if (d->containerId().isEmpty()) {
MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container.")); MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container."));
return; return;
@@ -446,9 +450,9 @@ void DockerDevice::setData(const DockerDeviceData &data)
d->setData(data); d->setData(data);
} }
void DockerDevice::updateContainerAccess() const bool DockerDevice::updateContainerAccess() const
{ {
d->updateContainerAccess(); return d->updateContainerAccess();
} }
CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
@@ -461,7 +465,8 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd,
if (!m_settings) if (!m_settings)
return {}; return {};
updateContainerAccess(); if (!updateContainerAccess())
return {};
CommandLine dockerCmd{m_settings->dockerBinaryPath.filePath(), {"exec"}}; CommandLine dockerCmd{m_settings->dockerBinaryPath.filePath(), {"exec"}};
@@ -614,11 +619,30 @@ QStringList DockerDevicePrivate::createMountArgs() const
return cmds; return cmds;
} }
bool DockerDevicePrivate::isImageAvailable() const
{
QtcProcess proc;
proc.setCommand(
{m_settings->dockerBinaryPath.filePath(),
{"image", "list", m_data.repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}});
proc.runBlocking();
if (proc.result() != ProcessResult::FinishedWithSuccess)
return false;
if (proc.stdOut().trimmed() == m_data.repoAndTag())
return true;
return false;
}
bool DockerDevicePrivate::createContainer() bool DockerDevicePrivate::createContainer()
{ {
if (!m_settings) if (!m_settings)
return false; return false;
if (!isImageAvailable())
return false;
const QString display = HostOsInfo::isLinuxHost() ? QString(":0") const QString display = HostOsInfo::isLinuxHost() ? QString(":0")
: QString("host.docker.internal:0"); : QString("host.docker.internal:0");
CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(), CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(),
@@ -671,10 +695,10 @@ bool DockerDevicePrivate::createContainer()
return true; return true;
} }
void DockerDevicePrivate::startContainer() bool DockerDevicePrivate::startContainer()
{ {
if (!createContainer()) if (!createContainer())
return; return false;
m_shell = std::make_unique<ContainerShell>(m_settings, m_container, q->rootPath()); m_shell = std::make_unique<ContainerShell>(m_settings, m_container, q->rootPath());
@@ -693,23 +717,25 @@ void DockerDevicePrivate::startContainer()
"or restart Qt Creator.")); "or restart Qt Creator."));
}); });
if (!m_shell->start()) { if (m_shell->start())
qCWarning(dockerDeviceLog) << "Container shell failed to start"; return true;
}
qCWarning(dockerDeviceLog) << "Container shell failed to start";
return false;
} }
void DockerDevicePrivate::updateContainerAccess() bool DockerDevicePrivate::updateContainerAccess()
{ {
if (m_isShutdown) if (m_isShutdown)
return; return false;
if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false) if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false)
return; return false;
if (m_shell) if (m_shell)
return; return true;
startContainer(); return startContainer();
} }
void DockerDevice::setMounts(const QStringList &mounts) const void DockerDevice::setMounts(const QStringList &mounts) const
@@ -881,7 +907,8 @@ void DockerDevice::aboutToBeRemoved() const
void DockerDevicePrivate::fetchSystemEnviroment() void DockerDevicePrivate::fetchSystemEnviroment()
{ {
updateContainerAccess(); if (!updateContainerAccess())
return;
if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) { if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) {
const RunResult result = runInShell({"env", {}}); const RunResult result = runInShell({"env", {}});
@@ -905,7 +932,8 @@ void DockerDevicePrivate::fetchSystemEnviroment()
RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData) RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData)
{ {
updateContainerAccess(); if (!updateContainerAccess())
return {};
QTC_ASSERT(m_shell, return {}); QTC_ASSERT(m_shell, return {});
return m_shell->runInShell(cmd, stdInData); return m_shell->runInShell(cmd, stdInData);
} }

View File

@@ -95,7 +95,7 @@ public:
void setData(const DockerDeviceData &data); void setData(const DockerDeviceData &data);
void updateContainerAccess() const; bool updateContainerAccess() const;
void setMounts(const QStringList &mounts) const; void setMounts(const QStringList &mounts) const;
bool prepareForBuild(const ProjectExplorer::Target *target) override; bool prepareForBuild(const ProjectExplorer::Target *target) override;