Post stack slug to chatops proxy (#4559)

# What this PR does
It's part of work on
https://github.com/grafana/oncall-gateway/issues/247. I added stack_slug
to call to the chatops-proxy tenant/register API.
On a side note I figured out that we didn't cleanup slack integration in
chatops-proxy, once it's uninstalled on OnCall side, so it's
[fixed](https://github.com/grafana/oncall/pull/4559/files#diff-1784f1d0d65fa477f4562e73aa23fe1c757b171f36e03f12600bdb021f121307R577)
as well.
Changes are validated locally.
This commit is contained in:
Innokentii Konstantinov 2024-06-21 14:40:34 +08:00 committed by GitHub
parent d0ec596046
commit bc6272744b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 58 additions and 4 deletions

View file

@ -65,7 +65,7 @@ class ChatopsProxyAPIClient:
# OnCall Tenant
def register_tenant(
self, service_tenant_id: str, cluster_slug: str, service_type: str, stack_id: int
self, service_tenant_id: str, cluster_slug: str, service_type: str, stack_id: int, stack_slug: str
) -> tuple[Tenant, requests.models.Response]:
url = f"{self.api_base_url}/tenants/register"
d = {
@ -74,6 +74,7 @@ class ChatopsProxyAPIClient:
"cluster_slug": cluster_slug,
"service_type": service_type,
"stack_id": stack_id,
"stack_slug": stack_slug,
}
}
response = requests.post(url=url, json=d, headers=self._headers)
@ -170,6 +171,22 @@ class ChatopsProxyAPIClient:
self._check_response(response)
return OAuthInstallation(**response.json()["oauth_installation"]), response
def delete_oauth_installation(
self,
stack_id: int,
provider_type: str,
grafana_user_id: int,
) -> tuple[bool, requests.models.Response]:
url = f"{self.api_base_url}/oauth_installations/uninstall"
d = {
"stack_id": stack_id,
"provider_type": provider_type,
"grafana_user_id": grafana_user_id,
}
response = requests.post(url=url, json=d, headers=self._headers)
self._check_response(response)
return response.json()["removed"], response
def _check_response(self, response: requests.models.Response):
"""
Wraps an exceptional response to ChatopsProxyAPIException

View file

@ -18,14 +18,16 @@ def register_oncall_tenant_async(**kwargs):
cluster_slug = kwargs.get("cluster_slug")
service_type = kwargs.get("service_type")
stack_id = kwargs.get("stack_id")
stack_slug = kwargs.get("stack_slug")
client = ChatopsProxyAPIClient(settings.ONCALL_GATEWAY_URL, settings.ONCALL_GATEWAY_API_TOKEN)
try:
client.register_tenant(service_tenant_id, cluster_slug, service_type, stack_id)
client.register_tenant(service_tenant_id, cluster_slug, service_type, stack_id, stack_slug)
except ChatopsProxyAPIException as api_exc:
task_logger.error(
f'msg="Failed to register OnCall tenant: {api_exc.msg}" service_tenant_id={service_tenant_id} cluster_slug={cluster_slug}'
)
# TODO: remove this check once new upsert tenant api is released
if api_exc.status == 409:
# 409 Indicates that it's impossible to register tenant, because tenant already registered.
# Not retrying in this case, because manual conflict-resolution needed.

View file

@ -48,7 +48,7 @@ def get_slack_oauth_response_from_chatops_proxy(stack_id) -> dict:
return slack_installation.oauth_response
def register_oncall_tenant(service_tenant_id: str, cluster_slug: str, stack_id: int):
def register_oncall_tenant(service_tenant_id: str, cluster_slug: str, stack_id: int, stack_slug: str):
"""
register_oncall_tenant tries to register oncall tenant synchronously and fall back to task in case of any exceptions
to make sure that tenant is registered.
@ -61,6 +61,7 @@ def register_oncall_tenant(service_tenant_id: str, cluster_slug: str, stack_id:
cluster_slug,
SERVICE_TYPE_ONCALL,
stack_id,
stack_slug,
)
except Exception as e:
logger.error(
@ -141,3 +142,23 @@ def unlink_slack_team(service_tenant_id: str, slack_team_id: str):
"service_type": SERVICE_TYPE_ONCALL,
}
)
def uninstall_slack(stack_id: int, grafana_user_id: int) -> bool:
"""
uninstall_slack uninstalls slack integration from chatops-proxy and returns bool indicating if it was removed.
If such installation does not exist - returns True as well.s
"""
client = ChatopsProxyAPIClient(settings.ONCALL_GATEWAY_URL, settings.ONCALL_GATEWAY_API_TOKEN)
try:
removed, response = client.delete_oauth_installation(stack_id, PROVIDER_TYPE_SLACK, grafana_user_id)
except ChatopsProxyAPIException as api_exc:
if api_exc.status == 404:
return True
logger.exception(
"uninstall_slack: error trying to install slack from chatops-proxy: " "error=%s",
api_exc,
)
return False
return removed is True

View file

@ -15,6 +15,7 @@ from rest_framework.views import APIView
from apps.api.permissions import RBACPermission
from apps.auth_token.auth import PluginAuthentication
from apps.base.utils import live_settings
from apps.chatops_proxy.utils import uninstall_slack as uninstall_slack_from_chatops_proxy
from apps.slack.client import SlackClient
from apps.slack.errors import SlackAPIError
from apps.slack.scenarios.alertgroup_appearance import STEPS_ROUTING as ALERTGROUP_APPEARANCE_ROUTING
@ -573,8 +574,19 @@ class ResetSlackView(APIView):
"Grafana OnCall is temporary unable to connect your slack account or install OnCall to your slack workspace",
status=400,
)
if settings.UNIFIED_SLACK_APP_ENABLED:
# If unified slack app is enabled - uninstall slack integration from chatops-proxy first and on success -
# uninstall it from OnCall.
removed = uninstall_slack_from_chatops_proxy(request.user.organization.stack_id, request.user.user_id)
else:
# just a placeholder value to continute uninstallation until UNIFIED_SLACK_APP_ENABLED is not enabled
removed = True
if not removed:
return Response({"error": "Failed to uninstall slack integration"}, status=500)
try:
uninstall_slack_integration(request.user.organization, request.user)
except SlackInstallationExc as e:
return Response({"error": e.error_message}, status=400)
return Response(status=200)

View file

@ -61,7 +61,9 @@ class OrganizationQuerySet(models.QuerySet):
def create(self, **kwargs):
instance = super().create(**kwargs)
if settings.FEATURE_MULTIREGION_ENABLED:
register_oncall_tenant(str(instance.uuid), settings.ONCALL_BACKEND_REGION, instance.stack_id)
register_oncall_tenant(
str(instance.uuid), settings.ONCALL_BACKEND_REGION, instance.stack_id, instance.stack_slug
)
return instance
def delete(self):