diff --git a/solrorbit/builder/builder.py b/solrorbit/builder/builder.py index 160caa5f..2e9e111a 100644 --- a/solrorbit/builder/builder.py +++ b/solrorbit/builder/builder.py @@ -281,9 +281,17 @@ def cluster_distribution_version(cfg, client_factory=client.ClientFactory): hosts = cfg.opts("client", "hosts").default client_options = cfg.opts("client", "options").default client_instance = client_factory(hosts, client_options).create() - if isinstance(client_instance, client.SolrClient): - return "9.10.1" - return None + if not isinstance(client_instance, client.SolrClient): + return None + try: + return client_instance.get_version() + except Exception as e: + # This version selects the workload branch (WorkloadRepository.update -> versions.best_match), + # so falling back to a default would silently benchmark the cluster with another major's workloads. + raise exceptions.SystemSetupError( + f"Could not determine the distribution version of the cluster at {hosts}. Specify it with " + f"--distribution-version. Cause: {e}" + ) from e def to_ip_port(hosts): diff --git a/tests/builder/mechanic_test.py b/tests/builder/mechanic_test.py index 069122b3..33dfc6f8 100644 --- a/tests/builder/mechanic_test.py +++ b/tests/builder/mechanic_test.py @@ -28,8 +28,9 @@ import unittest.mock as mock from unittest import TestCase -from solrorbit import config, exceptions +from solrorbit import client, config, exceptions from solrorbit.builder import builder +from solrorbit.utils import opts, versions class HostHandlingTests(TestCase): @@ -140,3 +141,50 @@ def test_start_stop_nodes(self, cleanup): m.stop_engine() self.assertFalse(launcher.started) self.assertEqual(cleanup.call_count, 2) + + +class ClusterDistributionVersionTests(TestCase): + @staticmethod + def cfg_for(hosts="localhost:8983"): + cfg = config.Config() + cfg.add(config.Scope.application, "client", "hosts", opts.TargetHosts(hosts)) + cfg.add(config.Scope.application, "client", "options", opts.ClientOptions("timeout:60")) + return cfg + + @staticmethod + def factory_returning(client_instance): + return lambda hosts, client_options: mock.Mock(create=lambda: client_instance) + + def test_reads_the_version_from_the_cluster(self): + solr_client = mock.create_autospec(client.SolrClient, instance=True) + solr_client.get_version.return_value = "10.0.0" + + version = builder.cluster_distribution_version(self.cfg_for(), client_factory=self.factory_returning(solr_client)) + + self.assertEqual("10.0.0", version) + solr_client.get_version.assert_called_once_with() + + def test_selects_the_workload_branch_of_the_actual_major(self): + # The version is not informational: WorkloadRepository.update feeds it to versions.best_match, + # so a wrong value benchmarks the cluster with another major's workloads. + solr_client = mock.create_autospec(client.SolrClient, instance=True) + solr_client.get_version.return_value = "10.0.0" + + version = builder.cluster_distribution_version(self.cfg_for(), client_factory=self.factory_returning(solr_client)) + + self.assertEqual("10", versions.best_match(["main", "9", "10"], version)) + + def test_fails_instead_of_guessing_when_the_cluster_cannot_be_reached(self): + solr_client = mock.create_autospec(client.SolrClient, instance=True) + solr_client.get_version.side_effect = client.SolrClientError("connection refused") + + with self.assertRaises(exceptions.SystemSetupError) as ctx: + builder.cluster_distribution_version(self.cfg_for(), client_factory=self.factory_returning(solr_client)) + + self.assertIn("--distribution-version", str(ctx.exception)) + self.assertIn("connection refused", str(ctx.exception)) + + def test_returns_none_for_a_non_solr_client(self): + self.assertIsNone( + builder.cluster_distribution_version(self.cfg_for(), client_factory=self.factory_returning(mock.Mock())) + )