Exemple #1
0
def _needs_upgrade():
    version_configured = options.get('sentry:version-configured')
    if not version_configured:
        # If we were never previously upgraded (being a new install)
        # we want to force an upgrade, even if the values are set.
        return True

    smtp_disabled = not is_smtp_enabled()

    # Check all required options to see if they've been set
    for key in options.filter(flag=options.FLAG_REQUIRED):
        # ignore required flags which can be empty
        if key.flags & options.FLAG_ALLOW_EMPTY:
            continue
        # Ignore mail.* keys if smtp is disabled
        if smtp_disabled and key.name[:5] == 'mail.':
            continue
        if not options.isset(key.name):
            return True

    if version_configured != sentry.get_version():
        # Everything looks good, but version changed, so let's bump it
        options.set('sentry:version-configured', sentry.get_version())

    return False
Exemple #2
0
def get_install_id():
    from sentry import options
    install_id = options.get('sentry:install-id')
    if not install_id:
        install_id = sha1(uuid4().bytes).hexdigest()
        options.set('sentry:install-id', install_id)
    return install_id
Exemple #3
0
    def test_registration_requires_subscribe_choice_with_newsletter(self):
        options.set('auth.allow-registration', True)
        with self.feature('auth:register'):
            resp = self.client.post(
                self.path, {
                    'username': '******',
                    'password': '******',
                    'name': 'Foo Bar',
                    'op': 'register',
                }
            )
        assert resp.status_code == 200

        with self.feature('auth:register'):
            resp = self.client.post(
                self.path, {
                    'username': '******',
                    'password': '******',
                    'name': 'Foo Bar',
                    'op': 'register',
                    'subscribe': '0',
                }
            )
        assert resp.status_code == 302

        user = User.objects.get(username='******')
        assert user.email == '*****@*****.**'
        assert user.check_password('foobar')
        assert user.name == 'Foo Bar'
        assert not OrganizationMember.objects.filter(
            user=user,
        ).exists()

        assert newsletter.get_subscriptions(user) == {'subscriptions': []}
    def test_snuba_environment(self):
        options.set('snuba.events-queries.enabled', True)
        url = u'/api/0/issues/{}/events/latest/'.format(self.group.id)
        response = self.client.get(url, format='json', data={'environment': ['production']})

        assert response.status_code == 200
        assert response.data['id'] == six.text_type(self.event2.event_id)
Exemple #5
0
    def put(self, request):
        # TODO(dcramer): this should validate options before saving them
        for k, v in six.iteritems(request.DATA):
            if v and isinstance(v, six.string_types):
                v = v.strip()
            try:
                option = options.lookup_key(k)
            except options.UnknownOption:
                # TODO(dcramer): unify API errors
                return Response({
                    'error': 'unknown_option',
                    'errorDetail': {
                        'option': k,
                    },
                }, status=400)

            try:
                if not (option.flags & options.FLAG_ALLOW_EMPTY) and not v:
                    options.delete(k)
                else:
                    options.set(k, v)
            except TypeError as e:
                return Response({
                    'error': 'invalid_type',
                    'errorDetail': {
                        'option': k,
                        'message': six.text_type(e),
                    },
                }, status=400)
        # TODO(dcramer): this has nothing to do with configuring options and
        # should not be set here
        options.set('sentry:version-configured', sentry.get_version())
        return Response(status=200)
Exemple #6
0
    def test_chunk_parameters(self):
        response = self.client.get(
            self.url,
            HTTP_AUTHORIZATION=u'Bearer {}'.format(self.token.token),
            format='json'
        )

        endpoint = options.get('system.upload-url-prefix')
        # We fallback to default system url if config is not set
        if len(endpoint) == 0:
            endpoint = options.get('system.url-prefix')

        assert response.status_code == 200, response.content
        assert response.data['chunkSize'] == CHUNK_UPLOAD_BLOB_SIZE
        assert response.data['chunksPerRequest'] == MAX_CHUNKS_PER_REQUEST
        assert response.data['maxRequestSize'] == MAX_REQUEST_SIZE
        assert response.data['maxFileSize'] == options.get('system.maximum-file-size')
        assert response.data['concurrency'] == MAX_CONCURRENCY
        assert response.data['hashAlgorithm'] == HASH_ALGORITHM
        assert response.data['url'] == options.get('system.url-prefix') + self.url

        options.set('system.upload-url-prefix', 'test')
        response = self.client.get(
            self.url,
            HTTP_AUTHORIZATION=u'Bearer {}'.format(self.token.token),
            format='json'
        )

        assert response.data['url'] == options.get('system.upload-url-prefix') + self.url
Exemple #7
0
def sync_docs():
    from sentry import http, options

    session = http.build_session()

    logger.info('Syncing documentation (platform index)')
    data = session.get(BASE_URL.format('_index.json')).json()
    platform_list = []
    for platform_id, integrations in data['platforms'].iteritems():
        platform_list.append({
            'id': platform_id,
            'name': integrations['_self']['name'],
            'integrations': [
                {
                    'id': get_integration_id(platform_id, i_id),
                    'name': i_data['name'],
                    'type': i_data['type'],
                    'link': i_data['doc_link'],
                } for i_id, i_data in sorted(
                    integrations.iteritems(),
                    key=lambda x: x[1]['name']
                )
            ],
        })

    platform_list.sort(key=lambda x: x['name'])

    options.set('sentry:docs', {'platforms': platform_list})

    for platform_id, platform_data in data['platforms'].iteritems():
        for integration_id, integration in platform_data.iteritems():
            logger.info('Syncing documentation for %s integration', integration_id)
            sync_integration(platform_id, integration_id, integration['details'])
Exemple #8
0
    def test_update_is_not_available(self):
        options.set('sentry:latest_version', '5.5.1')

        with mock.patch('sentry.get_version') as get_version:
            get_version.return_value = '5.5.0'
            resp = self.client.get(self.path)
            assert self.UPDATE_MESSAGE in resp.content
Exemple #9
0
 def put(self, request):
     # TODO(dcramer): this should validate options before saving them
     for k, v in request.DATA.iteritems():
         if v and isinstance(v, basestring):
             v = v.strip()
         try:
             if not v:
                 options.delete(k)
             else:
                 options.set(k, v)
         except options.UnknownOption:
             # TODO(dcramer): unify API errors
             return Response({
                 'error': 'unknown_option',
                 'errorDetail': {
                     'option': k,
                 },
             }, status=400)
         except TypeError as e:
             return Response({
                 'error': 'invalid_type',
                 'errorDetail': {
                     'option': k,
                     'message': unicode(e),
                 },
             }, status=400)
     # TODO(dcramer): this has nothing to do with configuring options and
     # should not be set here
     options.set('sentry:version-configured', sentry.get_version())
     return Response(status=200)
Exemple #10
0
def set(option, value):
    "Set a configuration option to a new value."
    from sentry import options
    from sentry.options.manager import UnknownOption
    try:
        options.set(option, value)
    except UnknownOption:
        raise click.ClickException('unknown option: %s' % option)
Exemple #11
0
    def test_set_sentry_version_old(self, get_version):
        options.set(self.KEY, self.NEW)

        get_version.return_value = self.CURRENT

        set_sentry_version(latest=self.OLD)

        self.assertEqual(options.get(key=self.KEY), self.NEW)
Exemple #12
0
def default_issue_plugin_config(plugin, project, form_data):
    plugin_key = plugin.get_conf_key()
    for field, value in six.iteritems(form_data):
        key = '%s:%s' % (plugin_key, field)
        if project:
            ProjectOption.objects.set_value(project, key, value)
        else:
            options.set(key, value)
Exemple #13
0
 def put(self, request):
     try:
         for k, v in request.DATA.iteritems():
             options.set(k, v)
     except Exception as e:
         return Response(unicode(e), status=400)
     options.set('sentry:version-configured', sentry.get_version())
     return Response(status=200)
    def test_register_renders_correct_template(self):
        options.set('auth.allow-registration', True)
        register_path = reverse('sentry-register')
        resp = self.client.get(register_path)

        assert resp.status_code == 200
        assert resp.context['op'] == 'register'
        self.assertTemplateUsed('sentry/login.html')
Exemple #15
0
def default_plugin_config(plugin, project, request):
    plugin_key = plugin.get_conf_key()
    form_class = plugin.get_conf_form(project)
    template = plugin.get_conf_template(project)

    if form_class is None:
        return HttpResponseRedirect(reverse(
            'sentry-manage-project', args=[project.organization.slug, project.slug]))

    test_results = None

    form = form_class(
        request.POST if request.POST.get('plugin') == plugin.slug else None,
        initial=plugin.get_conf_options(project),
        prefix=plugin_key,
    )
    if form.is_valid():
        if 'action_test' in request.POST and plugin.is_testable():
            try:
                test_results = plugin.test_configuration(project)
            except Exception as exc:
                if hasattr(exc, 'read') and callable(exc.read):
                    test_results = '%s\n%s' % (exc, exc.read())
                else:
                    logging.exception('Plugin(%s) raised an error during test',
                                      plugin_key)
                    test_results = 'There was an internal error with the Plugin'
            if not test_results:
                test_results = 'No errors returned'
        else:
            for field, value in form.cleaned_data.iteritems():
                key = '%s:%s' % (plugin_key, field)
                if project:
                    ProjectOption.objects.set_value(project, key, value)
                else:
                    options.set(key, value)

            messages.add_message(
                request, messages.SUCCESS,
                _('Your settings were saved successfully.'))
            return HttpResponseRedirect(request.path)

    # TODO(mattrobenolt): Reliably determine if a plugin is configured
    # if hasattr(plugin, 'is_configured'):
    #     is_configured = plugin.is_configured(project)
    # else:
    #     is_configured = True
    is_configured = True

    return mark_safe(render_to_string(template, {
        'form': form,
        'request': request,
        'plugin': plugin,
        'plugin_description': plugin.get_description() or '',
        'plugin_test_results': test_results,
        'plugin_is_configured': is_configured,
    }, context_instance=RequestContext(request)))
Exemple #16
0
def set(option, value):
    "Set a configuration option to a new value."
    from sentry import options
    from sentry.options.manager import UnknownOption

    try:
        options.set(option, value)
    except UnknownOption:
        raise click.ClickException("unknown option: %s" % option)
    except TypeError as e:
        raise click.ClickException(unicode(e))
Exemple #17
0
def set_sentry_version(latest=None, **kwargs):
    import sentry
    current = sentry.get_version()

    version = options.get('sentry:latest_version')

    for ver in (current, version):
        if Version(ver) >= Version(latest):
            return

    options.set('sentry:latest_version', (latest or current))
Exemple #18
0
def send_beacon():
    """
    Send a Beacon to a remote server operated by the Sentry team.

    See the documentation for more details.
    """
    from sentry import options
    from sentry.models import Organization, Project, Team, User

    if not settings.SENTRY_BEACON:
        logger.info('Not sending beacon (disabled)')
        return

    # TODO(dcramer): move version code off of PyPi and into beacon
    install_id = options.get('sentry:install-id')
    if not install_id:
        logger.info('Generated installation ID: %s', install_id)
        install_id = sha1(uuid4().hex).hexdigest()
        options.set('sentry:install-id', install_id)

    internal_project_ids = filter(bool, [
        settings.SENTRY_PROJECT, settings.SENTRY_FRONTEND_PROJECT,
    ])
    platform_list = list(set(Project.objects.exclude(
        id__in=internal_project_ids,
    ).values_list('platform', flat=True)))

    payload = {
        'install_id': install_id,
        'version': sentry.get_version(),
        'admin_email': settings.SENTRY_ADMIN_EMAIL,
        'data': {
            # TODO(dcramer): we'd also like to get an idea about the throughput
            # of the system (i.e. events in 24h)
            'platforms': platform_list,
            'users': User.objects.count(),
            'projects': Project.objects.count(),
            'teams': Team.objects.count(),
            'organizations': Organization.objects.count(),
        }
    }

    # TODO(dcramer): relay the response 'notices' as admin broadcasts
    try:
        request = safe_urlopen(BEACON_URL, json=payload, timeout=5)
        response = safe_urlread(request)
    except Exception:
        logger.warning('Failed sending beacon', exc_info=True)
        return

    data = json.loads(response)
    if 'version' in data:
        options.set('sentry:latest_version', data['version']['stable'])
Exemple #19
0
def send_beacon():
    """
    Send a Beacon to a remote server operated by the Sentry team.

    See the documentation for more details.
    """
    from sentry import options
    from sentry.models import Organization, Project, Team, User

    if not settings.SENTRY_BEACON:
        logger.info('Not sending beacon (disabled)')
        return

    install_id = options.get('sentry:install-id')
    if not install_id:
        logger.info('Generated installation ID: %s', install_id)
        install_id = sha1(uuid4().hex).hexdigest()
        options.set('sentry:install-id', install_id)

    end = timezone.now()
    events_24h = tsdb.get_sums(
        model=tsdb.models.internal,
        keys=['events.total'],
        start=end - timedelta(hours=24),
        end=end,
    )['events.total']

    payload = {
        'install_id': install_id,
        'version': sentry.get_version(),
        'admin_email': settings.SENTRY_ADMIN_EMAIL,
        'data': {
            # TODO(dcramer): we'd also like to get an idea about the throughput
            # of the system (i.e. events in 24h)
            'users': User.objects.count(),
            'projects': Project.objects.count(),
            'teams': Team.objects.count(),
            'organizations': Organization.objects.count(),
            'events.24h': events_24h,
        }
    }

    # TODO(dcramer): relay the response 'notices' as admin broadcasts
    try:
        request = safe_urlopen(BEACON_URL, json=payload, timeout=5)
        response = safe_urlread(request)
    except Exception:
        logger.warning('Failed sending beacon', exc_info=True)
        return

    data = json.loads(response)
    if 'version' in data:
        options.set('sentry:latest_version', data['version']['stable'])
Exemple #20
0
    def test_optimizer_enabled(self):
        prev_optimizer_enabled = options.get('snuba.search.pre-snuba-candidates-optimizer')
        options.set('snuba.search.pre-snuba-candidates-optimizer', True)

        try:
            results = self.backend.query(
                [self.project],
                environments=[self.environments['production']],
                tags={'server': 'example.com'})
            assert set(results) == set([self.group1])
        finally:
            options.set('snuba.search.pre-snuba-candidates-optimizer', prev_optimizer_enabled)
Exemple #21
0
def sync_integration(platform_id, integration_id, path):
    from sentry import http, options

    session = http.build_session()

    data = session.get(BASE_URL.format(path)).json()

    key = get_integration_id(platform_id, integration_id)
    options.set('sentry:docs:{}'.format(key), {
        'id': key,
        'name': data['name'],
        'html': data['body'],
        'link': data['doc_link'],
    })
Exemple #22
0
    def test_closed(self):
        project = self.project  # force creation

        url = '/extensions/github/webhook/'

        secret = 'b3002c3e321d4b7880360d397db2ccfd'

        options.set('github-app.webhook-secret', secret)

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            provider='github',
            external_id='12345',
            name='octocat',
            metadata={'access_token': '1234', 'expires_at': future_expires.isoformat()}
        )
        integration.add_organization(project.organization.id)

        repo = Repository.objects.create(
            organization_id=project.organization.id,
            external_id='35129377',
            provider='integrations:github',
            name='baxterthehacker/public-repo',
        )

        response = self.client.post(
            path=url,
            data=PULL_REQUEST_CLOSED_EVENT_EXAMPLE,
            content_type='application/json',
            HTTP_X_GITHUB_EVENT='pull_request',
            HTTP_X_HUB_SIGNATURE='sha1=49db856f5658b365b73a2fa73a7cffa543f4d3af',
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4())
        )

        assert response.status_code == 204

        prs = PullRequest.objects.filter(
            repository_id=repo.id,
            organization_id=project.organization.id,
        )

        assert len(prs) == 1

        pr = prs[0]

        assert pr.key == '1'
        assert pr.message == u'new closed body'
        assert pr.title == u'new closed title'
        assert pr.author.name == u'baxterthehacker'
        assert pr.merge_commit_sha == '0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c'
 def test_registration_valid(self):
     options.set('auth.allow-registration', True)
     with self.feature('auth:register'):
         resp = self.client.post(self.path, {
             'username': '******',
             'password': '******',
             'name': 'Foo Bar',
             'op': 'register',
         })
     assert resp.status_code == 302
     user = User.objects.get(username='******')
     assert user.email == '*****@*****.**'
     assert user.check_password('foobar')
     assert user.name == 'Foo Bar'
Exemple #24
0
def set_sentry_version(latest=None, **kwargs):
    import sentry

    current = sentry.VERSION

    version = options.get("sentry:latest_version")

    for ver in (current, version):
        if Version(ver) >= Version(latest):
            latest = ver

    if latest == version:
        return

    options.set("sentry:latest_version", (latest or current))
Exemple #25
0
 def put(self, request):
     # TODO(dcramer): this should validate options before saving them
     for k, v in request.DATA.iteritems():
         try:
             options.set(k, v)
         except options.UnknownOption:
             # TODO(dcramer): unify API errors
             return Response({
                 'error': 'unknown_option',
                 'errorDetail': {
                     'option': k,
                 },
             }, status=400)
     options.set('sentry:version-configured', sentry.get_version())
     return Response(status=200)
Exemple #26
0
    def test_opened(self):
        project = self.project  # force creation

        url = '/extensions/github/webhook/'

        secret = 'b3002c3e321d4b7880360d397db2ccfd'
        options.set('github-app.webhook-secret', secret)

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            provider='github',
            external_id='12345',
            name='octocat',
            metadata={'access_token': '1234', 'expires_at': future_expires.isoformat()}
        )
        integration.add_organization(project.organization.id)

        repo = Repository.objects.create(
            organization_id=project.organization.id,
            external_id='35129377',
            provider='integrations:github',
            name='baxterthehacker/public-repo',
        )

        response = self.client.post(
            path=url,
            data=PULL_REQUEST_OPENED_EVENT_EXAMPLE,
            content_type='application/json',
            HTTP_X_GITHUB_EVENT='pull_request',
            HTTP_X_HUB_SIGNATURE='sha1=5b82806b2c96eb546b2898f0dca98cc326d7d9ae',
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4())
        )

        assert response.status_code == 204

        prs = PullRequest.objects.filter(
            repository_id=repo.id,
            organization_id=project.organization.id,
        )

        assert len(prs) == 1

        pr = prs[0]

        assert pr.key == '1'
        assert pr.message == u'This is a pretty simple change that we need to pull into master.'
        assert pr.title == u'Update the README with new information'
        assert pr.author.name == u'baxterthehacker'
Exemple #27
0
    def test_edited(self):
        project = self.project  # force creation

        url = '/extensions/github/webhook/'

        secret = 'b3002c3e321d4b7880360d397db2ccfd'

        options.set('github-app.webhook-secret', secret)

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            provider='github',
            external_id='12345',
            name='octocat',
            metadata={'access_token': '1234', 'expires_at': future_expires.isoformat()}
        )
        integration.add_organization(project.organization.id)

        repo = Repository.objects.create(
            organization_id=project.organization.id,
            external_id='35129377',
            provider='integrations:github',
            name='baxterthehacker/public-repo',
        )

        pr = PullRequest.objects.create(
            key='1',
            repository_id=repo.id,
            organization_id=project.organization.id,
        )

        response = self.client.post(
            path=url,
            data=PULL_REQUEST_EDITED_EVENT_EXAMPLE,
            content_type='application/json',
            HTTP_X_GITHUB_EVENT='pull_request',
            HTTP_X_HUB_SIGNATURE='sha1=42e0d5cf6bd7521fcc7576f7e41c31621c88091e',
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4())
        )

        assert response.status_code == 204

        pr = PullRequest.objects.get(id=pr.id)

        assert pr.key == '1'
        assert pr.message == u'new edited body'
        assert pr.title == u'new edited title'
        assert pr.author.name == u'baxterthehacker'
Exemple #28
0
    def test_simple(self, safe_urlread, safe_urlopen,
                    mock_get_all_package_versions):
        mock_get_all_package_versions.return_value = {'foo': '1.0'}
        safe_urlread.return_value = json.dumps({
            'notices': [],
            'version': {'stable': '1.0.0'},
        })

        assert options.set('system.admin-email', '*****@*****.**')
        send_beacon()

        install_id = options.get('sentry:install-id')
        assert install_id and len(install_id) == 40

        safe_urlopen.assert_called_once_with(BEACON_URL, json={
            'install_id': install_id,
            'version': sentry.get_version(),
            'data': {
                'organizations': 1,
                'users': 0,
                'projects': 1,
                'teams': 1,
                'events.24h': 0,
            },
            'admin_email': '*****@*****.**',
            'packages': mock_get_all_package_versions.return_value,
        }, timeout=5)
        safe_urlread.assert_called_once_with(safe_urlopen.return_value)

        assert options.get('sentry:latest_version') == '1.0.0'
Exemple #29
0
def _needs_upgrade():
    version_configured = options.get('sentry:version-configured')
    if not version_configured:
        # If we were never previously upgraded (being a new install)
        # we want to force an upgrade, even if the values are set.
        return True

    # Check all required options to see if they've been set
    for key in options.filter(flag=options.FLAG_REQUIRED):
        if not options.get(key.name):
            return True

    if version_configured != sentry.get_version():
        # Everything looks good, but version changed, so let's bump it
        options.set('sentry:version-configured', sentry.get_version())

    return False
Exemple #30
0
def set(key, value, secret):
    "Set a configuration option to a new value."
    from sentry import options
    from sentry.options.manager import UnknownOption

    if value is None:
        if secret:
            value = click.prompt('(hidden) Value', hide_input=True)
        else:
            value = click.prompt('Value')

    try:
        options.set(key, value)
    except UnknownOption:
        raise click.ClickException('unknown option: %s' % key)
    except TypeError as e:
        raise click.ClickException(six.text_type(e))
Exemple #31
0
    def test_invalid_signature_event(self):

        url = "/extensions/github/webhook/"

        secret = "2d7565c3537847b789d6995dca8d9f84"

        options.set("github-app.webhook-secret", secret)

        response = self.client.post(
            path=url,
            data=PUSH_EVENT_EXAMPLE_INSTALLATION,
            content_type="application/json",
            HTTP_X_GITHUB_EVENT="push",
            HTTP_X_HUB_SIGNATURE=
            "sha1=33521abeaaf9a57c2abf486e0ccd54d23cf36fec",
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
        )

        assert response.status_code == 401
Exemple #32
0
def test_move_to_symbolicate_event_old(
    default_project, mock_process_event, mock_save_event, mock_symbolicate_event, register_plugin
):
    # Temporarily test old behavior
    register_plugin(BasicPreprocessorPlugin)
    data = {
        "project": default_project.id,
        "platform": "native",
        "logentry": {"formatted": "test"},
        "event_id": EVENT_ID,
        "extra": {"foo": "bar"},
    }

    options.set("sentry:preprocess-use-new-behavior", False)
    preprocess_event(data=data)

    assert mock_symbolicate_event.delay.call_count == 0
    assert mock_process_event.delay.call_count == 1
    assert mock_save_event.delay.call_count == 0
Exemple #33
0
    def test_pre_and_post_filtering(self):
        prev_max_pre = options.get('snuba.search.max-pre-snuba-candidates')
        options.set('snuba.search.max-pre-snuba-candidates', 1)
        try:
            # normal queries work as expected
            results = self.backend.query(self.project, query='foo')
            assert set(results) == set([self.group1])
            results = self.backend.query(self.project, query='bar')
            assert set(results) == set([self.group2])

            # no candidate matches in Sentry, immediately return empty paginator
            results = self.backend.query(self.project, query='NO MATCHES IN SENTRY')
            assert set(results) == set()

            # too many candidates, skip pre-filter, requires >1 postfilter queries
            results = self.backend.query(self.project)
            assert set(results) == set([self.group1, self.group2])
        finally:
            options.set('snuba.search.max-pre-snuba-candidates', prev_max_pre)
Exemple #34
0
    def test_registration_single_org(self):
        options.set("auth.allow-registration", True)
        with self.feature("auth:register"):
            resp = self.client.post(
                self.path,
                {
                    "username": "******",
                    "password": "******",
                    "name": "Foo Bar",
                    "op": "register",
                },
            )
        assert resp.status_code == 302, (resp.context["register_form"].errors
                                         if resp.status_code == 200 else None)
        user = User.objects.get(
            username="******")

        # User is part of the default org
        assert OrganizationMember.objects.filter(user=user).exists()
Exemple #35
0
    def test_snuba_no_prev(self):
        options.set('snuba.events-queries.enabled', True)
        url = reverse(
            'sentry-api-0-project-event-details',
            kwargs={
                'event_id': self.prev_event.event_id,
                'project_slug': self.prev_event.project.slug,
                'organization_slug': self.prev_event.project.organization.slug,
            }
        )
        response = self.client.get(url, format='json', data={
            'enable_snuba': '1'
        })

        assert response.status_code == 200, response.content
        assert response.data['id'] == six.text_type(self.prev_event.event_id)
        assert response.data['previousEventID'] is None
        assert response.data['nextEventID'] == self.cur_event.event_id
        assert response.data['groupID'] == six.text_type(self.prev_event.group.id)
Exemple #36
0
    def test_unregistered_event(self):
        project = self.project  # force creation
        url = u"/extensions/github/webhook/".format(project.organization.id)

        secret = "b3002c3e321d4b7880360d397db2ccfd"

        options.set("github-app.webhook-secret", secret)

        response = self.client.post(
            path=url,
            data=PUSH_EVENT_EXAMPLE_INSTALLATION,
            content_type="application/json",
            HTTP_X_GITHUB_EVENT="UnregisteredEvent",
            HTTP_X_HUB_SIGNATURE=
            "sha1=56a3df597e02adbc17fb617502c70e19d96a6136",
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
        )

        assert response.status_code == 204
Exemple #37
0
    def test_update_repo_name(self):
        project = self.project  # force creation
        url = "/extensions/github/webhook/"
        secret = "b3002c3e321d4b7880360d397db2ccfd"
        options.set("github-app.webhook-secret", secret)

        future_expires = datetime.now().replace(microsecond=0) + timedelta(
            minutes=5)
        integration = Integration.objects.create(
            provider="github",
            external_id="12345",
            name="octocat",
            metadata={
                "access_token": "1234",
                "expires_at": future_expires.isoformat()
            },
        )
        integration.add_organization(project.organization, self.user)

        repo_out_of_date_name = Repository.objects.create(
            organization_id=project.organization.id,
            external_id="35129377",
            provider="integrations:github",
            name="emmathehacker/public-repo",  # out of date
            url="https://github.com/baxterthehacker/public-repo",
            config={"name": "baxterthehacker/public-repo"},
        )

        response = self.client.post(
            path=url,
            data=PULL_REQUEST_OPENED_EVENT_EXAMPLE,
            content_type="application/json",
            HTTP_X_GITHUB_EVENT="pull_request",
            HTTP_X_HUB_SIGNATURE=
            "sha1=bc7ce12fc1058a35bf99355e6fc0e6da72c35de3",
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
        )

        assert response.status_code == 204

        # name has been updated
        repo_out_of_date_name.refresh_from_db()
        assert repo_out_of_date_name.name == "baxterthehacker/public-repo"
 def test_registration_valid(self):
     options.set('auth.allow-registration', True)
     with self.feature('auth:register'):
         resp = self.client.post(
             self.path, {
                 'username': '******',
                 'password': '******',
                 'name': 'Foo Bar',
                 'op': 'register',
             }
         )
     assert resp.status_code == 302, resp.context['register_form'].errors if resp.status_code == 200 else None
     user = User.objects.get(username='******')
     assert user.email == '*****@*****.**'
     assert user.check_password('foobar')
     assert user.name == 'Foo Bar'
     assert not OrganizationMember.objects.filter(
         user=user,
     ).exists()
Exemple #39
0
    def put(self, request):
        # TODO(dcramer): this should validate options before saving them
        for k, v in six.iteritems(request.data):
            if v and isinstance(v, six.string_types):
                v = v.strip()
            try:
                option = options.lookup_key(k)
            except options.UnknownOption:
                # TODO(dcramer): unify API errors
                return Response(
                    {
                        "error": "unknown_option",
                        "errorDetail": {
                            "option": k
                        }
                    },
                    status=400)

            try:
                if not (option.flags & options.FLAG_ALLOW_EMPTY) and not v:
                    options.delete(k)
                else:
                    options.set(k, v)
            except (TypeError, AssertionError) as e:
                # TODO(chadwhitacre): Use a custom exception for the
                # immutability case, especially since asserts disappear with
                # `python -O`.
                return Response(
                    {
                        "error": "invalid_type"
                        if type(e) is TypeError else "immutable_option",
                        "errorDetail": {
                            "option": k,
                            "message": six.text_type(e)
                        },
                    },
                    status=400,
                )
        # TODO(dcramer): this has nothing to do with configuring options and
        # should not be set here
        options.set("sentry:version-configured", sentry.get_version())
        return Response(status=200)
Exemple #40
0
def _safe_modify(killswitch_name, modify_func):
    from sentry import killswitches, options

    option_value = options.get(killswitch_name)
    new_option_value = modify_func(option_value)

    if option_value == new_option_value:
        click.echo("No changes!")
        raise click.Abort()

    click.echo("Before:")
    click.echo(killswitches.print_conditions(option_value))
    click.echo("After:")
    click.echo(killswitches.print_conditions(new_option_value))

    click.confirm("Should the changes be applied?",
                  default=False,
                  show_default=True,
                  abort=True)
    options.set(killswitch_name, new_option_value)
    def test_getfile_fs_cache(self):
        file_content = b"this is a test"

        file = self.create_file(name="dummy.txt")
        file.putfile(BytesIO(file_content))
        release_file = self.create_release_file(file=file)

        expected_path = os.path.join(
            options.get("releasefile.cache-path"),
            str(self.organization.id),
            str(file.id),
        )

        # Set the threshold to zero to force caching on the file system
        options.set("releasefile.cache-limit", 0)
        with ReleaseFile.cache.getfile(release_file) as f:
            assert f.read() == file_content
            assert f.name == expected_path

        # Check that the file was cached
        os.stat(expected_path)
Exemple #42
0
    def test_processes_resource_change_task_uses_sampling_option(self, delay):
        options.set('post-process.use-error-hook-sampling', True)
        options.set('post-process.error-hook-sample-rate', 1)
        event = self.store_event(
            data={
                'message': 'Foo bar',
                'level': 'error',
                'exception': {"type": "Foo", "value": "shits on fiah yo"},
                'timestamp': timezone.now().isoformat()[:19]
            },
            project_id=self.project.id,
            assert_no_errors=False
        )

        self.create_service_hook(
            project=self.project,
            organization=self.project.organization,
            actor=self.user,
            events=['error.created'],
        )

        post_process_group(
            event=event,
            is_new=False,
            is_regression=False,
            is_sample=False,
            is_new_group_environment=False,
        )

        kwargs = {
            'project_id': self.project.id,
            'group_id': event.group.id,
        }

        delay.assert_called_once_with(
            action='created',
            sender='Error',
            instance_id=event.event_id,
            **kwargs
        )
Exemple #43
0
    def test_caching(self):
        # Set the threshold to zero to force caching on the file system
        options.set("releasefile.cache-limit", 0)

        project = self.project
        release = Release.objects.create(
            organization_id=project.organization_id, version="abc")
        release.add_project(project)

        file = File.objects.create(
            name="file.min.js",
            type="release.file",
            headers={"Content-Type": "application/json; charset=utf-8"},
        )

        binary_body = unicode_body.encode("utf-8")
        file.putfile(BytesIO(binary_body))

        ReleaseFile.objects.create(
            name="file.min.js",
            release_id=release.id,
            organization_id=project.organization_id,
            file=file,
        )

        result = fetch_release_file("file.min.js", release)

        assert isinstance(result.body, bytes)
        assert result == http.UrlResult(
            "file.min.js",
            {"content-type": "application/json; charset=utf-8"},
            binary_body,
            200,
            "utf-8",
        )

        # test with cache hit, coming from the FS
        new_result = fetch_release_file("file.min.js", release)

        assert result == new_result
Exemple #44
0
    def test_simple(self, safe_urlread, safe_urlopen,
                    mock_get_all_package_versions):
        mock_get_all_package_versions.return_value = {"foo": "1.0"}
        safe_urlread.return_value = json.dumps({
            "notices": [],
            "version": {
                "stable": "1.0.0"
            }
        })

        assert options.set("system.admin-email", "*****@*****.**")
        assert options.set("beacon.anonymous", False)
        send_beacon()

        install_id = options.get("sentry:install-id")
        assert install_id and len(install_id) == 40

        safe_urlopen.assert_called_once_with(
            BEACON_URL,
            json={
                "install_id": install_id,
                "version": sentry.get_version(),
                "docker": sentry.is_docker(),
                "python_version": platform.python_version(),
                "data": {
                    "organizations": 1,
                    "users": 0,
                    "projects": 1,
                    "teams": 1,
                    "events.24h": 0,
                },
                "anonymous": False,
                "admin_email": "*****@*****.**",
                "packages": mock_get_all_package_versions.return_value,
            },
            timeout=5,
        )
        safe_urlread.assert_called_once_with(safe_urlopen.return_value)

        assert options.get("sentry:latest_version") == "1.0.0"
Exemple #45
0
def _needs_upgrade():
    version_configured = options.get('sentry:version-configured')
    if not version_configured:
        # If we were never previously upgraded (being a new install)
        # we want to force an upgrade, even if the values are set.
        return True

    smtp_disabled = not is_smtp_enabled()

    # Check all required options to see if they've been set
    for key in options.filter(flag=options.FLAG_REQUIRED):
        # Ignore mail.* keys if smtp is disabled
        if smtp_disabled and key.name[:5] == 'mail.':
            continue
        if not options.isset(key.name):
            return True

    if version_configured != sentry.get_version():
        # Everything looks good, but version changed, so let's bump it
        options.set('sentry:version-configured', sentry.get_version())

    return False
Exemple #46
0
def _push(killswitch_name, infile, yes):
    """
    Write back a killswitch into the DB.

    For a list of killswitches to write, use `sentry killswitches list`.

    For example:

        sentry killswitches pull store.load-shed-pipeline-projects file.txt
        <edit file.txt>
        sentry killswitches push store.load-shed-pipeline-projects file.txt
    """
    from sentry import killswitches, options

    option_value = options.get(killswitch_name)

    edited_text = infile.read()
    try:
        new_option_value = killswitches.validate_user_input(
            killswitch_name, yaml.safe_load(edited_text)
        )
    except ValueError as e:
        click.echo(f"Invalid data: {e}")
        raise click.Abort()

    if option_value == new_option_value:
        click.echo("No changes!", err=True)
        raise click.Abort()

    click.echo("Before:")
    click.echo(killswitches.print_conditions(killswitch_name, option_value))
    click.echo("After:")
    click.echo(killswitches.print_conditions(killswitch_name, new_option_value))

    if not yes:
        click.confirm(
            "Should the changes be applied?", default=False, show_default=True, abort=True
        )
    options.set(killswitch_name, new_option_value)
    def test_simple(self, safe_urlread, safe_urlopen, mock_get_all_package_versions):
        mock_get_all_package_versions.return_value = {'foo': '1.0'}
        safe_urlread.return_value = json.dumps({
            'notices': [],
            'version': {
                'stable': '1.0.0'
            },
        })

        assert options.set('system.admin-email', '*****@*****.**')
        assert options.set('beacon.anonymous', False)
        send_beacon()

        install_id = options.get('sentry:install-id')
        assert install_id and len(install_id) == 40

        safe_urlopen.assert_called_once_with(
            BEACON_URL,
            json={
                'install_id': install_id,
                'version': sentry.get_version(),
                'docker': sentry.is_docker(),
                'data': {
                    'organizations': 1,
                    'users': 0,
                    'projects': 1,
                    'teams': 1,
                    'events.24h': 0,
                },
                'anonymous': False,
                'admin_email': '*****@*****.**',
                'packages': mock_get_all_package_versions.return_value,
            },
            timeout=5
        )
        safe_urlread.assert_called_once_with(safe_urlopen.return_value)

        assert options.get('sentry:latest_version') == '1.0.0'
Exemple #48
0
    def test_registration_valid(self, mock_record):
        options.set("auth.allow-registration", True)
        with self.feature("auth:register"):
            resp = self.client.post(
                self.path,
                {
                    "username": "******",
                    "password": "******",
                    "name": "Foo Bar",
                    "op": "register",
                },
            )
        assert resp.status_code == 302, (resp.context["register_form"].errors
                                         if resp.status_code == 200 else None)
        frontend_events = {"event_name": "Sign Up"}
        marketing_query = urlencode(
            {"frontend_events": json.dumps(frontend_events)})
        assert marketing_query in resp.url

        user = User.objects.get(
            username="******")
        assert user.email == "*****@*****.**"
        assert user.check_password("foobar")
        assert user.name == "Foo Bar"
        assert not OrganizationMember.objects.filter(user=user).exists()

        signup_record = [
            r for r in mock_record.call_args_list if r[0][0] == "user.signup"
        ]
        assert signup_record == [
            mock.call(
                "user.signup",
                user_id=user.id,
                source="register-form",
                provider=None,
                referrer="in-app",
            )
        ]
Exemple #49
0
    def put(self, request):
        # TODO(dcramer): this should validate options before saving them
        for k, v in six.iteritems(request.data):
            if v and isinstance(v, six.string_types):
                v = v.strip()
            try:
                option = options.lookup_key(k)
            except options.UnknownOption:
                # TODO(dcramer): unify API errors
                return Response(
                    {
                        "error": "unknown_option",
                        "errorDetail": {
                            "option": k
                        }
                    },
                    status=400)

            try:
                if not (option.flags & options.FLAG_ALLOW_EMPTY) and not v:
                    options.delete(k)
                else:
                    options.set(k, v)
            except TypeError as e:
                return Response(
                    {
                        "error": "invalid_type",
                        "errorDetail": {
                            "option": k,
                            "message": six.text_type(e)
                        },
                    },
                    status=400,
                )
        # TODO(dcramer): this has nothing to do with configuring options and
        # should not be set here
        options.set("sentry:version-configured", sentry.get_version())
        return Response(status=200)
Exemple #50
0
    def test_get_oldest_latest_for_environments(self):
        options.set('snuba.events-queries.enabled', True)
        project = self.create_project()

        min_ago = (timezone.now() - timedelta(minutes=1)).isoformat()[:19]

        self.store_event(data={
            'event_id': 'a' * 32,
            'environment': 'production',
            'timestamp': min_ago,
            'fingerprint': ['group-1']
        },
                         project_id=project.id)
        self.store_event(data={
            'event_id': 'b' * 32,
            'environment': 'production',
            'timestamp': min_ago,
            'fingerprint': ['group-1']
        },
                         project_id=project.id)
        self.store_event(data={
            'event_id': 'c' * 32,
            'timestamp': min_ago,
            'fingerprint': ['group-1']
        },
                         project_id=project.id)

        group = Group.objects.first()

        assert group.get_latest_event_for_environments().event_id == 'c' * 32
        assert group.get_latest_event_for_environments(['staging']) is None
        assert group.get_latest_event_for_environments(['production'
                                                        ]).event_id == 'b' * 32
        assert group.get_oldest_event_for_environments().event_id == 'a' * 32
        assert group.get_oldest_event_for_environments(
            ['staging', 'production']).event_id == 'a' * 32
        assert group.get_oldest_event_for_environments(['staging']) is None
Exemple #51
0
    def test_registration_subscribe_to_newsletter(self):
        options.set('auth.allow-registration', True)
        with self.feature('auth:register'):
            resp = self.client.post(
                self.path, {
                    'username': '******',
                    'password': '******',
                    'name': 'Foo Bar',
                    'op': 'register',
                    'subscribe': '1',
                })
        assert resp.status_code == 302

        user = User.objects.get(
            username='******')
        assert user.email == '*****@*****.**'
        assert user.check_password('foobar')
        assert user.name == 'Foo Bar'

        results = newsletter.get_subscriptions(user)['subscriptions']
        assert len(results) == 1
        assert results[0].list_id == newsletter.get_default_list_id()
        assert results[0].subscribed
        assert not results[0].verified
Exemple #52
0
    def test_pagination(self):
        # test with and without max-pre-snuba-candidates enabled
        prev_max_pre = options.get('snuba.search.max-pre-snuba-candidates')
        options.set('snuba.search.max-pre-snuba-candidates', None)
        try:
            results = self.backend.query(self.project, limit=1, sort_by='date')
            assert set(results) == set([self.group1])

            results = self.backend.query(self.project,
                                         cursor=results.next,
                                         limit=1,
                                         sort_by='date')
            assert set(results) == set([self.group2])

            results = self.backend.query(self.project,
                                         cursor=results.next,
                                         limit=1,
                                         sort_by='date')
            assert set(results) == set([])
        finally:
            options.set('snuba.search.max-pre-snuba-candidates', prev_max_pre)

        results = self.backend.query(self.project, limit=1, sort_by='date')
        assert set(results) == set([self.group1])

        results = self.backend.query(self.project,
                                     cursor=results.next,
                                     limit=1,
                                     sort_by='date')
        assert set(results) == set([self.group2])

        results = self.backend.query(self.project,
                                     cursor=results.next,
                                     limit=1,
                                     sort_by='date')
        assert set(results) == set([])
Exemple #53
0
def plugin_config(plugin, project, request):
    """
    Configure the plugin site wide.

    Returns a tuple composed of a redirection boolean and the content to
    be displayed.
    """
    NOTSET = object()

    plugin_key = plugin.get_conf_key()
    if project:
        form_class = plugin.project_conf_form
        template = plugin.project_conf_template
    else:
        form_class = plugin.site_conf_form
        template = plugin.site_conf_template

    test_results = None

    initials = plugin.get_form_initial(project)
    for field in form_class.base_fields:
        key = '%s:%s' % (plugin_key, field)
        if project:
            value = ProjectOption.objects.get_value(project, key, NOTSET)
        else:
            value = options.get(key)
        if value is not NOTSET:
            initials[field] = value

    form = form_class(
        request.POST if request.POST.get('plugin') == plugin.slug else None,
        initial=initials,
        prefix=plugin_key)
    if form.is_valid():
        if 'action_test' in request.POST and plugin.is_testable():
            try:
                test_results = plugin.test_configuration(project)
            except Exception as exc:
                if hasattr(exc, 'read') and callable(exc.read):
                    test_results = '%s\n%s' % (exc, exc.read())
                else:
                    logging.exception('Plugin(%s) raised an error during test',
                                      plugin_key)
                    test_results = 'There was an internal error with the Plugin'
            if not test_results:
                test_results = 'No errors returned'
        else:
            for field, value in form.cleaned_data.iteritems():
                key = '%s:%s' % (plugin_key, field)
                if project:
                    ProjectOption.objects.set_value(project, key, value)
                else:
                    options.set(key, value)

            return ('redirect', None)

    # TODO(mattrobenolt): Reliably determine if a plugin is configured
    # if hasattr(plugin, 'is_configured'):
    #     is_configured = plugin.is_configured(project)
    # else:
    #     is_configured = True
    is_configured = True

    from django.template.loader import render_to_string
    return ('display',
            mark_safe(
                render_to_string(
                    template, {
                        'form': form,
                        'request': request,
                        'plugin': plugin,
                        'plugin_description': plugin.get_description() or '',
                        'plugin_test_results': test_results,
                        'plugin_is_configured': is_configured,
                    },
                    context_instance=RequestContext(request))))
Exemple #54
0
 def setUp(self):
     super(GroupEventsTest, self).setUp()
     self.min_ago = timezone.now() - timedelta(minutes=1)
     options.set('snuba.events-queries.enabled', True)
Exemple #55
0
 def test_registration_disabled(self):
     options.set("auth.allow-registration", True)
     with self.feature({"auth:register": False}):
         resp = self.client.get(self.path)
         assert resp.context["register_form"] is None
Exemple #56
0
    def test_multiple_orgs(self, mock_get_jwt):
        mock_get_jwt.return_value = ""

        project = self.project  # force creation

        url = "/extensions/github/webhook/"

        secret = "b3002c3e321d4b7880360d397db2ccfd"

        options.set("github-app.webhook-secret", secret)
        Repository.objects.create(
            organization_id=project.organization.id,
            external_id="35129377",
            provider="integrations:github",
            name="baxterthehacker/public-repo",
        )

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            external_id="12345",
            provider="github",
            metadata={"access_token": "1234", "expires_at": future_expires.isoformat()},
        )
        integration.add_organization(project.organization, self.user)

        org2 = self.create_organization()
        project2 = self.create_project(organization=org2, name="bar")

        Repository.objects.create(
            organization_id=project2.organization.id,
            external_id="77",
            provider="integrations:github",
            name="another/repo",
        )

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            external_id="99",
            provider="github",
            metadata={"access_token": "1234", "expires_at": future_expires.isoformat()},
        )
        integration.add_organization(org2, self.user)

        response = self.client.post(
            path=url,
            data=PUSH_EVENT_EXAMPLE_INSTALLATION,
            content_type="application/json",
            HTTP_X_GITHUB_EVENT="push",
            HTTP_X_HUB_SIGNATURE="sha1=56a3df597e02adbc17fb617502c70e19d96a6136",
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
        )

        assert response.status_code == 204

        commit_list = list(
            Commit.objects.filter(organization_id=project.organization_id)
            .select_related("author")
            .order_by("-date_added")
        )

        assert len(commit_list) == 2

        commit_list = list(
            Commit.objects.filter(organization_id=org2.id)
            .select_related("author")
            .order_by("-date_added")
        )
        assert len(commit_list) == 0
Exemple #57
0
    def test_anonymous_lookup(self):
        project = self.project  # force creation

        url = "/extensions/github/webhook/"

        secret = "b3002c3e321d4b7880360d397db2ccfd"

        options.set("github-app.webhook-secret", secret)

        future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
        integration = Integration.objects.create(
            provider="github",
            external_id="12345",
            name="octocat",
            metadata={"access_token": "1234", "expires_at": future_expires.isoformat()},
        )
        integration.add_organization(project.organization, self.user)

        Repository.objects.create(
            organization_id=project.organization.id,
            external_id="35129377",
            provider="integrations:github",
            name="baxterthehacker/public-repo",
        )

        CommitAuthor.objects.create(
            external_id="github:baxterthehacker",
            organization_id=project.organization_id,
            email="*****@*****.**",
            name="bàxterthehacker",
        )

        response = self.client.post(
            path=url,
            data=PUSH_EVENT_EXAMPLE_INSTALLATION,
            content_type="application/json",
            HTTP_X_GITHUB_EVENT="push",
            HTTP_X_HUB_SIGNATURE="sha1=56a3df597e02adbc17fb617502c70e19d96a6136",
            HTTP_X_GITHUB_DELIVERY=six.text_type(uuid4()),
        )

        assert response.status_code == 204

        commit_list = list(
            Commit.objects.filter(organization_id=project.organization_id)
            .select_related("author")
            .order_by("-date_added")
        )

        # should be skipping the #skipsentry commit
        assert len(commit_list) == 2

        commit = commit_list[0]

        assert commit.key == "133d60480286590a610a0eb7352ff6e02b9674c4"
        assert commit.message == "Update README.md (àgain)"
        assert commit.author.name == "bàxterthehacker"
        assert commit.author.email == "*****@*****.**"
        assert commit.date_added == datetime(2015, 5, 5, 23, 45, 15, tzinfo=timezone.utc)

        commit = commit_list[1]

        assert commit.key == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"
        assert commit.message == "Update README.md"
        assert commit.author.name == "bàxterthehacker"
        assert commit.author.email == "*****@*****.**"
        assert commit.date_added == datetime(2015, 5, 5, 23, 40, 15, tzinfo=timezone.utc)
 def setUp(self):
     super(GroupEventsTest, self).setUp()
     options.set('snuba.events-queries.enabled', False)
 def tearDown(self):
     options.set('snuba.events-queries.enabled', True)
Exemple #60
0
def send_beacon():
    """
    Send a Beacon to a remote server operated by the Sentry team.

    See the documentation for more details.
    """
    from sentry import options
    from sentry.models import Broadcast, Organization, Project, Team, User

    if not settings.SENTRY_BEACON:
        logger.info('Not sending beacon (disabled)')
        return

    install_id = options.get('sentry:install-id')
    if not install_id:
        logger.info('Generated installation ID: %s', install_id)
        install_id = sha1(uuid4().hex).hexdigest()
        options.set('sentry:install-id', install_id)

    end = timezone.now()
    events_24h = tsdb.get_sums(
        model=tsdb.models.internal,
        keys=['events.total'],
        start=end - timedelta(hours=24),
        end=end,
    )['events.total']

    payload = {
        'install_id': install_id,
        'version': sentry.get_version(),
        'admin_email': settings.SENTRY_ADMIN_EMAIL,
        'data': {
            # TODO(dcramer): we'd also like to get an idea about the throughput
            # of the system (i.e. events in 24h)
            'users': User.objects.count(),
            'projects': Project.objects.count(),
            'teams': Team.objects.count(),
            'organizations': Organization.objects.count(),
            'events.24h': events_24h,
        }
    }

    # TODO(dcramer): relay the response 'notices' as admin broadcasts
    try:
        request = safe_urlopen(BEACON_URL, json=payload, timeout=5)
        response = safe_urlread(request)
    except Exception:
        logger.warning('Failed sending beacon', exc_info=True)
        return

    data = json.loads(response)

    if 'version' in data:
        options.set('sentry:latest_version', data['version']['stable'])

    if 'notices' in data:
        upstream_ids = set()
        for notice in data['notices']:
            upstream_ids.add(notice['id'])
            Broadcast.objects.create_or_update(upstream_id=notice['id'],
                                               defaults={
                                                   'title': notice['title'],
                                                   'link': notice.get('link'),
                                                   'message':
                                                   notice['message'],
                                               })

        Broadcast.objects.filter(upstream_id__isnull=False, ).exclude(
            upstream_id__in=upstream_ids, ).update(is_active=False, )