Prevent running out of memory when signing split packages

* Launch only up to 4 gpg processes at a time
* Prevent `gpg: signing failed: Nicht genügend Hauptspeicher verfügbar`
This commit is contained in:
Martchus 2021-04-17 17:30:18 +02:00
parent 56ad8ab761
commit e4755a283b
2 changed files with 84 additions and 48 deletions

View File

@ -539,6 +539,24 @@ struct BinaryPackageInfo {
bool artefactAlreadyPresent = false;
};
struct SigningSession : public MultiSession<std::string> {
explicit SigningSession(
std::vector<BinaryPackageInfo> &&binaryPackages, const std::string *repoPath, boost::asio::io_context &ioContext, HandlerType &&handler);
std::vector<BinaryPackageInfo> binaryPackages;
std::vector<BinaryPackageInfo>::iterator currentPackage;
const std::string *repoPath;
std::mutex mutex;
};
inline SigningSession::SigningSession(std::vector<BinaryPackageInfo> &&binaryPackages, const std::string *repoPath,
boost::asio::io_context &ioContext, SigningSession::HandlerType &&handler)
: MultiSession<std::string>(ioContext, std::move(handler))
, binaryPackages(std::move(binaryPackages))
, currentPackage(this->binaryPackages.begin())
, repoPath(repoPath)
{
}
enum class InvocationResult {
Ok,
Skipped,
@ -579,6 +597,7 @@ private:
const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName, PackageBuildProgress &packageProgress);
void invokeGpg(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName,
PackageBuildProgress &packageProgress, std::vector<BinaryPackageInfo> &&binaryPackages, BuildResult &&buildResult);
void invokeGpg(const std::shared_ptr<SigningSession> &signingSession, const std::string &packageName, PackageBuildProgress &packageProgress);
void invokeRepoAdd(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const std::string &packageName,
PackageBuildProgress &packageProgress, BuildResult &&buildResult);
void checkDownloadErrorsAndMakePackages(BatchProcessingSession::ContainerType &&failedPackages);

View File

@ -1138,62 +1138,79 @@ void ConductBuild::addPackageToRepo(
void ConductBuild::invokeGpg(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const string &packageName,
PackageBuildProgress &packageProgress, std::vector<BinaryPackageInfo> &&binaryPackages, BuildResult &&buildResult)
{
const auto *const repoPath = buildResult.repoPath;
auto signingSession = MultiSession<std::string>::create(m_setup.building.ioContext,
const auto signingSession = std::make_shared<SigningSession>(std::move(binaryPackages), buildResult.repoPath, m_setup.building.ioContext,
[this, makepkgchrootSession, &packageName, &packageProgress, buildResult = std::move(buildResult)](
MultiSession<std::string>::ContainerType &&failedPackages) mutable {
checkGpgErrorsAndContinueAddingPackagesToRepo(
makepkgchrootSession, packageName, packageProgress, std::move(buildResult), std::move(failedPackages));
});
for (const auto &binaryPackage : binaryPackages) {
auto processSession
= m_buildAction->makeBuildProcess("gpg for " + binaryPackage.name, packageProgress.buildDirectory % "/gpg-" % binaryPackage.name + ".log",
[this, signingSession, &packageProgress, repoPath, isAny = binaryPackage.isAny, binaryPackageName = binaryPackage.fileName](
boost::process::child &&child, ProcessResult &&result) mutable {
if (result.errorCode) {
// check for invocation error
m_buildAction->log()(
Phrases::ErrorMessage, "Unable to invoke gpg for ", binaryPackageName, ": ", result.errorCode.message(), '\n');
} else if (child.exit_code() != 0) {
// check for bad exit code
m_buildAction->log()(Phrases::ErrorMessage, "gpg invocation for ", binaryPackageName,
" exited with non-zero exit code: ", child.exit_code(), '\n');
} else {
// move signature to repository
try {
const auto buildDirSignaturePath
= std::filesystem::path(argsToString(packageProgress.buildDirectory, '/', binaryPackageName, ".sig"));
if (!std::filesystem::exists(buildDirSignaturePath)) {
m_buildAction->log()(Phrases::ErrorMessage, "Signature of \"", binaryPackageName,
"\" could not be created: ", buildDirSignaturePath, " does not exist after invoking gpg\n");
} else if (!isAny) {
std::filesystem::copy(buildDirSignaturePath, *repoPath % '/' % binaryPackageName + ".sig",
std::filesystem::copy_options::update_existing);
return;
} else {
std::filesystem::copy(buildDirSignaturePath, argsToString(repoPath, "/../any/", binaryPackageName, ".sig"),
std::filesystem::copy_options::update_existing);
const auto symlink = std::filesystem::path(argsToString(repoPath, '/', binaryPackageName, ".sig"));
if (std::filesystem::exists(symlink) && !std::filesystem::is_symlink(symlink)) {
std::filesystem::remove(symlink);
}
std::filesystem::create_symlink("../any/" % binaryPackageName + ".sig", symlink);
return;
}
} catch (const std::filesystem::filesystem_error &e) {
m_buildAction->log()(
Phrases::ErrorMessage, "Unable to copy signature of \"", binaryPackageName, "\" to repository: ", e.what(), '\n');
}
}
// consider the package failed
signingSession->addResponse(std::move(binaryPackageName));
});
processSession->launch(boost::process::start_dir(packageProgress.buildDirectory), m_gpgPath, "--detach-sign", "--yes", "--use-agent",
"--no-armor", "-u", m_gpgKey, binaryPackage.fileName);
m_buildAction->log()(Phrases::InfoMessage, "Signing ", binaryPackage.fileName, '\n');
constexpr auto gpgParallelLimit = 4;
const auto lock = std::unique_lock<std::mutex>(signingSession->mutex);
for (auto i = 0; i != gpgParallelLimit && signingSession->currentPackage != signingSession->binaryPackages.end();
++i, ++signingSession->currentPackage) {
invokeGpg(signingSession, packageName, packageProgress);
}
}
void ConductBuild::invokeGpg(
const std::shared_ptr<SigningSession> &signingSession, const std::string &packageName, PackageBuildProgress &packageProgress)
{
const auto &binaryPackage = *signingSession->currentPackage;
auto processSession = m_buildAction->makeBuildProcess("gpg for " + binaryPackage.name,
packageProgress.buildDirectory % "/gpg-" % binaryPackage.name + ".log",
[this, signingSession, &packageName, &packageProgress, isAny = binaryPackage.isAny, binaryPackageName = binaryPackage.fileName](
boost::process::child &&child, ProcessResult &&result) mutable {
// make the next gpg invocation
if (const auto lock = std::unique_lock<std::mutex>(signingSession->mutex);
signingSession->currentPackage != signingSession->binaryPackages.end()
&& ++signingSession->currentPackage != signingSession->binaryPackages.end()) {
invokeGpg(signingSession, packageName, packageProgress);
}
// handle results of gpg invocation
if (result.errorCode) {
// check for invocation error
m_buildAction->log()(Phrases::ErrorMessage, "Unable to invoke gpg for ", binaryPackageName, ": ", result.errorCode.message(), '\n');
} else if (child.exit_code() != 0) {
// check for bad exit code
m_buildAction->log()(
Phrases::ErrorMessage, "gpg invocation for ", binaryPackageName, " exited with non-zero exit code: ", child.exit_code(), '\n');
} else {
// move signature to repository
try {
const auto buildDirSignaturePath
= std::filesystem::path(argsToString(packageProgress.buildDirectory, '/', binaryPackageName, ".sig"));
if (!std::filesystem::exists(buildDirSignaturePath)) {
m_buildAction->log()(Phrases::ErrorMessage, "Signature of \"", binaryPackageName,
"\" could not be created: ", buildDirSignaturePath, " does not exist after invoking gpg\n");
} else if (!isAny) {
std::filesystem::copy(buildDirSignaturePath, *signingSession->repoPath % '/' % binaryPackageName + ".sig",
std::filesystem::copy_options::update_existing);
return;
} else {
std::filesystem::copy(buildDirSignaturePath, argsToString(signingSession->repoPath, "/../any/", binaryPackageName, ".sig"),
std::filesystem::copy_options::update_existing);
const auto symlink = std::filesystem::path(argsToString(signingSession->repoPath, '/', binaryPackageName, ".sig"));
if (std::filesystem::exists(symlink) && !std::filesystem::is_symlink(symlink)) {
std::filesystem::remove(symlink);
}
std::filesystem::create_symlink("../any/" % binaryPackageName + ".sig", symlink);
return;
}
} catch (const std::filesystem::filesystem_error &e) {
m_buildAction->log()(
Phrases::ErrorMessage, "Unable to copy signature of \"", binaryPackageName, "\" to repository: ", e.what(), '\n');
}
}
// consider the package failed
signingSession->addResponse(std::move(binaryPackageName));
});
processSession->launch(boost::process::start_dir(packageProgress.buildDirectory), m_gpgPath, "--detach-sign", "--yes", "--use-agent",
"--no-armor", "-u", m_gpgKey, binaryPackage.fileName);
m_buildAction->log()(Phrases::InfoMessage, "Signing ", binaryPackage.fileName, '\n');
}
void ConductBuild::invokeRepoAdd(const BatchProcessingSession::SharedPointerType &makepkgchrootSession, const string &packageName,
PackageBuildProgress &packageProgress, BuildResult &&buildResult)
{