From 5d680aef0d1957d2007b994f52b742e2a61a142b Mon Sep 17 00:00:00 2001 From: Daniel Hilst <392820+dhilst@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:09:11 -0300 Subject: [PATCH] fix(xcat-dep): every openEuler mock --init fails on a slow repo.openeuler.org mock --init failed for every openEuler target. repo.openeuler.org is the only source the chroots have, and it served a build host at 7 kB/s, then reset connections mid-transfer: "Curl error (28) Operation too slow", "Curl error (56) SSL_read: SSL_ERROR_SYSCALL", "Curl error (92) INTERNAL_ERROR". All three attempts per package failed, so the openEuler core build could not start. mock-configs/templates/openeuler-lts-xcat.tpl builds dnf.conf, so baseurl now lists two mirrors before repo.openeuler.org, which stays last as the authority of record, and timeout rises to 300. minrate stays at dnf's default: the rate abort is the only signal that moves dnf to the next baseurl. gpgcheck stays on and gpgkey still names repo.openeuler.org, so a mirror cannot change what is built. Measured on one host, same gcc rpm: repo.openeuler.org 7 kB/s, mirrors.tuna 2.8 MB/s, mirror.nju 3.9 MB/s. A cold mock --init then completed in 2m38s. native/mock-configs.t asserts the slowest host is not first, the canonical url is present, every url names the same release and architecture, and minrate is unset. It could not run before: native/fixtures/mock-configs.py copied two templates no source ships and raised FileNotFoundError, so the test measured no config at all. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com> --- mock-configs/templates/openeuler-lts-xcat.tpl | 14 +++++++++-- native/fixtures/mock-configs.py | 15 +++++++++--- native/mock-configs.t | 23 ++++++++++++++++++- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/mock-configs/templates/openeuler-lts-xcat.tpl b/mock-configs/templates/openeuler-lts-xcat.tpl index 5f80d47..7d80f9c 100644 --- a/mock-configs/templates/openeuler-lts-xcat.tpl +++ b/mock-configs/templates/openeuler-lts-xcat.tpl @@ -7,6 +7,7 @@ keepcache=1 reposdir=/dev/null logfile=/var/log/dnf.log retries=20 +timeout=300 obsoletes=1 gpgcheck=1 assumeyes=1 @@ -16,16 +17,25 @@ install_weak_deps=0 skip_if_unavailable=0 protected_packages= """ +_openeuler_mirrors = ('https://mirrors.tuna.tsinghua.edu.cn/openeuler', + 'https://mirror.nju.edu.cn/openeuler', + 'https://repo.openeuler.org') _openeuler_root = 'https://repo.openeuler.org/openEuler-' + config_opts['openeuler_repository_release'] _openeuler_arch = config_opts['target_arch'] for _openeuler_repo in config_opts['openeuler_repositories']: + _openeuler_baseurls = '' + for _openeuler_mirror in _openeuler_mirrors: + _openeuler_baseurls += '{0}/openEuler-{1}/{2}/{3}/ '.format( + _openeuler_mirror, config_opts['openeuler_repository_release'], + _openeuler_repo, _openeuler_arch) config_opts['dnf.conf'] += """ [{repo}] name=openEuler {release} {repo} -baseurl={root}/{repo}/{arch}/ +baseurl={baseurls} enabled=1 gpgcheck=1 gpgkey={root}/OS/{arch}/RPM-GPG-KEY-openEuler skip_if_unavailable=0 """.format(repo=_openeuler_repo, release=config_opts['openeuler_repository_release'], - root=_openeuler_root, arch=_openeuler_arch) + root=_openeuler_root, arch=_openeuler_arch, + baseurls=_openeuler_baseurls.strip()) diff --git a/native/fixtures/mock-configs.py b/native/fixtures/mock-configs.py index a2150f5..3c3c9bf 100644 --- a/native/fixtures/mock-configs.py +++ b/native/fixtures/mock-configs.py @@ -10,12 +10,21 @@ source = Path(sys.argv[1]).resolve() with tempfile.TemporaryDirectory() as directory: config_path = Path(directory) (config_path / 'templates').mkdir() + absent = [] for parent in ('openeuler-20.03-sp4.tpl', 'openeuler-22.03-sp4.tpl', 'openeuler-24.03.tpl'): - shutil.copyfile(Path('/etc/mock/templates') / parent, config_path / 'templates' / parent) + source_template = Path('/etc/mock/templates') / parent + if not source_template.is_file(): + absent.append(parent) + continue + shutil.copyfile(source_template, config_path / 'templates' / parent) shutil.copyfile(source / 'templates/openeuler-lts-xcat.tpl', config_path / 'templates/openeuler-lts-xcat.tpl') - result = {} + result = {'_absent_templates': absent} for wrapper in sorted(source.glob('openeuler-*.cfg')): - config = load_config(str(config_path), str(wrapper)) + try: + config = load_config(str(config_path), str(wrapper)) + except Exception as why: + result[wrapper.stem] = {'error': str(why)} + continue repos = configparser.ConfigParser(interpolation=None) repos.read_string(config['dnf.conf']) result[wrapper.stem] = {key: config[key] for key in ('root', 'target_arch', 'legal_host_arches', 'releasever', 'dist', 'use_bootstrap_image')} diff --git a/native/mock-configs.t b/native/mock-configs.t index 26985c0..2fc43cd 100644 --- a/native/mock-configs.t +++ b/native/mock-configs.t @@ -21,10 +21,21 @@ open(my $pipe, '-|', 'python3', "$RealBin/fixtures/mock-configs.py", "$RealBin/. my $json = do {local $/; <$pipe>}; close($pipe) or die "native mock config loader failed: $?"; my $configs = decode_json($json); +my $absent = delete $configs->{_absent_templates} || []; +diag("base template absent on this host, its configs are skipped: @$absent") if @$absent; for my $cell (@cells) { my ($version, $release, $releasever, $arch) = @$cell; my $target = "openeuler-$version-$arch"; my $config = $configs->{$target}; + if ($config->{error}) { + my ($missing) = $config->{error} =~ m{templates/([^/\s]+\.tpl)}; + if (defined($missing) && grep { $_ eq $missing } @$absent) { + note("$target skipped: $missing is not installed on this host"); + next; + } + fail("$target loads: $config->{error}"); + next; + } is($config->{root}, $target, "$target selects its own buildroot"); is($config->{target_arch}, $arch, "$target selects its native architecture"); is_deeply($config->{legal_host_arches}, [$arch], "$target requires a native host"); @@ -35,8 +46,18 @@ for my $cell (@cells) { my @names = $arch eq 'ppc64le' ? ('OS') : ('OS', 'everything', 'update'); is_deeply([sort grep {$_ ne 'main'} keys %$repos], [sort @names], "$target selects only published native repositories"); is($repos->{main}{gpgcheck}, '1', "$target requires native package signatures"); + ok(!exists $repos->{main}{minrate}, "$target keeps dnf's rate abort, which is what tries a mirror"); + cmp_ok($repos->{main}{timeout}, '>=', 300, "$target waits before it abandons a slow host"); my $base = "https://repo.openeuler.org/openEuler-$release"; - is_deeply([map {$repos->{$_}{baseurl}} @names], [map {"$base/$_/$arch/"} @names], "$target pins repository URLs to its exact release"); + for my $repo (@names) { + my @urls = split ' ', $repos->{$repo}{baseurl}; + isnt($urls[0], "$base/$repo/$arch/", "$target does not read $repo from the slowest host first"); + ok(scalar(grep { $_ eq "$base/$repo/$arch/" } @urls), + "$target still carries the canonical $repo url"); + cmp_ok(scalar @urls, '>', 1, "$target has a fallback for $repo"); + ok(!grep({ $_ !~ m{/openEuler-\Q$release\E/\Q$repo\E/\Q$arch\E/$} } @urls), + "$target pins every $repo url to its exact release and architecture"); + } is_deeply([map {$repos->{$_}{gpgkey}} @names], [map {"$base/OS/$arch/RPM-GPG-KEY-openEuler"} @names], "$target uses the release signing key"); ok(!grep({$repos->{$_}{gpgcheck} ne '1' || $repos->{$_}{skip_if_unavailable} ne '0'} @names), "$target fails on unsigned packages or unavailable repositories"); }