databricks-sdk 0.27.0__tar.gz → 0.28.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of databricks-sdk might be problematic. Click here for more details.
- {databricks-sdk-0.27.0/databricks_sdk.egg-info → databricks-sdk-0.28.0}/PKG-INFO +1 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/__init__.py +9 -9
- databricks-sdk-0.28.0/databricks/sdk/azure.py +27 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/config.py +6 -9
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/core.py +12 -4
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/environments.py +34 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/__init__.py +1 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/mapper.py +4 -0
- databricks-sdk-0.28.0/databricks/sdk/errors/private_link.py +60 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/catalog.py +666 -628
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/compute.py +72 -105
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/jobs.py +1 -12
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/marketplace.py +9 -31
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/pipelines.py +118 -3
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/serving.py +78 -10
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/sharing.py +37 -2
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/sql.py +0 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/vectorsearch.py +188 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/workspace.py +8 -4
- databricks-sdk-0.28.0/databricks/sdk/version.py +1 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0/databricks_sdk.egg-info}/PKG-INFO +1 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks_sdk.egg-info/SOURCES.txt +1 -0
- databricks-sdk-0.27.0/databricks/sdk/azure.py +0 -54
- databricks-sdk-0.27.0/databricks/sdk/version.py +0 -1
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/LICENSE +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/NOTICE +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/README.md +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/__init__.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/_property.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/_widgets/__init__.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/_widgets/default_widgets_utils.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/_widgets/ipywidgets_utils.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/casing.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/clock.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/credentials_provider.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/dbutils.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/base.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/overrides.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/platform.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/errors/sdk.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/mixins/__init__.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/mixins/compute.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/mixins/files.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/mixins/workspace.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/oauth.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/py.typed +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/retries.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/runtime/__init__.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/runtime/dbutils_stub.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/__init__.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/_internal.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/billing.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/dashboards.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/files.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/iam.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/ml.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/oauth2.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/provisioning.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks/sdk/service/settings.py +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks_sdk.egg-info/dependency_links.txt +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks_sdk.egg-info/requires.txt +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/databricks_sdk.egg-info/top_level.txt +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/setup.cfg +0 -0
- {databricks-sdk-0.27.0 → databricks-sdk-0.28.0}/setup.py +0 -0
|
@@ -13,9 +13,9 @@ from databricks.sdk.service.catalog import (AccountMetastoreAssignmentsAPI,
|
|
|
13
13
|
ArtifactAllowlistsAPI, CatalogsAPI,
|
|
14
14
|
ConnectionsAPI,
|
|
15
15
|
ExternalLocationsAPI, FunctionsAPI,
|
|
16
|
-
GrantsAPI,
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
GrantsAPI, MetastoresAPI,
|
|
17
|
+
ModelVersionsAPI, OnlineTablesAPI,
|
|
18
|
+
QualityMonitorsAPI,
|
|
19
19
|
RegisteredModelsAPI, SchemasAPI,
|
|
20
20
|
StorageCredentialsAPI,
|
|
21
21
|
SystemSchemasAPI,
|
|
@@ -194,7 +194,6 @@ class WorkspaceClient:
|
|
|
194
194
|
self._instance_profiles = InstanceProfilesAPI(self._api_client)
|
|
195
195
|
self._ip_access_lists = IpAccessListsAPI(self._api_client)
|
|
196
196
|
self._jobs = JobsAPI(self._api_client)
|
|
197
|
-
self._lakehouse_monitors = LakehouseMonitorsAPI(self._api_client)
|
|
198
197
|
self._lakeview = LakeviewAPI(self._api_client)
|
|
199
198
|
self._libraries = LibrariesAPI(self._api_client)
|
|
200
199
|
self._metastores = MetastoresAPI(self._api_client)
|
|
@@ -214,6 +213,7 @@ class WorkspaceClient:
|
|
|
214
213
|
self._api_client)
|
|
215
214
|
self._provider_providers = ProviderProvidersAPI(self._api_client)
|
|
216
215
|
self._providers = ProvidersAPI(self._api_client)
|
|
216
|
+
self._quality_monitors = QualityMonitorsAPI(self._api_client)
|
|
217
217
|
self._queries = QueriesAPI(self._api_client)
|
|
218
218
|
self._query_history = QueryHistoryAPI(self._api_client)
|
|
219
219
|
self._query_visualizations = QueryVisualizationsAPI(self._api_client)
|
|
@@ -425,11 +425,6 @@ class WorkspaceClient:
|
|
|
425
425
|
"""The Jobs API allows you to create, edit, and delete jobs."""
|
|
426
426
|
return self._jobs
|
|
427
427
|
|
|
428
|
-
@property
|
|
429
|
-
def lakehouse_monitors(self) -> LakehouseMonitorsAPI:
|
|
430
|
-
"""A monitor computes and monitors data or model quality metrics for a table over time."""
|
|
431
|
-
return self._lakehouse_monitors
|
|
432
|
-
|
|
433
428
|
@property
|
|
434
429
|
def lakeview(self) -> LakeviewAPI:
|
|
435
430
|
"""These APIs provide specific management operations for Lakeview dashboards."""
|
|
@@ -520,6 +515,11 @@ class WorkspaceClient:
|
|
|
520
515
|
"""A data provider is an object representing the organization in the real world who shares the data."""
|
|
521
516
|
return self._providers
|
|
522
517
|
|
|
518
|
+
@property
|
|
519
|
+
def quality_monitors(self) -> QualityMonitorsAPI:
|
|
520
|
+
"""A monitor computes and monitors data or model quality metrics for a table over time."""
|
|
521
|
+
return self._quality_monitors
|
|
522
|
+
|
|
523
523
|
@property
|
|
524
524
|
def queries(self) -> QueriesAPI:
|
|
525
525
|
"""These endpoints are used for CRUD operations on query definitions."""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from .oauth import TokenSource
|
|
4
|
+
from .service.provisioning import Workspace
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def add_workspace_id_header(cfg: 'Config', headers: Dict[str, str]):
|
|
8
|
+
if cfg.azure_workspace_resource_id:
|
|
9
|
+
headers["X-Databricks-Azure-Workspace-Resource-Id"] = cfg.azure_workspace_resource_id
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def add_sp_management_token(token_source: 'TokenSource', headers: Dict[str, str]):
|
|
13
|
+
mgmt_token = token_source.token()
|
|
14
|
+
headers['X-Databricks-Azure-SP-Management-Token'] = mgmt_token.access_token
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_azure_resource_id(workspace: Workspace):
|
|
18
|
+
"""
|
|
19
|
+
Returns the Azure Resource ID for the given workspace, if it is an Azure workspace.
|
|
20
|
+
:param workspace:
|
|
21
|
+
:return:
|
|
22
|
+
"""
|
|
23
|
+
if workspace.azure_workspace_info is None:
|
|
24
|
+
return None
|
|
25
|
+
return (f'/subscriptions/{workspace.azure_workspace_info.subscription_id}'
|
|
26
|
+
f'/resourceGroups/{workspace.azure_workspace_info.resource_group}'
|
|
27
|
+
f'/providers/Microsoft.Databricks/workspaces/{workspace.workspace_name}')
|
|
@@ -10,11 +10,10 @@ from typing import Dict, Iterable, Optional
|
|
|
10
10
|
|
|
11
11
|
import requests
|
|
12
12
|
|
|
13
|
-
from .azure import AzureEnvironment
|
|
14
13
|
from .clock import Clock, RealClock
|
|
15
14
|
from .credentials_provider import CredentialsProvider, DefaultCredentials
|
|
16
|
-
from .environments import (ALL_ENVS,
|
|
17
|
-
DatabricksEnvironment)
|
|
15
|
+
from .environments import (ALL_ENVS, AzureEnvironment, Cloud,
|
|
16
|
+
DatabricksEnvironment, get_environment_for_hostname)
|
|
18
17
|
from .oauth import OidcEndpoints
|
|
19
18
|
from .version import __version__
|
|
20
19
|
|
|
@@ -154,11 +153,7 @@ class Config:
|
|
|
154
153
|
"""Returns the environment based on configuration."""
|
|
155
154
|
if self.databricks_environment:
|
|
156
155
|
return self.databricks_environment
|
|
157
|
-
if self.host:
|
|
158
|
-
for environment in ALL_ENVS:
|
|
159
|
-
if self.host.endswith(environment.dns_zone):
|
|
160
|
-
return environment
|
|
161
|
-
if self.azure_workspace_resource_id:
|
|
156
|
+
if not self.host and self.azure_workspace_resource_id:
|
|
162
157
|
azure_env = self._get_azure_environment_name()
|
|
163
158
|
for environment in ALL_ENVS:
|
|
164
159
|
if environment.cloud != Cloud.AZURE:
|
|
@@ -168,10 +163,12 @@ class Config:
|
|
|
168
163
|
if environment.dns_zone.startswith(".dev") or environment.dns_zone.startswith(".staging"):
|
|
169
164
|
continue
|
|
170
165
|
return environment
|
|
171
|
-
return
|
|
166
|
+
return get_environment_for_hostname(self.host)
|
|
172
167
|
|
|
173
168
|
@property
|
|
174
169
|
def is_azure(self) -> bool:
|
|
170
|
+
if self.azure_workspace_resource_id:
|
|
171
|
+
return True
|
|
175
172
|
return self.environment.cloud == Cloud.AZURE
|
|
176
173
|
|
|
177
174
|
@property
|
|
@@ -12,6 +12,7 @@ from .config import *
|
|
|
12
12
|
# To preserve backwards compatibility (as these definitions were previously in this module)
|
|
13
13
|
from .credentials_provider import *
|
|
14
14
|
from .errors import DatabricksError, error_mapper
|
|
15
|
+
from .errors.private_link import _is_private_link_redirect
|
|
15
16
|
from .retries import retried
|
|
16
17
|
|
|
17
18
|
__all__ = ['Config', 'DatabricksError']
|
|
@@ -145,11 +146,14 @@ class ApiClient:
|
|
|
145
146
|
if not len(response.content):
|
|
146
147
|
return resp
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
if
|
|
150
|
-
return
|
|
149
|
+
jsonResponse = response.json()
|
|
150
|
+
if jsonResponse is None:
|
|
151
|
+
return resp
|
|
152
|
+
|
|
153
|
+
if isinstance(jsonResponse, list):
|
|
154
|
+
return jsonResponse
|
|
151
155
|
|
|
152
|
-
return {**resp, **
|
|
156
|
+
return {**resp, **jsonResponse}
|
|
153
157
|
|
|
154
158
|
@staticmethod
|
|
155
159
|
def _is_retryable(err: BaseException) -> Optional[str]:
|
|
@@ -236,6 +240,10 @@ class ApiClient:
|
|
|
236
240
|
# See https://stackoverflow.com/a/58821552/277035
|
|
237
241
|
payload = response.json()
|
|
238
242
|
raise self._make_nicer_error(response=response, **payload) from None
|
|
243
|
+
# Private link failures happen via a redirect to the login page. From a requests-perspective, the request
|
|
244
|
+
# is successful, but the response is not what we expect. We need to handle this case separately.
|
|
245
|
+
if _is_private_link_redirect(response):
|
|
246
|
+
raise self._make_nicer_error(response=response) from None
|
|
239
247
|
return response
|
|
240
248
|
except requests.exceptions.JSONDecodeError:
|
|
241
249
|
message = self._make_sense_from_html(response.text)
|
|
@@ -2,7 +2,31 @@ from dataclasses import dataclass
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class AzureEnvironment:
|
|
8
|
+
name: str
|
|
9
|
+
service_management_endpoint: str
|
|
10
|
+
resource_manager_endpoint: str
|
|
11
|
+
active_directory_endpoint: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
ARM_DATABRICKS_RESOURCE_ID = "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d"
|
|
15
|
+
|
|
16
|
+
ENVIRONMENTS = dict(
|
|
17
|
+
PUBLIC=AzureEnvironment(name="PUBLIC",
|
|
18
|
+
service_management_endpoint="https://management.core.windows.net/",
|
|
19
|
+
resource_manager_endpoint="https://management.azure.com/",
|
|
20
|
+
active_directory_endpoint="https://login.microsoftonline.com/"),
|
|
21
|
+
USGOVERNMENT=AzureEnvironment(name="USGOVERNMENT",
|
|
22
|
+
service_management_endpoint="https://management.core.usgovcloudapi.net/",
|
|
23
|
+
resource_manager_endpoint="https://management.usgovcloudapi.net/",
|
|
24
|
+
active_directory_endpoint="https://login.microsoftonline.us/"),
|
|
25
|
+
CHINA=AzureEnvironment(name="CHINA",
|
|
26
|
+
service_management_endpoint="https://management.core.chinacloudapi.cn/",
|
|
27
|
+
resource_manager_endpoint="https://management.chinacloudapi.cn/",
|
|
28
|
+
active_directory_endpoint="https://login.chinacloudapi.cn/"),
|
|
29
|
+
)
|
|
6
30
|
|
|
7
31
|
|
|
8
32
|
class Cloud(Enum):
|
|
@@ -70,3 +94,12 @@ ALL_ENVS = [
|
|
|
70
94
|
DatabricksEnvironment(Cloud.GCP, ".staging.gcp.databricks.com"),
|
|
71
95
|
DatabricksEnvironment(Cloud.GCP, ".gcp.databricks.com")
|
|
72
96
|
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_environment_for_hostname(hostname: str) -> DatabricksEnvironment:
|
|
100
|
+
if not hostname:
|
|
101
|
+
return DEFAULT_ENVIRONMENT
|
|
102
|
+
for env in ALL_ENVS:
|
|
103
|
+
if hostname.endswith(env.dns_zone):
|
|
104
|
+
return env
|
|
105
|
+
return DEFAULT_ENVIRONMENT
|
|
@@ -4,6 +4,8 @@ from databricks.sdk.errors import platform
|
|
|
4
4
|
from databricks.sdk.errors.base import DatabricksError
|
|
5
5
|
|
|
6
6
|
from .overrides import _ALL_OVERRIDES
|
|
7
|
+
from .private_link import (_get_private_link_validation_error,
|
|
8
|
+
_is_private_link_redirect)
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def error_mapper(response: requests.Response, raw: dict) -> DatabricksError:
|
|
@@ -21,6 +23,8 @@ def error_mapper(response: requests.Response, raw: dict) -> DatabricksError:
|
|
|
21
23
|
# where there's a default exception class per HTTP status code, and we do
|
|
22
24
|
# rely on Databricks platform exception mapper to do the right thing.
|
|
23
25
|
return platform.STATUS_CODE_MAPPING[status_code](**raw)
|
|
26
|
+
if _is_private_link_redirect(response):
|
|
27
|
+
return _get_private_link_validation_error(response.url)
|
|
24
28
|
|
|
25
29
|
# backwards-compatible error creation for cases like using older versions of
|
|
26
30
|
# the SDK on way never releases of the platform.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from urllib import parse
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from ..environments import Cloud, get_environment_for_hostname
|
|
7
|
+
from .platform import PermissionDenied
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class _PrivateLinkInfo:
|
|
12
|
+
serviceName: str
|
|
13
|
+
endpointName: str
|
|
14
|
+
referencePage: str
|
|
15
|
+
|
|
16
|
+
def error_message(self):
|
|
17
|
+
return (
|
|
18
|
+
f'The requested workspace has {self.serviceName} enabled and is not accessible from the current network. '
|
|
19
|
+
f'Ensure that {self.serviceName} is properly configured and that your device has access to the '
|
|
20
|
+
f'{self.endpointName}. For more information, see {self.referencePage}.')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_private_link_info_map = {
|
|
24
|
+
Cloud.AWS:
|
|
25
|
+
_PrivateLinkInfo(serviceName='AWS PrivateLink',
|
|
26
|
+
endpointName='AWS VPC endpoint',
|
|
27
|
+
referencePage='https://docs.databricks.com/en/security/network/classic/privatelink.html',
|
|
28
|
+
),
|
|
29
|
+
Cloud.AZURE:
|
|
30
|
+
_PrivateLinkInfo(
|
|
31
|
+
serviceName='Azure Private Link',
|
|
32
|
+
endpointName='Azure Private Link endpoint',
|
|
33
|
+
referencePage='https://learn.microsoft.com/en-us/azure/databricks/security/network/classic/private-link-standard#authentication-troubleshooting',
|
|
34
|
+
),
|
|
35
|
+
Cloud.GCP:
|
|
36
|
+
_PrivateLinkInfo(
|
|
37
|
+
serviceName='Private Service Connect',
|
|
38
|
+
endpointName='GCP VPC endpoint',
|
|
39
|
+
referencePage='https://docs.gcp.databricks.com/en/security/network/classic/private-service-connect.html',
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class PrivateLinkValidationError(PermissionDenied):
|
|
45
|
+
"""Raised when a user tries to access a Private Link-enabled workspace, but the user's network does not have access
|
|
46
|
+
to the workspace."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _is_private_link_redirect(resp: requests.Response) -> bool:
|
|
50
|
+
parsed = parse.urlparse(resp.url)
|
|
51
|
+
return parsed.path == '/login.html' and 'error=private-link-validation-error' in parsed.query
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _get_private_link_validation_error(url: str) -> _PrivateLinkInfo:
|
|
55
|
+
parsed = parse.urlparse(url)
|
|
56
|
+
env = get_environment_for_hostname(parsed.hostname)
|
|
57
|
+
return PrivateLinkValidationError(message=_private_link_info_map[env.cloud].error_message(),
|
|
58
|
+
error_code='PRIVATE_LINK_VALIDATION_ERROR',
|
|
59
|
+
status_code=403,
|
|
60
|
+
)
|