예제 #1
0
    def update_organization_config(self, data):
        # data = {"project_mappings": [[sentry_project_id, vercel_project_id]]}
        vercel_client = self.get_client()
        config = self.org_integration.config
        new_mappings = data["project_mappings"]
        old_mappings = config.get("project_mappings") or []

        for mapping in new_mappings:
            # skip any mappings that already exist
            if mapping in old_mappings:
                continue
            [sentry_project_id, vercel_project_id] = mapping
            sentry_project = Project.objects.get(id=sentry_project_id)
            enabled_dsn = ProjectKey.get_default(project=sentry_project)
            if not enabled_dsn:
                raise IntegrationError("You must have an enabled DSN to continue!")
            source_code_provider = self.get_source_code_provider(vercel_client, vercel_project_id)
            if not source_code_provider:
                raise IntegrationError(
                    "You must connect your Vercel project to a Git repository to continue!"
                )
            sentry_project_dsn = enabled_dsn.get_dsn(public=True)
            uuid = uuid4().hex

            sentry_app_installation = SentryAppInstallationForProvider.objects.get(
                organization=sentry_project.organization.id, provider="vercel"
            )
            sentry_auth_token = sentry_app_installation.get_token(
                self.organization_id, provider="vercel"
            )
            secret_names = [
                "SENTRY_ORG_%s" % uuid,
                "SENTRY_PROJECT_%s" % uuid,
                "NEXT_PUBLIC_SENTRY_DSN_%s" % uuid,
                "SENTRY_AUTH_TOKEN_%s" % uuid,
            ]
            values = [
                sentry_project.organization.slug,
                sentry_project.slug,
                sentry_project_dsn,
                sentry_auth_token,
            ]
            env_var_names = [
                "SENTRY_ORG",
                "SENTRY_PROJECT",
                "NEXT_PUBLIC_SENTRY_DSN",
                "SENTRY_AUTH_TOKEN",
                "VERCEL_%s_COMMIT_SHA" % source_code_provider.upper(),
            ]

            secrets = []
            for name, val in zip(secret_names, values):
                secrets.append(self.create_secret(vercel_client, vercel_project_id, name, val))

            secrets.append("")
            for secret, env_var in zip(secrets, env_var_names):
                self.create_env_var(vercel_client, vercel_project_id, env_var, secret)

        config.update(data)
        self.org_integration.update(config=config)
예제 #2
0
    def project_key(self):
        from sentry.models import ProjectKey

        if not settings.SENTRY_PROJECT:
            return None

        key = None
        try:
            if settings.SENTRY_PROJECT_KEY is not None:
                key = ProjectKey.objects.get(
                    id=settings.SENTRY_PROJECT_KEY,
                    project=settings.SENTRY_PROJECT,
                )
            else:
                key = ProjectKey.get_default(settings.SENTRY_PROJECT)
        except Exception as exc:
            # if the relation fails to query or is missing completely, lets handle
            # it gracefully
            self.error_logger.warn('internal-error.unable-to-fetch-project', extra={
                'project_id': settings.SENTRY_PROJECT,
                'project_key': settings.SENTRY_PROJECT_KEY,
                'error_message': six.text_type(exc),
            })
        if key is None:
            self.error_logger.warn('internal-error.no-project-available', extra={
                'project_id': settings.SENTRY_PROJECT,
                'project_key': settings.SENTRY_PROJECT_KEY,
            })
        return key
예제 #3
0
def get_project_key():
    from sentry.models import ProjectKey

    if not settings.SENTRY_PROJECT:
        return None

    key = None
    try:
        if settings.SENTRY_PROJECT_KEY is not None:
            key = ProjectKey.objects.get(id=settings.SENTRY_PROJECT_KEY,
                                         project=settings.SENTRY_PROJECT)
        else:
            key = ProjectKey.get_default(settings.SENTRY_PROJECT)
    except Exception as exc:
        # if the relation fails to query or is missing completely, lets handle
        # it gracefully
        sdk_logger.warning(
            "internal-error.unable-to-fetch-project",
            extra={
                "project_id": settings.SENTRY_PROJECT,
                "project_key": settings.SENTRY_PROJECT_KEY,
                "error_message": str(exc),
            },
        )
    if key is None:
        sdk_logger.warning(
            "internal-error.no-project-available",
            extra={
                "project_id": settings.SENTRY_PROJECT,
                "project_key": settings.SENTRY_PROJECT_KEY,
            },
        )
    return key
예제 #4
0
파일: utils.py 프로젝트: waterdrops/sentry
def get_dsn_for_project(organization_id, project_id):
    try:
        project = Project.objects.get(organization_id=organization_id, id=project_id)
    except Project.DoesNotExist:
        raise IntegrationError("No valid project")

    enabled_dsn = ProjectKey.get_default(project=project)
    if not enabled_dsn:
        raise IntegrationError("Project does not have DSN enabled")
    return enabled_dsn.get_dsn(public=True)
    def send(self, **kwargs):
        # Report the issue to an upstream Sentry if active
        # NOTE: we don't want to check self.is_enabled() like normal, since
        # is_enabled behavior is overridden in this class. We explicitly
        # want to check if the remote is active.
        if self.remote.is_active():
            from sentry import options
            # Append some extra tags that are useful for remote reporting
            super_kwargs = copy.deepcopy(kwargs)
            super_kwargs['tags']['install-id'] = options.get(
                'sentry:install-id')
            super(SentryInternalClient, self).send(**super_kwargs)

        if not is_current_event_safe():
            return

        # These imports all need to be internal to this function as this class
        # is set up by django while still parsing LOGGING settings and we
        # cannot import this stuff until settings are finalized.
        from sentry.models import ProjectKey
        from sentry.web.api import StoreView
        from django.test import RequestFactory
        key = None
        if settings.SENTRY_PROJECT_KEY is not None:
            key = ProjectKey.objects.filter(
                id=settings.SENTRY_PROJECT_KEY,
                project=settings.SENTRY_PROJECT).first()
        if key is None:
            key = ProjectKey.get_default(settings.SENTRY_PROJECT)
        if key is None:
            return

        client_string = 'raven-python/%s' % (raven.VERSION, )
        headers = {
            'HTTP_X_SENTRY_AUTH':
            get_auth_header(
                protocol=self.protocol_version,
                timestamp=time.time(),
                client=client_string,
                api_key=key.public_key,
                api_secret=key.secret_key,
            ),
            'HTTP_CONTENT_ENCODING':
            self.get_content_encoding(),
        }
        self.request_factory = self.request_factory or RequestFactory()
        request = self.request_factory.post(
            '/api/store',
            data=self.encode(kwargs),
            content_type='application/octet-stream',
            **headers)
        StoreView.as_view()(
            request,
            project_id=six.text_type(settings.SENTRY_PROJECT),
        )
예제 #6
0
    def get(self, request, project):
        data = options.get('sentry:docs')
        project_key = ProjectKey.get_default(project)

        context = {
            'platforms': data['platforms'],
        }
        if project_key:
            context['dsn'] = project_key.dsn_private
            context['dsnPublic'] = project_key.dsn_public

        return Response(context)
예제 #7
0
파일: raven.py 프로젝트: binlee1990/sentry
    def send(self, **kwargs):
        # Report the issue to an upstream Sentry if active
        # NOTE: we don't want to check self.is_enabled() like normal, since
        # is_enabled behavior is overridden in this class. We explicitly
        # want to check if the remote is active.
        if self.remote.is_active():
            from sentry import options
            # Append some extra tags that are useful for remote reporting
            super_kwargs = copy.deepcopy(kwargs)
            super_kwargs['tags']['install-id'] = options.get('sentry:install-id')
            super(SentryInternalClient, self).send(**super_kwargs)

        if not is_current_event_safe():
            return

        # These imports all need to be internal to this function as this class
        # is set up by django while still parsing LOGGING settings and we
        # cannot import this stuff until settings are finalized.
        from sentry.models import ProjectKey
        from sentry.web.api import StoreView
        from django.test import RequestFactory
        key = None
        if settings.SENTRY_PROJECT_KEY is not None:
            key = ProjectKey.objects.filter(
                id=settings.SENTRY_PROJECT_KEY,
                project=settings.SENTRY_PROJECT).first()
        if key is None:
            key = ProjectKey.get_default(settings.SENTRY_PROJECT)
        if key is None:
            return

        client_string = 'raven-python/%s' % (raven.VERSION,)
        headers = {
            'HTTP_X_SENTRY_AUTH': get_auth_header(
                protocol=self.protocol_version,
                timestamp=time.time(),
                client=client_string,
                api_key=key.public_key,
                api_secret=key.secret_key,
            ),
            'HTTP_CONTENT_ENCODING': self.get_content_encoding(),
        }
        self.request_factory = self.request_factory or RequestFactory()
        request = self.request_factory.post(
            '/api/store',
            data=self.encode(kwargs),
            content_type='application/octet-stream',
            **headers
        )
        StoreView.as_view()(
            request,
            project_id=six.text_type(settings.SENTRY_PROJECT),
        )
예제 #8
0
    def get(self, request, project, platform):
        data = load_doc(platform)
        if not data:
            raise ResourceDoesNotExist

        project_key = ProjectKey.get_default(project)

        return Response({
            'id': data['id'],
            'name': data['name'],
            'html': replace_keys(data['html'], project_key),
            'link': data['link'],
        })
예제 #9
0
    def get(self, request, project, platform):
        data = load_doc(platform)
        if not data:
            raise ResourceDoesNotExist

        project_key = ProjectKey.get_default(project)

        return Response({
            'id': data['id'],
            'name': data['name'],
            'html': replace_keys(data['html'], project_key),
            'link': data['link'],
        })
    def get(self, request, project, platform):
        data = load_doc(platform)
        if not data:
            raise ResourceDoesNotExist

        project_key = ProjectKey.get_default(project)

        return Response({
            "id": data["id"],
            "name": data["name"],
            "html": replace_keys(data["html"], project_key),
            "link": data["link"],
        })
예제 #11
0
    def dispatch(self, request, pipeline):
        organization = pipeline.organization

        # TODO: make project selection part of flow
        project = Project.objects.filter(organization=organization,
                                         platform="node-awslambda").first()
        if not project:
            raise IntegrationError("No valid project")

        enabled_dsn = ProjectKey.get_default(project=project)
        if not enabled_dsn:
            raise IntegrationError("Project does not have DSN enabled")
        sentry_project_dsn = enabled_dsn.get_dsn(public=True)

        arn = pipeline.fetch_state("arn")
        aws_external_id = pipeline.fetch_state("aws_external_id")

        lambda_client = gen_aws_lambda_client(arn, aws_external_id)

        lambda_functions = filter(
            lambda x: x.get("Runtime") in SUPPORTED_RUNTIMES,
            lambda_client.list_functions()["Functions"],
        )

        for function in lambda_functions:
            name = function["FunctionName"]
            # TODO: load existing layers and environment and append to them
            try:
                lambda_client.update_function_configuration(
                    FunctionName=name,
                    Layers=[options.get("aws-lambda.node-layer-arn")],
                    Environment={
                        "Variables": {
                            "NODE_OPTIONS": "-r @sentry/serverless/dist/auto",
                            "SENTRY_DSN": sentry_project_dsn,
                            "SENTRY_TRACES_SAMPLE_RATE": "1.0",
                        }
                    },
                )
            except Exception as e:
                logger.info(
                    "update_function_configuration.error",
                    extra={
                        "organization_id": organization.id,
                        "lambda_name": name,
                        "arn": arn,
                        "error": six.text_type(e),
                    },
                )

        return pipeline.next_step()
예제 #12
0
    def get(self, request, project):
        data = load_doc('_platforms')
        if data is None:
            raise RuntimeError('Docs not built')
        project_key = ProjectKey.get_default(project)

        context = {
            'platforms': data['platforms'],
        }
        if project_key:
            context['dsn'] = project_key.dsn_private
            context['dsnPublic'] = project_key.dsn_public

        return Response(context)
예제 #13
0
    def update_organization_config(self, data):
        # data = {"project_mappings": [[sentry_project_id, vercel_project_id]]}
        vercel_client = self.get_client()
        config = self.org_integration.config

        new_mappings = data["project_mappings"]
        old_mappings = config.get("project_mappings") or []

        for mapping in new_mappings:
            # skip any mappings that already exist
            if mapping in old_mappings:
                continue
            [sentry_project_id, vercel_project_id] = mapping

            sentry_project = Project.objects.get(id=sentry_project_id)
            enabled_dsn = ProjectKey.get_default(project=sentry_project)
            if not enabled_dsn:
                raise IntegrationError(
                    "You must have an enabled DSN to continue!")
            sentry_project_dsn = enabled_dsn.get_dsn(public=True)

            org_secret = self.create_secret(vercel_client, vercel_project_id,
                                            "SENTRY_ORG",
                                            sentry_project.organization.slug)
            project_secret = self.create_secret(
                vercel_client,
                vercel_project_id,
                "SENTRY_PROJECT_%s" % sentry_project_id,
                sentry_project.slug,
            )
            dsn_secret = self.create_secret(
                vercel_client,
                vercel_project_id,
                "NEXT_PUBLIC_SENTRY_DSN_%s" % sentry_project_id,
                sentry_project_dsn,
            )

            self.create_env_var(vercel_client, vercel_project_id, "SENTRY_ORG",
                                org_secret)
            self.create_env_var(vercel_client, vercel_project_id,
                                "SENTRY_PROJECT", project_secret)
            self.create_env_var(vercel_client, vercel_project_id,
                                "NEXT_PUBLIC_SENTRY_DSN", dsn_secret)

        config.update(data)
        self.org_integration.update(config=config)
예제 #14
0
    def test_upgrade_org_config_no_dsn(self):
        """Test that the function doesn't progress if there is no active DSN"""

        with self.tasks():
            self.assert_setup_flow()

        project_id = self.project.id
        org = self.organization
        data = {
            "project_mappings": [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }
        integration = Integration.objects.get(provider=self.provider.key)
        installation = integration.get_installation(org.id)

        dsn = ProjectKey.get_default(project=Project.objects.get(id=project_id))
        dsn.update(id=dsn.id, status=ProjectKeyStatus.INACTIVE)
        with self.assertRaises(ValidationError):
            installation.update_organization_config(data)
예제 #15
0
    def update_organization_config(self, data):
        # data = {"project_mappings": [[sentry_project_id, vercel_project_id]]}

        metadata = self.model.metadata
        vercel_client = VercelClient(metadata["access_token"],
                                     metadata.get("team_id"))
        config = self.org_integration.config
        [sentry_project_id, vercel_project_id] = data["project_mappings"][
            -1]  # TODO: update this to work in the case where a project is removed
        sentry_project = Project.objects.get(id=sentry_project_id)
        enabled_dsn = ProjectKey.get_default(project=sentry_project)
        if not enabled_dsn:
            raise IntegrationError("You must have an enabled DSN to continue!")
        sentry_project_dsn = enabled_dsn.get_dsn(public=True)

        org_secret = self.create_secret(vercel_client, vercel_project_id,
                                        "SENTRY_ORG",
                                        sentry_project.organization.slug)
        project_secret = self.create_secret(
            vercel_client,
            vercel_project_id,
            "SENTRY_PROJECT_%s" % sentry_project_id,
            sentry_project.slug,
        )
        dsn_secret = self.create_secret(
            vercel_client,
            vercel_project_id,
            "NEXT_PUBLIC_SENTRY_DSN_%s" % sentry_project_id,
            sentry_project_dsn,
        )

        self.create_env_var(vercel_client, vercel_project_id, "SENTRY_ORG",
                            org_secret)
        self.create_env_var(vercel_client, vercel_project_id, "SENTRY_PROJECT",
                            project_secret)
        self.create_env_var(vercel_client, vercel_project_id,
                            "NEXT_PUBLIC_SENTRY_DSN", dsn_secret)

        config.update(data)
        self.org_integration.update(config=config)
예제 #16
0
    def test_update_org_config_vars_exist(self):
        """Test the case wherein the secret and env vars already exist"""

        with self.tasks():
            self.assert_setup_flow()

        org = self.organization
        project_id = self.project.id
        enabled_dsn = ProjectKey.get_default(project=Project.objects.get(
            id=project_id)).get_dsn(public=True)
        sentry_auth_token = SentryAppInstallationForProvider.get_token(
            org.id, "vercel")

        env_var_map = {
            "SENTRY_ORG": {
                "type": "encrypted",
                "value": org.slug
            },
            "SENTRY_PROJECT": {
                "type": "encrypted",
                "value": self.project.slug
            },
            "SENTRY_DSN": {
                "type": "encrypted",
                "value": enabled_dsn
            },
            "SENTRY_AUTH_TOKEN": {
                "type": "secret",
                "value": sentry_auth_token
            },
            "VERCEL_GIT_COMMIT_SHA": {
                "type": "system",
                "value": "VERCEL_GIT_COMMIT_SHA"
            },
        }

        # mock get_project API call
        responses.add(
            responses.GET,
            "https://api.vercel.com/v1/projects/%s" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={
                "link": {
                    "type": "github"
                },
                "framework": "gatsby"
            },
        )

        # mock update env vars
        count = 0
        for env_var, details in env_var_map.items():
            # mock try to create env var
            responses.add(
                responses.POST,
                "https://api.vercel.com/v7/projects/%s/env" %
                "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
                json={"error": {
                    "code": "ENV_ALREADY_EXISTS"
                }},
                status=400,
            )
            # mock get env var
            responses.add(
                responses.GET,
                "https://api.vercel.com/v7/projects/%s/env" %
                "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
                json={"envs": [{
                    "id": count,
                    "key": env_var
                }]},
            )
            # mock update env var
            responses.add(
                responses.PATCH,
                "https://api.vercel.com/v7/projects/%s/env/%s" %
                ("Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H", count),
                json={
                    "key": env_var,
                    "value": details["value"],
                    "target": ["production"],
                    "type": details["type"],
                },
            )
            count += 1

        data = {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }
        integration = Integration.objects.get(provider=self.provider.key)
        installation = integration.get_installation(org.id)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {}
        installation.update_organization_config(data)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }

        req_params = json.loads(responses.calls[5].request.body)
        assert req_params["key"] == "SENTRY_ORG"
        assert req_params["value"] == org.slug
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[8].request.body)
        assert req_params["key"] == "SENTRY_PROJECT"
        assert req_params["value"] == self.project.slug
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[11].request.body)
        assert req_params["key"] == "SENTRY_DSN"
        assert req_params["value"] == enabled_dsn
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[14].request.body)
        assert req_params["key"] == "SENTRY_AUTH_TOKEN"
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[17].request.body)
        assert req_params["key"] == "VERCEL_GIT_COMMIT_SHA"
        assert req_params["value"] == "VERCEL_GIT_COMMIT_SHA"
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "system"
예제 #17
0
    def test_update_organization_config(self):
        """Test that Vercel environment variables are created"""
        with self.tasks():
            self.assert_setup_flow()

        org = self.organization
        project_id = self.project.id
        enabled_dsn = ProjectKey.get_default(project=Project.objects.get(
            id=project_id)).get_dsn(public=True)
        sentry_auth_token = SentryAppInstallationForProvider.get_token(
            org.id, "vercel")

        env_var_map = {
            "SENTRY_ORG": {
                "type": "encrypted",
                "value": org.slug
            },
            "SENTRY_PROJECT": {
                "type": "encrypted",
                "value": self.project.slug
            },
            "SENTRY_DSN": {
                "type": "encrypted",
                "value": enabled_dsn
            },
            "SENTRY_AUTH_TOKEN": {
                "type": "encrypted",
                "value": sentry_auth_token
            },
            "VERCEL_GIT_COMMIT_SHA": {
                "type": "system",
                "value": "VERCEL_GIT_COMMIT_SHA"
            },
        }

        # mock get_project API call
        responses.add(
            responses.GET,
            "https://api.vercel.com/v1/projects/%s" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={
                "link": {
                    "type": "github"
                },
                "framework": "nextjs"
            },
        )

        # mock create the env vars
        for env_var, details in env_var_map.items():
            responses.add(
                responses.POST,
                "https://api.vercel.com/v7/projects/%s/env" %
                "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
                json={
                    "key": env_var,
                    "value": details["value"],
                    "target": ["production"],
                    "type": details["type"],
                },
            )

        integration = Integration.objects.get(provider=self.provider.key)
        installation = integration.get_installation(org.id)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {}
        data = {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }

        installation.update_organization_config(data)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }

        # assert the env vars were created correctly
        req_params = json.loads(responses.calls[5].request.body)
        assert req_params["key"] == "SENTRY_ORG"
        assert req_params["value"] == org.slug
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[6].request.body)
        assert req_params["key"] == "SENTRY_PROJECT"
        assert req_params["value"] == self.project.slug
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[7].request.body)
        assert req_params["key"] == "NEXT_PUBLIC_SENTRY_DSN"
        assert req_params["value"] == enabled_dsn
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[8].request.body)
        assert req_params["key"] == "SENTRY_AUTH_TOKEN"
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "encrypted"

        req_params = json.loads(responses.calls[9].request.body)
        assert req_params["key"] == "VERCEL_GIT_COMMIT_SHA"
        assert req_params["value"] == "VERCEL_GIT_COMMIT_SHA"
        assert req_params["target"] == ["production"]
        assert req_params["type"] == "system"
예제 #18
0
    def dispatch(self, request, pipeline):
        if "finish_pipeline" in request.GET:
            return pipeline.finish_pipeline()

        organization = pipeline.organization

        arn = pipeline.fetch_state("arn")
        region = parse_arn(arn)["region"]
        # the layer ARN has to be located within a specific region
        node_layer_arn = get_aws_node_arn(region)

        project_id = pipeline.fetch_state("project_id")
        aws_external_id = pipeline.fetch_state("aws_external_id")
        enabled_lambdas = pipeline.fetch_state("enabled_lambdas")

        try:
            project = Project.objects.get(organization=organization,
                                          id=project_id)
        except Project.DoesNotExist:
            raise IntegrationError("No valid project")

        enabled_dsn = ProjectKey.get_default(project=project)
        if not enabled_dsn:
            raise IntegrationError("Project does not have DSN enabled")
        sentry_project_dsn = enabled_dsn.get_dsn(public=True)

        lambda_client = gen_aws_client(arn, aws_external_id)

        lambda_functions = filter(
            lambda x: x.get("Runtime") in SUPPORTED_RUNTIMES,
            lambda_client.list_functions()["Functions"],
        )
        lambda_functions.sort(key=lambda x: x["FunctionName"].lower())

        failures = []

        for function in lambda_functions:
            name = function["FunctionName"]
            # check to see if the user wants to enable this function
            if not enabled_lambdas.get(name):
                continue
            try:
                # update the env variables
                env_variables = function.get("Environment",
                                             {}).get("Variables", {})
                env_variables.update({
                    "NODE_OPTIONS": "-r @sentry/serverless/dist/auto",
                    "SENTRY_DSN": sentry_project_dsn,
                    "SENTRY_TRACES_SAMPLE_RATE": "1.0",
                })

                # find the sentry layer and update it or insert new layer to end
                layers = function.get("Layers", [])
                sentry_layer_index = get_index_of_sentry_layer(
                    layers, node_layer_arn)
                if sentry_layer_index > -1:
                    layers[sentry_layer_index] = node_layer_arn
                else:
                    layers.append(node_layer_arn)

                lambda_client.update_function_configuration(
                    FunctionName=name,
                    Layers=layers,
                    Environment={"Variables": env_variables},
                )
            except Exception as e:
                failures.append(function)
                logger.info(
                    "update_function_configuration.error",
                    extra={
                        "organization_id": organization.id,
                        "lambda_name": name,
                        "arn": arn,
                        "error": six.text_type(e),
                    },
                )

        # if we have failures, show them to the user
        # otherwise, finish
        if failures:
            return self.render_react_view(request, "awsLambdaFailureDetails",
                                          {"lambdaFunctionFailures": failures})
        else:
            return pipeline.finish_pipeline()
예제 #19
0
    def dispatch(self, request, pipeline):
        if "finish_pipeline" in request.GET:
            return pipeline.finish_pipeline()

        organization = pipeline.organization

        arn = pipeline.fetch_state("arn")
        project_id = pipeline.fetch_state("project_id")
        aws_external_id = pipeline.fetch_state("aws_external_id")
        enabled_lambdas = pipeline.fetch_state("enabled_lambdas")

        try:
            project = Project.objects.get(organization=organization,
                                          id=project_id)
        except Project.DoesNotExist:
            raise IntegrationError("No valid project")

        enabled_dsn = ProjectKey.get_default(project=project)
        if not enabled_dsn:
            raise IntegrationError("Project does not have DSN enabled")
        sentry_project_dsn = enabled_dsn.get_dsn(public=True)

        lambda_client = gen_aws_lambda_client(arn, aws_external_id)

        lambda_functions = filter(
            lambda x: x.get("Runtime") in SUPPORTED_RUNTIMES,
            lambda_client.list_functions()["Functions"],
        )
        lambda_functions.sort(key=lambda x: x["FunctionName"].lower())

        failures = []

        for function in lambda_functions:
            name = function["FunctionName"]
            # check to see if the user wants to enable this function
            if not enabled_lambdas.get(name):
                continue
            # TODO: load existing layers and environment and append to them
            try:
                lambda_client.update_function_configuration(
                    FunctionName=name,
                    Layers=[options.get("aws-lambda.node-layer-arn")],
                    Environment={
                        "Variables": {
                            "NODE_OPTIONS": "-r @sentry/serverless/dist/auto",
                            "SENTRY_DSN": sentry_project_dsn,
                            "SENTRY_TRACES_SAMPLE_RATE": "1.0",
                        }
                    },
                )
            except Exception as e:
                failures.append(function)
                logger.info(
                    "update_function_configuration.error",
                    extra={
                        "organization_id": organization.id,
                        "lambda_name": name,
                        "arn": arn,
                        "error": six.text_type(e),
                    },
                )

        # if we have failures, show them to the user
        # otherwise, finish
        if failures:
            return self.render_react_view(request, "awsLambdaFailureDetails",
                                          {"lambdaFunctionFailures": failures})
        else:
            return pipeline.finish_pipeline()
예제 #20
0
    def test_update_organization_config(self):
        """Test that Vercel environment variables are created"""
        with self.tasks():
            self.assert_setup_flow()

        uuid = self.mock_uuid4.hex
        secret_names = [
            "sentry_org_%s" % uuid,
            "sentry_project_%s" % uuid,
            "next_public_sentry_dsn_%s" % uuid,
            "sentry_auth_token_%s" % uuid,
        ]
        responses.add(
            responses.GET,
            "https://api.vercel.com/v1/projects/%s" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={
                "link": {
                    "type": "github"
                },
                "framework": "nextjs"
            },
        )

        for i, name in enumerate(secret_names):
            responses.add(
                responses.POST,
                "https://api.vercel.com/v2/now/secrets",
                json={"uid": "sec_%s" % i},
            )
        # mock get envs for all
        responses.add(
            responses.GET,
            "https://api.vercel.com/v5/projects/%s/env" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={
                "envs": [],
                "pagination": {
                    "count": 0
                },
            },
        )

        for i, name in enumerate(secret_names):
            responses.add(
                responses.POST,
                "https://api.vercel.com/v4/projects/%s/env" %
                "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
                json={
                    "value": "sec_%s" % i,
                    "target": "production",
                    "key": name
                },
            )
        responses.add(
            responses.POST,
            "https://api.vercel.com/v4/projects/%s/env" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={
                "value": "",
                "target": "production",
                "key": "VERCEL_GITHUB_COMMIT_SHA"
            },
        )

        org = self.organization
        project_id = self.project.id
        data = {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }
        enabled_dsn = ProjectKey.get_default(project=Project.objects.get(
            id=project_id)).get_dsn(public=True)
        sentry_auth_token = SentryAppInstallationForProvider.objects.get(
            organization=org.id, provider="vercel")
        sentry_auth_token = sentry_auth_token.sentry_app_installation.api_token.token
        integration = Integration.objects.get(provider=self.provider.key)
        installation = integration.get_installation(org.id)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {}
        with patch("sentry.integrations.vercel.integration.uuid4",
                   new=self.mock_uuid4):
            installation.update_organization_config(data)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }

        req_params = json.loads(responses.calls[7].request.body)
        assert req_params["name"] == "SENTRY_ORG_%s" % uuid
        assert req_params["value"] == org.slug

        req_params = json.loads(responses.calls[8].request.body)
        assert req_params["name"] == "SENTRY_PROJECT_%s" % uuid
        assert req_params["value"] == self.project.slug

        req_params = json.loads(responses.calls[9].request.body)
        assert req_params["name"] == "NEXT_PUBLIC_SENTRY_DSN_%s" % uuid
        assert req_params["value"] == enabled_dsn

        req_params = json.loads(responses.calls[10].request.body)
        assert req_params["name"] == "SENTRY_AUTH_TOKEN_%s" % uuid
        assert req_params["value"] == sentry_auth_token

        req_params = json.loads(responses.calls[12].request.body)
        assert req_params["key"] == "SENTRY_ORG"
        assert req_params["value"] == "sec_0"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[14].request.body)
        assert req_params["key"] == "SENTRY_PROJECT"
        assert req_params["value"] == "sec_1"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[16].request.body)
        assert req_params["key"] == "NEXT_PUBLIC_SENTRY_DSN"
        assert req_params["value"] == "sec_2"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[18].request.body)
        assert req_params["key"] == "SENTRY_AUTH_TOKEN"
        assert req_params["value"] == "sec_3"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[20].request.body)
        assert req_params["key"] == "VERCEL_GITHUB_COMMIT_SHA"
        assert req_params["value"] == ""
        assert req_params["target"] == "production"
예제 #21
0
    def test_python_lambda_setup_layer_success(self, mock_gen_aws_client,
                                               mock_get_supported_functions):
        mock_client = Mock()
        mock_gen_aws_client.return_value = mock_client
        mock_client.update_function_configuration = MagicMock()
        mock_client.describe_account = MagicMock(
            return_value={"Account": {
                "Name": "my_name"
            }})

        mock_get_supported_functions.return_value = [{
            "FunctionName":
            "lambdaA",
            "Handler":
            "lambda_handler.test_handler",
            "Runtime":
            "python3.6",
            "FunctionArn":
            "arn:aws:lambda:us-east-2:599817902985:function:lambdaA",
        }]

        aws_external_id = "12-323"
        self.pipeline.state.step_index = 2
        self.pipeline.state.data = {
            "region": region,
            "account_number": account_number,
            "aws_external_id": aws_external_id,
            "project_id": self.projectA.id,
        }

        sentry_project_dsn = ProjectKey.get_default(
            project=self.projectA).get_dsn(public=True)

        resp = self.client.post(
            self.setup_path,
            data={"lambdaA": True},
            format="json",
            HTTP_ACCEPT="application/json",
            headers={
                "Content-Type": "application/json",
                "Accept": "application/json"
            },
        )

        assert resp.status_code == 200

        mock_client.update_function_configuration.assert_called_once_with(
            FunctionName="lambdaA",
            Layers=["arn:aws:lambda:us-east-2:1234:layer:my-python-layer:34"],
            Environment={
                "Variables": {
                    "SENTRY_INITIAL_HANDLER": "lambda_handler.test_handler",
                    "SENTRY_DSN": sentry_project_dsn,
                    "SENTRY_TRACES_SAMPLE_RATE": "1.0",
                }
            },
            Handler=
            "sentry_sdk.integrations.init_serverless_sdk.sentry_lambda_handler",
        )

        integration = Integration.objects.get(provider=self.provider.key)
        assert integration.name == "my_name us-east-2"
        assert integration.external_id == "599817902985-us-east-2"
        assert integration.metadata == {
            "region": region,
            "account_number": account_number,
            "aws_external_id": aws_external_id,
        }
        assert OrganizationIntegration.objects.filter(
            integration=integration, organization=self.organization)
 def sentry_dsn(self):
     return ProjectKey.get_default(project=self.project).get_dsn(
         public=True)
예제 #23
0
    def test_update_organization_config(self):
        """Test that Vercel environment variables are created"""
        with self.tasks():
            self.assert_setup_flow()

        project_id = self.project.id
        secret_names = [
            "sentry_org",
            "sentry_project_%s" % project_id,
            "next_public_sentry_dsn_%s" % project_id,
        ]

        for i, name in enumerate(secret_names):
            responses.add(responses.GET,
                          "https://api.vercel.com/v3/now/secrets/%s" % name,
                          status=404)
            responses.add(
                responses.POST,
                "https://api.vercel.com/v2/now/secrets",
                json={"uid": "sec_%s" % i},
            )
        # mock get envs for all
        responses.add(
            responses.GET,
            "https://api.vercel.com/v5/projects/%s/env" %
            "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
            json={"envs": []},
        )

        for i, name in enumerate(secret_names):
            responses.add(
                responses.POST,
                "https://api.vercel.com/v4/projects/%s/env" %
                "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H",
                json={
                    "value": "sec_%s" % i,
                    "target": "production",
                    "key": name
                },
            )

        org = self.organization
        data = {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }
        enabled_dsn = ProjectKey.get_default(project=Project.objects.get(
            id=project_id)).get_dsn(public=True)
        integration = Integration.objects.get(provider=self.provider.key)
        installation = integration.get_installation(org.id)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {}
        installation.update_organization_config(data)
        org_integration = OrganizationIntegration.objects.get(
            organization_id=org.id, integration_id=integration.id)
        assert org_integration.config == {
            "project_mappings":
            [[project_id, "Qme9NXBpguaRxcXssZ1NWHVaM98MAL6PHDXUs1jPrgiM8H"]]
        }

        req_params = json.loads(responses.calls[5].request.body)
        assert req_params["name"] == "SENTRY_ORG"
        assert req_params["value"] == org.slug

        req_params = json.loads(responses.calls[7].request.body)
        assert req_params["name"] == "SENTRY_PROJECT_%s" % project_id
        assert req_params["value"] == self.project.slug

        req_params = json.loads(responses.calls[9].request.body)
        assert req_params["name"] == "NEXT_PUBLIC_SENTRY_DSN_%s" % project_id
        assert req_params["value"] == enabled_dsn

        req_params = json.loads(responses.calls[11].request.body)
        assert req_params["key"] == "SENTRY_ORG"
        assert req_params["value"] == "sec_0"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[13].request.body)
        assert req_params["key"] == "SENTRY_PROJECT"
        assert req_params["value"] == "sec_1"
        assert req_params["target"] == "production"

        req_params = json.loads(responses.calls[15].request.body)
        assert req_params["key"] == "NEXT_PUBLIC_SENTRY_DSN"
        assert req_params["value"] == "sec_2"
        assert req_params["target"] == "production"
예제 #24
0
    def test_lambda_setup_layer_success(self, mock_gen_aws_client,
                                        mock_get_supported_functions):
        mock_client = Mock()
        mock_gen_aws_client.return_value = mock_client
        mock_client.update_function_configuration = MagicMock()
        mock_client.describe_account = MagicMock(
            return_value={"Account": {
                "Name": "my_name"
            }})

        mock_get_supported_functions.return_value = [
            {
                "FunctionName":
                "lambdaA",
                "Runtime":
                "nodejs12.x",
                "FunctionArn":
                "arn:aws:lambda:us-east-2:599817902985:function:lambdaA",
            },
            {
                "FunctionName":
                "lambdaB",
                "Runtime":
                "nodejs10.x",
                "FunctionArn":
                "arn:aws:lambda:us-east-2:599817902985:function:lambdaB",
            },
        ]

        aws_external_id = "12-323"
        self.pipeline.state.step_index = 2
        self.pipeline.state.data = {
            "arn": arn,
            "aws_external_id": aws_external_id,
            "project_id": self.projectA.id,
        }

        sentry_project_dsn = ProjectKey.get_default(
            project=self.projectA).get_dsn(public=True)

        resp = self.client.post(
            self.setup_path,
            {"lambdaB": True},
            format="json",
            HTTP_ACCEPT="application/json",
            headers={
                "Content-Type": "application/json",
                "Accept": "application/json"
            },
        )

        assert resp.status_code == 200

        mock_client.update_function_configuration.assert_called_with(
            FunctionName="lambdaB",
            Layers=["arn:aws:lambda:us-east-2:1234:layer:my-layer:3"],
            Environment={
                "Variables": {
                    "NODE_OPTIONS": "-r @sentry/serverless/dist/auto",
                    "SENTRY_DSN": sentry_project_dsn,
                    "SENTRY_TRACES_SAMPLE_RATE": "1.0",
                }
            },
        )

        integration = Integration.objects.get(provider=self.provider.key)
        assert integration.name == "my_name us-east-2"
        assert integration.external_id == "599817902985-us-east-2"
        assert integration.metadata == {
            "arn": arn,
            "aws_external_id": aws_external_id
        }
        assert OrganizationIntegration.objects.filter(
            integration=integration, organization=self.organization)
예제 #25
0
    def update_organization_config(self, data):
        # data = {"project_mappings": [[sentry_project_id, vercel_project_id]]}

        vercel_client = self.get_client()
        config = self.org_integration.config
        try:
            new_mappings = data["project_mappings"]
        except KeyError:
            raise ValidationError("Failed to update configuration.")

        old_mappings = config.get("project_mappings") or []

        for mapping in new_mappings:
            # skip any mappings that already exist
            if mapping in old_mappings:
                continue

            [sentry_project_id, vercel_project_id] = mapping
            sentry_project = Project.objects.get(id=sentry_project_id)

            enabled_dsn = ProjectKey.get_default(project=sentry_project)
            if not enabled_dsn:
                raise ValidationError({
                    "project_mappings":
                    ["You must have an enabled DSN to continue!"]
                })

            sentry_project_dsn = enabled_dsn.get_dsn(public=True)

            vercel_project = vercel_client.get_project(vercel_project_id)
            source_code_provider = vercel_project.get("link", {}).get("type")

            if not source_code_provider:
                raise ValidationError({
                    "project_mappings": [
                        "You must connect your Vercel project to a Git repository to continue!"
                    ]
                })

            is_next_js = vercel_project.get("framework") == "nextjs"
            dsn_env_name = "NEXT_PUBLIC_SENTRY_DSN" if is_next_js else "SENTRY_DSN"

            sentry_auth_token = SentryAppInstallationToken.objects.get_token(
                sentry_project.organization.id,
                "vercel",
            )

            env_var_map = {
                "SENTRY_ORG": {
                    "type": "encrypted",
                    "value": sentry_project.organization.slug
                },
                "SENTRY_PROJECT": {
                    "type": "encrypted",
                    "value": sentry_project.slug
                },
                dsn_env_name: {
                    "type": "encrypted",
                    "value": sentry_project_dsn
                },
                "SENTRY_AUTH_TOKEN": {
                    "type": "encrypted",
                    "value": sentry_auth_token,
                },
                "VERCEL_GIT_COMMIT_SHA": {
                    "type": "system",
                    "value": "VERCEL_GIT_COMMIT_SHA"
                },
            }

            for env_var, details in env_var_map.items():
                self.create_env_var(vercel_client, vercel_project_id, env_var,
                                    details["value"], details["type"])

        config.update(data)
        self.org_integration.update(config=config)