ob-metaflow-extensions 1.1.86__py2.py3-none-any.whl → 1.1.88__py2.py3-none-any.whl
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.
- metaflow_extensions/outerbounds/plugins/__init__.py +2 -2
- metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py +92 -36
- metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py +14 -0
- {ob_metaflow_extensions-1.1.86.dist-info → ob_metaflow_extensions-1.1.88.dist-info}/METADATA +1 -1
- {ob_metaflow_extensions-1.1.86.dist-info → ob_metaflow_extensions-1.1.88.dist-info}/RECORD +7 -7
- {ob_metaflow_extensions-1.1.86.dist-info → ob_metaflow_extensions-1.1.88.dist-info}/WHEEL +0 -0
- {ob_metaflow_extensions-1.1.86.dist-info → ob_metaflow_extensions-1.1.88.dist-info}/top_level.txt +0 -0
|
@@ -104,7 +104,7 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
104
104
|
tmp_aws_config_file = f.name
|
|
105
105
|
os.rename(tmp_aws_config_file, aws_config_file)
|
|
106
106
|
os.environ["AWS_CONFIG_FILE"] = aws_config_file
|
|
107
|
-
os.environ["
|
|
107
|
+
os.environ["AWS_PROFILE"] = "cspr"
|
|
108
108
|
else:
|
|
109
109
|
os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file
|
|
110
110
|
os.environ["AWS_ROLE_ARN"] = token_info["role_arn"]
|
|
@@ -122,7 +122,7 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
122
122
|
# AWS_CONFIG_FILE environment variable above.
|
|
123
123
|
if role_arn == USE_CSPR_ROLE_ARN_IF_SET:
|
|
124
124
|
# Otherwise start from the default profile, assuming CSPR role
|
|
125
|
-
session = boto3.session.Session(profile_name="
|
|
125
|
+
session = boto3.session.Session(profile_name="cspr")
|
|
126
126
|
else:
|
|
127
127
|
session = boto3.session.Session(profile_name="task")
|
|
128
128
|
else:
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
-
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
5
7
|
from concurrent.futures import ThreadPoolExecutor
|
|
6
8
|
from typing import Dict
|
|
9
|
+
|
|
7
10
|
from metaflow.exception import MetaflowException
|
|
8
|
-
from metaflow.metaflow_config import
|
|
9
|
-
FAST_BAKERY_URL,
|
|
10
|
-
get_pinned_conda_libs,
|
|
11
|
-
)
|
|
11
|
+
from metaflow.metaflow_config import FAST_BAKERY_URL, get_pinned_conda_libs
|
|
12
12
|
from metaflow.metaflow_environment import MetaflowEnvironment
|
|
13
|
-
from metaflow.plugins.pypi.conda_environment import CondaEnvironment
|
|
14
|
-
from .fast_bakery import FastBakery, FastBakeryApiResponse, FastBakeryException
|
|
15
13
|
from metaflow.plugins.aws.batch.batch_decorator import BatchDecorator
|
|
16
14
|
from metaflow.plugins.kubernetes.kubernetes_decorator import KubernetesDecorator
|
|
17
15
|
from metaflow.plugins.pypi.conda_decorator import CondaStepDecorator
|
|
16
|
+
from metaflow.plugins.pypi.conda_environment import CondaEnvironment
|
|
18
17
|
from metaflow.plugins.pypi.pypi_decorator import PyPIStepDecorator
|
|
19
18
|
|
|
19
|
+
from .fast_bakery import FastBakery, FastBakeryApiResponse, FastBakeryException
|
|
20
|
+
|
|
20
21
|
BAKERY_METAFILE = ".imagebakery-cache"
|
|
21
22
|
|
|
23
|
+
import fcntl
|
|
22
24
|
import json
|
|
23
25
|
import os
|
|
24
|
-
import fcntl
|
|
25
|
-
from functools import wraps
|
|
26
26
|
from concurrent.futures import ThreadPoolExecutor
|
|
27
|
-
|
|
27
|
+
from functools import wraps
|
|
28
28
|
|
|
29
29
|
# TODO - ensure that both @conda/@pypi are not assigned to the same step
|
|
30
30
|
|
|
@@ -36,6 +36,9 @@ def cache_request(cache_file):
|
|
|
36
36
|
call_args = kwargs.copy()
|
|
37
37
|
call_args.update(zip(func.__code__.co_varnames, args))
|
|
38
38
|
call_args.pop("self", None)
|
|
39
|
+
call_args.pop("ref", None)
|
|
40
|
+
# invalidate cache when moving from one deployment to another
|
|
41
|
+
call_args.update({"fast_bakery_url": FAST_BAKERY_URL})
|
|
39
42
|
cache_key = hashlib.md5(
|
|
40
43
|
json.dumps(call_args, sort_keys=True).encode("utf-8")
|
|
41
44
|
).hexdigest()
|
|
@@ -79,7 +82,7 @@ def cache_request(cache_file):
|
|
|
79
82
|
|
|
80
83
|
|
|
81
84
|
class DockerEnvironmentException(MetaflowException):
|
|
82
|
-
headline = "Ran into an error while
|
|
85
|
+
headline = "Ran into an error while baking image"
|
|
83
86
|
|
|
84
87
|
def __init__(self, msg):
|
|
85
88
|
super(DockerEnvironmentException, self).__init__(msg)
|
|
@@ -93,8 +96,8 @@ class DockerEnvironment(MetaflowEnvironment):
|
|
|
93
96
|
self.skipped_steps = set()
|
|
94
97
|
self.flow = flow
|
|
95
98
|
|
|
96
|
-
self.bakery = FastBakery(url=FAST_BAKERY_URL)
|
|
97
99
|
self.results = {}
|
|
100
|
+
self.images_baked = 0
|
|
98
101
|
|
|
99
102
|
def set_local_root(self, local_root):
|
|
100
103
|
self.local_root = local_root
|
|
@@ -102,15 +105,31 @@ class DockerEnvironment(MetaflowEnvironment):
|
|
|
102
105
|
def decospecs(self):
|
|
103
106
|
return ("conda", "fast_bakery_internal") + super().decospecs()
|
|
104
107
|
|
|
105
|
-
def validate_environment(self,
|
|
108
|
+
def validate_environment(self, logger, datastore_type):
|
|
106
109
|
self.datastore_type = datastore_type
|
|
107
|
-
self.
|
|
110
|
+
self.logger = logger
|
|
108
111
|
|
|
109
112
|
# Avoiding circular imports.
|
|
110
113
|
from metaflow.plugins import DATASTORES
|
|
111
114
|
|
|
112
115
|
self.datastore = [d for d in DATASTORES if d.TYPE == self.datastore_type][0]
|
|
113
116
|
|
|
117
|
+
# Mixing @pypi/@conda in a single step is not supported yet
|
|
118
|
+
for step in self.flow:
|
|
119
|
+
if (
|
|
120
|
+
sum(
|
|
121
|
+
1
|
|
122
|
+
for deco in step.decorators
|
|
123
|
+
if isinstance(deco, (PyPIStepDecorator, CondaStepDecorator))
|
|
124
|
+
)
|
|
125
|
+
> 1
|
|
126
|
+
):
|
|
127
|
+
raise MetaflowException(
|
|
128
|
+
"Mixing and matching PyPI packages and Conda packages within a\n"
|
|
129
|
+
"step is not yet supported. Use one of @pypi or @conda only for the *%s* step."
|
|
130
|
+
% step.name
|
|
131
|
+
)
|
|
132
|
+
|
|
114
133
|
def init_environment(self, echo):
|
|
115
134
|
self.skipped_steps = {
|
|
116
135
|
step.name
|
|
@@ -125,14 +144,21 @@ class DockerEnvironment(MetaflowEnvironment):
|
|
|
125
144
|
step for step in self.flow if step.name not in self.skipped_steps
|
|
126
145
|
]
|
|
127
146
|
if steps_to_bake:
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
self.logger("🚀 Baking container image(s) ...")
|
|
148
|
+
start_time = time.time()
|
|
149
|
+
self.results = self._bake(steps_to_bake)
|
|
130
150
|
for step in self.flow:
|
|
131
151
|
for d in step.decorators:
|
|
132
152
|
if isinstance(d, (BatchDecorator, KubernetesDecorator)):
|
|
133
153
|
d.attributes["image"] = self.results[step.name].container_image
|
|
134
154
|
d.attributes["executable"] = self.results[step.name].python_path
|
|
135
|
-
|
|
155
|
+
if self.images_baked > 0:
|
|
156
|
+
bake_time = time.time() - start_time
|
|
157
|
+
self.logger(
|
|
158
|
+
f"🎉 All container image(s) baked in {bake_time:.2f} seconds!"
|
|
159
|
+
)
|
|
160
|
+
else:
|
|
161
|
+
self.logger("🎉 All container image(s) baked!")
|
|
136
162
|
|
|
137
163
|
if self.skipped_steps:
|
|
138
164
|
self.delegate = CondaEnvironment(self.flow)
|
|
@@ -140,29 +166,54 @@ class DockerEnvironment(MetaflowEnvironment):
|
|
|
140
166
|
self.delegate.validate_environment(echo, self.datastore_type)
|
|
141
167
|
self.delegate.init_environment(echo, self.skipped_steps)
|
|
142
168
|
|
|
143
|
-
def _bake(self, steps
|
|
169
|
+
def _bake(self, steps) -> Dict[str, FastBakeryApiResponse]:
|
|
144
170
|
metafile_path = get_fastbakery_metafile_path(self.local_root, self.flow.name)
|
|
171
|
+
logger_lock = threading.Lock()
|
|
145
172
|
|
|
146
173
|
@cache_request(metafile_path)
|
|
147
174
|
def _cached_bake(
|
|
148
|
-
|
|
175
|
+
ref=None,
|
|
176
|
+
python=None,
|
|
177
|
+
pypi_packages=None,
|
|
178
|
+
conda_packages=None,
|
|
179
|
+
base_image=None,
|
|
149
180
|
):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
181
|
+
bakery = FastBakery(url=FAST_BAKERY_URL)
|
|
182
|
+
bakery._reset_payload()
|
|
183
|
+
bakery.python_version(python)
|
|
184
|
+
bakery.pypi_packages(pypi_packages)
|
|
185
|
+
bakery.conda_packages(conda_packages)
|
|
186
|
+
bakery.base_image(base_image)
|
|
187
|
+
# bakery.ignore_cache()
|
|
188
|
+
|
|
189
|
+
with logger_lock:
|
|
190
|
+
self.logger(f"🍳 Baking [{ref}] ...")
|
|
191
|
+
self.logger(f" 🐍 Python: {python}")
|
|
192
|
+
|
|
193
|
+
if pypi_packages:
|
|
194
|
+
self.logger(f" 📦 PyPI packages:")
|
|
195
|
+
for package, version in pypi_packages.items():
|
|
196
|
+
self.logger(f" 🔧 {package}: {version}")
|
|
197
|
+
|
|
198
|
+
if conda_packages:
|
|
199
|
+
self.logger(f" 📦 Conda packages:")
|
|
200
|
+
for package, version in conda_packages.items():
|
|
201
|
+
self.logger(f" 🔧 {package}: {version}")
|
|
202
|
+
|
|
203
|
+
self.logger(f" 🏗️ Base image: {base_image}")
|
|
204
|
+
|
|
205
|
+
start_time = time.time()
|
|
156
206
|
try:
|
|
157
|
-
res =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
207
|
+
res = bakery.bake()
|
|
208
|
+
# TODO: Get actual bake time from bakery
|
|
209
|
+
bake_time = time.time() - start_time
|
|
210
|
+
|
|
211
|
+
with logger_lock:
|
|
212
|
+
self.logger(f"🏁 Baked [{ref}] in {bake_time:.2f} seconds!")
|
|
213
|
+
self.images_baked += 1
|
|
163
214
|
return res
|
|
164
215
|
except FastBakeryException as ex:
|
|
165
|
-
raise DockerEnvironmentException(str(ex))
|
|
216
|
+
raise DockerEnvironmentException(f"Bake [{ref}] failed: {str(ex)}")
|
|
166
217
|
|
|
167
218
|
def prepare_step(step):
|
|
168
219
|
base_image = next(
|
|
@@ -216,10 +267,15 @@ class DockerEnvironment(MetaflowEnvironment):
|
|
|
216
267
|
}
|
|
217
268
|
|
|
218
269
|
with ThreadPoolExecutor() as executor:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
270
|
+
prepared_args = list(executor.map(prepare_step, steps))
|
|
271
|
+
for i, args in enumerate(prepared_args, 1):
|
|
272
|
+
args["ref"] = f"#{i:02d}"
|
|
273
|
+
futures = [executor.submit(_cached_bake, **args) for args in prepared_args]
|
|
274
|
+
results = {}
|
|
275
|
+
for step, future in zip(steps, futures):
|
|
276
|
+
results[step.name] = future.result()
|
|
277
|
+
|
|
278
|
+
return results
|
|
223
279
|
|
|
224
280
|
def executable(self, step_name, default=None):
|
|
225
281
|
if step_name in self.skipped_steps:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict, Optional
|
|
2
2
|
import requests
|
|
3
|
+
import time
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class FastBakeryException(Exception):
|
|
@@ -140,6 +141,19 @@ class FastBakery:
|
|
|
140
141
|
headers = {**self.headers, **(SERVICE_HEADERS or {})}
|
|
141
142
|
except ImportError:
|
|
142
143
|
headers = self.headers
|
|
144
|
+
|
|
145
|
+
retryable_status_codes = [409]
|
|
146
|
+
|
|
147
|
+
for attempt in range(2): # 0 = initial attempt, 1-2 = retries
|
|
148
|
+
response = requests.post(self.url, json=payload, headers=headers)
|
|
149
|
+
|
|
150
|
+
if response.status_code not in retryable_status_codes:
|
|
151
|
+
break
|
|
152
|
+
|
|
153
|
+
if attempt < 2: # Don't sleep after the last attempt
|
|
154
|
+
sleep_time = 0.5 * (attempt + 1)
|
|
155
|
+
time.sleep(sleep_time)
|
|
156
|
+
|
|
143
157
|
response = requests.post(self.url, json=payload, headers=headers)
|
|
144
158
|
self._handle_error_response(response)
|
|
145
159
|
return FastBakeryApiResponse(response.json())
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
metaflow_extensions/outerbounds/__init__.py,sha256=TRGvIUMjkfneWtYUFSWoubu_Kf2ekAL4WLbV3IxOj9k,499
|
|
2
2
|
metaflow_extensions/outerbounds/remote_config.py,sha256=Zpfpjgz68_ZgxlXezjzlsDLo4840rkWuZgwDB_5H57U,4059
|
|
3
3
|
metaflow_extensions/outerbounds/config/__init__.py,sha256=JsQGRuGFz28fQWjUvxUgR8EKBLGRdLUIk_buPLJplJY,1225
|
|
4
|
-
metaflow_extensions/outerbounds/plugins/__init__.py,sha256=
|
|
4
|
+
metaflow_extensions/outerbounds/plugins/__init__.py,sha256=RNN9wxnIeNHO9S9lI-sgVINbAcO0C-29lYNd4BqNs-M,12508
|
|
5
5
|
metaflow_extensions/outerbounds/plugins/auth_server.py,sha256=1v2GBqoMBxp5E7Lejz139w-jxJtPnLDvvHXP0HhEIHI,2361
|
|
6
6
|
metaflow_extensions/outerbounds/plugins/perimeters.py,sha256=QXh3SFP7GQbS-RAIxUOPbhPzQ7KDFVxZkTdKqFKgXjI,2697
|
|
7
7
|
metaflow_extensions/outerbounds/plugins/fast_bakery/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py,sha256=
|
|
9
|
-
metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py,sha256=
|
|
8
|
+
metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py,sha256=9iokzw--nCpk-omGXDSB-oct7-57rCJbq62HKvmt2NA,12126
|
|
9
|
+
metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py,sha256=6OQdij49tjBwQuQ8Kdu16kfLBzKLGM4_f80KQm6WurQ,5069
|
|
10
10
|
metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_cli.py,sha256=kqFyu2bJSnc9_9aYfBpz5xK6L6luWFZK_NMuh8f1eVk,1494
|
|
11
11
|
metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_decorator.py,sha256=EZDbyrfZ7fgcU-P9dMS_hpCxsdDeUE0K5VU3uNM4aW4,1506
|
|
12
12
|
metaflow_extensions/outerbounds/plugins/kubernetes/__init__.py,sha256=5zG8gShSj8m7rgF4xgWBZFuY3GDP5n1T0ktjRpGJLHA,69
|
|
@@ -33,7 +33,7 @@ metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py,
|
|
|
33
33
|
metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py,sha256=WUuhz2YQfI4fz7nIcipwwWq781eaoHEk7n4GAn1npDg,63
|
|
34
34
|
metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py,sha256=BbZiaH3uILlEZ6ntBLKeNyqn3If8nIXZFq_Apd7Dhco,70
|
|
35
35
|
metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py,sha256=5zG8gShSj8m7rgF4xgWBZFuY3GDP5n1T0ktjRpGJLHA,69
|
|
36
|
-
ob_metaflow_extensions-1.1.
|
|
37
|
-
ob_metaflow_extensions-1.1.
|
|
38
|
-
ob_metaflow_extensions-1.1.
|
|
39
|
-
ob_metaflow_extensions-1.1.
|
|
36
|
+
ob_metaflow_extensions-1.1.88.dist-info/METADATA,sha256=zgizoMrEL9ZXm9vj3WTY0K6y0DbQuoBkBuxiZGeXkCc,520
|
|
37
|
+
ob_metaflow_extensions-1.1.88.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
|
|
38
|
+
ob_metaflow_extensions-1.1.88.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
|
|
39
|
+
ob_metaflow_extensions-1.1.88.dist-info/RECORD,,
|
|
File without changes
|
{ob_metaflow_extensions-1.1.86.dist-info → ob_metaflow_extensions-1.1.88.dist-info}/top_level.txt
RENAMED
|
File without changes
|