ob-metaflow-extensions 1.1.110__tar.gz → 1.1.112__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 ob-metaflow-extensions might be problematic. Click here for more details.

Files changed (53) hide show
  1. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/PKG-INFO +1 -1
  2. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/__init__.py +2 -0
  3. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/secrets/secrets.py +9 -14
  4. ob-metaflow-extensions-1.1.112/metaflow_extensions/outerbounds/plugins/snowflake/__init__.py +3 -0
  5. ob-metaflow-extensions-1.1.112/metaflow_extensions/outerbounds/plugins/snowflake/snowflake.py +308 -0
  6. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py +1 -0
  7. ob-metaflow-extensions-1.1.112/metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py +1 -0
  8. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/ob_metaflow_extensions.egg-info/PKG-INFO +1 -1
  9. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/ob_metaflow_extensions.egg-info/SOURCES.txt +3 -0
  10. ob-metaflow-extensions-1.1.112/ob_metaflow_extensions.egg-info/requires.txt +3 -0
  11. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/setup.py +2 -2
  12. ob-metaflow-extensions-1.1.110/ob_metaflow_extensions.egg-info/requires.txt +0 -3
  13. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/README.md +0 -0
  14. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/__init__.py +0 -0
  15. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/config/__init__.py +0 -0
  16. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/auth_server.py +0 -0
  17. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/fast_bakery/__init__.py +0 -0
  18. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py +0 -0
  19. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py +0 -0
  20. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_cli.py +0 -0
  21. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_decorator.py +0 -0
  22. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/kubernetes/__init__.py +0 -0
  23. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/kubernetes/kubernetes_client.py +0 -0
  24. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nim/__init__.py +0 -0
  25. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nim/nim_manager.py +0 -0
  26. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nvcf/__init__.py +0 -0
  27. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nvcf/heartbeat_store.py +0 -0
  28. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf.py +0 -0
  29. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf_cli.py +0 -0
  30. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf_decorator.py +0 -0
  31. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/perimeters.py +0 -0
  32. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/profilers/deco_injector.py +0 -0
  33. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/profilers/gpu_profile_decorator.py +0 -0
  34. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/secrets/__init__.py +0 -0
  35. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/__init__.py +0 -0
  36. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark.py +0 -0
  37. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_cli.py +0 -0
  38. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_client.py +0 -0
  39. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_decorator.py +0 -0
  40. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_exceptions.py +0 -0
  41. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_job.py +0 -0
  42. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_service_spec.py +0 -0
  43. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/plugins/tensorboard/__init__.py +0 -0
  44. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/profilers/__init__.py +0 -0
  45. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/profilers/gpu.py +0 -0
  46. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/remote_config.py +0 -0
  47. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/toplevel/__init__.py +0 -0
  48. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py +0 -0
  49. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py +0 -0
  50. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py +0 -0
  51. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/ob_metaflow_extensions.egg-info/dependency_links.txt +0 -0
  52. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/ob_metaflow_extensions.egg-info/top_level.txt +0 -0
  53. {ob-metaflow-extensions-1.1.110 → ob-metaflow-extensions-1.1.112}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.1.110
3
+ Version: 1.1.112
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -336,3 +336,5 @@ ENVIRONMENTS_DESC = [
336
336
  SECRETS_PROVIDERS_DESC = [
337
337
  ("outerbounds", ".secrets.secrets.OuterboundsSecretsProvider"),
338
338
  ]
339
+ # Adding an override here so the library can be imported at the metaflow.plugins level
340
+ __mf_promote_submodules__ = ["snowflake"]
@@ -37,15 +37,13 @@ class OuterboundsSecretsProvider(SecretsProvider):
37
37
  secrets manager on the core oss and returns the secrets.
38
38
  """
39
39
  headers = {"Content-Type": "application/json", "Connection": "keep-alive"}
40
- perimeter, integrations_secrets_metadata_url = self._get_secret_configs()
40
+ perimeter, integrations_url = self._get_secret_configs()
41
41
  integration_name = secret_id
42
42
  request_payload = {
43
43
  "perimeter_name": perimeter,
44
44
  "integration_name": integration_name,
45
45
  }
46
- response = self._make_request(
47
- integrations_secrets_metadata_url, headers, request_payload
48
- )
46
+ response = self._make_request(integrations_url, headers, request_payload)
49
47
  secret_resource_id = response.secret_resource_id
50
48
  secret_backend_type = response.secret_backend_type
51
49
 
@@ -105,26 +103,23 @@ class OuterboundsSecretsProvider(SecretsProvider):
105
103
  # if the perimeter is not in metaflow config, try to get it from the environment
106
104
  perimeter = environ.get("OBP_PERIMETER", "")
107
105
 
108
- if "OBP_INTEGRATIONS_SECRETS_METADATA_URL" in conf:
109
- integrations_secrets_metadata_url = conf[
110
- "OBP_INTEGRATIONS_SECRETS_METADATA_URL"
111
- ]
106
+ if "OBP_INTEGRATIONS_URL" in conf:
107
+ integrations_url = conf["OBP_INTEGRATIONS_URL"]
112
108
  else:
113
- # if the integrations secrets metadata url is not in metaflow config, try to get it from the environment
114
- integrations_secrets_metadata_url = environ.get(
115
- "OBP_INTEGRATIONS_SECRETS_METADATA_URL", ""
116
- )
109
+ # if the integrations is not in metaflow config, try to get it from the environment
110
+ integrations_url = environ.get("OBP_INTEGRATIONS_URL", "")
117
111
 
118
112
  if not perimeter:
119
113
  raise OuterboundsSecretsException(
120
114
  "No perimeter set. Please make sure to run `outerbounds configure <...>` command which can be found on the Ourebounds UI or reach out to your Outerbounds support team."
121
115
  )
122
116
 
123
- if not integrations_secrets_metadata_url:
117
+ if not integrations_url:
124
118
  raise OuterboundsSecretsException(
125
- "No integrations secrets metadata url set. Please notify your Outerbounds support team about this issue."
119
+ "No integrations url set. Please notify your Outerbounds support team about this issue."
126
120
  )
127
121
 
122
+ integrations_secrets_metadata_url = f"{integrations_url}/secrets/metadata"
128
123
  return perimeter, integrations_secrets_metadata_url
129
124
 
130
125
  def _make_request(self, url, headers: Dict, payload: Dict):
@@ -0,0 +1,3 @@
1
+ from .snowflake import connect, get_snowflake_token, Snowflake
2
+
3
+ __mf_promote_submodules__ = ["plugins.snowflake"]
@@ -0,0 +1,308 @@
1
+ """
2
+ This library is an abstraction layer for connecting to snowflake using Outerbounds
3
+ OIDC tokens. It expects that a security integration that authenticates tokens minted
4
+ by Outerbounds has already been configured in the target snowflake account.
5
+ """
6
+ from metaflow.metaflow_config import SERVICE_URL
7
+ from metaflow.metaflow_config_funcs import init_config
8
+ from typing import Dict
9
+ from os import environ
10
+
11
+ import json
12
+ import requests
13
+ import time
14
+
15
+
16
+ class OuterboundsSnowflakeConnectorException(Exception):
17
+ pass
18
+
19
+
20
+ class OuterboundsSnowflakeIntegrationSpecApiResponse:
21
+ def __init__(self, response):
22
+ self.response = response
23
+
24
+ @property
25
+ def account(self):
26
+ return self.response["account"]
27
+
28
+ @property
29
+ def user(self):
30
+ return self.response["user"]
31
+
32
+ @property
33
+ def default_role(self):
34
+ return self.response["default_role"]
35
+
36
+ @property
37
+ def warehouse(self):
38
+ return self.response["warehouse"]
39
+
40
+ @property
41
+ def database(self):
42
+ return self.response["database"]
43
+
44
+
45
+ def get_snowflake_token(user: str = "", role: str = "", integration: str = "") -> str:
46
+ """
47
+ Uses the Outerbounds source token to request for a snowflake compatible OIDC
48
+ token. This token can then be used to connect to snowflake.
49
+ user: str
50
+ The user the token will be minted for
51
+ role: str
52
+ The role to which the token will be scoped to
53
+ integration: str
54
+ The name of the snowflake integration to use. If not set, an existing integration will be used provided that only one exists per perimeter. If integration is not set and more than one exists, then we raise an exception.
55
+ """
56
+ provisioner = SnowflakeIntegrationProvisioner(integration)
57
+ if not user or not role or not integration:
58
+ integration_spec = provisioner.get_snowflake_integration_spec()
59
+ if not user:
60
+ user = integration_spec.user
61
+
62
+ if not role:
63
+ role = integration_spec.default_role
64
+
65
+ if not integration:
66
+ integration = provisioner.get_integration_name()
67
+
68
+ snowflake_token_url = provisioner.get_snowflake_token_url()
69
+ perimeter = provisioner.get_perimeter()
70
+ payload = {
71
+ "perimeterName": perimeter,
72
+ "snowflakeUser": user,
73
+ "snowflakeRole": role,
74
+ "integrationName": integration,
75
+ }
76
+ json_payload = json.dumps(payload)
77
+ headers = provisioner.get_service_auth_header()
78
+ response = requests.get(snowflake_token_url, data=json_payload, headers=headers)
79
+ response.raise_for_status()
80
+ return response.json()["token"]
81
+
82
+
83
+ def connect(user: str = "", role: str = "", integration: str = "", **kwargs):
84
+ """
85
+ Connect to snowflake using the token minted by Outerbounds
86
+ user: str
87
+ The user name used to authenticate with snowflake
88
+ role: str
89
+ The role to request when connect with snowflake
90
+ integration: str
91
+ The name of the snowflake integration to use. If not set, an existing integration will be used provided that only one exists in the current perimeter. If integration is not set and more than one exists in the current perimeter, then we raise an exception.
92
+ kwargs: dict
93
+ Additional arguments to pass to the python snowflake connector
94
+ """
95
+ # ensure password is not set
96
+ if "password" in kwargs:
97
+ raise OuterboundsSnowflakeConnectorException(
98
+ "Password should not be set when using Outerbounds snowflake connector."
99
+ )
100
+
101
+ provisioner = SnowflakeIntegrationProvisioner(integration)
102
+ get_defaults = any(
103
+ key not in kwargs for key in ["account", "warehouse", "database"]
104
+ )
105
+ if not user or not role or not integration or get_defaults:
106
+ integration_spec = provisioner.get_snowflake_integration_spec()
107
+ if not user:
108
+ user = integration_spec.user
109
+
110
+ if not role:
111
+ role = integration_spec.default_role
112
+
113
+ if not integration:
114
+ integration = provisioner.get_integration_name()
115
+
116
+ if "account" not in kwargs:
117
+ kwargs["account"] = integration_spec.account
118
+
119
+ if "warehouse" not in kwargs:
120
+ kwargs["warehouse"] = integration_spec.warehouse
121
+
122
+ # if the user is attempting to use a warehouse different from what is specified in the
123
+ # integration we will not set the database
124
+ if (
125
+ "database" not in kwargs
126
+ and kwargs["warehouse"] == integration_spec.warehouse
127
+ ):
128
+ kwargs["database"] = integration_spec.database
129
+
130
+ # get snowflake token
131
+ token = get_snowflake_token(user=user, role=role, integration=integration)
132
+ kwargs["token"] = token
133
+ kwargs["authenticator"] = "oauth"
134
+ kwargs["role"] = role
135
+ kwargs["user"] = user
136
+
137
+ # connect to snowflake
138
+ try:
139
+ from snowflake.connector import connect
140
+
141
+ cn = connect(**kwargs)
142
+ return cn
143
+ except ImportError as ie:
144
+ raise OuterboundsSnowflakeConnectorException(
145
+ f"Error importing snowflake connector: {ie}.\nPlease make sure the 'snowflake-connector-python' package has been installed by running 'pip install -U \"outerbounds[snowflake]\"' or using the Metaflow decorators @pypi or @conda."
146
+ )
147
+ except Exception as e:
148
+ raise OuterboundsSnowflakeConnectorException(
149
+ f"Error connecting to snowflake: {e}"
150
+ )
151
+
152
+
153
+ class Snowflake:
154
+ def __init__(
155
+ self, user: str = "", role: str = "", integration: str = "", **kwargs
156
+ ) -> None:
157
+ self.cn = connect(user, role, integration, **kwargs)
158
+
159
+ def __enter__(self):
160
+ return self.cn
161
+
162
+ def __exit__(self, exception_type, exception_value, traceback):
163
+ self.cn.close()
164
+
165
+ def close(self):
166
+ self.cn.close()
167
+
168
+
169
+ class SnowflakeIntegrationProvisioner:
170
+ def __init__(self, integration_name: str) -> None:
171
+ self.conf = init_config()
172
+ self.integration_name = integration_name
173
+
174
+ def get_snowflake_integration_spec(
175
+ self,
176
+ ) -> OuterboundsSnowflakeIntegrationSpecApiResponse:
177
+ integrations_url = self._get_integration_url()
178
+ perimeter = self.get_perimeter()
179
+ headers = {"Content-Type": "application/json", "Connection": "keep-alive"}
180
+ request_payload = {
181
+ "perimeter_name": perimeter,
182
+ }
183
+ # if integration is not set, list all integrations
184
+ if not self.integration_name:
185
+ list_snowflake_integrations_url = f"{integrations_url}/snowflake"
186
+ response = self._make_request(
187
+ list_snowflake_integrations_url, headers, request_payload
188
+ )
189
+ snowflake_integrations = response.get("integrations", [])
190
+ if not snowflake_integrations:
191
+ raise OuterboundsSnowflakeConnectorException(
192
+ "No snowflake integrations found. Please make sure you have created a Snowflake integration on the Outerbounds UI first."
193
+ )
194
+
195
+ if len(snowflake_integrations) > 1:
196
+ raise OuterboundsSnowflakeConnectorException(
197
+ f"Multiple snowflake integrations found. Please specify a specific integration name you would like to use."
198
+ )
199
+
200
+ self.integration_name = snowflake_integrations[0]["integration_name"]
201
+ return OuterboundsSnowflakeIntegrationSpecApiResponse(
202
+ snowflake_integrations[0]["integration_spec"]
203
+ )
204
+
205
+ get_snowflake_integration_url = (
206
+ f"{integrations_url}/snowflake/{self.integration_name}"
207
+ )
208
+ response = self._make_request(
209
+ get_snowflake_integration_url, headers, request_payload
210
+ )
211
+ self.integration_name = response["integration_name"]
212
+ return OuterboundsSnowflakeIntegrationSpecApiResponse(
213
+ response["integration_spec"]
214
+ )
215
+
216
+ def get_integration_name(self) -> str:
217
+ return self.integration_name
218
+
219
+ def get_perimeter(self) -> str:
220
+ if "OBP_PERIMETER" in self.conf:
221
+ perimeter = self.conf["OBP_PERIMETER"]
222
+ else:
223
+ # if the perimeter is not in metaflow config, try to get it from the environment
224
+ perimeter = environ.get("OBP_PERIMETER", "")
225
+ if not perimeter:
226
+ raise OuterboundsSnowflakeConnectorException(
227
+ "No perimeter set. Please make sure to run `outerbounds configure <...>` command which can be found on the Ourebounds UI or reach out to your Outerbounds support team."
228
+ )
229
+
230
+ return perimeter
231
+
232
+ def get_snowflake_token_url(self) -> str:
233
+ if "OBP_AUTH_SERVER" in self.conf:
234
+ auth_host = self.conf["OBP_AUTH_SERVER"]
235
+ else:
236
+ from urllib.parse import urlparse
237
+
238
+ auth_host = "auth." + urlparse(SERVICE_URL).hostname.split(".", 1)[1]
239
+
240
+ return "https://" + auth_host + "/generate/snowflake"
241
+
242
+ def get_service_auth_header(self) -> str:
243
+ if "METAFLOW_SERVICE_AUTH_KEY" in self.conf:
244
+ return {"x-api-key": self.conf["METAFLOW_SERVICE_AUTH_KEY"]}
245
+ else:
246
+ return json.loads(environ.get("METAFLOW_SERVICE_HEADERS"))
247
+
248
+ def _get_integration_url(self) -> str:
249
+ from metaflow_extensions.outerbounds.remote_config import init_config
250
+ from os import environ
251
+
252
+ if "OBP_INTEGRATIONS_URL" in self.conf:
253
+ integrations_url = self.conf["OBP_INTEGRATIONS_URL"]
254
+ else:
255
+ # if the integrations url is not in metaflow config, try to get it from the environment
256
+ integrations_url = environ.get("OBP_INTEGRATIONS_URL", "")
257
+
258
+ if not integrations_url:
259
+ raise OuterboundsSnowflakeConnectorException(
260
+ "No integrations url set. Please notify your Outerbounds support team about this issue."
261
+ )
262
+
263
+ return integrations_url
264
+
265
+ def _make_request(self, url, headers: Dict, payload: Dict) -> Dict:
266
+ try:
267
+ from metaflow.metaflow_config import SERVICE_HEADERS
268
+
269
+ request_headers = {**headers, **(SERVICE_HEADERS or {})}
270
+ except ImportError:
271
+ headers = headers
272
+
273
+ retryable_status_codes = [409]
274
+ json_payload = json.dumps(payload)
275
+ for attempt in range(2): # 0 = initial attempt, 1-2 = retries
276
+ response = requests.get(url, data=json_payload, headers=request_headers)
277
+ if response.status_code not in retryable_status_codes:
278
+ break
279
+
280
+ if attempt < 2: # Don't sleep after the last attempt
281
+ sleep_time = 0.5 * (attempt + 1)
282
+ time.sleep(sleep_time)
283
+
284
+ response = requests.get(url, data=json_payload, headers=request_headers)
285
+ self._handle_error_response(response)
286
+ return response.json()
287
+
288
+ @staticmethod
289
+ def _handle_error_response(response: requests.Response):
290
+ if response.status_code >= 500:
291
+ raise OuterboundsSnowflakeConnectorException(
292
+ f"Server error: {response.text}. Please reach out to your Outerbounds support team."
293
+ )
294
+
295
+ body = response.json()
296
+ status_code = body.get("error", {}).get("statusCode", response.status_code)
297
+ if status_code == 404:
298
+ raise OuterboundsSnowflakeConnectorException(f"Secret not found: {body}")
299
+
300
+ if status_code >= 400:
301
+ try:
302
+ raise OuterboundsSnowflakeConnectorException(
303
+ f"status_code={status_code}\t*{body['error']['details']['kind']}*\n{body['error']['details']['message']}"
304
+ )
305
+ except KeyError:
306
+ raise OuterboundsSnowflakeConnectorException(
307
+ f"status_code={status_code} Unexpected error: {body}"
308
+ )
@@ -51,3 +51,4 @@ def S3(*args, **kwargs):
51
51
 
52
52
 
53
53
  from .. import profilers
54
+ from ..plugins.snowflake import Snowflake
@@ -0,0 +1 @@
1
+ __mf_promote_submodules__ = ["plugins.snowflake"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.1.110
3
+ Version: 1.1.112
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -24,6 +24,8 @@ metaflow_extensions/outerbounds/plugins/profilers/deco_injector.py
24
24
  metaflow_extensions/outerbounds/plugins/profilers/gpu_profile_decorator.py
25
25
  metaflow_extensions/outerbounds/plugins/secrets/__init__.py
26
26
  metaflow_extensions/outerbounds/plugins/secrets/secrets.py
27
+ metaflow_extensions/outerbounds/plugins/snowflake/__init__.py
28
+ metaflow_extensions/outerbounds/plugins/snowflake/snowflake.py
27
29
  metaflow_extensions/outerbounds/plugins/snowpark/__init__.py
28
30
  metaflow_extensions/outerbounds/plugins/snowpark/snowpark.py
29
31
  metaflow_extensions/outerbounds/plugins/snowpark/snowpark_cli.py
@@ -40,6 +42,7 @@ metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py
40
42
  metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py
41
43
  metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py
42
44
  metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py
45
+ metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py
43
46
  ob_metaflow_extensions.egg-info/PKG-INFO
44
47
  ob_metaflow_extensions.egg-info/SOURCES.txt
45
48
  ob_metaflow_extensions.egg-info/dependency_links.txt
@@ -0,0 +1,3 @@
1
+ boto3
2
+ kubernetes
3
+ ob-metaflow==2.12.36.3
@@ -2,7 +2,7 @@ from setuptools import setup, find_namespace_packages
2
2
  from pathlib import Path
3
3
 
4
4
 
5
- version = "1.1.110"
5
+ version = "1.1.112"
6
6
  this_directory = Path(__file__).parent
7
7
  long_description = (this_directory / "README.md").read_text()
8
8
 
@@ -18,6 +18,6 @@ setup(
18
18
  install_requires=[
19
19
  "boto3",
20
20
  "kubernetes",
21
- "ob-metaflow == 2.12.36.1",
21
+ "ob-metaflow == 2.12.36.3",
22
22
  ],
23
23
  )
@@ -1,3 +0,0 @@
1
- boto3
2
- kubernetes
3
- ob-metaflow==2.12.36.1