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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import unittest
from unittest.mock import MagicMock

BUILD = Path(__file__).resolve().parents[1] / 'build/v2'
# Archived with the historical V2 launcher it exercises.
BUILD = Path(__file__).resolve().parents[1] / 'v2'
spec = importlib.util.spec_from_file_location('native_config', BUILD / 'native_config.py')
native = importlib.util.module_from_spec(spec)
spec.loader.exec_module(native)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from urllib.request import Request, urlopen


BUILD = Path(__file__).resolve().parents[1] / 'build/v2'
# Archived with the historical V2 launcher it exercises.
BUILD = Path(__file__).resolve().parents[1] / 'v2'
spec = importlib.util.spec_from_file_location('web_paths', BUILD / 'web_paths.py')
paths = importlib.util.module_from_spec(spec)
spec.loader.exec_module(paths)
Expand Down Expand Up @@ -103,8 +104,8 @@ def test_invalid_origin_and_canonical_cannot_enter_nginx(self):
def test_fileserver_proxies_overwrite_client_real_ip(self):
# The Go fileserver records X-Real-IP from loopback as the Checkout IP,
# so every nginx location in front of it must replace the client value.
root = BUILD.parents[1]
for name in ('build/v2/nginx-dev.conf', 'services/seafile.nginx.conf',
root = Path(__file__).resolve().parents[4]
for name in ('docs/history/storage-build-20261020/v2/nginx-dev.conf', 'services/seafile.nginx.conf',
'templates/seafile.nginx.conf.template'):
for block in (root / name).read_text().split('location')[1:]:
if 'proxy_pass http://127.0.0.1:8082' in block:
Expand Down
11 changes: 9 additions & 2 deletions scripts/scripts_14.0/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,8 @@ def write_cloudfile_seafile_conf():
dir_acl_enabled -- so a new capability needs no change here.
"""
lines = _seafile_conf_cloudfile_lines()
from library_storage import auto_storage_enabled, configure_auto_storage, retained_legacy_storage
from library_storage import (auto_storage_enabled, configure_auto_storage,
retained_legacy_storage, retained_legacy_switch)
conf_path = join(topdir, 'conf', 'seafile.conf')
fresh = globals().get('_cloudfile_fresh_install', False)
if auto_storage_enabled(conf_path, os.environ, fresh=fresh):
Expand Down Expand Up @@ -921,7 +922,13 @@ def write_cloudfile_seafile_conf():

if (not cf_enabled('CF_ENABLE_S3_STORAGE') or
not get_conf('SEAF_SERVER_STORAGE_TYPE', '').strip()):
lines.append(retained_legacy_storage(conf_path))
retained = retained_legacy_storage(conf_path)
# Retained multi routing keeps its allocator switch; otherwise new
# libraries would silently move from automatic directories to default.
if retained and retained_legacy_switch(conf_path):
lines = ['s3_storage_enabled = true\n' if line == 's3_storage_enabled = false\n' else line
for line in lines]
lines.append(retained)
_replace_block(join(topdir, 'conf', 'seafile.conf'),
CF_BEGIN, CF_END, ''.join(lines))

Expand Down
15 changes: 15 additions & 0 deletions scripts/scripts_14.0/library_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ def retained_legacy_storage(path):
return output.getvalue()


def retained_legacy_switch(path):
"""Keep the legacy allocator switch together with retained storage classes.

The removed Compose overlay also supplied CF_ENABLE_S3_STORAGE=true. Its
persisted multi routing survives without that value, so new libraries must
keep the same automatic allocation rather than silently using the default.
"""
path = Path(path)
if not path.exists():
return False
current = _read_config(path)
return (current.getboolean('storage', 'enable_storage_classes', fallback=False) and
current.getboolean('cloudfile', 's3_storage_enabled', fallback=False))


def _require_root():
root = Path(LIBRARY_ROOT)
for path in (root, *root.parents):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_application_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def test_new_hub_preserves_native_bytes_admin_bundle_and_base(self):
self.assertTrue((self.package / 'seahub/media/assets/frontend/static/js/old.js').exists())

def test_changed_native_pin_refuses_reuse_without_creating_output(self):
self.manifest['sources']['seafile-server']['ref'] = 'a' * 40
self.manifest['sources']['seafile-server']['ref'] = 'c' * 40
self.manifest_path.write_text(json.dumps(self.manifest))
with self.assertRaisesRegex(ValueError, 'non-Hub'):
self.run_assembly()
Expand Down
27 changes: 25 additions & 2 deletions tests/test_library_storage_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def configure(self, cli=None):
storage.configure_auto_storage(self.conf, '[cloudfile]\nexample = true\n', self.installdir, self.env)
return output.getvalue()

def bootstrap_writer(self, environment):
def bootstrap_writer(self, environment, lines=('[cloudfile]\nexample = true\n',)):
source = SOURCE.with_name('bootstrap.py')
names = {'write_cloudfile_seafile_conf', '_replace_block'}
nodes = [node for node in ast.parse(source.read_text()).body
Expand All @@ -79,7 +79,7 @@ def bootstrap_writer(self, environment):
get_conf=lambda key, default='': environment.get(key, default),
cf_enabled=lambda key: environment.get(key, 'false') == 'true',
logwarning=lambda message: None,
_seafile_conf_cloudfile_lines=lambda: ['[cloudfile]\nexample = true\n'])
_seafile_conf_cloudfile_lines=lambda: list(lines))
exec(compile(ast.Module(body=nodes, type_ignores=[]), str(source), 'exec'), namespace)
return namespace['write_cloudfile_seafile_conf']

Expand Down Expand Up @@ -112,6 +112,29 @@ def test_bootstrap_legacy_multi_survives_environment_overlay_removal(self):
self.assertEqual('multiple', config['block_backend']['name'])
self.assertFalse(config.has_section('cloudfile_v2'))

def test_bootstrap_legacy_multi_keeps_automatic_allocator_switch(self):
self.conf.write_text(self.original + storage.BEGIN + '\n[cloudfile]\ns3_storage_enabled = true\n' +
'[storage]\nenable_storage_classes = true\nstorage_classes_file = existing.json\n' +
'[block_backend]\nname = multiple\n' + storage.END + '\n')
generated = ('[cloudfile]\n', 's3_storage_enabled = false\n')
with patch.dict(sys.modules, {'library_storage': storage}), patch.dict(os.environ, {}, clear=True):
self.bootstrap_writer({'CF_ENABLE_S3_STORAGE': 'false'}, generated)()
config = ConfigParser(interpolation=None)
config.read(self.conf)
self.assertEqual('true', config['cloudfile']['s3_storage_enabled'])
self.assertEqual('multiple', config['block_backend']['name'])

def test_bootstrap_without_retained_multi_keeps_switch_disabled(self):
self.conf.write_text(self.original + storage.BEGIN + '\n[cloudfile]\ns3_storage_enabled = true\n' +
storage.END + '\n')
generated = ('[cloudfile]\n', 's3_storage_enabled = false\n')
with patch.dict(sys.modules, {'library_storage': storage}), patch.dict(os.environ, {}, clear=True):
self.bootstrap_writer({'CF_ENABLE_S3_STORAGE': 'false'}, generated)()
config = ConfigParser(interpolation=None)
config.read(self.conf)
self.assertEqual('false', config['cloudfile']['s3_storage_enabled'])
self.assertFalse(config.has_section('storage'))

def test_bootstrap_s3_switch_without_removed_overlay_type_keeps_multi(self):
original = self.original + storage.BEGIN + '\n[cloudfile]\nexample = true\n' + (
'[storage]\nenable_storage_classes = true\nstorage_classes_file = existing.json\n' +
Expand Down
Loading