Exemple #1
0
    def test_success_operational_exception(self, espy_mock):
        """Test an index attempt that experiences an operational exception that
        it managed to recover from.
        """

        # It's mocks all the way down.
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # The transaction executor will try three times, so we will fail
        # twice for the purposes of this test.
        bad_results = [
            elasticsearch.exceptions.ConnectionError(500, ''),
            elasticsearch.exceptions.ConnectionError(500, '')
        ]

        # Replace the underlying index method with this function.
        def esindex_fn(*args, **kwargs):
            try:
                result = bad_results.pop(0)
                raise result
            except IndexError:
                return sub_mock.index

        sub_mock.index.side_effect = esindex_fn

        # Submit a crash like normal, except that the index method will
        # raise twice, then pass as normal.
        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=crash_id,
        )

        # The actual call to index the document (crash).
        document = {
            'crash_id': crash_id,
            'processed_crash': a_processed_crash,
            'raw_crash': a_raw_crash
        }

        additional = {
            'doc_type': 'crash_reports',
            'id': crash_id,
            'index': 'socorro_integration_test_reports'
        }

        sub_mock.index.assert_called_with(body=document, **additional)
    def test_index_crash_with_bad_keys(self):
        a_raw_crash_with_bad_keys = {
            "foo": "alpha",
            "": "bad key 1",
            ".": "bad key 2",
            "na\xefve": "bad key 3",
        }

        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash_with_bad_keys),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash["uuid"],
        )

        # Ensure that the document was indexed by attempting to retreive it.
        doc = self.es_client.get(
            index=self.config.elasticsearch.elasticsearch_index,
            id=a_processed_crash["uuid"],
        )
        # Make sure the invalid keys aren't in the crash.
        raw_crash = doc["_source"]["raw_crash"]
        assert raw_crash == {"foo": "alpha"}
        es_storage.close()
Exemple #3
0
    def test_fatal_operational_exception(self, espy_mock):
        """Test an index attempt that experiences an operational exception that
        it can't recover from.
        """

        # It's mocks all the way down.
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # It's bad but at least we expected it.
        failure_exception = elasticsearch.exceptions.ConnectionError(500, '')
        sub_mock.index.side_effect = failure_exception

        # Submit a crash and ensure that it failed.
        with pytest.raises(elasticsearch.exceptions.ConnectionError):
            es_storage.save_raw_and_processed(a_raw_crash, None,
                                              a_processed_crash, crash_id)
Exemple #4
0
    def test_index_crash_with_bad_keys(self):
        a_raw_crash_with_bad_keys = {
            'foo': 'alpha',
            '': 'bad key 1',
            '.': 'bad key 2',
            u'na\xefve': 'bad key 3',
        }

        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash_with_bad_keys),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid']
        )

        # Ensure that the document was indexed by attempting to retreive it.
        doc = self.es_client.get(
            index=self.config.elasticsearch.elasticsearch_index,
            id=a_processed_crash['uuid']
        )
        # Make sure the invalid keys aren't in the crash.
        raw_crash = doc['_source']['raw_crash']
        assert raw_crash == {'foo': 'alpha'}
        es_storage.close()
Exemple #5
0
    def test_index_crash_with_bad_keys(self):
        a_raw_crash_with_bad_keys = {
            'foo': 'alpha',
            '': 'bad key 1',
            '.': 'bad key 2',
            u'na\xefve': 'bad key 3',
        }

        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash_with_bad_keys),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid']
        )

        # Ensure that the document was indexed by attempting to retreive it.
        doc = self.es_client.get(
            index=self.config.elasticsearch.elasticsearch_index,
            id=a_processed_crash['uuid']
        )
        # Make sure the invalid keys aren't in the crash.
        raw_crash = doc['_source']['raw_crash']
        assert raw_crash == {'foo': 'alpha'}
        es_storage.close()
Exemple #6
0
    def test_indexing_bogus_number_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus number field.

        Expected behavior is to remove that field and retry indexing.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 1234567890,
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                    '9300]][indices:data/write/index]]; nested: '
                    'MapperParsingException[failed to parse '
                    '[processed_crash.bogus-field]]; nested: '
                    'NumberFormatException[For input string: '
                    '"18446744073709480735"]; '
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id
        )

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed': string_to_datetime(
                    '2012-04-08 10:56:41.558922'
                ),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id
        )
Exemple #7
0
    def test_indexing_bogus_number_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus number field.

        Expected behavior is to remove that field and retry indexing.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 1234567890,
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                    '9300]][indices:data/write/index]]; nested: '
                    'MapperParsingException[failed to parse '
                    '[processed_crash.bogus-field]]; nested: '
                    'NumberFormatException[For input string: '
                    '"18446744073709480735"]; '
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id
        )

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed': string_to_datetime(
                    '2012-04-08 10:56:41.558922'
                ),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id
        )
    def test_indexing_bogus_number_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus number field.

        Expected behavior is to remove that field and retry indexing.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash["uuid"]
        raw_crash = {}
        processed_crash = {
            "date_processed": "2012-04-08 10:56:41.558922",
            "bogus-field": 1234567890,
            "foo": "bar",
        }

        def mock_index(*args, **kwargs):
            if "bogus-field" in kwargs["body"]["processed_crash"]:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    ("RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:"
                     "9300]][indices:data/write/index]]; nested: "
                     "MapperParsingException[failed to parse "
                     "[processed_crash.bogus-field]]; nested: "
                     "NumberFormatException[For input string: "
                     '"18446744073709480735"]; '),
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id,
        )

        expected_doc = {
            "crash_id": crash_id,
            "removed_fields": "processed_crash.bogus-field",
            "processed_crash": {
                "date_processed":
                string_to_datetime("2012-04-08 10:56:41.558922"),
                "foo": "bar",
            },
            "raw_crash": {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id,
        )
Exemple #9
0
    def test_indexing_bogus_number_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus number field.
        Expected behavior is to remove that field and retry indexing.
        """
        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 1234567890,
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                    '9300]][indices:data/write/index]]; nested: '
                    'MapperParsingException[failed to parse '
                    '[processed_crash.bogus-field]]; nested: '
                    'NumberFormatException[For input string: '
                    '"18446744073709480735"]; ')

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(raw_crash, None, processed_crash,
                                          crash_id)

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed':
                string_to_datetime('2012-04-08 10:56:41.558922'),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id)
Exemple #10
0
    def test_indexing_unhandled_errors(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of unhandled errors.

        Expected behavior is to fail indexing and raise the error.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
        }

        # Test with an error from which a field name cannot be extracted.
        def mock_index_unparsable_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400,
                'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                '9300]][indices:data/write/index]]; nested: '
                'MapperParsingException[BROKEN PART]; NumberFormatException'
            )

            return True

        es_class_mock().index.side_effect = mock_index_unparsable_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(raw_crash),
                dumps=None,
                processed_crash=deepcopy(processed_crash),
                crash_id=crash_id
            )

        # Test with an error that we do not handle.
        def mock_index_unhandled_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400,
                'Something went wrong'
            )

            return True

        es_class_mock().index.side_effect = mock_index_unhandled_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(raw_crash),
                dumps=None,
                processed_crash=deepcopy(processed_crash),
                crash_id=crash_id
            )
Exemple #11
0
    def test_indexing_unhandled_errors(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of unhandled errors.

        Expected behavior is to fail indexing and raise the error.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
        }

        # Test with an error from which a field name cannot be extracted.
        def mock_index_unparsable_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400,
                'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                '9300]][indices:data/write/index]]; nested: '
                'MapperParsingException[BROKEN PART]; NumberFormatException'
            )

            return True

        es_class_mock().index.side_effect = mock_index_unparsable_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(raw_crash),
                dumps=None,
                processed_crash=deepcopy(processed_crash),
                crash_id=crash_id
            )

        # Test with an error that we do not handle.
        def mock_index_unhandled_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400,
                'Something went wrong'
            )

            return True

        es_class_mock().index.side_effect = mock_index_unhandled_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(raw_crash),
                dumps=None,
                processed_crash=deepcopy(processed_crash),
                crash_id=crash_id
            )
Exemple #12
0
    def test_index_crash(self):
        """Test indexing a crash document"""
        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid'])

        # Ensure that the document was indexed by attempting to retreive it.
        assert (self.es_client.get(
            index=self.config.elasticsearch.elasticsearch_index,
            id=a_processed_crash['uuid']))
        es_storage.close()
Exemple #13
0
    def test_crash_size_capture(self):
        """Verify we capture raw/processed crash sizes in ES crashstorage"""
        es_storage = ESCrashStorage(config=self.config)

        es_storage._submit_crash_to_elasticsearch = mock.Mock()

        es_storage.save_raw_and_processed(raw_crash=a_raw_crash,
                                          dumps=None,
                                          processed_crash=a_processed_crash,
                                          crash_id=a_processed_crash['uuid'])

        mock_calls = [str(call) for call in self.config.metrics.mock_calls]
        # NOTE(willkg): The sizes of these json documents depend on what's in them. If we changed
        # a_processed_crash and a_raw_crash, then these numbers will change.
        assert 'call.histogram(\'processor.es.raw_crash_size\', 27)' in mock_calls
        assert 'call.histogram(\'processor.es.processed_crash_size\', 1785)' in mock_calls
Exemple #14
0
    def test_index_crash(self):
        """Mock test the entire crash submission mechanism"""
        es_storage = ESCrashStorage(config=self.config)

        # This is the function that would actually connect to ES; by mocking
        # it entirely we are ensuring that ES doesn't actually get touched.
        es_storage._submit_crash_to_elasticsearch = mock.Mock()

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid'])

        # Ensure that the indexing function is only called once.
        assert es_storage._submit_crash_to_elasticsearch.call_count == 1
Exemple #15
0
    def test_index_crash(self):
        """Mock test the entire crash submission mechanism"""
        es_storage = ESCrashStorage(config=self.config)

        # This is the function that would actually connect to ES; by mocking
        # it entirely we are ensuring that ES doesn't actually get touched.
        es_storage._submit_crash_to_elasticsearch = mock.Mock()

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid']
        )

        # Ensure that the indexing function is only called once.
        assert es_storage._submit_crash_to_elasticsearch.call_count == 1
    def test_index_crash(self):
        """Test indexing a crash document."""
        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash["uuid"],
        )

        # Ensure that the document was indexed by attempting to retreive it.
        assert self.es_client.get(
            index=es_storage.es_context.get_index_template(),
            id=a_processed_crash["uuid"],
        )
        es_storage.close()
Exemple #17
0
    def test_indexing_unhandled_errors(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of unhandled errors.
        Expected behavior is to fail indexing and raise the error.
        """
        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
        }

        # Test with an error from which a field name cannot be extracted.
        def mock_index_unparsable_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400, 'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                '9300]][indices:data/write/index]]; nested: '
                'MapperParsingException[BROKEN PART]; NumberFormatException')

            return True

        es_class_mock().index.side_effect = mock_index_unparsable_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(raw_crash, None, processed_crash,
                                              crash_id)

        # Test with an error that we do not handle.
        def mock_index_unhandled_error(*args, **kwargs):
            raise elasticsearch.exceptions.TransportError(
                400, 'Something went wrong')

            return True

        es_class_mock().index.side_effect = mock_index_unhandled_error

        with pytest.raises(elasticsearch.exceptions.TransportError):
            es_storage.save_raw_and_processed(raw_crash, None, processed_crash,
                                              crash_id)
Exemple #18
0
    def test_success(self, espy_mock):
        """Test a successful index of a crash report.
        """

        # It's mocks all the way down.
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # Submit a crash like normal, except that the back-end ES object is
        # mocked (see the decorator above).
        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=crash_id,
        )

        # Ensure that the ES objects were instantiated by ConnectionContext.
        ok_(espy_mock.Elasticsearch.called)

        # Ensure that the IndicesClient was also instantiated (this happens in
        # IndexCreator but is part of the crashstorage workflow).
        ok_(espy_mock.client.IndicesClient.called)

        # The actual call to index the document (crash).
        document = {
            'crash_id': crash_id,
            'processed_crash': a_processed_crash,
            'raw_crash': a_raw_crash
        }

        additional = {
            'doc_type': 'crash_reports',
            'id': crash_id,
            'index': 'socorro_integration_test_reports'
        }

        sub_mock.index.assert_called_with(
            body=document,
            **additional
        )
Exemple #19
0
    def test_success(self, espy_mock):
        """Test a successful index of a crash report.
        """

        # It's mocks all the way down.
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # Submit a crash like normal, except that the back-end ES object is
        # mocked (see the decorator above).
        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=crash_id,
        )

        # Ensure that the ES objects were instantiated by ConnectionContext.
        ok_(espy_mock.Elasticsearch.called)

        # Ensure that the IndicesClient was also instantiated (this happens in
        # IndexCreator but is part of the crashstorage workflow).
        ok_(espy_mock.client.IndicesClient.called)

        # The actual call to index the document (crash).
        document = {
            'crash_id': crash_id,
            'processed_crash': a_processed_crash,
            'raw_crash': a_raw_crash
        }

        additional = {
            'doc_type': 'crash_reports',
            'id': crash_id,
            'index': 'socorro_integration_test_reports'
        }

        sub_mock.index.assert_called_with(
            body=document,
            **additional
        )
Exemple #20
0
    def test_crash_size_capture(self):
        """Verify we capture raw/processed crash sizes in ES crashstorage"""
        es_storage = ESCrashStorage(config=self.config)

        es_storage._submit_crash_to_elasticsearch = mock.Mock()

        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=a_processed_crash['uuid']
        )

        mock_calls = [str(call) for call in self.config.metrics.mock_calls]
        # NOTE(willkg): The sizes of these json documents depend on what's in them. If we changed
        # a_processed_crash and a_raw_crash, then these numbers will change.
        assert 'call.histogram(\'processor.es.raw_crash_size\', 27)' in mock_calls
        assert 'call.histogram(\'processor.es.processed_crash_size\', 1785)' in mock_calls
Exemple #21
0
    def test_index_crash(self):
        """Test indexing a crash document."""
        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=a_processed_crash['uuid']
        )

        # Ensure that the document was indexed by attempting to retreive it.
        assert (
            self.es_client.get(
                index=es_storage.es_context.get_index_template(),
                id=a_processed_crash['uuid']
            )
        )
        es_storage.close()
Exemple #22
0
    def test_crash_size_capture(self):
        """Verify we capture raw/processed crash sizes in ES crashstorage"""
        with MetricsMock() as mm:
            es_storage = ESCrashStorage(config=self.config, namespace='processor.es')

            es_storage._submit_crash_to_elasticsearch = mock.Mock()

            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(a_raw_crash),
                dumps=None,
                processed_crash=deepcopy(a_processed_crash),
                crash_id=a_processed_crash['uuid']
            )

            # NOTE(willkg): The sizes of these json documents depend on what's
            # in them. If we changed a_processed_crash and a_raw_crash, then
            # these numbers will change.
            assert mm.has_record('histogram', stat='processor.es.raw_crash_size', value=27)
            assert mm.has_record('histogram', stat='processor.es.processed_crash_size', value=1738)
Exemple #23
0
    def test_crash_size_capture(self):
        """Verify we capture raw/processed crash sizes in ES crashstorage"""
        with MetricsMock() as mm:
            es_storage = ESCrashStorage(config=self.config, namespace='processor.es')

            es_storage._submit_crash_to_elasticsearch = mock.Mock()

            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(a_raw_crash),
                dumps=None,
                processed_crash=deepcopy(a_processed_crash),
                crash_id=a_processed_crash['uuid']
            )

            # NOTE(willkg): The sizes of these json documents depend on what's
            # in them. If we changed a_processed_crash and a_raw_crash, then
            # these numbers will change.
            assert mm.has_record('histogram', stat='processor.es.raw_crash_size', value=27)
            assert mm.has_record('histogram', stat='processor.es.processed_crash_size', value=1738)
Exemple #24
0
    def test_fatal_failure(self, espy_mock):
        """Test an index attempt that fails catastrophically"""
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # Oh the humanity!
        failure_exception = Exception('horrors')
        sub_mock.index.side_effect = failure_exception

        # Submit a crash and ensure that it failed.
        with pytest.raises(Exception):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(a_raw_crash),
                dumps=None,
                processed_crsah=deepcopy(a_processed_crash),
                crash_id=crash_id)
    def test_index_crash(self):
        """Test indexing a crash document.
        """

        es_storage = ESCrashStorage(config=self.config)

        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=a_processed_crash['uuid']
        )

        # Ensure that the document was indexed by attempting to retreive it.
        ok_(
            self.es_client.get(
                index=self.config.elasticsearch.elasticsearch_index,
                id=a_processed_crash['uuid']
            )
        )
    def test_success(self, espy_mock):
        """Test a successful index of a crash report"""
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        crash_id = a_processed_crash["uuid"]

        # Submit a crash like normal, except that the back-end ES object is
        # mocked (see the decorator above).
        es_storage = ESCrashStorage(config=self.config)
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(a_raw_crash),
            dumps=None,
            processed_crash=deepcopy(a_processed_crash),
            crash_id=crash_id,
        )

        # Ensure that the ES objects were instantiated by ConnectionContext.
        assert espy_mock.Elasticsearch.called

        # Ensure that the IndicesClient was also instantiated (this happens in
        # IndexCreator but is part of the crashstorage workflow).
        assert espy_mock.client.IndicesClient.called

        expected_processed_crash = deepcopy(a_processed_crash)
        reconstitute_datetimes(expected_processed_crash)

        # The actual call to index the document (crash).
        document = {
            "crash_id": crash_id,
            "processed_crash": expected_processed_crash,
            "raw_crash": a_raw_crash,
        }

        additional = {
            "doc_type": "crash_reports",
            "id": crash_id,
            "index": "socorro_integration_test_reports",
        }

        sub_mock.index.assert_called_with(body=document, **additional)
Exemple #27
0
    def test_fatal_failure(self, espy_mock):
        """Test an index attempt that fails catastrophically"""
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # Oh the humanity!
        failure_exception = Exception('horrors')
        sub_mock.index.side_effect = failure_exception

        # Submit a crash and ensure that it failed.
        with pytest.raises(Exception):
            es_storage.save_raw_and_processed(
                raw_crash=deepcopy(a_raw_crash),
                dumps=None,
                processed_crsah=deepcopy(a_processed_crash),
                crash_id=crash_id
            )
Exemple #28
0
    def test_indexing_bogus_string_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus string field.

        Expected behavior is to remove that field and retry indexing.

        """

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 'some bogus value',
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-5exxx97][inet[/172.3.9.12:'
                    '9300]][indices:data/write/index]]; nested: '
                    'IllegalArgumentException[Document contains at least one '
                    'immense term in field="processed_crash.bogus-field.full" '
                    '(whose UTF8 encoding is longer than the max length 32766)'
                    ', all of which were skipped.  Please correct the analyzer'
                    ' to not produce such terms.  The prefix of the first '
                    'immense term is: \'[124, 91, 48, 93, 91, 71, 70, 88, 49, '
                    '45, 93, 58, 32, 65, 116, 116, 101, 109, 112, 116, 32, '
                    '116, 111, 32, 99, 114, 101, 97, 116, 101]...\', original '
                    'message: bytes can be at most 32766 in length; got 98489]'
                    '; nested: MaxBytesLengthExceededException'
                    '[bytes can be at most 32766 in length; got 98489]; ')

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id)

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed':
                string_to_datetime('2012-04-08 10:56:41.558922'),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id)
Exemple #29
0
    def test_indexing_bogus_string_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus string field.

        Expected behavior is to remove that field and retry indexing.

        """

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 'some bogus value',
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-5exxx97][inet[/172.3.9.12:'
                    '9300]][indices:data/write/index]]; nested: '
                    'IllegalArgumentException[Document contains at least one '
                    'immense term in field="processed_crash.bogus-field.full" '
                    '(whose UTF8 encoding is longer than the max length 32766)'
                    ', all of which were skipped.  Please correct the analyzer'
                    ' to not produce such terms.  The prefix of the first '
                    'immense term is: \'[124, 91, 48, 93, 91, 71, 70, 88, 49, '
                    '45, 93, 58, 32, 65, 116, 116, 101, 109, 112, 116, 32, '
                    '116, 111, 32, 99, 114, 101, 97, 116, 101]...\', original '
                    'message: bytes can be at most 32766 in length; got 98489]'
                    '; nested: MaxBytesLengthExceededException'
                    '[bytes can be at most 32766 in length; got 98489]; '
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id
        )

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed': string_to_datetime(
                    '2012-04-08 10:56:41.558922'
                ),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id
        )
    def test_indexing_unknown_property_field(self, es_class_mock,
                                             es_client_mock):
        """Test an index attempt that fails because of an unknown property.

        Expected behavior is to remove that field and retry indexing.

        """
        es_storage = ESCrashStorage(config=self.config)

        crash_id = create_new_ooid()
        raw_crash = {"uuid": crash_id}
        processed_crash = {
            "date_processed": "2019-12-11 10:56:41.558922",
            "bogus-field": {
                "key": {
                    "nested_key": "val"
                }
            },
        }

        def mock_index(*args, **kwargs):
            if "bogus-field" in kwargs["body"]["processed_crash"]:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    ("RemoteTransportException[[Madam Slay]"
                     "[inet[/172.31.22.181:9300]][indices:data/write/index]]; "
                     "nested: MapperParsingException"
                     "[failed to parse [processed_crash.bogus-field]]; "
                     "nested: "
                     "ElasticsearchIllegalArgumentException[unknown property [key]]"
                     ),
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit crash and verify.
        es_storage.save_raw_and_processed(
            raw_crash=raw_crash,
            dumps=None,
            processed_crash=processed_crash,
            crash_id=crash_id,
        )

        expected_doc = {
            "crash_id": crash_id,
            "removed_fields": "processed_crash.bogus-field",
            "processed_crash": {
                "date_processed":
                string_to_datetime("2019-12-11 10:56:41.558922"),
            },
            "raw_crash": {
                "uuid": crash_id
            },
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id,
        )
    def test_indexing_bogus_string_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus string field.

        Expected behavior is to remove that field and retry indexing.

        """

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash["uuid"]
        raw_crash = {}
        processed_crash = {
            "date_processed": "2012-04-08 10:56:41.558922",
            "bogus-field": "some bogus value",
            "foo": "bar",
        }

        def mock_index(*args, **kwargs):
            if "bogus-field" in kwargs["body"]["processed_crash"]:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    "RemoteTransportException[[i-5exxx97][inet[/172.3.9.12:"
                    "9300]][indices:data/write/index]]; nested: "
                    "IllegalArgumentException[Document contains at least one "
                    'immense term in field="processed_crash.bogus-field.full" '
                    "(whose UTF8 encoding is longer than the max length 32766)"
                    ", all of which were skipped.  Please correct the analyzer"
                    " to not produce such terms.  The prefix of the first "
                    "immense term is: '[124, 91, 48, 93, 91, 71, 70, 88, 49, "
                    "45, 93, 58, 32, 65, 116, 116, 101, 109, 112, 116, 32, "
                    "116, 111, 32, 99, 114, 101, 97, 116, 101]...', original "
                    "message: bytes can be at most 32766 in length; got 98489]"
                    "; nested: MaxBytesLengthExceededException"
                    "[bytes can be at most 32766 in length; got 98489]; ",
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash=deepcopy(raw_crash),
            dumps=None,
            processed_crash=deepcopy(processed_crash),
            crash_id=crash_id,
        )

        expected_doc = {
            "crash_id": crash_id,
            "removed_fields": "processed_crash.bogus-field",
            "processed_crash": {
                "date_processed":
                string_to_datetime("2012-04-08 10:56:41.558922"),
                "foo": "bar",
            },
            "raw_crash": {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id,
        )
Exemple #32
0
    def test_success_operational_exception(self, espy_mock):
        """Test an index attempt that experiences a operational exception that
        it managed to recover from.
        """

        # It's mocks all the way down.
        sub_mock = mock.MagicMock()
        espy_mock.Elasticsearch.return_value = sub_mock

        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']

        # The transaction executor will try three times, so we will fail
        # twice for the purposes of this test.
        bad_results = [
            elasticsearch.exceptions.ConnectionError,
            elasticsearch.exceptions.ConnectionError
        ]

        # Replace the underlying index method with this function.
        def esindex_fn(*args, **kwargs):
            try:
                result = bad_results.pop(0)
                raise result
            except IndexError:
                return sub_mock.index

        sub_mock.index.side_effect = esindex_fn

        # Submit a crash like normal, except that the index method will
        # raise twice, then pass as normal.
        es_storage.save_raw_and_processed(
            raw_crash=a_raw_crash,
            dumps=None,
            processed_crash=a_processed_crash,
            crash_id=crash_id,
        )

        # The actual call to index the document (crash).
        document = {
            'crash_id': crash_id,
            'processed_crash': a_processed_crash,
            'raw_crash': a_raw_crash
        }

        additional = {
            'doc_type': 'crash_reports',
            'id': crash_id,
            'index': 'socorro_integration_test_reports'
        }

        sub_mock.index.assert_called_with(
            body=document,
            **additional
        )
Exemple #33
0
    def test_indexing_bogus_number_field(self, es_class_mock, es_client_mock):
        """Test an index attempt that fails because of a bogus number field.
        Expected behavior is to remove that field and retry indexing.
        """
        # ESCrashStorage uses the "limited backoff" transaction executor.
        # In real life this will retry operational exceptions over time, but
        # in unit tests, we just want it to hurry up and fail.
        backoff_config = self.config
        backoff_config['backoff_delays'] = [0, 0, 0]
        backoff_config['wait_log_interval'] = 0

        es_storage = ESCrashStorage(config=self.config)

        crash_id = a_processed_crash['uuid']
        raw_crash = {}
        processed_crash = {
            'date_processed': '2012-04-08 10:56:41.558922',
            'bogus-field': 1234567890,
            'foo': 'bar',
        }

        def mock_index(*args, **kwargs):
            if 'bogus-field' in kwargs['body']['processed_crash']:
                raise elasticsearch.exceptions.TransportError(
                    400,
                    'RemoteTransportException[[i-f94dae31][inet[/172.31.1.54:'
                    '9300]][indices:data/write/index]]; nested: '
                    'MapperParsingException[failed to parse '
                    '[processed_crash.bogus-field]]; nested: '
                    'NumberFormatException[For input string: '
                    '"18446744073709480735"]; '
                )

            return True

        es_class_mock().index.side_effect = mock_index

        # Submit a crash and ensure that it succeeds.
        es_storage.save_raw_and_processed(
            raw_crash,
            None,
            processed_crash,
            crash_id
        )

        expected_doc = {
            'crash_id': crash_id,
            'removed_fields': 'processed_crash.bogus-field',
            'processed_crash': {
                'date_processed': string_to_datetime(
                    '2012-04-08 10:56:41.558922'
                ),
                'foo': 'bar',
            },
            'raw_crash': {},
        }
        es_class_mock().index.assert_called_with(
            index=self.config.elasticsearch.elasticsearch_index,
            doc_type=self.config.elasticsearch.elasticsearch_doctype,
            body=expected_doc,
            id=crash_id
        )