Skip to content
Merged
2 changes: 1 addition & 1 deletion scripts/scripts_cf14.0/cloudfile_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ def write_cloudfile_seafile_conf():
conf_path = join(topdir, 'conf', 'seafile.conf')
fresh = fresh_install()
if auto_storage_enabled(conf_path, os.environ, fresh=fresh):
configure_auto_storage(conf_path, ''.join(lines), installdir)
configure_auto_storage(conf_path, ''.join(lines), installdir, fresh=fresh)
return

# Keep the S3 configuration in the same restart-safe generated block as
Expand Down
16 changes: 15 additions & 1 deletion scripts/scripts_cf14.0/library_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def _render_auto(original, generated_body):
return ''.join(out).rstrip() + '\n\n' + BEGIN + '\n' + body + END + '\n'


def configure_auto_storage(conf, generated_body, installdir, environment=None):
def configure_auto_storage(conf, generated_body, installdir, environment=None, *, fresh=False):
"""Preflight old routing, register supported libraries, then replace config.

Neither a failed preflight nor a failed apply changes seafile.conf. Apply
Expand All @@ -129,6 +129,20 @@ def configure_auto_storage(conf, generated_body, installdir, environment=None):
final = _render_auto(original, generated_body)
check = ConfigParser(interpolation=None)
check.read_string(final)

# A fresh installation has no old repositories to register. Existing V2
# already has persistent native RepoStorageId routes; do not rescan and
# rewrite every library on each restart. Preflight is only for an explicit
# transition from an existing non-V2 Seafile installation.
if fresh or current_root == LIBRARY_ROOT:
if current_root == LIBRARY_ROOT and (
current.getboolean('storage', 'enable_storage_classes', fallback=False) or
any(current.get(section, 'name', fallback='filesystem') != 'filesystem'
for section in ('commit_object_backend', 'fs_object_backend', 'block_backend'))):
raise ValueError('Existing V2 storage configuration is incompatible')
_atomic_write(conf, final)
return

if not current.has_section('cloudfile_v2'):
current.add_section('cloudfile_v2')
current.set('cloudfile_v2', 'auto_local_root', LIBRARY_ROOT)
Expand Down
22 changes: 22 additions & 0 deletions tests/test_library_storage_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,25 @@ def test_new_auto_uses_filesystem_and_reconciles_before_write(self):
self.assertTrue(storage.auto_storage_enabled(self.conf, {}))
self.assertEqual([], list(self.conf.parent.glob('.storage-*')))

def test_fresh_uuid_v2_install_skips_native_inventory(self):
with patch.object(storage.subprocess, 'run') as run:
storage.configure_auto_storage(self.conf, '[cloudfile]\nexample = true\n',
self.installdir, self.env, fresh=True)
run.assert_not_called()
config = ConfigParser(interpolation=None)
config.read(self.conf)
self.assertEqual(str(self.root), config['cloudfile_v2']['auto_local_root'])
self.assertEqual('false', config['storage']['enable_storage_classes'])

def test_existing_v2_restart_never_rewrites_route_registry(self):
self.configure() # explicit first transition from an existing CE config
calls_before = len(self.calls)
with patch.object(storage.subprocess, 'run') as run:
storage.configure_auto_storage(self.conf, '[cloudfile]\nexample = true\n',
self.installdir, self.env)
run.assert_not_called()
self.assertEqual(calls_before, len(self.calls))

def test_native_layout_symlinks_are_resolved_but_managed_root_is_not(self):
shared = self.top / 'shared-seafile'
shared.mkdir()
Expand All @@ -214,6 +233,9 @@ def test_explicit_transition_inventories_original_classes(self):
def test_schema_missing_and_state_drift_allow_explicit_apply(self):
for status in ('SCHEMA_MISSING', 'STATE_DRIFT'):
with self.subTest(status=status):
# Every case models a distinct explicit CE transition.
# A completed V2 restart intentionally skips reconcile.
self.conf.write_text(self.original)
modes = []
def cli(command, **kwargs):
modes.append(command[-1])
Expand Down
6 changes: 5 additions & 1 deletion tests/test_v06_default_library_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ def test_merged_new_install_compose_contract(self):
mounts = [v for v in process["volumes"] if v["target"] == parent]
self.assertEqual(1, len(mounts), "one parent mount per process")
self.assertEqual("rw", mounts[0].get("mode", "rw"))
self.assertFalse(mounts[0]["bind"]["create_host_path"])
# Compose JSON may omit explicit false from the normalized form.
# The deployment overlay must still declare it for safe mounts.
self.assertFalse(mounts[0].get("bind", {}).get("create_host_path", False))
self.assertEqual(2, (ROOT / "docker-compose.auto-library-storage.yml")
.read_text().count("create_host_path: false"))
self.assertEqual(False, str(server["environment"]["CF_ENABLE_CHECKOUT"]).lower() == "true")


Expand Down
Loading