Пример #1
0
    def test_cron_job(self, exacttarget_mock):
        config_manager = self._setup_config_manager()
        et_mock = exacttarget_mock.return_value

        # Make get_subscriber raise an exception
        list_service = et_mock.list.return_value = mock.Mock()
        list_service.get_subscriber = mock.Mock(
            side_effect=exacttarget.NewsletterException())

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert not information['automatic-emails']['last_error']
            assert information['automatic-emails']['last_success']
            eq_(et_mock.trigger_send.call_count, 4)

            last_email = u'z\[email protected]'

            # Verify the last call to trigger_send
            fields = {
                'EMAIL_ADDRESS_': last_email,
                'EMAIL_FORMAT_': 'H',
                'TOKEN': last_email
            }

            et_mock.trigger_send.assert_called_with('socorro_dev_test', fields)

            # Verify that user's data was updated
            conf = config.crontabber['class-AutomaticEmailsCronApp']
            es = SuperS().es(
                urls=conf.elasticsearch.elasticsearch_urls,
                timeout=conf.elasticsearch.elasticsearch_timeout,
            )
            search = es.indexes(conf.elasticsearch.elasticsearch_emails_index)
            search = search.doctypes('emails')
            es.get_es().refresh()

            emails_list = ('*****@*****.**',
                           '"Quidam" <*****@*****.**>',
                           '*****@*****.**')
            search = search.filter(_id__in=emails_list)
            res = search.values_list('last_sending')
            eq_(len(res), 3)
            now = utc_now()
            for row in res:
                date = string_to_datetime(row[0])
                eq_(date.year, now.year)
                eq_(date.month, now.month)
                eq_(date.day, now.day)
Пример #2
0
    def test_mocked_fetch(self):
        config_manager = self._setup_config_manager()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['fetch-adi-from-hive']
            assert not information['fetch-adi-from-hive']['last_error']

            config.logger.info.assert_called_with(
                'Faking the fetching of ADI from Hive :)')
Пример #3
0
    def test_reports_clean_with_dependency(self):
        config_manager = self._setup_config_manager(
            'socorro.cron.jobs.matviews.DuplicatesCronApp|1h\n'
            'socorro.cron.jobs.matviews.ReportsCleanCronApp|1h')

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['reports-clean']
            assert not information['reports-clean']['last_error']
            assert information['reports-clean']['last_success']
Пример #4
0
    def test_run_job_based_on_last_success(self, requests_mocker):
        """specifically setting 0 days back and no prior run
        will pick it up from now's date"""
        requests_mocker.get(BUGZILLA_BASE_URL, json=SAMPLE_BUGZILLA_RESULTS)
        config_manager = self._setup_config_manager(0)

        cursor = self.conn.cursor()
        # these are matching the SAMPLE_CSV above
        cursor.execute("""insert into bug_associations
        (bug_id,signature)
        values
        (8, 'legitimate(sig)');
        """)
        self.conn.commit()

        # second time
        config_manager = self._setup_config_manager(0)
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            state = tab.job_state_database.copy()
            self._wind_clock(state, days=1)
            tab.job_state_database.update(state)

        # Create a CSV file for one day back.
        # This'll make sure there's a .csv file whose day
        # is that of the last run.
        self._setup_config_manager(1)

        config_manager = self._setup_config_manager(0)
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['bugzilla-associations']
            assert not information['bugzilla-associations']['last_error']
            assert information['bugzilla-associations']['last_success']
Пример #5
0
    def test_duplicates(self):
        config_manager = self._setup_config_manager(
          'socorro.cron.jobs.matviews.DuplicatesCronApp|1d'
        )

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['duplicates']
            assert not information['duplicates']['last_error']
            assert information['duplicates']['last_success']
Пример #6
0
    def test_all_matviews(self, mocked_utc_now):

        # Pretend it's 03AM UTC
        def mock_utc_now():
            n = utc_now()
            n = n.replace(hour=3)
            return n

        mocked_utc_now.side_effect = mock_utc_now

        config_manager = self._setup_config_manager(
            'socorro.unittest.cron.jobs.test_matviews.ReportsCleanJob|1d\n'
            'socorro.unittest.cron.jobs.test_matviews.FTPScraperJob|1d\n'
            ''
            'socorro.cron.jobs.matviews.ProductVersionsCronApp|1d\n'
            'socorro.cron.jobs.matviews.SignaturesCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.TCBSCronApp|1d\n'
            'socorro.cron.jobs.matviews.ADUCronApp|1d\n'
            'socorro.cron.jobs.matviews.BuildADUCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.CrashesByUserCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.CrashesByUserBuildCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.HomePageGraphCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.HomePageGraphBuildCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.TCBSBuildCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.ExplosivenessCronApp|1d|02:00\n'
            'socorro.cron.jobs.matviews.GraphicsDeviceCronApp|1d|02:00\n')

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()

            for app_name in (
                    'product-versions-matview',
                    'signatures-matview',
                    'tcbs-matview',
                    'adu-matview',
                    'build-adu-matview',
                    'crashes-by-user-matview',
                    'crashes-by-user-build-matview',
                    'home-page-graph-matview',
                    'home-page-graph-matview-build',
                    'tcbs-build-matview',
                    'explosiveness-matview',
                    'graphics-device-matview',
            ):

                ok_(app_name in information, app_name)
                ok_(not information[app_name]['last_error'], app_name)
                ok_(information[app_name]['last_success'], app_name)
Пример #7
0
    def test_basic_run_job_with_reports_with_existing_bugs_same(self):
        config_manager = self._setup_config_manager(3)

        cursor = self.conn.cursor()
        # these are matching the SAMPLE_CSV above
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('123', 'legitimate(sig)');
        """)
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('456', 'MWSBAR.DLL@0x2589f');
        """)
        # exactly the same as the fixture
        cursor.execute("""insert into bugs
        (id,status,resolution,short_desc)
        values
        (8, 'CLOSED', 'RESOLVED', 'newlines in sigs');
        """)
        cursor.execute("""insert into bug_associations
        (bug_id,signature)
        values
        (8, 'legitimate(sig)');
        """)
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['bugzilla-associations']
            assert not information['bugzilla-associations']['last_error']
            assert information['bugzilla-associations']['last_success']

        cursor.execute('select id, short_desc from bugs where id = 8')
        bug = cursor.fetchone()
        eq_(bug[1], 'newlines in sigs')

        cursor.execute(
          'select signature from bug_associations where bug_id = 8')
        association = cursor.fetchone()
        eq_(association[0], 'legitimate(sig)')
        cursor.execute('select * from bug_associations')
Пример #8
0
    def test_basic_run_no_errors(self):
        # a mutable where commands sent are stored
        commands_sent = []
        self.Popen.side_effect = functools.partial(
            mocked_Popen,
            _commands_sent=commands_sent,
            _exit_code=0,
            _stdout='Bla bla',
            _stderr='',
        )

        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['modulelist']
            #print information['modulelist']['last_error']
            #print information['modulelist']['last_error']['traceback']
            if information['modulelist']['last_error']:
                raise AssertionError(information['modulelist']['last_error'])

            assert len(commands_sent) == 3
            first = commands_sent[0]
            second = commands_sent[1]
            third = commands_sent[2]
            yesterday = utc_now()
            yesterday -= datetime.timedelta(days=1)
            yesterday_fmt = yesterday.strftime('%Y%m%d')
            ok_('PIG_CLASSPATH=/some/place pig' in first)
            ok_('-param start_date=%s' % yesterday_fmt in first)
            ok_('-param end_date=%s' % yesterday_fmt in first)
            ok_('/some/place/modulelist.pig' in first)

            ok_('PIG_CLASSPATH=/some/place hadoop fs -getmerge' in second)
            ok_('modulelist-%s-%s' % (yesterday_fmt, yesterday_fmt) in second)
            ok_('/some/other/place/%s-modulelist.txt' %
                (yesterday_fmt, ) in second)

            ok_('PIG_CLASSPATH=/some/place hadoop fs ' in third)
            ok_('modulelist-%s-%s' % (yesterday_fmt, yesterday_fmt) in second)

            # note that all jobs spew out 'Bla bla' on stdout
            config.logger.info.assert_called_with('Bla bla')
    def test_download_error(self, rget):
        config_manager = self._setup_config_manager()

        def mocked_get(url):
            return Response('not here', status_code=404)

        rget.side_effect = mocked_get

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['featured-versions-automatic']
            assert information['featured-versions-automatic']['last_error']
            error = information['featured-versions-automatic']['last_error']
            ok_('DownloadError' in error['type'])
            ok_('404' in error['value'])
Пример #10
0
    def test_symbols_unpack_non_archive_file(self):

        source_file = os.path.join(self.temp_source_directory,
                                   os.path.basename(__file__))
        shutil.copy(__file__, source_file)

        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            config.logger.warning.assert_called_with(
                "Don't know how to unpack %s" % (source_file, ))

        information = self._load_structure()
        assert information['symbols-unpack']
        assert not information['symbols-unpack']['last_error']
        assert information['symbols-unpack']['last_success']
Пример #11
0
    def test_run_job_no_data_but_scped(self):
        config_manager = self._setup_config_manager(
            public_output_path='',
            private_user='******',
            private_server='secure.mozilla.org',
            private_location='/var/data/',
            private_ssh_command='chmod 0640 /var/data/*',
        )

        def comm():
            # no errors
            return '', ''

        self.Popen().communicate.side_effect = comm

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['daily-url']
            assert not information['daily-url']['last_error']
            assert information['daily-url']['last_success']

        # even though the files created are empty they should nevertheless
        # be scp'ed
        # can expect the command exactly
        now = datetime.datetime.utcnow() - datetime.timedelta(days=1)
        private = now.strftime('%Y%m%d-crashdata.csv.gz')
        private_path = os.path.join(self.tempdir, private)
        assert os.path.isfile(private_path)
        scp_command = 'scp "%s" "[email protected]:/var/data/"' % private_path
        ssh_command = 'ssh "*****@*****.**" "chmod 0640 /var/data/*"'
        self.Popen.assert_any_call(scp_command,
                                   stdin=PIPE,
                                   stderr=PIPE,
                                   stdout=PIPE,
                                   shell=True)

        self.Popen.assert_any_call(ssh_command,
                                   stdin=PIPE,
                                   stderr=PIPE,
                                   stdout=PIPE,
                                   shell=True)
Пример #12
0
    def test_with_bugzilla_failure(self, requests_mocker):
        requests_mocker.get(
            BUGZILLA_BASE_URL,
            text='error loading content',
            status_code=500
        )
        config_manager = self._setup_config_manager(3)

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['bugzilla-associations']
            # There has been an error.
            last_error = information['bugzilla-associations']['last_error']
            assert last_error
            assert 'HTTPError' in last_error['type']
            assert not information['bugzilla-associations']['last_success']
Пример #13
0
    def test_server_status(self):
        """ Simple test of status monitor """
        config_manager = self._setup_config_manager()

        cursor = self.conn.cursor()

        # Create partitions to support the status query
        # Load report_partition_info data
        cursor.execute("""
            INSERT into report_partition_info
              (table_name, build_order, keys, indexes,
              fkeys, partition_column, timetype)
            VALUES
             ('reports', '1', '{id,uuid}',
             '{date_processed,hangid,"product,version",reason,signature,url}',
             '{}', 'date_processed', 'TIMESTAMPTZ')
        """)
        cursor.execute('SELECT weekly_report_partitions()')

        # We have to do this here to accommodate separate crontabber processes
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()
        cursor.execute('select count(*) from server_status')

        res_expected = 1
        res, = cursor.fetchone()
        eq_(res, res_expected)

        cursor.execute("""select
                date_recently_completed
                , date_oldest_job_queued -- is NULL until we upgrade Rabbit
                , avg_process_sec
                , waiting_job_count -- should be 1
                -- , date_created -- leaving timestamp verification out
            from server_status
        """)

        res_expected = (None, None, 0.0, 1)
        res = cursor.fetchone()
        eq_(res, res_expected)
Пример #14
0
    def test_run(self, connect_s3):

        key = mock.MagicMock()
        connect_s3().get_bucket().get_key.return_value = None
        connect_s3().get_bucket().new_key.return_value = key

        with self._setup_config_manager().context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            app_name = 'upload-crash-report-json-schema'
            ok_(information[app_name])
            ok_(not information[app_name]['last_error'])
            ok_(information[app_name]['last_success'])

        key.set_contents_from_string.assert_called_with(
            CRASH_REPORT_JSON_SCHEMA_AS_STRING
        )
Пример #15
0
    def test_run_job_one_day_back(self):
        """specifically setting 0 days back and no priror run
        will pick it up from now's date"""
        config_manager = self._setup_config_manager(1)

        cursor = self.conn.cursor()
        # these are matching the SAMPLE_CSV above
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('123', 'legitimate(sig)');
        """)
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('456', 'MWSBAR.DLL@0x2589f');
        """)
        # exactly the same as the fixture
        cursor.execute("""insert into bugs
        (id,status,resolution,short_desc)
        values
        (8, 'CLOSED', 'RESOLVED', 'newlines in sigs');
        """)
        cursor.execute("""insert into bug_associations
        (bug_id,signature)
        values
        (8, 'legitimate(sig)');
        """)
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['bugzilla-associations']
            assert not information['bugzilla-associations']['last_error']
            assert information['bugzilla-associations']['last_success']

        cursor.execute('select id, short_desc from bugs where id = 8')
        bug = cursor.fetchone()
        eq_(bug[1], 'newlines in sigs')
Пример #16
0
    def test_basic_run(self):
        # We need to prepare to return a size for the new key
        self.mock_key.size = 123456789
        self.mock_key.generate_url.return_value = (
            'https://s3.example.com/latest.csv')

        # Run the crontabber job to remove the test table.
        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

        # Basic assertion test of stored procedure.
        information = self._load_structure()
        assert information['missing-symbols']
        assert not information['missing-symbols']['last_error']
        assert information['missing-symbols']['last_success']

        self.mock_boto_class()._connect.assert_called_with()
        self.mock_boto_class.close.assert_called_with()
        self.mock_bucket.new_key.assert_called_with('latest.csv')
        content = StringIO()
        writer = csv.writer(content)
        writer.writerow((
            'debug_file',
            'debug_id',
            'code_file',
            'code_id',
        ))
        writer.writerow((
            'nvwgf2um.pdb',
            '9D492B844FF34800B34320464AA1E7E41',
            'nvwgf2um.dll',
            '561D1D4Ff58000',
        ))
        self.mock_key.set_contents_from_string.assert_called_with(
            content.getvalue())

        # this is becausse 123456789 bytes is 117.74 Mb
        tab.config.logger.info.assert_called_with(
            'Generated https://s3.example.com/latest.csv '
            '(123,456,789 bytes, 117.74 Mb)')
Пример #17
0
    def test_half_way_exception(self):
        """If the save_raw_crash() fails on the second crash_id of 2,
        the first one should be removed from the table."""
        config_manager = self._setup_config_manager()

        cursor = self.conn.cursor()
        cursor.execute("""
            INSERT INTO reprocessing_jobs
              (crash_id)
            VALUES
             ('13c4a348-5d04-11e3-8118-d231feb1dc81'),
             ('23d5b459-6e15-22f4-9229-e342ffc2ed92')
        """)
        self.conn.commit()

        def mocked_save_raw_crash(raw_crash, dumps, crash_id):
            if crash_id == '23d5b459-6e15-22f4-9229-e342ffc2ed92':
                raise Exception('something unpredictable happened')

        self.rabbit_queue_mocked().save_raw_crash.side_effect = (
            mocked_save_raw_crash
        )

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = tab.job_state_database['reprocessing-jobs']
            # Note, we're expecting it to fail.
            assert information['last_error']
            eq_(
                information['last_error']['value'],
                'something unpredictable happened'
            )
            assert not information['last_success']

        cursor = self.conn.cursor()
        cursor.execute('select crash_id from reprocessing_jobs')
        records = cursor.fetchall()
        eq_(len(records), 1)
        crash_id, = records[0]
        eq_(crash_id, '23d5b459-6e15-22f4-9229-e342ffc2ed92')
Пример #18
0
    def test_fetch_with_zero_hive_results(self, fake_hive):
        config_manager = self._setup_config_manager()

        def return_test_data(fake):
            # a generator that yields literally nothing
            # http://stackoverflow.com/a/13243870/205832
            return
            yield

        fake_hive.connect.return_value \
            .cursor.return_value.__iter__ = return_test_data

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['fetch-adi-from-hive']

            assert information['fetch-adi-from-hive']['last_error']
            ok_(
                NoRowsWritten.__name__ in
                information['fetch-adi-from-hive']['last_error']['type']
            )
            assert information['fetch-adi-from-hive']['last_error']

        fake_hive.connect.assert_called_with(
            database='default',
            authMechanism='PLAIN',
            host='localhost',
            user='******',
            password='******',
            port=10000,
            timeout=1800000,
        )

        pgcursor = self.conn.cursor()
        pgcursor.execute(
            "select count(*) from raw_adi_logs"
        )
        count, = pgcursor.fetchone()
        eq_(count, 0)
Пример #19
0
    def test_symbols_unpack_subdirectories_careful_dir_cleanup(self):
        """same test almost as test_symbols_unpack_subdirectories()
        but this time we put a file in one of the directories and assert
        that that does not get deleted"""
        root_dir = self.temp_source_directory
        foo_dir = os.path.join(root_dir, 'foo')
        os.mkdir(foo_dir)
        with open(os.path.join(foo_dir, 'some.file'), 'w') as f:
            f.write('anything')
        bar_dir = os.path.join(foo_dir, 'bar')
        os.mkdir(bar_dir)
        source_file = os.path.join(bar_dir, os.path.basename(ZIP_FILE))
        shutil.copy(ZIP_FILE, source_file)

        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

        information = self._load_structure()
        assert information['symbols-unpack']
        assert not information['symbols-unpack']['last_error'], (
            information['symbols-unpack']['last_error'])
        assert information['symbols-unpack']['last_success']

        ok_(os.listdir(self.temp_destination_directory), 'empty')
        # there should now be a directory named `sample-<todays date>
        destination_dir = self.temp_destination_directory
        ok_(os.path.isdir(destination_dir))
        # and it should contain files
        ok_(os.listdir(destination_dir))
        # and the original should now have been deleted
        ok_(not os.path.isfile(source_file))

        # because there was nothing else in that directory, or its parent
        # those directories should be removed now
        ok_(not os.path.isdir(bar_dir))
        ok_(os.path.isdir(foo_dir))
        ok_(os.path.isfile(os.path.join(foo_dir, 'some.file')))
        assert os.path.isdir(root_dir)
Пример #20
0
    def test_reprocessing(self):
        """ Simple test of reprocessing"""
        config_manager = self._setup_config_manager()
        c = config_manager.get_config()

        cursor = self.conn.cursor()

        # Create partitions to support the status query
        # Load report_partition_info data
        cursor.execute("""
            INSERT into reprocessing_jobs
              (crash_id)
            VALUES
             ('13c4a348-5d04-11e3-8118-d231feb1dc81')
        """)

        # We have to do this here to accommodate separate crontabber processes
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = tab.job_state_database['reprocessing-jobs']
            assert not information['last_error']
            assert information['last_success']

        cursor = self.conn.cursor()
        cursor.execute('select count(*) from reprocessing_jobs')

        res_expected = 0
        res, = cursor.fetchone()
        eq_(res, res_expected)

        self.rabbit_queue_mocked.return_value.save_raw_crash \
            .assert_called_once_with(
                DotDict({'legacy_processing': 0}),
                [],
                '13c4a348-5d04-11e3-8118-d231feb1dc81'
            )
Пример #21
0
    def test_basic_run_job_with_some_reports(self):
        config_manager = self._setup_config_manager(3)

        cursor = self.conn.cursor()
        # these are matching the SAMPLE_CSV above
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('123', 'legitimate(sig)');
        """)
        cursor.execute("""insert into reports
        (uuid,signature)
        values
        ('456', 'MWSBAR.DLL@0x2589f');
        """)
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['bugzilla-associations']
            assert not information['bugzilla-associations']['last_error']
            assert information['bugzilla-associations']['last_success']

        cursor.execute('select id from bugs order by id')
        bugs = cursor.fetchall()
        eq_(len(bugs), 2)
        # the only bugs with matching those signatures are: 5 and 8
        bug_ids = [x[0] for x in bugs]
        eq_(bug_ids, [5, 8])

        cursor.execute('select bug_id from bug_associations order by bug_id')
        associations = cursor.fetchall()
        eq_(len(associations), 2)
        bug_ids = [x[0] for x in associations]
        eq_(bug_ids, [5, 8])
Пример #22
0
    def test_failing_hadoop_cleanup_job(self):
        # a mutable where commands sent are stored
        commands_sent = []
        self.Popen.side_effect = functools.partial(
            mocked_Popen,
            _commands_sent=commands_sent,
            _exit_code=lambda cmd: 1 if cmd.count('-rmr') else 0,
            _stdout='',
            _stderr=lambda cmd: 'Iniquity' if cmd.count('-rmr') else '',
        )

        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()
            information = self._load_structure()
            assert information['modulelist']
            assert information['modulelist']['last_error']
            _traceback = information['modulelist']['last_error']['traceback']
            ok_('hadoop cleanup failed' in _traceback)
            # the other two where cancelled
            eq_(len(commands_sent), 3)
            config.logger.error.has_calls([mock.call('Iniquity')])
Пример #23
0
    def test_one_matview_alone(self):
        config_manager = self._setup_config_manager(
            'socorro.unittest.cron.jobs.test_matviews.ReportsCleanJob|1d\n'
            'socorro.unittest.cron.jobs.test_matviews.FTPScraperJob|1d\n'
            ''
            'socorro.cron.jobs.matviews.ProductVersionsCronApp|1d')

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['reports-clean']
            assert not information['reports-clean']['last_error']
            assert information['reports-clean']['last_success']

            assert information['ftpscraper']
            assert not information['ftpscraper']['last_error']
            assert information['ftpscraper']['last_success']

            assert information['product-versions-matview']
            assert not information['product-versions-matview']['last_error']
            assert information['product-versions-matview']['last_success']
Пример #24
0
    def test_symbols_unpack_zip_file(self):

        source_file = os.path.join(self.temp_source_directory,
                                   os.path.basename(ZIP_FILE))
        shutil.copy(ZIP_FILE, source_file)

        config_manager = self._setup_config_manager()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

        information = self._load_structure()
        assert information['symbols-unpack']
        assert not information['symbols-unpack']['last_error']
        assert information['symbols-unpack']['last_success']

        ok_(os.listdir(self.temp_destination_directory), 'empty')
        # there should now be a directory named `sample-<todays date>
        destination_dir = self.temp_destination_directory
        ok_(os.path.isdir(destination_dir))
        # and it should contain files
        ok_(os.listdir(destination_dir))
        # and the original should now have been deleted
        ok_(not os.path.isfile(source_file))
Пример #25
0
    def test_run_job_with_mocked_data_with_scp_errors(self):
        config_manager = self._setup_config_manager(
            public_output_path='',
            private_user='******',
            private_server='secure.mozilla.org',
            private_location='/var/data/',
        )
        self._insert_waterwolf_mock_data()

        def comm():
            # some errors
            return '', "CRAP!"

        self.Popen().communicate.side_effect = comm

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['daily-url']
            assert not information['daily-url']['last_error']
            assert information['daily-url']['last_success']
            ok_(config.logger.warn.called)
Пример #26
0
    def test_mocked_fetch(self):
        config_manager = self._setup_config_manager()

        def return_test_data(fake):
            test_data = [[
                '2014-01-01', 'WinterWolf', 'Ginko', '2.3.1', '10.0.4',
                'nightly-ww3v20', 'nightly', 'a-guid', '1'
            ],
                         [
                             '2019-01-01', 'NothingMuch', 'Ginko', '2.3.2',
                             '10.0.5a', None, 'release', 'a-guid', '2'
                         ]]
            for item in test_data:
                yield item

        with patch('socorro.cron.jobs.fetch_adi_from_hive.pyhs2') as fake_hive:
            fake_hive.connect.return_value \
                .cursor.return_value.__iter__ = return_test_data

            with config_manager.context() as config:
                tab = CronTabber(config)
                tab.run_all()

                information = self._load_structure()
                assert information['fetch-adi-from-hive']

                if information['fetch-adi-from-hive']['last_error']:
                    raise AssertionError(
                        information['fetch-adi-from-hive']['last_error'])

        fake_hive.connect.assert_called_with(database='default',
                                             authMechanism='PLAIN',
                                             host='localhost',
                                             user='******',
                                             password='******',
                                             port=10000)

        pgcursor = self.conn.cursor()
        columns = ('report_date', 'product_name', 'product_os_platform',
                   'product_os_version', 'product_version', 'build',
                   'build_channel', 'product_guid', 'count')
        pgcursor.execute(""" select %s from raw_adi_logs """ %
                         ','.join(columns))

        adi = [dict(zip(columns, row)) for row in pgcursor.fetchall()]

        eq_(adi, [{
            'report_date': datetime.date(2014, 1, 1),
            'product_name': 'WinterWolf',
            'product_os_platform': 'Ginko',
            'product_os_version': '2.3.1',
            'product_version': '10.0.4',
            'build': 'nightly-ww3v20',
            'build_channel': 'nightly',
            'product_guid': 'a-guid',
            'count': 1
        }, {
            'report_date': datetime.date(2019, 1, 1),
            'product_name': 'NothingMuch',
            'product_os_platform': 'Ginko',
            'product_os_version': '2.3.2',
            'product_version': '10.0.5a',
            'build': None,
            'build_channel': 'release',
            'product_guid': 'a-guid',
            'count': 2
        }])
    def test_basic_run_job(self, rget):
        config_manager = self._setup_config_manager()

        def mocked_get(url):
            if 'firefox_versions.json' in url:
                return Response({
                    'FIREFOX_NIGHTLY': '52.0a1',
                    # Kept for legacy and smooth transition.
                    # We USED to consider the latest AURORA version a
                    # featured version but we no longer build aurora
                    # so Socorro shouldn't pick this up any more
                    # even if product-details.mozilla.org supplies it.
                    'FIREFOX_AURORA': '51.0a2',
                    'FIREFOX_ESR': '45.4.0esr',
                    'FIREFOX_ESR_NEXT': '',
                    'LATEST_FIREFOX_DEVEL_VERSION': '50.0b7',
                    'LATEST_FIREFOX_OLDER_VERSION': '3.6.28',
                    'LATEST_FIREFOX_RELEASED_DEVEL_VERSION': '50.0b7',
                    'LATEST_FIREFOX_VERSION': '49.0.1'
                })
            elif 'mobile_versions.json' in url:
                return Response({
                    'nightly_version': '52.0a1',
                    'alpha_version': '51.0a2',
                    'beta_version': '50.0b6',
                    'version': '49.0',
                    'ios_beta_version': '6.0',
                    'ios_version': '5.0'
                })
            elif 'thunderbird_versions.json' in url:
                return Response({
                    'LATEST_THUNDERBIRD_VERSION': '45.4.0',
                    'LATEST_THUNDERBIRD_DEVEL_VERSION': '50.0b1',
                    'LATEST_THUNDERBIRD_ALPHA_VERSION': '51.0a2',
                    'LATEST_THUNDERBIRD_NIGHTLY_VERSION': '52.0a1',
                })
            else:
                raise NotImplementedError(url)

        rget.side_effect = mocked_get

        # Check what's set up in the fixture
        rows = execute_query_fetchall(
            self.conn, 'select product_name, version_string, featured_version '
            'from product_versions order by version_string')
        assert sorted(rows) == [
            ('Firefox', '15.0a1', True),
            ('Firefox', '24.5.0', True),
            ('Firefox', '49.0.1', False),
            ('Firefox', '50.0b', False),
            ('Firefox', '51.0a2', False),
            ('Firefox', '52.0a1', False),
        ]

        # This is necessary so we get a new cursor when we do other
        # selects after the crontabber app has run.
        self.conn.commit()

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['featured-versions-automatic']
            assert not information['featured-versions-automatic']['last_error']
            assert information['featured-versions-automatic']['last_success']

            config.logger.info.assert_called_with(
                'Set featured versions for Thunderbird to: '
                '45.4.0, 50.0b1, 52.0a1')

        rows = execute_query_fetchall(
            self.conn, 'select product_name, version_string, featured_version '
            'from product_versions')
        eq_(
            sorted(rows),
            [
                ('Firefox', '15.0a1', False),
                ('Firefox', '24.5.0', False),
                ('Firefox', '49.0.1', True),
                ('Firefox', '50.0b', True),
                # Note that the 'Aurora' branch is still mentioned but
                # note that it's NOT featured (hence 'False').
                ('Firefox', '51.0a2', False),
                ('Firefox', '52.0a1', True),
            ])
Пример #28
0
    def test_scrape_json_releases(self):
        @responsify
        def mocked_get(url, today=None, timeout=None):
            if today is None:
                today = utc_now()
            html_wrap = "<html><body>\n%s\n</body></html>"
            if url.endswith('/firefox/'):
                return html_wrap % """
                <a href="candidates/">candidates</a>
                <a href="nightly/">nightly</a>
                """
            if url.endswith('/firefox/candidates/'):
                return html_wrap % """
                <a href="28.0-candidates/">28.0-candidiates</a>
                <a href="10.0b4-candidates/">10.0b4-candidiates</a>
                <a href="None-candidates/">None-candidiates</a>
                """
            if url.endswith('-candidates/'):
                return html_wrap % """
                <a href="build1/">build1</a>
                """
            if url.endswith('/build1/'):
                return html_wrap % """
                <a href="linux-i686/">linux-i686</a>
                """
            if url.endswith('/firefox/candidates/28.0-candidates/'
                            'build1/linux-i686/en-US/'):
                return html_wrap % """
                    <a href="firefox-28.0.json">firefox-28.0.json</a>
                """
            if url.endswith('/firefox/candidates/10.0b4-candidates/'
                            'build1/linux-i686/en-US/'):
                return html_wrap % """
                    <a href="firefox-10.0b4.json">firefox-10.0b4.json</a>
                    <a href="firefox-10.0b4.en-US.linux-i686.mozinfo.json">
                     firefox-10.0b4.en-US.linux-i686.mozinfo.json</a>
                    <a href="JUNK.json">
                     JUNK.json</a>
                """
            if url.endswith('/firefox/candidates/None-candidates/'
                            'build1/linux-i686/en-US/'):
                return html_wrap % """
                    <a href="None.json">None.json</a>
                """
            if 'None.json' in url:
                return """ """
            if 'firefox-28.0.json' in url:
                return """
                {
                    "buildid": "20140113161827",
                    "moz_app_maxversion": "28.0.*",
                    "moz_app_name": "firefox",
                    "moz_app_vendor": "Mozilla",
                    "moz_app_version": "28.0",
                    "moz_pkg_platform": "linux-i686",
                    "moz_source_repo":
                        "http://hg.mozilla.org/releases/mozilla-release",
                    "moz_update_channel": "release"
                }
                """
            if 'firefox-10.0b4.json' in url:
                return """
                {
                    "buildid": "20140113161826",
                    "moz_app_maxversion": "10.0.*",
                    "moz_app_name": "firefox",
                    "moz_app_vendor": "Mozilla",
                    "moz_app_version": "27.0",
                    "moz_pkg_platform": "linux-i686",
                    "moz_source_repo":
                        "http://hg.mozilla.org/releases/mozilla-beta",
                    "moz_update_channel": "beta"
                }
                """
            # Ignore unrecognized JSON files, see bug 1065071
            if 'JUNK.json' in url:
                return """
                {
                    "something": "unexpected",
                    "nothing": "else"
                }
                """
            # Nightly tests for nightly and aurora builds
            if url.endswith('/firefox/nightly/'):
                return html_wrap % """
                    <a href="2014/">2014</a>
                """
            if url.endswith(today.strftime('/firefox/nightly/%Y/%m/')):
                return html_wrap % """
                    <a href="%s-03-02-03-mozilla-central/">txt</a>
                    <a href="%s-03-02-04-mozilla-central/">txt</a>
                """ % (today.strftime('%Y-%m-%d'), today.strftime('%Y-%m-%d'))
            if url.endswith(
                    today.strftime('/firefox/nightly/%Y/%m/'
                                   '%Y-%m-%d-03-02-03-mozilla-central/')):
                return html_wrap % """
                    <a href="firefox-30.0a1.en-US.linux-i686.json">txt</a>
                """
            if url.endswith(
                    today.strftime(
                        '/firefox/nightly/%Y/%m/%Y-%m-%d-03-02-04-mozilla-central/'
                    )):
                return html_wrap % """
                    <a href="firefox-30.0a2.en-US.linux-i686.json">txt</a>
                """
            if url.endswith(
                    today.strftime(
                        '/firefox/nightly/%Y/%m/%Y-%m-%d-03-02-04-mozilla-central/'
                        'firefox-30.0a2.en-US.linux-i686.json')):
                return """
                    {

                        "as": "$(CC)",
                        "buildid": "20140205030204",
                        "cc": "/usr/bin/ccache stuff",
                        "cxx": "/usr/bin/ccache stuff",
                        "host_alias": "x86_64-unknown-linux-gnu",
                        "host_cpu": "x86_64",
                        "host_os": "linux-gnu",
                        "host_vendor": "unknown",
                        "ld": "ld",
                        "moz_app_id": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
                        "moz_app_maxversion": "30.0a2",
                        "moz_app_name": "firefox",
                        "moz_app_vendor": "Mozilla",
                        "moz_app_version": "30.0a2",
                        "moz_pkg_platform": "linux-i686",
                        "moz_source_repo":
                            "https://hg.mozilla.org/mozilla-central",
                        "moz_source_stamp": "1f170f9fead0",
                        "moz_update_channel": "nightly",
                        "target_alias": "i686-pc-linux",
                        "target_cpu": "i686",
                        "target_os": "linux-gnu",
                        "target_vendor": "pc"

                    }
                """
            if url.endswith(
                    today.strftime(
                        '/firefox/nightly/%Y/%m/%Y-%m-%d-03-02-03-mozilla-central/'
                        'firefox-30.0a1.en-US.linux-i686.json')):
                return """
                    {

                        "as": "$(CC)",
                        "buildid": "20140205030203",
                        "cc": "/usr/bin/ccache ",
                        "cxx": "/usr/bin/ccache stuff",
                        "host_alias": "x86_64-unknown-linux-gnu",
                        "host_cpu": "x86_64",
                        "host_os": "linux-gnu",
                        "host_vendor": "unknown",
                        "ld": "ld",
                        "moz_app_id": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
                        "moz_app_maxversion": "30.0a1",
                        "moz_app_name": "firefox",
                        "moz_app_vendor": "Mozilla",
                        "moz_app_version": "30.0a1",
                        "moz_pkg_platform": "linux-i686",
                        "moz_source_repo":
                            "https://hg.mozilla.org/mozilla-central",
                        "moz_source_stamp": "1f170f9fead0",
                        "moz_update_channel": "nightly",
                        "target_alias": "i686-pc-linux",
                        "target_cpu": "i686",
                        "target_os": "linux-gnu",
                        "target_vendor": "pc"

                    }
                """
            raise NotImplementedError(url)

        self.mocked_session().get.side_effect = mocked_get

        config_manager = self._setup_config_manager_firefox()
        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['ftpscraper']
            assert not information['ftpscraper']['last_error']
            assert information['ftpscraper']['last_success']

            config.logger.warning.assert_any_call(
                'Unable to JSON parse content %r', ' ', exc_info=True)

            base_url = config.crontabber['class-FTPScraperCronApp'].base_url
            config.logger.warning.assert_any_call(
                'warning, unsupported JSON file: %s',
                base_url + 'firefox/candidates/'
                '10.0b4-candidates/build1/linux-i686/en-US/JUNK.json')

        cursor = self.conn.cursor()
        columns = 'product_name', 'build_id', 'build_type'
        cursor.execute("""
            select %s
            from releases_raw
        """ % ','.join(columns))
        builds = [dict(zip(columns, row)) for row in cursor.fetchall()]
        build_ids = dict((str(x['build_id']), x) for x in builds)

        assert '20140113161827' in build_ids
        assert '20140113161826' in build_ids
        assert '20140205030203' in build_ids
        assert len(build_ids) == 4
        expected = [{
            'build_id': 20140113161827,
            'product_name': 'firefox',
            'build_type': 'release'
        }, {
            'build_id': 20140113161827,
            'product_name': 'firefox',
            'build_type': 'beta'
        }, {
            'build_id': 20140113161826,
            'product_name': 'firefox',
            'build_type': 'beta'
        }, {
            'build_id': 20140205030203,
            'product_name': 'firefox',
            'build_type': 'nightly'
        }, {
            'build_id': 20140205030204,
            'product_name': 'firefox',
            'build_type': 'aurora'
        }]
        assert builds == expected
Пример #29
0
    def test_email_after_delay(self, exacttarget_mock):
        """Test that a user will receive an email if he or she sends us a new
        crash report after the delay is passed (but not before). """
        config_manager = self._setup_config_manager(
            delay_between_emails=1, restrict_products=['EarthRaccoon'])
        email = '*****@*****.**'
        list_service_mock = exacttarget_mock.return_value.list.return_value
        list_service_mock.get_subscriber.return_value = {'token': email}
        trigger_send_mock = exacttarget_mock.return_value.trigger_send
        tomorrow = utc_now() + datetime.timedelta(days=1, hours=2)
        twohourslater = utc_now() + datetime.timedelta(hours=2)

        storage_config_manager = self._setup_storage_config()
        with storage_config_manager.context() as storage_config:
            storage = ElasticSearchCrashStorage(storage_config)

        with config_manager.context() as config:
            # 1. Send an email to the user and update emailing data
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert not information['automatic-emails']['last_error']
            assert information['automatic-emails']['last_success']

            exacttarget_mock.return_value.trigger_send.assert_called_with(
                'socorro_dev_test', {
                    'EMAIL_ADDRESS_': email,
                    'EMAIL_FORMAT_': 'H',
                    'TOKEN': email
                })
            eq_(trigger_send_mock.call_count, 1)

            # 2. Test that before 'delay' is passed user doesn't receive
            # another email

            # Insert a new crash report with the same email address
            storage.save_processed({
                'uuid':
                '50',
                'email':
                email,
                'product':
                'EarthRaccoon',
                'version':
                '20.0',
                'release_channel':
                'Release',
                'date_processed':
                utc_now() + datetime.timedelta(hours=1)
            })
            storage.es.refresh()

            # Run crontabber with time pushed by two hours
            with mock.patch('crontabber.app.utc_now') as cronutc_mock:
                with mock.patch('crontabber.base.utc_now') as baseutc_mock:
                    cronutc_mock.return_value = twohourslater
                    baseutc_mock.return_value = twohourslater
                    tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert not information['automatic-emails']['last_error']
            assert information['automatic-emails']['last_success']

            # No new email was sent
            eq_(trigger_send_mock.call_count, 1)

            # 3. Verify that, after 'delay' is passed, a new email is sent
            # to our user

            # Insert a new crash report with the same email address
            storage.save_processed({
                'uuid':
                '51',
                'email':
                email,
                'product':
                'EarthRaccoon',
                'version':
                '20.0',
                'release_channel':
                'Release',
                'date_processed':
                utc_now() + datetime.timedelta(days=1)
            })
            storage.es.refresh()

            # Run crontabber with time pushed by a day
            with mock.patch('crontabber.app.utc_now') as cronutc_mock:
                with mock.patch('crontabber.base.utc_now') as baseutc_mock:
                    cronutc_mock.return_value = tomorrow
                    baseutc_mock.return_value = tomorrow
                    tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert not information['automatic-emails']['last_error']
            assert information['automatic-emails']['last_success']

            # A new email was sent
            eq_(trigger_send_mock.call_count, 2)
Пример #30
0
    def test_email_cannot_be_sent_twice(self, exacttarget_mock):
        config_manager = self._setup_config_manager(
            restrict_products=['NightlyTrain'])
        et_mock = exacttarget_mock.return_value

        # Prepare failures
        _failures = []
        _email_sent = []

        class SomeRandomError(Exception):
            pass

        def trigger_send(template, fields):
            email = fields['EMAIL_ADDRESS_']
            if email == '*****@*****.**' and email not in _failures:
                _failures.append(email)
                raise SomeRandomError('This is an error. ')
            else:
                _email_sent.append(email)

        et_mock.trigger_send = trigger_send

        with config_manager.context() as config:
            tab = CronTabber(config)
            tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert information['automatic-emails']['last_error']
            eq_(information['automatic-emails']['last_error']['type'],
                str(SomeRandomError))

            # Verify that user's data was updated, but not all of it
            eq_(_email_sent, ['*****@*****.**', '*****@*****.**'])
            emails_list = ('*****@*****.**', '*****@*****.**', '*****@*****.**',
                           '*****@*****.**', '*****@*****.**')

            conf = config.crontabber['class-AutomaticEmailsCronApp']
            es = SuperS().es(
                urls=conf.elasticsearch.elasticsearch_urls,
                timeout=conf.elasticsearch.elasticsearch_timeout,
            )
            search = es.indexes(conf.elasticsearch.elasticsearch_emails_index)
            search = search.doctypes('emails')
            es.get_es().refresh()

            search = search.filter(_id__in=emails_list)
            res = search.execute()
            eq_(res.count, 2)

            now = utc_now()
            for row in res.results:
                assert row['_id'] in ('*****@*****.**', '*****@*****.**')
                date = string_to_datetime(row['_source']['last_sending'])
                eq_(date.year, now.year)
                eq_(date.month, now.month)
                eq_(date.day, now.day)

            # Run crontabber again and verify that all users are updated,
            # and emails are not sent twice
            state = tab.job_state_database['automatic-emails']
            self._wind_clock(state, hours=1)
            tab.job_state_database['automatic-emails'] = state

            tab.run_all()

            information = self._load_structure()
            assert information['automatic-emails']
            assert not information['automatic-emails']['last_error']
            assert information['automatic-emails']['last_success']

            # Verify that users were not sent an email twice
            eq_(_email_sent, [
                '*****@*****.**', '*****@*****.**', '*****@*****.**',
                '*****@*****.**', '*****@*****.**'
            ])