| |
@@ -21,7 +21,6 @@
|
| |
#
|
| |
# Written by Chenxiong Qi <cqi@redhat.com>
|
| |
|
| |
- import unittest
|
| |
import json
|
| |
|
| |
from mock import patch, PropertyMock, Mock, call
|
| |
@@ -326,7 +325,7 @@
|
| |
"published": False,
|
| |
})
|
| |
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.create_odcs_client')
|
| |
+ @patch('freshmaker.odcsclient.create_odcs_client')
|
| |
def test_batches_records(self, create_odcs_client):
|
| |
"""
|
| |
Tests that batches are properly recorded in DB.
|
| |
@@ -478,243 +477,6 @@
|
| |
self.assertEqual(build.state, ArtifactBuildState.FAILED.value)
|
| |
|
| |
|
| |
- class TestGetPackagesForCompose(helpers.FreshmakerTestCase):
|
| |
- """Test ErrataAdvisoryRPMsSignedHandler._get_packages_for_compose"""
|
| |
-
|
| |
- @helpers.mock_koji
|
| |
- def test_get_packages(self, mocked_koji):
|
| |
- build_nvr = 'chkconfig-1.7.2-1.el7_3.1'
|
| |
- mocked_koji.add_build(build_nvr)
|
| |
- mocked_koji.add_build_rpms(
|
| |
- build_nvr,
|
| |
- [build_nvr, "chkconfig-debuginfo-1.7.2-1.el7_3.1"])
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- packages = handler._get_packages_for_compose(build_nvr)
|
| |
-
|
| |
- self.assertEqual(set(['chkconfig', 'chkconfig-debuginfo']),
|
| |
- set(packages))
|
| |
-
|
| |
-
|
| |
- class TestGetComposeSource(helpers.FreshmakerTestCase):
|
| |
- """Test ErrataAdvisoryRPMsSignedHandler._get_compose_source"""
|
| |
-
|
| |
- @helpers.mock_koji
|
| |
- def test_get_tag(self, mocked_koji):
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-9.el6")
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- tag = handler._get_compose_source('rh-postgresql96-3.0-9.el6')
|
| |
- self.assertEqual('tag-candidate', tag)
|
| |
-
|
| |
- @helpers.mock_koji
|
| |
- def test_get_None_if_tag_has_new_build(self, mocked_koji):
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-9.el6")
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-10.el6")
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- tag = handler._get_compose_source('rh-postgresql96-3.0-9.el6')
|
| |
- self.assertEqual(None, tag)
|
| |
-
|
| |
- @helpers.mock_koji
|
| |
- def test_get_tag_prefer_final_over_candidate(self, mocked_koji):
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-9.el6",
|
| |
- ["tag-candidate", "tag"])
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- tag = handler._get_compose_source('rh-postgresql96-3.0-9.el6')
|
| |
- self.assertEqual('tag', tag)
|
| |
-
|
| |
- @helpers.mock_koji
|
| |
- def test_get_tag_fallback_to_second_tag(self, mocked_koji):
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-10.el6",
|
| |
- ["tag"])
|
| |
- mocked_koji.add_build("rh-postgresql96-3.0-9.el6",
|
| |
- ["tag", "tag-candidate"])
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- tag = handler._get_compose_source('rh-postgresql96-3.0-9.el6')
|
| |
- self.assertEqual('tag-candidate', tag)
|
| |
-
|
| |
-
|
| |
- class TestPrepareYumRepo(helpers.ModelsTestCase):
|
| |
- """Test ErrataAdvisoryRPMsSignedHandler._prepare_yum_repo"""
|
| |
-
|
| |
- def setUp(self):
|
| |
- super(TestPrepareYumRepo, self).setUp()
|
| |
-
|
| |
- self.ev = Event.create(db.session, 'msg-id', '123', 100)
|
| |
- ArtifactBuild.create(
|
| |
- db.session, self.ev, "parent", "image",
|
| |
- state=ArtifactBuildState.PLANNED)
|
| |
- db.session.commit()
|
| |
-
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'create_odcs_client')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_packages_for_compose')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_compose_source')
|
| |
- @patch('time.sleep')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.Errata')
|
| |
- def test_get_repo_url_when_succeed_to_generate_compose(
|
| |
- self, errata, sleep, _get_compose_source,
|
| |
- _get_packages_for_compose, create_odcs_client):
|
| |
- odcs = create_odcs_client.return_value
|
| |
- _get_packages_for_compose.return_value = ['httpd', 'httpd-debuginfo']
|
| |
- _get_compose_source.return_value = 'rhel-7.2-candidate'
|
| |
- odcs.new_compose.return_value = {
|
| |
- "id": 3,
|
| |
- "result_repo": "http://localhost/composes/latest-odcs-3-1/compose/Temporary",
|
| |
- "result_repofile": "http://localhost/composes/latest-odcs-3-1/compose/Temporary/odcs-3.repo",
|
| |
- "source": "f26",
|
| |
- "source_type": 1,
|
| |
- "state": 0,
|
| |
- "state_name": "wait",
|
| |
- }
|
| |
-
|
| |
- errata.return_value.get_builds.return_value = set(["httpd-2.4.15-1.f27"])
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- compose = handler._prepare_yum_repo(self.ev)
|
| |
-
|
| |
- db.session.refresh(self.ev)
|
| |
- self.assertEqual(3, compose['id'])
|
| |
-
|
| |
- _get_compose_source.assert_called_once_with("httpd-2.4.15-1.f27")
|
| |
- _get_packages_for_compose.assert_called_once_with("httpd-2.4.15-1.f27")
|
| |
-
|
| |
- # Ensure new_compose is called to request a new compose
|
| |
- odcs.new_compose.assert_called_once_with(
|
| |
- 'rhel-7.2-candidate', 'tag', packages=['httpd', 'httpd-debuginfo'],
|
| |
- sigkeys=[], flags=["no_deps"])
|
| |
-
|
| |
- # We should get the right repo URL eventually
|
| |
- self.assertEqual(
|
| |
- "http://localhost/composes/latest-odcs-3-1/compose/Temporary/odcs-3.repo",
|
| |
- compose['result_repofile'])
|
| |
-
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'create_odcs_client')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_packages_for_compose')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_compose_source')
|
| |
- @patch('time.sleep')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.Errata')
|
| |
- def test_get_repo_url_packages_in_multiple_tags(
|
| |
- self, errata, sleep, _get_compose_source,
|
| |
- _get_packages_for_compose, create_odcs_client):
|
| |
- _get_packages_for_compose.return_value = ['httpd', 'httpd-debuginfo']
|
| |
- _get_compose_source.side_effect = [
|
| |
- 'rhel-7.2-candidate', 'rhel-7.7-candidate']
|
| |
-
|
| |
- errata.return_value.get_builds.return_value = [
|
| |
- set(["httpd-2.4.15-1.f27"]), set(["foo-2.4.15-1.f27"])]
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- repo_url = handler._prepare_yum_repo(self.ev)
|
| |
-
|
| |
- create_odcs_client.return_value.new_compose.assert_not_called()
|
| |
- self.assertEqual(repo_url, None)
|
| |
-
|
| |
- db.session.refresh(self.ev)
|
| |
- for build in self.ev.builds:
|
| |
- self.assertEqual(build.state, ArtifactBuildState.FAILED.value)
|
| |
- self.assertEqual(build.state_reason, "Packages for errata "
|
| |
- "advisory 123 found in multiple different tags.")
|
| |
-
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'create_odcs_client')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_packages_for_compose')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler._get_compose_source')
|
| |
- @patch('time.sleep')
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.Errata')
|
| |
- def test_get_repo_url_packages_not_found_in_tag(
|
| |
- self, errata, sleep, _get_compose_source,
|
| |
- _get_packages_for_compose, create_odcs_client):
|
| |
- _get_packages_for_compose.return_value = ['httpd', 'httpd-debuginfo']
|
| |
- _get_compose_source.return_value = None
|
| |
-
|
| |
- errata.return_value.get_builds.return_value = [
|
| |
- set(["httpd-2.4.15-1.f27"]), set(["foo-2.4.15-1.f27"])]
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- repo_url = handler._prepare_yum_repo(self.ev)
|
| |
-
|
| |
- create_odcs_client.return_value.new_compose.assert_not_called()
|
| |
- self.assertEqual(repo_url, None)
|
| |
-
|
| |
- db.session.refresh(self.ev)
|
| |
- for build in self.ev.builds:
|
| |
- self.assertEqual(build.state, ArtifactBuildState.FAILED.value)
|
| |
- self.assertTrue(build.state_reason.endswith(
|
| |
- "of advisory 123 is the latest build in its candidate tag."))
|
| |
-
|
| |
- def _get_fake_container_image(self):
|
| |
- return {
|
| |
- u'rpm_manifest': [
|
| |
- {u'rpms': [{u'architecture': u'noarch',
|
| |
- u'gpg': u'199e2f91fd431d51',
|
| |
- u'name': u'apache-commons-lang',
|
| |
- u'nvra': u'apache-commons-lang-2.6-15.el7.noarch',
|
| |
- u'release': u'15.el7',
|
| |
- u'srpm_name': u'apache-commons-lang',
|
| |
- u'srpm_nevra': u'apache-commons-lang-0:2.6-15.el7.src',
|
| |
- u'summary': u'Provides a host of helper utilities for the java.lang API',
|
| |
- u'version': u'2.6'},
|
| |
- {u'architecture': u'noarch',
|
| |
- u'gpg': u'199e2f91fd431d51',
|
| |
- u'name': u'avalon-logkit',
|
| |
- u'nvra': u'avalon-logkit-2.1-14.el7.noarch',
|
| |
- u'release': u'14.el7',
|
| |
- u'srpm_name': u'avalon-logkit',
|
| |
- u'srpm_nevra': u'avalon-logkit-0:2.1-14.el7.src',
|
| |
- u'summary': u'Java logging toolkit',
|
| |
- u'version': u'2.1'}]}]}
|
| |
-
|
| |
- @patch('freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'create_odcs_client')
|
| |
- @patch('time.sleep')
|
| |
- def test_prepare_odcs_compose_with_image_rpms(
|
| |
- self, sleep, create_odcs_client):
|
| |
- odcs = create_odcs_client.return_value
|
| |
- odcs.new_compose.return_value = {
|
| |
- "id": 3,
|
| |
- "result_repo": "http://localhost/composes/latest-odcs-3-1/compose/Temporary",
|
| |
- "result_repofile": "http://localhost/composes/latest-odcs-3-1/compose/Temporary/odcs-3.repo",
|
| |
- "source": "f26",
|
| |
- "source_type": 1,
|
| |
- "state": 0,
|
| |
- "state_name": "wait",
|
| |
- }
|
| |
-
|
| |
- image = self._get_fake_container_image()
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- compose = handler._prepare_odcs_compose_with_image_rpms(image)
|
| |
-
|
| |
- db.session.refresh(self.ev)
|
| |
- self.assertEqual(3, compose['id'])
|
| |
-
|
| |
- # Ensure new_compose is called to request a new compose
|
| |
- odcs.new_compose.assert_called_once_with(
|
| |
- '', 'build', builds=set(['avalon-logkit-2.1-14.el7', 'apache-commons-lang-2.6-15.el7']),
|
| |
- flags=['no_deps'], packages=set([u'avalon-logkit', u'apache-commons-lang']), sigkeys=[])
|
| |
-
|
| |
- def test_prepare_odcs_compose_with_image_rpms_no_rpm_manifest(self):
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
-
|
| |
- compose = handler._prepare_odcs_compose_with_image_rpms({})
|
| |
- self.assertEqual(compose, None)
|
| |
-
|
| |
- compose = handler._prepare_odcs_compose_with_image_rpms(
|
| |
- {"rpm_manifest": []})
|
| |
- self.assertEqual(compose, None)
|
| |
-
|
| |
- compose = handler._prepare_odcs_compose_with_image_rpms(
|
| |
- {"rpm_manifest": [{"rpms": []}]})
|
| |
- self.assertEqual(compose, None)
|
| |
-
|
| |
-
|
| |
class TestErrataAdvisoryStateChangedHandler(helpers.ModelsTestCase):
|
| |
|
| |
@patch('freshmaker.errata.Errata.advisories_from_event')
|
| |
@@ -923,11 +685,8 @@
|
| |
'freshmaker.handlers.errata.ErrataAdvisoryRPMsSignedHandler.')
|
| |
|
| |
self.mock_prepare_pulp_repo = self.patcher.patch(
|
| |
- '_prepare_pulp_repo', side_effect=[{'id': 1}, {'id': 2}])
|
| |
-
|
| |
- self.mock_request_boot_iso_compose = self.patcher.patch(
|
| |
- '_request_boot_iso_compose',
|
| |
- side_effect=[{'id': 100}, {'id': 200}])
|
| |
+ 'freshmaker.odcsclient.FreshmakerODCSClient.prepare_pulp_repo',
|
| |
+ side_effect=[{'id': 1}, {'id': 2}])
|
| |
|
| |
self.patcher.patch_dict(
|
| |
'freshmaker.models.EVENT_TYPES', {self.mock_event.__class__: 0})
|
| |
@@ -1067,100 +826,6 @@
|
| |
self.assertEqual(ArtifactBuildState.PLANNED.value, parent_image.state)
|
| |
self.mock_prepare_pulp_repo.assert_not_called()
|
| |
|
| |
- @unittest.skip('Enable again when enable to request boot.iso compose')
|
| |
- def test_pulp_compose_is_stored_for_each_build(self):
|
| |
- batches = [
|
| |
- [ContainerImage({
|
| |
- "brew": {
|
| |
- "completion_date": "20170420T17:05:37.000-0400",
|
| |
- "build": "rhel-server-docker-7.3-82",
|
| |
- "package": "rhel-server-docker"
|
| |
- },
|
| |
- 'parsed_data': {
|
| |
- 'layers': [
|
| |
- 'sha512:12345678980',
|
| |
- 'sha512:10987654321'
|
| |
- ]
|
| |
- },
|
| |
- "parent": None,
|
| |
- "content_sets": ["content-set-1"],
|
| |
- "repository": "repo-1",
|
| |
- "commit": "123456789",
|
| |
- "target": "target-candidate",
|
| |
- "git_branch": "rhel-7",
|
| |
- "error": None,
|
| |
- "published": False,
|
| |
- })],
|
| |
- [ContainerImage({
|
| |
- "brew": {
|
| |
- "build": "rh-dotnetcore10-docker-1.0-16",
|
| |
- "package": "rh-dotnetcore10-docker",
|
| |
- "completion_date": "20170511T10:06:09.000-0400"
|
| |
- },
|
| |
- 'parsed_data': {
|
| |
- 'layers': [
|
| |
- 'sha512:2345af2e293',
|
| |
- 'sha512:12345678980',
|
| |
- 'sha512:10987654321'
|
| |
- ]
|
| |
- },
|
| |
- "parent": ContainerImage({
|
| |
- "brew": {
|
| |
- "completion_date": "20170420T17:05:37.000-0400",
|
| |
- "build": "rhel-server-docker-7.3-82",
|
| |
- "package": "rhel-server-docker"
|
| |
- },
|
| |
- 'parsed_data': {
|
| |
- 'layers': [
|
| |
- 'sha512:12345678980',
|
| |
- 'sha512:10987654321'
|
| |
- ]
|
| |
- },
|
| |
- "parent": None,
|
| |
- "content_sets": ["content-set-1"],
|
| |
- "repository": "repo-1",
|
| |
- "commit": "123456789",
|
| |
- "target": "target-candidate",
|
| |
- "git_branch": "rhel-7",
|
| |
- "error": None
|
| |
- }),
|
| |
- "content_sets": ["content-set-1"],
|
| |
- "repository": "repo-1",
|
| |
- "commit": "987654321",
|
| |
- "target": "target-candidate",
|
| |
- "git_branch": "rhel-7",
|
| |
- "error": None,
|
| |
- "published": False,
|
| |
- })]
|
| |
- ]
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- handler._record_batches(batches, self.mock_event)
|
| |
-
|
| |
- query = db.session.query(ArtifactBuild)
|
| |
- parent_build = query.filter(
|
| |
- ArtifactBuild.original_nvr == 'rhel-server-docker-7.3-82'
|
| |
- ).first()
|
| |
- self.assertEqual(2, len(parent_build.composes))
|
| |
- compose_ids = sorted([rel.compose.odcs_compose_id
|
| |
- for rel in parent_build.composes])
|
| |
- # Ensure both pulp compose id and boot.iso compose id are stored
|
| |
- self.assertEqual([1, 100], compose_ids)
|
| |
-
|
| |
- child_build = query.filter(
|
| |
- ArtifactBuild.original_nvr == 'rh-dotnetcore10-docker-1.0-16'
|
| |
- ).first()
|
| |
- self.assertEqual(1, len(child_build.composes))
|
| |
- self.assertEqual(2, child_build.composes[0].compose.odcs_compose_id)
|
| |
-
|
| |
- self.mock_prepare_pulp_repo.assert_has_calls([
|
| |
- call(child_build.event, ["content-set-1"]),
|
| |
- call(child_build.event, ["content-set-1"])
|
| |
- ])
|
| |
-
|
| |
- self.mock_request_boot_iso_compose.assert_called_once_with(
|
| |
- batches[0][0])
|
| |
-
|
| |
def test_pulp_compose_generated_just_once(self):
|
| |
batches = [
|
| |
[ContainerImage({
|
| |
@@ -1449,76 +1114,6 @@
|
| |
self.mock_prepare_pulp_repo.assert_not_called()
|
| |
|
| |
|
| |
- class TestPrepareYumReposForRebuilds(helpers.ModelsTestCase):
|
| |
- """Test ErrataAdvisoryRPMsSignedHandler._prepare_yum_repos_for_rebuilds"""
|
| |
-
|
| |
- def setUp(self):
|
| |
- super(TestPrepareYumReposForRebuilds, self).setUp()
|
| |
-
|
| |
- self.patcher = helpers.Patcher(
|
| |
- 'freshmaker.handlers.errata.errata_advisory_rpms_signed.'
|
| |
- 'ErrataAdvisoryRPMsSignedHandler.')
|
| |
-
|
| |
- self.mock_prepare_yum_repo = self.patcher.patch(
|
| |
- '_prepare_yum_repo',
|
| |
- side_effect=[
|
| |
- {'id': 1, 'result_repofile': 'http://localhost/repo/1'},
|
| |
- {'id': 2, 'result_repofile': 'http://localhost/repo/2'},
|
| |
- {'id': 3, 'result_repofile': 'http://localhost/repo/3'},
|
| |
- {'id': 4, 'result_repofile': 'http://localhost/repo/4'},
|
| |
- ])
|
| |
-
|
| |
- self.mock_find_dependent_event = self.patcher.patch(
|
| |
- 'freshmaker.models.Event.find_dependent_events')
|
| |
-
|
| |
- self.db_event = Event.create(
|
| |
- db.session, 'msg-1', 'search-key-1',
|
| |
- EVENT_TYPES[ErrataAdvisoryRPMsSignedEvent],
|
| |
- state=EventState.INITIALIZED,
|
| |
- released=False)
|
| |
- self.build_1 = ArtifactBuild.create(
|
| |
- db.session, self.db_event, 'build-1', ArtifactType.IMAGE)
|
| |
- self.build_2 = ArtifactBuild.create(
|
| |
- db.session, self.db_event, 'build-2', ArtifactType.IMAGE)
|
| |
-
|
| |
- db.session.commit()
|
| |
-
|
| |
- def tearDown(self):
|
| |
- super(TestPrepareYumReposForRebuilds, self).tearDown()
|
| |
- self.patcher.unpatch_all()
|
| |
-
|
| |
- def test_prepare_without_dependent_events(self):
|
| |
- self.mock_find_dependent_event.return_value = []
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- urls = handler._prepare_yum_repos_for_rebuilds(self.db_event)
|
| |
-
|
| |
- self.assertEqual(1, self.build_1.composes[0].compose.id)
|
| |
- self.assertEqual(1, self.build_2.composes[0].compose.id)
|
| |
- self.assertEqual(['http://localhost/repo/1'], urls)
|
| |
-
|
| |
- def test_prepare_with_dependent_events(self):
|
| |
- self.mock_find_dependent_event.return_value = [
|
| |
- Mock(), Mock(), Mock()
|
| |
- ]
|
| |
-
|
| |
- handler = ErrataAdvisoryRPMsSignedHandler()
|
| |
- urls = handler._prepare_yum_repos_for_rebuilds(self.db_event)
|
| |
-
|
| |
- odcs_compose_ids = [rel.compose.id for rel in self.build_1.composes]
|
| |
- self.assertEqual([1, 2, 3, 4], sorted(odcs_compose_ids))
|
| |
-
|
| |
- odcs_compose_ids = [rel.compose.id for rel in self.build_2.composes]
|
| |
- self.assertEqual([1, 2, 3, 4], sorted(odcs_compose_ids))
|
| |
-
|
| |
- self.assertEqual([
|
| |
- 'http://localhost/repo/1',
|
| |
- 'http://localhost/repo/2',
|
| |
- 'http://localhost/repo/3',
|
| |
- 'http://localhost/repo/4',
|
| |
- ], sorted(urls))
|
| |
-
|
| |
-
|
| |
class TestSkipNonRPMAdvisory(helpers.FreshmakerTestCase):
|
| |
|
| |
def test_ensure_to_handle_rpm_adivsory(self):
|
| |
Currently, half of the ErrataAdvisoryRPMsSignedHandler is code to ask for various
ODCS composes. This is wrong from following reasons:
used in the future by other handlers.
harder to read/understand.
to understand.
This commit also removes the "boot.iso" compose type from Freshmaker, because we
have workarounded the boot.iso generation and we have no plans to support boot.iso
generation in ODCS ever because of its complexity.
There should be follow-up of this commit to further change the new FreshmakerODCSClient
API to name the methods better and make them even more general, but for simplicity,
it is not done in this commit - this commit only moves the code around without
changing it.