Skip to content

Commit 635a6ac

Browse files
authored
Merge pull request #56095 from nextcloud/feat/add-serverid
2 parents d92b02a + 1f74ed9 commit 635a6ac

File tree

7 files changed

+99
-3
lines changed

7 files changed

+99
-3
lines changed

apps/settings/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => $baseDir . '/../lib/SetupChecks/ReadOnlyConfig.php',
133133
'OCA\\Settings\\SetupChecks\\SchedulingTableSize' => $baseDir . '/../lib/SetupChecks/SchedulingTableSize.php',
134134
'OCA\\Settings\\SetupChecks\\SecurityHeaders' => $baseDir . '/../lib/SetupChecks/SecurityHeaders.php',
135+
'OCA\\Settings\\SetupChecks\\ServerIdConfig' => $baseDir . '/../lib/SetupChecks/ServerIdConfig.php',
135136
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => $baseDir . '/../lib/SetupChecks/SupportedDatabase.php',
136137
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => $baseDir . '/../lib/SetupChecks/SystemIs64bit.php',
137138
'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => $baseDir . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php',

apps/settings/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class ComposerStaticInitSettings
147147
'OCA\\Settings\\SetupChecks\\ReadOnlyConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ReadOnlyConfig.php',
148148
'OCA\\Settings\\SetupChecks\\SchedulingTableSize' => __DIR__ . '/..' . '/../lib/SetupChecks/SchedulingTableSize.php',
149149
'OCA\\Settings\\SetupChecks\\SecurityHeaders' => __DIR__ . '/..' . '/../lib/SetupChecks/SecurityHeaders.php',
150+
'OCA\\Settings\\SetupChecks\\ServerIdConfig' => __DIR__ . '/..' . '/../lib/SetupChecks/ServerIdConfig.php',
150151
'OCA\\Settings\\SetupChecks\\SupportedDatabase' => __DIR__ . '/..' . '/../lib/SetupChecks/SupportedDatabase.php',
151152
'OCA\\Settings\\SetupChecks\\SystemIs64bit' => __DIR__ . '/..' . '/../lib/SetupChecks/SystemIs64bit.php',
152153
'OCA\\Settings\\SetupChecks\\TaskProcessingPickupSpeed' => __DIR__ . '/..' . '/../lib/SetupChecks/TaskProcessingPickupSpeed.php',

apps/settings/lib/AppInfo/Application.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
use OCA\Settings\SetupChecks\ReadOnlyConfig;
7070
use OCA\Settings\SetupChecks\SchedulingTableSize;
7171
use OCA\Settings\SetupChecks\SecurityHeaders;
72+
use OCA\Settings\SetupChecks\ServerIdConfig;
7273
use OCA\Settings\SetupChecks\SupportedDatabase;
7374
use OCA\Settings\SetupChecks\SystemIs64bit;
7475
use OCA\Settings\SetupChecks\TaskProcessingPickupSpeed;
@@ -207,6 +208,7 @@ public function register(IRegistrationContext $context): void {
207208
$context->registerSetupCheck(RandomnessSecure::class);
208209
$context->registerSetupCheck(ReadOnlyConfig::class);
209210
$context->registerSetupCheck(SecurityHeaders::class);
211+
$context->registerSetupCheck(ServerIdConfig::class);
210212
$context->registerSetupCheck(SchedulingTableSize::class);
211213
$context->registerSetupCheck(SupportedDatabase::class);
212214
$context->registerSetupCheck(SystemIs64bit::class);
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OCA\Settings\SetupChecks;
10+
11+
use OCP\IConfig;
12+
use OCP\IL10N;
13+
use OCP\IURLGenerator;
14+
use OCP\SetupCheck\ISetupCheck;
15+
use OCP\SetupCheck\SetupResult;
16+
use Override;
17+
18+
final class ServerIdConfig implements ISetupCheck {
19+
public function __construct(
20+
private readonly IL10N $l10n,
21+
private readonly IConfig $config,
22+
private readonly IURLGenerator $urlGenerator,
23+
) {
24+
}
25+
26+
#[Override]
27+
public function getName(): string {
28+
return $this->l10n->t('Configuration server ID');
29+
}
30+
31+
#[Override]
32+
public function getCategory(): string {
33+
return 'config';
34+
}
35+
36+
#[Override]
37+
public function run(): SetupResult {
38+
$serverid = $this->config->getSystemValueInt('serverid', PHP_INT_MIN);
39+
$linkToDoc = $this->urlGenerator->linkToDocs('admin-update');
40+
41+
if ($serverid === PHP_INT_MIN) {
42+
return SetupResult::info(
43+
$this->l10n->t('server identifier isn’t configured. It is recommended if your Nextcloud instance is running on several PHP servers. Add a serverid in your configuration.'),
44+
$linkToDoc,
45+
);
46+
}
47+
48+
if ($serverid < 0 || $serverid > 1023) {
49+
return SetupResult::error(
50+
$this->l10n->t('"%d" is not a valid server identifier. It must be between 0 and 1023.', [$serverid]),
51+
$linkToDoc,
52+
);
53+
}
54+
55+
return SetupResult::success($this->l10n->t('server identifier is configured and valid.'));
56+
}
57+
}

config/config.sample.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@
4545
*/
4646
'instanceid' => '',
4747

48+
/**
49+
* This is a unique identifier for your server.
50+
* It is useful when your Nextcloud instance is using different PHP servers.
51+
* Once it's set it shouldn't be changed.
52+
*
53+
* Value must be an integer, comprised between 0 and 1023.
54+
*
55+
* When config.php is shared between different servers, this value should be overriden with "NC_serverid=<int>" on each server.
56+
* Note that it must be overriden for CLI and for your webserver.
57+
*
58+
* Example for CLI: NC_serverid=42 occ config:list system
59+
*/
60+
'serverid' => -1,
61+
4862
/**
4963
* The salt used to hash all passwords, auto-generated by the Nextcloud
5064
* installer. (There are also per-user salts.) If you lose this salt, you lose

lib/private/Snowflake/Generator.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace OC\Snowflake;
1111

1212
use OCP\AppFramework\Utility\ITimeFactory;
13+
use OCP\IConfig;
1314
use OCP\Snowflake\IGenerator;
1415
use Override;
1516

@@ -23,6 +24,7 @@
2324
final class Generator implements IGenerator {
2425
public function __construct(
2526
private readonly ITimeFactory $timeFactory,
27+
private readonly IConfig $config,
2628
private readonly ISequence $sequenceGenerator,
2729
) {
2830
}
@@ -100,8 +102,14 @@ private function getCurrentTime(): array {
100102
];
101103
}
102104

105+
/**
106+
* Return configured serverid or generate one if not set
107+
*/
103108
private function getServerId(): int {
104-
return crc32(gethostname() ?: random_bytes(8));
109+
$serverid = $this->config->getSystemValueInt('serverid', -1);
110+
return $serverid > 0
111+
? $serverid
112+
: crc32(gethostname() ?: random_bytes(8));
105113
}
106114

107115
private function isCli(): bool {

tests/lib/Snowflake/GeneratorTest.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use OC\Snowflake\Generator;
1515
use OC\Snowflake\ISequence;
1616
use OCP\AppFramework\Utility\ITimeFactory;
17+
use OCP\IConfig;
1718
use OCP\Snowflake\IGenerator;
1819
use PHPUnit\Framework\Attributes\DataProvider;
1920
use PHPUnit\Framework\MockObject\MockObject;
@@ -24,17 +25,25 @@
2425
*/
2526
class GeneratorTest extends TestCase {
2627
private Decoder $decoder;
28+
private IConfig&MockObject $config;
2729
private ISequence&MockObject $sequence;
2830

2931
public function setUp():void {
3032
$this->decoder = new Decoder();
33+
34+
$this->config = $this->createMock(IConfig::class);
35+
$this->config->method('getSystemValueInt')
36+
->with('serverid')
37+
->willReturn(42);
38+
3139
$this->sequence = $this->createMock(ISequence::class);
3240
$this->sequence->method('isAvailable')->willReturn(true);
3341
$this->sequence->method('nextId')->willReturn(421);
42+
3443
}
3544

3645
public function testGenerator(): void {
37-
$generator = new Generator(new TimeFactory(), $this->sequence);
46+
$generator = new Generator(new TimeFactory(), $this->config, $this->sequence);
3847
$snowflakeId = $generator->nextId();
3948
$data = $this->decoder->decode($generator->nextId());
4049

@@ -52,6 +61,9 @@ public function testGenerator(): void {
5261

5362
// Check CLI
5463
$this->assertTrue($data['isCli']);
64+
65+
// Check serverId
66+
$this->assertEquals(42, $data['serverId']);
5567
}
5668

5769
#[DataProvider('provideSnowflakeData')]
@@ -60,11 +72,12 @@ public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, i
6072
$timeFactory = $this->createMock(ITimeFactory::class);
6173
$timeFactory->method('now')->willReturn($dt);
6274

63-
$generator = new Generator($timeFactory, $this->sequence);
75+
$generator = new Generator($timeFactory, $this->config, $this->sequence);
6476
$data = $this->decoder->decode($generator->nextId());
6577

6678
$this->assertEquals($expectedSeconds, ($data['createdAt']->format('U') - IGenerator::TS_OFFSET));
6779
$this->assertEquals($expectedMilliseconds, (int)$data['createdAt']->format('v'));
80+
$this->assertEquals(42, $data['serverId']);
6881
}
6982

7083
public static function provideSnowflakeData(): array {

0 commit comments

Comments
 (0)