Browse Source

Revert "feat(hybridcloud) Use control file for user/sentryapp avatars (#54118)"

This reverts commit 25d04ff6223f34b7f4f7b2d92e2c2505a457e6f2.

Co-authored-by: markstory <24086+markstory@users.noreply.github.com>
getsentry-bot 1 year ago
parent
commit
4e10fc336a

+ 1 - 0
pyproject.toml

@@ -746,6 +746,7 @@ module = [
     "sentry.tasks.deliver_from_outbox",
     "sentry.tasks.derive_code_mappings",
     "sentry.tasks.digests",
+    "sentry.tasks.files",
     "sentry.tasks.groupowner",
     "sentry.tasks.integrations",
     "sentry.tasks.integrations.create_comment",

+ 16 - 26
src/sentry/models/avatars/base.py

@@ -43,11 +43,10 @@ class AvatarBase(Model):
         return super().save(*args, **kwargs)
 
     def get_file(self):
-        # If we're getting a file, and the preferred write file
-        # type isn't present, move data over to new storage async.
-        file_id = getattr(self, self.file_write_fk(), None)
+        # Favor control_file_id if it exists and is set.
+        # Otherwise fallback to file_id. If still None, return.
         file_class = self.file_class()
-
+        file_id = getattr(self, self.file_fk())
         if file_id is None:
             file_id = self.file_id
             file_class = File
@@ -71,6 +70,9 @@ class AvatarBase(Model):
             self.update(**update)
             return None
 
+    def get_file_id(self):
+        return self.file_id
+
     def delete(self, *args, **kwargs):
         file = self.get_file()
         if file:
@@ -101,32 +103,21 @@ class AvatarBase(Model):
                 cache.set(cache_key, photo)
         return photo
 
-    def file_class(self):
-        return File
+    @classmethod
+    def file_class(cls):
+        from sentry.models import File
 
-    def file_fk(self) -> str:
-        """
-        Get the foreign key currently used by this record for blob storage.
-        Varies in ControlAvatarBase
-        """
-        return "file_id"
+        return File
 
-    def file_write_fk(self) -> str:
-        """
-        Get the foreign key that should be used for writes.
-        Varies in ControlAvatarBase
-        """
+    @classmethod
+    def file_fk(cls) -> str:
         return "file_id"
 
     @classmethod
     def save_avatar(cls, relation, type, avatar=None, filename=None, color=None):
         if avatar:
-            # Create an instance of the current class so we can
-            # access where new files should be stored.
-            dummy = cls()
-            file_class = dummy.file_class()
-            with atomic_transaction(using=router.db_for_write(file_class)):
-                photo = file_class.objects.create(name=filename, type=cls.FILE_TYPE)
+            with atomic_transaction(using=router.db_for_write(cls.file_class())):
+                photo = cls.file_class().objects.create(name=filename, type=cls.FILE_TYPE)
                 # XXX: Avatar may come in as a string instance in python2
                 # if it's not wrapped in BytesIO.
                 if isinstance(avatar, str):
@@ -150,12 +141,11 @@ class AvatarBase(Model):
                 file.delete()
 
             if photo:
-                if instance.file_fk() != instance.file_write_fk():
-                    setattr(instance, instance.file_fk(), None)
-                setattr(instance, instance.file_write_fk(), photo.id)
+                setattr(instance, cls.file_fk(), photo.id)
                 instance.ident = uuid4().hex
 
             instance.avatar_type = [i for i, n in cls.AVATAR_TYPES if n == type][0]
+
             instance.save()
 
         if photo and not created:

+ 12 - 24
src/sentry/models/avatars/control_base.py

@@ -1,8 +1,6 @@
-from typing import Type, Union
-
 from sentry.db.models import BoundedBigIntegerField
 from sentry.models.avatars.base import AvatarBase
-from sentry.models.files import ControlFile, File
+from sentry.models.files import ControlFileBlob, File
 
 
 class ControlAvatarBase(AvatarBase):
@@ -11,29 +9,19 @@ class ControlAvatarBase(AvatarBase):
     class Meta:
         abstract = True
 
-    def file_class(self) -> Union[Type[ControlFile], Type[File]]:
-        """
-        Select the file class this avatar has used.
-        File classes can vary by the avatar as we have migrated
-        storage for saas, but self-hosted and single-tenant instances
-        did not have relations and storage migrated.
-        """
-        if self.control_file_id:
+    @classmethod
+    def file_class(cls):
+        from sentry.models import ControlFile
+
+        if ControlFileBlob._storage_config():
             return ControlFile
-        if self.file_id:
-            return File
-        return ControlFile
+        return File
 
-    def file_fk(self) -> str:
-        if self.control_file_id:
+    @classmethod
+    def file_fk(cls) -> str:
+        if ControlFileBlob._storage_config():
             return "control_file_id"
-        if self.file_id:
-            return "file_id"
-        return "control_file_id"
-
-    def file_write_fk(self) -> str:
-        """Prefer controlfile as user/sentryapp avatars are stored in control silo"""
-        return "control_file_id"
+        return "file_id"
 
-    def get_file_id(self) -> int:
+    def get_file_id(self):
         return self.control_file_id or self.file_id

+ 2 - 21
src/sentry/models/avatars/user_avatar.py

@@ -1,7 +1,5 @@
 from __future__ import annotations
 
-from enum import IntEnum
-
 from django.db import models
 
 from sentry.db.models import BaseManager, FlexibleForeignKey, control_silo_only_model
@@ -9,23 +7,6 @@ from sentry.db.models import BaseManager, FlexibleForeignKey, control_silo_only_
 from . import ControlAvatarBase
 
 
-class UserAvatarType(IntEnum):
-    LETTER_AVATAR = 0
-    UPLOAD = 1
-    GRAVATAR = 2
-
-    def api_name(self):
-        return self.name.lower()
-
-    @classmethod
-    def as_choices(cls) -> tuple[tuple[int, str], ...]:
-        return (
-            (cls.LETTER_AVATAR, cls.LETTER_AVATAR.api_name()),
-            (cls.UPLOAD, cls.UPLOAD.api_name()),
-            (cls.GRAVATAR, cls.GRAVATAR.api_name()),
-        )
-
-
 @control_silo_only_model
 class UserAvatar(ControlAvatarBase):
     """
@@ -33,12 +14,12 @@ class UserAvatar(ControlAvatarBase):
     and contains their preferences for avatar type.
     """
 
-    AVATAR_TYPES = UserAvatarType.as_choices()
+    AVATAR_TYPES = ((0, "letter_avatar"), (1, "upload"), (2, "gravatar"))
 
     FILE_TYPE = "avatar.file"
 
     user = FlexibleForeignKey("sentry.User", unique=True, related_name="avatar")
-    avatar_type = models.PositiveSmallIntegerField(default=0, choices=UserAvatarType.as_choices())
+    avatar_type = models.PositiveSmallIntegerField(default=0, choices=AVATAR_TYPES)
 
     objects = BaseManager(cache_fields=["user"])
 

+ 19 - 33
src/sentry/models/files/control_fileblob.py

@@ -1,8 +1,3 @@
-from __future__ import annotations
-
-from typing import Any, Dict
-
-from sentry import options
 from sentry.db.models import control_silo_only_model
 from sentry.models.files.abstractfileblob import AbstractFileBlob
 from sentry.models.files.control_fileblobowner import ControlFileBlobOwner
@@ -10,32 +5,6 @@ from sentry.options.manager import UnknownOption
 from sentry.tasks.files import delete_file_control
 
 
-def control_file_storage_config() -> Dict[str, Any] | None:
-    """
-    When sentry is deployed in a siloed mode file relations
-    used by control silo models are stored separately from
-    region silo resources.
-
-    While we consistently write to the ControlFile django
-    model for control silo resources, we can't ensure
-    that each deployment has separate control + region storage
-    backends. We coalesce those options here. None means use the
-    global default storage options.
-    """
-    try:
-        # If these options exist, use them. Otherwise fallback to default behavior
-        storage_backend = options.get("filestore.control.backend")
-        storage_options = options.get("filestore.control.options")
-        if storage_backend:
-            return {
-                "backend": storage_backend,
-                "options": storage_options,
-            }
-    except UnknownOption:
-        pass
-    return None
-
-
 @control_silo_only_model
 class ControlFileBlob(AbstractFileBlob):
     class Meta:
@@ -45,6 +14,23 @@ class ControlFileBlob(AbstractFileBlob):
     FILE_BLOB_OWNER_MODEL = ControlFileBlobOwner
     DELETE_FILE_TASK = delete_file_control
 
+    # TODO(hybrid-cloud): This is a temporary measure to allow concurrent production deployments of filestore
+    # We override the behavior of only the control silo file storage implementation to use
+    # the new control instance while production runs in monolith mode
     @classmethod
-    def _storage_config(cls) -> Dict[str, Any] | None:
-        return control_file_storage_config()
+    def _storage_config(cls):
+        from sentry import options as options_store
+
+        config = None
+        try:
+            # If these options exist, use them. Otherwise fallback to default behavior
+            backend = options_store.get("filestore.control.backend")
+            options = options_store.get("filestore.control.options")
+            if backend:
+                config = {
+                    "backend": backend,
+                    "options": options,
+                }
+        except UnknownOption:
+            pass
+        return config

+ 8 - 7
src/sentry/tasks/files.py

@@ -113,15 +113,17 @@ def copy_file_to_control_and_update_model(
     file_id: int,
     **kwargs,
 ):
-    from sentry.models.files import ControlFile, File
+    from sentry.models.files import ControlFile, ControlFileBlob, File
+
+    if ControlFileBlob._storage_config() is None:
+        return
 
     lock = f"copy-file-lock-{model_name}:{model_id}"
 
     with locks.get(lock, duration=60, name="copy-file-lock").acquire():
         # Short circuit duplicate copy calls
-        model_class = apps.get_model(app_name, model_name)
-        instance = model_class.objects.get(id=model_id)
-        if instance.control_file_id:
+        model = apps.get_app_config(app_name).get_model(model_name).objects.get(id=model_id)
+        if model.control_file_id:
             return
 
         file_model = File.objects.get(id=file_id)
@@ -137,6 +139,5 @@ def copy_file_to_control_and_update_model(
         )
         control_file.putfile(file_handle)
 
-        instance.control_file_id = control_file.id
-        instance.file_id = None
-        instance.save()
+        model.control_file_id = control_file.id
+        model.save()

+ 2 - 46
tests/sentry/api/endpoints/test_sentry_app_avatar.py

@@ -2,7 +2,6 @@ from base64 import b64encode
 
 from sentry import options as options_store
 from sentry.models import SentryAppAvatar
-from sentry.models.files.control_file import ControlFile
 from sentry.testutils.cases import APITestCase
 from sentry.testutils.silo import control_silo_test
 
@@ -38,7 +37,7 @@ class SentryAppAvatarTestBase(APITestCase):
 
 
 @control_silo_test(stable=True)
-class SentryAppAvatarGetTest(SentryAppAvatarTestBase):
+class SentryAppAvatarTest(SentryAppAvatarTestBase):
     def test_get(self):
         response = self.get_success_response(self.unpublished_app.slug)
 
@@ -54,30 +53,6 @@ class SentryAppAvatarGetTest(SentryAppAvatarTestBase):
         assert simple_avatar["color"] is False
         assert response.data["uuid"] == str(self.unpublished_app.uuid)
 
-    def test_get_upload_control_file(self):
-        self.method = "put"
-        self.create_avatar(is_color=True)
-
-        self.method = "get"
-        self.create_avatar(is_color=True)
-        response = self.get_success_response(self.unpublished_app.slug)
-
-        assert response.status_code == 200, response.content
-        assert response.data["uuid"] == str(self.unpublished_app.uuid)
-        uploads = [
-            avatar for avatar in response.data["avatars"] if avatar["avatarType"] == "upload"
-        ]
-        upload = uploads[0]
-        assert upload["avatarType"] == "upload"
-        assert upload["avatarUuid"]
-
-        avatar = SentryAppAvatar.objects.filter(sentry_app_id=self.unpublished_app.id).first()
-        assert avatar
-        assert avatar.get_file_id()
-        assert avatar.control_file_id
-        file = avatar.get_file()
-        assert isinstance(file, ControlFile)
-
 
 @control_silo_test(stable=True)
 class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
@@ -90,25 +65,11 @@ class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
         assert avatar.get_file_id()
         assert avatar.get_avatar_type_display() == "upload"
         color_avatar = self.get_avatar(resp)
-        assert color_avatar
-        assert color_avatar["avatarType"] == "upload"
-        assert color_avatar["avatarUuid"] is not None
-        assert color_avatar["color"] is True
-
-    def test_upload_control_file(self):
-        resp = self.create_avatar(is_color=True)
-
-        avatar = SentryAppAvatar.objects.get(sentry_app=self.unpublished_app, color=True)
-        assert avatar.get_file_id()
-        assert avatar.control_file_id
-        assert avatar.get_avatar_type_display() == "upload"
-        color_avatar = self.get_avatar(resp)
-        assert color_avatar
         assert color_avatar["avatarType"] == "upload"
         assert color_avatar["avatarUuid"] is not None
         assert color_avatar["color"] is True
 
-    def test_upload_control_with_storage_options(self):
+    def test_upload_control(self):
         with self.options(
             {
                 "filestore.control.backend": options_store.get("filestore.backend"),
@@ -121,7 +82,6 @@ class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
             assert avatar.get_file_id()
             assert avatar.get_avatar_type_display() == "upload"
             color_avatar = self.get_avatar(resp)
-            assert color_avatar
             assert color_avatar["avatarType"] == "upload"
             assert color_avatar["avatarUuid"] is not None
             assert color_avatar["color"] is True
@@ -136,7 +96,6 @@ class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
         assert avatars[0].get_file_id()
         assert avatars[0].get_avatar_type_display() == "upload"
         color_avatar = self.get_avatar(resp)
-        assert color_avatar
         assert color_avatar["color"] is True
         assert color_avatar["avatarType"] == "upload"
         assert color_avatar["avatarUuid"] is not None
@@ -144,7 +103,6 @@ class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
         assert avatars[1].get_file_id()
         assert avatars[1].get_avatar_type_display() == "upload"
         simple_avatar = self.get_avatar(resp, False)
-        assert simple_avatar
         assert simple_avatar["color"] is False
         assert simple_avatar["avatarType"] == "upload"
         assert simple_avatar["avatarUuid"] is not None
@@ -171,12 +129,10 @@ class SentryAppAvatarPutTest(SentryAppAvatarTestBase):
         color_avatar = self.get_avatar(response)
         simple_avatar = self.get_avatar(response, False)
 
-        assert color_avatar
         assert color_avatar["avatarType"] == "default"
         assert color_avatar["avatarUuid"] is not None
         assert color_avatar["color"] is True
 
-        assert simple_avatar
         assert simple_avatar["avatarType"] == "default"
         assert simple_avatar["avatarUuid"] is not None
         assert simple_avatar["color"] is False

+ 24 - 145
tests/sentry/api/endpoints/test_user_avatar.py

@@ -1,20 +1,17 @@
 from base64 import b64encode
-from io import BytesIO
-from unittest import mock
 
 from django.urls import reverse
 
 from sentry import options as options_store
-from sentry.models.avatars.user_avatar import UserAvatar, UserAvatarType
-from sentry.models.files import ControlFile, File
-from sentry.silo.base import SiloMode
+from sentry.models import UserAvatar
+from sentry.models.files import ControlFile
 from sentry.testutils.cases import APITestCase
-from sentry.testutils.silo import assume_test_silo_mode, control_silo_test
+from sentry.testutils.silo import control_silo_test
 
 
 @control_silo_test(stable=True)
 class UserAvatarTest(APITestCase):
-    def test_get_letter_avatar(self):
+    def test_get(self):
         user = self.create_user(email="a@example.com")
 
         self.login_as(user=user)
@@ -27,84 +24,7 @@ class UserAvatarTest(APITestCase):
         assert response.data["avatar"]["avatarType"] == "letter_avatar"
         assert response.data["avatar"]["avatarUuid"] is None
 
-    def test_get_gravatar(self):
-        user = self.create_user(email="a@example.com")
-        UserAvatar.objects.create(user=user, avatar_type=UserAvatarType.GRAVATAR)
-
-        self.login_as(user=user)
-
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
-        response = self.client.get(url, format="json")
-
-        assert response.status_code == 200, response.content
-        assert response.data["id"] == str(user.id)
-        assert response.data["avatar"]["avatarType"] == "gravatar"
-        assert response.data["avatar"]["avatarUuid"] is None
-
-    def test_get_upload_control_file(self):
-        user = self.create_user(email="a@example.com")
-
-        photo = ControlFile.objects.create(name="test.png", type="avatar.file")
-        photo.putfile(BytesIO(b"test"))
-        UserAvatar.objects.create(
-            user=user, control_file_id=photo.id, avatar_type=UserAvatarType.UPLOAD
-        )
-
-        self.login_as(user=user)
-
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
-        response = self.client.get(url, format="json")
-
-        assert response.status_code == 200, response.content
-        assert response.data["id"] == str(user.id)
-        assert response.data["avatar"]["avatarType"] == "upload"
-        assert response.data["avatar"]["avatarUuid"]
-
-    def test_get_upload_file(self):
-        user = self.create_user(email="a@example.com")
-
-        with assume_test_silo_mode(SiloMode.REGION):
-            photo = File.objects.create(name="test.png", type="avatar.file")
-            photo.putfile(BytesIO(b"test"))
-        UserAvatar.objects.create(user=user, file_id=photo.id, avatar_type=UserAvatarType.UPLOAD)
-
-        self.login_as(user=user)
-
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
-        response = self.client.get(url, format="json")
-
-        assert response.status_code == 200, response.content
-        assert response.data["id"] == str(user.id)
-        assert response.data["avatar"]["avatarType"] == "upload"
-        assert response.data["avatar"]["avatarUuid"]
-
-    def test_get_prefers_control_file(self):
-        user = self.create_user(email="a@example.com")
-        with assume_test_silo_mode(SiloMode.REGION):
-            photo = File.objects.create(name="test.png", type="avatar.file")
-            photo.putfile(BytesIO(b"test"))
-        controlphoto = ControlFile.objects.create(name="control_test.png", type="avatar.file")
-        controlphoto.putfile(BytesIO(b"control test"))
-
-        avatar = UserAvatar.objects.create(
-            user=user,
-            file_id=photo.id,
-            control_file_id=controlphoto.id,
-            avatar_type=UserAvatarType.UPLOAD,
-        )
-
-        self.login_as(user=user)
-
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
-        response = self.client.get(url, format="json")
-
-        assert response.status_code == 200, response.content
-        assert response.data["id"] == str(user.id)
-        assert response.data["avatar"]["avatarType"] == "upload"
-        assert response.data["avatar"]["avatarUuid"]
-        assert isinstance(avatar.get_file(), ControlFile)
-
-    def test_put_gravatar(self):
+    def test_gravatar(self):
         user = self.create_user(email="a@example.com")
 
         self.login_as(user=user)
@@ -116,7 +36,7 @@ class UserAvatarTest(APITestCase):
         assert response.status_code == 200, response.content
         assert avatar.get_avatar_type_display() == "gravatar"
 
-    def test_put_upload(self):
+    def test_upload(self):
         user = self.create_user(email="a@example.com")
 
         self.login_as(user=user)
@@ -135,47 +55,14 @@ class UserAvatarTest(APITestCase):
         assert response.status_code == 200, response.content
         assert avatar.get_avatar_type_display() == "upload"
         assert avatar.get_file_id()
-        assert avatar.control_file_id, "new files are control files"
-        assert ControlFile.objects.filter(id=avatar.control_file_id).exists()
-
-    def test_put_upload_saves_to_control_file(self):
-        user = self.create_user(email="a@example.com")
-
-        self.login_as(user=user)
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
 
+    def test_transition_to_control_before_options_set(self):
         with self.tasks():
-            response = self.client.put(
-                url,
-                data={
-                    "avatar_type": "upload",
-                    "avatar_photo": b64encode(self.load_fixture("avatar.jpg")),
-                },
-                format="json",
-            )
-
-        avatar = UserAvatar.objects.get(user=user)
-        assert response.status_code == 200, response.content
-        assert avatar.get_file_id()
-        assert avatar.control_file_id
-        assert isinstance(avatar.get_file(), ControlFile)
-        assert ControlFile.objects.filter(id=avatar.control_file_id).exists()
-
-    @mock.patch("sentry.tasks.files.copy_file_to_control_and_update_model.apply_async")
-    def test_get_upload_use_control_during_update(self, mock_task):
-        user = self.create_user(email="a@example.com")
-
-        with assume_test_silo_mode(SiloMode.REGION):
-            photo = File.objects.create(name="test.png", type="avatar.file")
-            photo.putfile(BytesIO(b"test"))
-        UserAvatar.objects.create(user=user, file_id=photo.id, avatar_type=UserAvatarType.UPLOAD)
-
-        self.login_as(user=user)
+            user = self.create_user(email="a@example.com")
 
-        url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
+            self.login_as(user=user)
 
-        # Run in monolith as this transitional operation can only occur there.
-        with assume_test_silo_mode(SiloMode.MONOLITH):
+            url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
             response = self.client.put(
                 url,
                 data={
@@ -185,32 +72,24 @@ class UserAvatarTest(APITestCase):
                 format="json",
             )
 
-        assert response.status_code == 200, response.content
-        assert response.data["id"] == str(user.id)
-        assert response.data["avatar"]["avatarType"] == "upload"
-        assert response.data["avatar"]["avatarUuid"]
-        assert mock_task.call_count == 1
-
-        avatar = UserAvatar.objects.get(user=user)
-        assert avatar
-        assert avatar.get_file_id()
-        assert avatar.file_id is None, "non-control file relation be removed."
-        assert avatar.control_file_id
-        assert isinstance(avatar.get_file(), ControlFile)
+            avatar = UserAvatar.objects.get(user=user)
+            assert response.status_code == 200, response.content
+            assert avatar.get_file_id()
+            assert isinstance(avatar.get_file(), ControlFile)
 
-    def test_put_upload_saves_to_control_file_with_separate_storage(self):
+    def test_transition_to_control_after_options_set(self):
         with self.options(
             {
                 "filestore.control.backend": options_store.get("filestore.backend"),
                 "filestore.control.options": options_store.get("filestore.options"),
             }
         ):
-            user = self.create_user(email="a@example.com")
+            with self.tasks():
+                user = self.create_user(email="a@example.com")
 
-            self.login_as(user=user)
-            url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
+                self.login_as(user=user)
 
-            with self.tasks():
+                url = reverse("sentry-api-0-user-avatar", kwargs={"user_id": "me"})
                 response = self.client.put(
                     url,
                     data={
@@ -219,11 +98,11 @@ class UserAvatarTest(APITestCase):
                     },
                     format="json",
                 )
-            avatar = UserAvatar.objects.get(user=user)
-            assert response.status_code == 200, response.content
-            assert avatar.get_file_id()
-            assert isinstance(avatar.get_file(), ControlFile)
-            assert ControlFile.objects.filter(id=avatar.control_file_id).exists()
+
+                avatar = UserAvatar.objects.get(user=user)
+                assert response.status_code == 200, response.content
+                assert avatar.get_file_id()
+                assert isinstance(avatar.get_file(), ControlFile)
 
     def test_put_bad(self):
         user = self.create_user(email="a@example.com")

+ 3 - 4
tests/sentry/models/test_avatar.py

@@ -43,10 +43,9 @@ class AvatarMigrationTestCase(TestCase):
         ):
             with self.tasks():
                 assert isinstance(avatar.get_file(), File)
-            avatar = UserAvatar.objects.get(id=avatar.id)
-            assert avatar.control_file_id is not None
-            assert avatar.file_id is None
-            assert isinstance(avatar.get_file(), ControlFile)
+                avatar = UserAvatar.objects.get(id=avatar.id)
+                assert avatar.control_file_id is not None
+                assert isinstance(avatar.get_file(), ControlFile)
 
 
 @region_silo_test(stable=True)

+ 0 - 14
tests/sentry/web/frontend/test_user_avatar.py

@@ -3,7 +3,6 @@ from io import BytesIO
 from django.urls import reverse
 
 from sentry.models import File, UserAvatar
-from sentry.models.files.control_file import ControlFile
 from sentry.silo import SiloMode
 from sentry.testutils.cases import TestCase
 from sentry.testutils.silo import assume_test_silo_mode, control_silo_test
@@ -24,16 +23,3 @@ class UserAvatarTest(TestCase):
         assert response["Cache-Control"] == FOREVER_CACHE
         assert response.get("Vary") is None
         assert response.get("Set-Cookie") is None
-
-    def test_headers_control_file(self):
-        user = self.create_user(email="a@example.com")
-        photo = ControlFile.objects.create(name="test.png", type="avatar.file")
-        photo.putfile(BytesIO(b"test"))
-        avatar = UserAvatar.objects.create(user=user, control_file_id=photo.id)
-
-        url = reverse("sentry-user-avatar-url", kwargs={"avatar_id": avatar.ident})
-        response = self.client.get(url)
-        assert response.status_code == 200
-        assert response["Cache-Control"] == FOREVER_CACHE
-        assert response.get("Vary") is None
-        assert response.get("Set-Cookie") is None