Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions www/config/service-units.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
'frequency' => 'every-minute',
'log-dir' => 'system/monitoring'
],
// This calculate the size of repository snapshots
'snapshots-size-calculation' => [
'title' => 'Snapshots size calculation',
'description' => 'Re-calculates the size of repository snapshots and stores it in the database',
'controller' => 'Service\Unit\Repo\Size',
'method' => 'calculate',
'frequency' => 'every-day',
'time' => '00:00',
'log-dir' => 'repos/size'
],
// This generates statistics of repositories (size, packages count) every day at midnight
'stats-generate' => [
'title' => 'Repositories statistics generation',
Expand Down
29 changes: 26 additions & 3 deletions www/controllers/App/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace Controllers\App;

use Exception;

class Session
{
/**
Expand All @@ -14,7 +12,7 @@ public static function load()
/**
* Start session
*/
if (!isset($_SESSION)) {
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start([
'cookie_secure' => (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on'),
'cookie_httponly' => true,
Expand All @@ -41,5 +39,30 @@ public static function load()
* Define the new session start time (or renew the current session)
*/
$_SESSION['start_time'] = time();

/**
* Release the session file lock immediately
* Otherwise PHP keeps an exclusive lock on the session file for the whole request duration, which serializes all the concurrent
* requests of the same user (e.g. slow ajax calls would block every other action of the interface)
* Any code that needs to persist new session variables must use Session::set()
*/
session_write_close();
}

/**
* Update session variables
* The session is closed right after being loaded, so it must be reopened to persist any change
*/
public static function set(array $variables) : void
{
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}

foreach ($variables as $name => $value) {
$_SESSION[$name] = $value;
}

session_write_close();
}
}
2 changes: 1 addition & 1 deletion www/controllers/Filesystem/Directory.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public static function deleteIfEmpty(array $directories) : void
/**
* Get directory size in bytes
*/
public static function getSize(string $path)
public static function getSize(string $path): string
{
$bytestotal = 0;
$path = realpath($path);
Expand Down
3 changes: 3 additions & 0 deletions www/controllers/HttpRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ public function get(array $params, bool $parseJson = false, string $jsonExtract
curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
}

// Add a custom user agent to avoid being blocked by some servers
curl_setopt($this->ch, CURLOPT_USERAGENT, 'Repomanager (+https://github.com/lbr38/repomanager)');

// If the target must be saved as a file directly
if ($saveToFile) {
if ($headRequest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,15 @@
* Retrieve repo size and packages count
*/
if ($myrepo->getPackageType() == 'rpm') {
$repoSize = \Controllers\Filesystem\Directory::getSize($snapshotPath);
$packagesCount = count(\Controllers\Filesystem\File::findRecursive($snapshotPath, [], ['rpm']));
}
if ($myrepo->getPackageType() == 'deb') {
$repoSize = \Controllers\Filesystem\Directory::getSize($snapshotPath);
$packagesCount = count(\Controllers\Filesystem\File::findRecursive($snapshotPath, [], ['deb']));
}

/**
* Convert repo size in the most suitable byte format
* Get the size of the snapshot
*/
$repoSize = \Controllers\Utils\Convert::sizeToHuman($repoSize);
$repoSize = $repoSnapshotController->getSize($snapId, true) ?? 'Unknown';

unset($repoSnapshotController);
6 changes: 4 additions & 2 deletions www/controllers/Layout/Layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ private function login()
*/
private function logout()
{
// Start the session
session_start();
// Start the session if not already active
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}

// Reinitialize the session array, empty it
$_SESSION = [];
Expand Down
159 changes: 78 additions & 81 deletions www/controllers/Repo/Metadata/Rpm.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,118 +12,115 @@ class Rpm extends Metadata
private $createrepoArgs = '-v --compress-type=gz --general-compress-type=gz';
private $modifyrepo = '/usr/bin/modifyrepo_c';
private $modifyrepoArgs = '--compress-type=gz';

public function setRoot(string $root)
private $additionalMetadataFiles = [
'updateinfo.xml.gz' => [
'mdtype' => 'updateinfo',
'temp' => 'tmp-updateinfo'
],
'comps.xml' => [
'mdtype' => 'group',
'temp' => 'tmp-comps'
],
'modules.yaml' => [
'mdtype' => 'modules',
'temp' => 'tmp-modules'
]
];

public function setRoot(string $root): void
{
$this->root = $root;
}

/**
* Add an additional metadata file to the repository metadata using modifyrepo_c
* Searches for the file with various compressions and deletes it afterwards
* Create metadata files
* Use createrepo_c to generate repository metadata
* Use modifyrepo_c to add additional metadata files to the repository metadata
*/
private function addMetadata(string $filePrefix, string $mdtype): void
public function create(): void
{
// Check if modifyrepo_c is available
if (!file_exists($this->modifyrepo)) {
throw new Exception('Could not find modifyrepo_c on the system');
}
$this->taskLogSubStepController->new('create-metadata', 'GENERATING REPOSITORY METADATA');

// Look for the metadata file with various compression formats (and plain format) and use the first one found
$metadataFile = null;
foreach ([$filePrefix, $filePrefix . '.gz', $filePrefix . '.xz', $filePrefix . '.bz2', $filePrefix . '.zst'] as $file) {
if (file_exists($this->root . '/' . $file)) {
$metadataFile = $file;
break;
// Check if createrepo_c and modifyrepo_c exist on the system
foreach ([$this->createrepo, $this->modifyrepo] as $bin) {
if (!file_exists($bin)) {
throw new Exception('Could not find ' . $bin . ' on the system');
}
}

// If file doesn't exist, return silently (file may not exist in the source repo)
if (empty($metadataFile)) {
return;
// Check if root path exists
if (!is_dir($this->root)) {
throw new Exception('Repository root directory ' . $this->root . ' does not exist');
}

// Add the file to the repository metadata using modifyrepo_c
$modifyrepoProcess = new Process($this->modifyrepo . ' ' . $this->modifyrepoArgs . ' --mdtype=' . $mdtype . ' ' . $this->root . '/' . $metadataFile . ' ' . $this->root . '/repodata/');
$modifyrepoProcess->setBackground(true);
$modifyrepoProcess->execute();

/**
* Retrieve PID of the launched process
* Then write PID to main PID file
* Rename additional metadata files to temporary names to avoid them being included automatically by createrepo_c
* They will be added later using modifyrepo_c
* This to avoid issues with broken modules.yaml (from Oracle 8 Appstream repo notably) raising errors in createrepo_c (modifyrepo_c does not have this issue)
* https://github.com/lbr38/repomanager/issues/399
* https://github.com/lbr38/repomanager/issues/408
*/
$this->taskController->addsubpid($modifyrepoProcess->getPid());

// Retrieve output from process
$this->taskLogSubStepController->output($modifyrepoProcess->getOutput(), 'pre');

if ($modifyrepoProcess->getExitCode() != 0) {
throw new Exception('Could not add ' . $mdtype . ' to repository metadata');
foreach ($this->additionalMetadataFiles as $file => $value) {
if (file_exists($this->root . '/' . $file)) {
if (!rename($this->root . '/' . $file, $this->root . '/' . $value['temp'])) {
throw new Exception('Could not rename ' . $this->root . '/' . $file . ' to ' . $this->root . '/' . $value['temp']);
}
}
}

$modifyrepoProcess->close();
// Launch createrepo_c to generate repository metadata
$process = new Process($this->createrepo . ' ' . $this->createrepoArgs . ' ' . $this->root . '/');
$process->setBackground(true);
$process->execute();

// Delete the file as it's now part of the metadata
if (!unlink($this->root . '/' . $metadataFile)) {
throw new Exception('Could not delete ' . $this->root . '/' . $metadataFile);
}
}
// Add PID to main PID file
$this->taskController->addsubpid($process->getPid());

/**
* Create metadata files
*/
public function create(): void
{
// Check which of createrepo or createrepo_c is present on the system
if (!file_exists($this->createrepo)) {
throw new Exception('Could not find createrepo on the system');
}
// Retrieve output from process
$this->taskLogSubStepController->output($process->getOutput(), 'pre');

// Check if root path exists
if (!is_dir($this->root)) {
throw new Exception("Repository root directory '" . $this->root . "' does not exist");
if ($process->getExitCode() != 0) {
throw new Exception('Could not generate repository metadata');
}

// If a comps.xml file exists in the root directory, include it in the metadata
if (file_exists($this->root . '/comps.xml')) {
$this->createrepoArgs .= ' --groupfile=' . $this->root . '/comps.xml';
}
$process->close();

$this->taskLogSubStepController->new('create-metadata', 'GENERATING REPOSITORY METADATA');
$this->taskLogSubStepController->completed();

// Create repository metadata
$myprocess = new Process($this->createrepo . ' ' . $this->createrepoArgs . ' ' . $this->root . '/');
$myprocess->setBackground(true);
$myprocess->execute();
$this->taskLogSubStepController->new('add-updateinfo', 'ADDING UPDATEINFO TO METADATA');

/**
* Retrieve PID of the launched process
* Then write PID to main PID file
*/
$this->taskController->addsubpid($myprocess->getPid());
// Add additional metadata files to the repository metadata using modifyrepo_c
foreach ($this->additionalMetadataFiles as $file => $value) {
// If the temporary file does not exist, skip the addition of this metadata file
if (!file_exists($this->root . '/' . $value['temp'])) {
continue;
}

// Retrieve output from process
$output = $myprocess->getOutput();
// Rename the temporary file back to its original name
if (!rename($this->root . '/' . $value['temp'], $this->root . '/' . $file)) {
throw new Exception('Could not rename ' . $this->root . '/' . $value['temp'] . ' to ' . $this->root . '/' . $file);
}

$this->taskLogSubStepController->output($output, 'pre');
// Add the file to the repository metadata using modifyrepo_c
$process = new Process($this->modifyrepo . ' ' . $this->modifyrepoArgs . ' --mdtype=' . $value['mdtype'] . ' ' . $this->root . '/' . $file . ' ' . $this->root . '/repodata/');
$process->setBackground(true);
$process->execute();

if ($myprocess->getExitCode() != 0) {
throw new Exception('Could not generate repository metadata');
}
// Retrieve PID of the launched process then write PID to main PID file
$this->taskController->addsubpid($process->getPid());

$myprocess->close();
// Retrieve output from process
$this->taskLogSubStepController->output($process->getOutput(), 'pre');

// Add updateinfo to metadata if it exists
$this->taskLogSubStepController->new('add-updateinfo', 'ADDING UPDATEINFO TO METADATA');
$this->addMetadata('updateinfo.xml', 'updateinfo');
$this->taskLogSubStepController->completed();
if ($process->getExitCode() != 0) {
throw new Exception('Could not add ' . $value['mdtype'] . ' to repository metadata');
}

// Delete temporary metadata files as they are no longer needed
foreach (['comps.xml', 'modules.yaml'] as $file) {
if (file_exists($this->root . '/' . $file)) {
if (!unlink($this->root . '/' . $file)) {
throw new Exception('Could not delete ' . $this->root . '/' . $file);
}
$process->close();

// Delete the file as it's now part of the metadata
if (!unlink($this->root . '/' . $file)) {
throw new Exception('Could not delete ' . $this->root . '/' . $file);
}
}

Expand Down
13 changes: 4 additions & 9 deletions www/controllers/Repo/Mirror/Rpm.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ private function downloadModules(string $url) : void
}

/**
* Download the modules file with a temporary compressed name, then uncompress it to a plain
* modules.yaml file so it can be found and included automatically by createrepo_c.
* Download the modules file with a temporary compressed name, then uncompress it to a plain modules.yaml file
*/
$modulesFileTargetName = 'modules.yaml' . ($modulesFileExtension != 'yaml' ? '.' . $modulesFileExtension : '');

Expand All @@ -127,7 +126,7 @@ private function downloadModules(string $url) : void
throw new Exception('<code>' . $modulesFileTargetName . '</code> checksum does not match provided checksum');
}

// Uncompress the modules file to a plain modules.yaml based on its actual mime type (required for createrepo_c to include it)
// Uncompress the modules file to a plain modules.yaml based on its actual mime type
$mime = mime_content_type($this->workingDir . '/' . $modulesFileTargetName);

try {
Expand Down Expand Up @@ -806,10 +805,8 @@ public function mirror() : void
* Loop through all the archs selected by the user to build all the possible URLs to explore
*/
foreach ($this->arch as $arch) {
$url = $this->url;

// Replace $releasever variable in the URL if exists
$url = str_replace('$releasever', $this->releasever, $url);
$url = str_replace('$releasever', $this->releasever, $this->url);

// If there is a $basearch variable in the URL, replace it with the current arch
if (preg_match('/\$basearch/i', $url)) {
Expand All @@ -823,8 +820,6 @@ public function mirror() : void

// If 'src' exists in the arch array
if (in_array('src', $this->arch)) {
$url = $this->url;

// Replace $releasever variable in the URL if exists
$url = str_replace('$releasever', $this->releasever, $url);

Expand Down Expand Up @@ -855,7 +850,7 @@ public function mirror() : void
'sslCertificatePath' => $this->sslCustomCertificate,
'sslPrivateKeyPath' => $this->sslCustomPrivateKey,
'sslCaCertificatePath' => $this->sslCustomCaCertificate,
'proxy' => PROXY ?? null,
'proxy' => PROXY ?? null
]);
} catch (Exception $e) {
// If the URL is not reachable, add it to the errorUrls array
Expand Down
7 changes: 7 additions & 0 deletions www/controllers/Repo/Snapshot/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Controllers\User\Permission\Repo as RepoPermission;
use Controllers\Repo\Snapshot\Snapshot;
use Controllers\Exception\AppException;
use Controllers\Filesystem\Directory;
use Controllers\Filesystem\File;
use Controllers\Utils\Validate;
use Controllers\Utils\Convert;
Expand Down Expand Up @@ -312,6 +313,9 @@ public function upload(array $packages, bool $overwrite = false, bool $ignoreIfE
$return['Following packages already exist and have been ignored'] = $packageIgnored;
}

// Calculate and update snapshot size
$this->repoSnapshotController->updateSize($this->snapId, Directory::getSize($this->snapshotPath));

return $return;
}

Expand Down Expand Up @@ -366,6 +370,9 @@ public function delete(array $files): array
throw new Exception('Nothing to delete');
}

// Calculate and update snapshot size
$this->repoSnapshotController->updateSize($this->snapId, Directory::getSize($this->snapshotPath));

return $deleted;
}

Expand Down
Loading
Loading