-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat(cells) Make organization-create work in control silo #115238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -33,6 +33,7 @@ | |||||
| from sentry.models.organizationmapping import OrganizationMapping | ||||||
| from sentry.models.organizationmember import OrganizationMember | ||||||
| from sentry.models.organizationmembermapping import OrganizationMemberMapping | ||||||
| from sentry.organizations.services.organization import organization_service | ||||||
| from sentry.search.utils import tokenize_query | ||||||
| from sentry.services.organization import ( | ||||||
| OrganizationOptions, | ||||||
|
|
@@ -41,6 +42,12 @@ | |||||
| ) | ||||||
| from sentry.services.organization.provisioning import organization_provisioning_service | ||||||
| from sentry.silo.base import SiloMode | ||||||
| from sentry.types.cell import ( | ||||||
| CellResolutionError, | ||||||
| RegionCategory, | ||||||
| get_locality_by_name, | ||||||
| get_new_org_cell_for_locality, | ||||||
| ) | ||||||
| from sentry.users.services.user.serial import serialize_generic_user | ||||||
| from sentry.users.services.user.service import user_service | ||||||
| from sentry.utils.pagination_factory import PaginatorLike | ||||||
|
|
@@ -53,6 +60,7 @@ class OrganizationPostSerializer(BaseOrganizationSerializer): | |||||
| agreeTerms = serializers.BooleanField(required=True) | ||||||
| aggregatedDataConsent = serializers.BooleanField(required=False) | ||||||
| idempotencyKey = serializers.CharField(max_length=IDEMPOTENCY_KEY_LENGTH, required=False) | ||||||
| dataStorageLocation = serializers.CharField(required=False) | ||||||
|
|
||||||
| def __init__(self, *args, **kwargs): | ||||||
| super().__init__(*args, **kwargs) | ||||||
|
|
@@ -66,6 +74,37 @@ def validate_agreeTerms(self, value): | |||||
| raise serializers.ValidationError("This attribute is required.") | ||||||
| return value | ||||||
|
|
||||||
| def validate_slug(self, value: str) -> str: | ||||||
| if SiloMode.get_current_mode() == SiloMode.CONTROL: | ||||||
| value = self._validate_slug_shape(value) | ||||||
| if OrganizationMapping.objects.filter(slug=value).exists(): | ||||||
| raise serializers.ValidationError(f'The slug "{value}" is already in use.') | ||||||
|
|
||||||
| return value | ||||||
| # TODO(cells) remove this path when cell scoped provisioning is removed. | ||||||
| return super().validate_slug(value) | ||||||
|
|
||||||
| def validate_dataStorageLocation(self, value: str) -> str: | ||||||
| try: | ||||||
| locality = get_locality_by_name(value) | ||||||
| except CellResolutionError: | ||||||
| raise serializers.ValidationError(f"Unknown data storage location {value!r}.") | ||||||
| if locality.category != RegionCategory.MULTI_TENANT or not locality.visible: | ||||||
| raise serializers.ValidationError(f"Unknown data storage location {value!r}.") | ||||||
| return value | ||||||
|
|
||||||
| def validate(self, attrs): | ||||||
| attrs = super().validate(attrs) | ||||||
|
|
||||||
| locality_name = attrs.get("dataStorageLocation") | ||||||
| if locality_name is None and SiloMode.get_current_mode() == SiloMode.CONTROL: | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a few cases of
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both of the silo mode checks in this serializer can be removed once cell provisioning is removed. I'll add some TODO's for future me. |
||||||
| locality_name = settings.SENTRY_MONOLITH_REGION | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block is intended to handle the transition from cell -> control requests. I'm planning on removing this once we don't have any more cell scoped requests. |
||||||
|
|
||||||
| if locality_name is not None: | ||||||
| attrs["cell_name"] = get_new_org_cell_for_locality(locality_name).name | ||||||
|
cursor[bot] marked this conversation as resolved.
|
||||||
|
|
||||||
| return attrs | ||||||
|
|
||||||
|
|
||||||
| @extend_schema(tags=["Users"]) | ||||||
| @all_silo_endpoint | ||||||
|
|
@@ -312,7 +351,6 @@ def _get_from_control(self, request: Request) -> Response: | |||||
| paginator_cls=paginator_cls, | ||||||
| ) | ||||||
|
|
||||||
| # XXX: endpoint useless for end-users as it needs user context. | ||||||
| def post(self, request: Request) -> Response: | ||||||
| """ | ||||||
| Create a New Organization | ||||||
|
|
@@ -329,13 +367,6 @@ def post(self, request: Request) -> Response: | |||||
| terms of service and privacy policy. | ||||||
| :auth: required, user-context-needed | ||||||
| """ | ||||||
| # TODO(cells): Move org creation to control as part of the broader | ||||||
| # org-listing/org-provisioning cutover. Since POST is private, the | ||||||
| # legacy cell-side path can be removed once the control implementation | ||||||
| # is ready. | ||||||
| if SiloMode.get_current_mode() == SiloMode.CONTROL: | ||||||
| return Response(status=404) | ||||||
|
|
||||||
| if not request.user.is_authenticated: | ||||||
| return Response({"detail": "This endpoint requires user info"}, status=401) | ||||||
|
|
||||||
|
|
@@ -403,16 +434,14 @@ def post(self, request: Request) -> Response: | |||||
| ) | ||||||
|
|
||||||
| rpc_org = organization_provisioning_service.provision_organization_in_cell( | ||||||
| cell_name=settings.SENTRY_LOCAL_CELL or settings.SENTRY_MONOLITH_REGION, | ||||||
| cell_name=result.get("cell_name"), | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
how come it's nullable? |
||||||
| provisioning_options=provision_args, | ||||||
| ) | ||||||
| org = Organization.objects.get(id=rpc_org.id) | ||||||
|
|
||||||
| # TODO(hybrid-cloud): We'll need to catch a more generic error | ||||||
| # when the internal RPC is implemented. | ||||||
|
Comment on lines
-411
to
-412
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this applies for the cell-provisioned version right? is the comment removed since it's different on control?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This TODO is a few years old now, and mentions internal RPC being added (which it has). RPC calls don't raise |
||||||
| except IntegrityError: | ||||||
| return Response( | ||||||
| {"detail": "An organization with this slug already exists."}, status=409 | ||||||
| ) | ||||||
|
|
||||||
| return Response(serialize(org, request.user), status=201) | ||||||
| org_data = organization_service.serialize_organization(id=rpc_org.id, as_user=rpc_user) | ||||||
|
|
||||||
| return Response(org_data, status=201) | ||||||
|
sentry[bot] marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unchecked None from serialize_organization returns empty 201Medium Severity
Reviewed by Cursor Bugbot for commit 858c4e8. Configure here. |
||||||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How come it's not mandatory? Is this for self-hosted/ST or some specific environment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This serializer and logic is also used for the current cell scoped endpoint which doesn't receive
dataStorageLocation. Once we are fully control,dataStorageLocationwill be required.