def nimbus_check_kinto_push_queue(): """ Because kinto has a restriction that it can only have a single pending review, this task brokers the queue of all experiments ready to be pushed to kinto and ensures that only a single experiment is ever in review. A scheduled task that - Checks the kinto collection for a single rejected experiment from a previous push - If one exists, pull it out of the collection and mark it as rejected - Checks if there is still a pending review and if so, aborts - Gets the list of all experiments ready to be pushed to kinto and pushes the first one """ metrics.incr("check_kinto_push_queue.started") for application, collection in NimbusExperiment.KINTO_APPLICATION_COLLECTION.items( ): kinto_client = KintoClient(collection) rejected_collection_data = kinto_client.get_rejected_collection_data() if rejected_collection_data: rejected_slug = kinto_client.get_rejected_record() experiment = NimbusExperiment.objects.get(slug=rejected_slug) experiment.status = NimbusExperiment.Status.DRAFT experiment.save() generate_nimbus_changelog( experiment, get_kinto_user(), message= f'Rejected: {rejected_collection_data["last_reviewer_comment"]}', ) kinto_client.rollback_changes() if kinto_client.has_pending_review(): metrics.incr(f"check_kinto_push_queue.{collection}_pending_review") return queued_experiments = NimbusExperiment.objects.filter( status=NimbusExperiment.Status.REVIEW, application=application) if queued_experiments.exists(): nimbus_push_experiment_to_kinto.delay( queued_experiments.first().id) metrics.incr( f"check_kinto_push_queue.{collection}_queued_experiment_selected" ) else: metrics.incr( f"check_kinto_push_queue.{collection}_no_experiments_queued") metrics.incr("check_kinto_push_queue.completed")
class TestKintoClient(MockKintoClientMixin, TestCase): def setUp(self): super().setUp() self.collection = "test-collection" self.client = KintoClient(self.collection) def test_push_to_kinto_sends_data_updates_collection(self): self.client.push_to_kinto({"test": "data"}) self.mock_kinto_client_creator.assert_called_with( server_url=settings.KINTO_HOST, auth=(settings.KINTO_USER, settings.KINTO_PASS), ) self.mock_kinto_client.create_record.assert_called_with( data={"test": "data"}, collection=self.collection, bucket=settings.KINTO_BUCKET, if_not_exists=True, ) self.mock_kinto_client.patch_collection.assert_called_with( id=self.collection, data={"status": KINTO_REVIEW_STATUS}, bucket=settings.KINTO_BUCKET, ) def test_rollback_changes_patches_collection(self): self.client.rollback_changes() self.mock_kinto_client_creator.assert_called_with( server_url=settings.KINTO_HOST, auth=(settings.KINTO_USER, settings.KINTO_PASS), ) self.mock_kinto_client.patch_collection.assert_called_with( id=self.collection, data={"status": KINTO_ROLLBACK_STATUS}, bucket=settings.KINTO_BUCKET, ) def test_returns_true_for_pending_review(self): self.setup_kinto_pending_review() self.assertTrue(self.client.has_pending_review()) def test_returns_false_for_no_pending_review(self): self.setup_kinto_no_pending_review() self.assertFalse(self.client.has_pending_review()) def test_returns_records(self): slug = "test-slug" self.setup_kinto_get_main_records([slug]) self.assertEqual(self.client.get_main_records(), [{"id": slug}]) def test_returns_no_records(self): self.setup_kinto_get_main_records([]) self.assertEqual(self.client.get_main_records(), []) def test_returns_nothing_when_not_rejects(self): self.setup_kinto_no_pending_review() self.assertIsNone(self.client.get_rejected_collection_data()) def test_returns_rejected_data(self): self.setup_kinto_rejected_review() self.assertTrue(self.client.get_rejected_collection_data()) def test_returns_rejected_record(self): self.mock_kinto_client.get_records.side_effect = [ [{ "id": "bug-12345-rapid-test-release-55" }], [ { "id": "bug-12345-rapid-test-release-55" }, { "id": "bug-9999-rapid-test-release-55" }, ], ] self.assertEqual(self.client.get_rejected_record(), "bug-9999-rapid-test-release-55")