outerbounds 0.3.53__py3-none-any.whl → 0.3.53rc1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,12 @@
1
1
  import click
2
2
  from . import local_setup_cli
3
3
  from . import workstations_cli
4
+ from . import perimeters_cli
4
5
 
5
6
 
6
7
  @click.command(
7
- cls=click.CommandCollection, sources=[local_setup_cli.cli, workstations_cli.cli]
8
+ cls=click.CommandCollection,
9
+ sources=[local_setup_cli.cli, workstations_cli.cli, perimeters_cli.cli],
8
10
  )
9
11
  def cli(**kwargs):
10
12
  pass
@@ -586,6 +586,12 @@ class ConfigurationWriter:
586
586
  self.out_dir = out_dir
587
587
  self.profile = profile
588
588
 
589
+ ob_config_dir = path.expanduser(os.getenv("OBP_CONFIG_DIR", out_dir))
590
+ self.ob_config_path = path.join(
591
+ ob_config_dir,
592
+ "ob_config_{}.json".format(profile) if profile else "ob_config.json",
593
+ )
594
+
589
595
  def decode(self):
590
596
  self.decoded_config = deserialize(self.encoded_config)
591
597
 
@@ -648,6 +654,15 @@ class ConfigurationWriter:
648
654
  with open(config_path, "w") as fd:
649
655
  json.dump(self.existing, fd, indent=4)
650
656
 
657
+ # Every time a config is initialized, we should also reset the corresponding ob_config[_profile].json
658
+ remote_config = metaflowconfig.init_config(self.out_dir, self.profile)
659
+ with open(self.ob_config_path, "w") as fd:
660
+ ob_config_dict = {
661
+ "OB_CURRENT_PERIMETER": remote_config["OBP_PERIMETER"],
662
+ "OB_CURRENT_PERIMETER_URL": remote_config["OBP_PERIMETER_URL"],
663
+ }
664
+ json.dump(ob_config_dict, fd, indent=4)
665
+
651
666
  def confirm_overwrite_config(self, config_path):
652
667
  if os.path.exists(config_path):
653
668
  if not click.confirm(
@@ -0,0 +1,374 @@
1
+ import base64
2
+ import hashlib
3
+ import json
4
+ import os
5
+ import re
6
+ import subprocess
7
+ import sys
8
+ import zlib
9
+ from base64 import b64decode, b64encode
10
+ from importlib.machinery import PathFinder
11
+ from os import path
12
+ from pathlib import Path
13
+ from typing import Any, Callable, Dict, List
14
+
15
+ import boto3
16
+ import click
17
+ import requests
18
+ from requests.exceptions import HTTPError
19
+
20
+ from ..utils import kubeconfig, metaflowconfig
21
+ from ..utils.schema import (
22
+ CommandStatus,
23
+ OuterboundsCommandResponse,
24
+ OuterboundsCommandStatus,
25
+ )
26
+
27
+
28
+ @click.group()
29
+ def cli(**kwargs):
30
+ pass
31
+
32
+
33
+ @cli.command(help="Switch current perimeter", hidden=True)
34
+ @click.option(
35
+ "-d",
36
+ "--config-dir",
37
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
38
+ help="Path to Metaflow configuration directory",
39
+ show_default=True,
40
+ )
41
+ @click.option(
42
+ "-p",
43
+ "--profile",
44
+ default=os.environ.get("METAFLOW_PROFILE", ""),
45
+ help="The named metaflow profile in which your workstation exists",
46
+ )
47
+ @click.option(
48
+ "-o",
49
+ "--output",
50
+ default="",
51
+ help="Show output in the specified format.",
52
+ type=click.Choice(["json", ""]),
53
+ )
54
+ @click.option("--id", default="", type=str, help="Perimeter name to switch to")
55
+ @click.option(
56
+ "-f",
57
+ "--force",
58
+ is_flag=True,
59
+ help="Force change the existing perimeter",
60
+ default=False,
61
+ )
62
+ def switch_perimeter(config_dir=None, profile=None, output="", id=None, force=False):
63
+ switch_perimeter_response = OuterboundsCommandResponse()
64
+
65
+ switch_perimeter_step = CommandStatus(
66
+ "SwitchPerimeter",
67
+ OuterboundsCommandStatus.OK,
68
+ "Perimeter was successfully switched!",
69
+ )
70
+
71
+ perimeters = get_perimeters_from_api_or_fail_command(
72
+ config_dir, profile, output, switch_perimeter_response, switch_perimeter_step
73
+ )
74
+ confirm_user_has_access_to_perimeter_or_fail(
75
+ id, perimeters, output, switch_perimeter_response, switch_perimeter_step
76
+ )
77
+
78
+ path_to_config = get_ob_config_file_path(config_dir, profile)
79
+
80
+ import fcntl
81
+
82
+ try:
83
+ if os.path.exists(path_to_config):
84
+ if not force:
85
+ fd = os.open(path_to_config, os.O_WRONLY)
86
+ # Try to acquire an exclusive lock
87
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
88
+ else:
89
+ click.secho(
90
+ "Force flag is set. Perimeter will be switched, but can have unintended consequences on other running processes.",
91
+ fg="yellow",
92
+ err=True,
93
+ )
94
+
95
+ ob_config_dict = {
96
+ "OB_CURRENT_PERIMETER": str(id),
97
+ "OB_CURRENT_PERIMETER_URL": perimeters[id]["remote_config_url"],
98
+ }
99
+
100
+ # Now that we have the lock, we can safely write to the file
101
+ with open(path_to_config, "w") as file:
102
+ json.dump(ob_config_dict, file, indent=4)
103
+
104
+ click.secho("Perimeter switched to {}".format(id), fg="green", err=True)
105
+ except BlockingIOError:
106
+ # This exception is raised if the file is already locked (non-blocking mode)
107
+ # Note that its the metaflow package (the extension actually) that acquires a shared read lock
108
+ # on the file whenever a process imports metaflow.
109
+ # In the future we might want to get smarter about it and show which process is holding the lock.
110
+ click.secho(
111
+ "Can't switch perimeter while Metaflow is in use. Please make sure there are no running python processes or notebooks using metaflow.",
112
+ fg="red",
113
+ err=True,
114
+ )
115
+ switch_perimeter_step.update(
116
+ status=OuterboundsCommandStatus.FAIL,
117
+ reason="Can't switch perimeter while Metaflow is in use.",
118
+ mitigation="Please make sure there are no running python processes or notebooks using metaflow.",
119
+ )
120
+
121
+ switch_perimeter_response.add_step(switch_perimeter_step)
122
+ if output == "json":
123
+ click.echo(json.dumps(switch_perimeter_response.as_dict(), indent=4))
124
+ return
125
+
126
+
127
+ @cli.command(help="Show current perimeter", hidden=True)
128
+ @click.option(
129
+ "-d",
130
+ "--config-dir",
131
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
132
+ help="Path to Metaflow configuration directory",
133
+ show_default=True,
134
+ )
135
+ @click.option(
136
+ "-p",
137
+ "--profile",
138
+ default=os.environ.get("METAFLOW_PROFILE", ""),
139
+ help="Configure a named profile. Activate the profile by setting "
140
+ "`METAFLOW_PROFILE` environment variable.",
141
+ )
142
+ @click.option(
143
+ "-o",
144
+ "--output",
145
+ default="",
146
+ help="Show output in the specified format.",
147
+ type=click.Choice(["json", ""]),
148
+ )
149
+ def show_current_perimeter(config_dir=None, profile=None, output=""):
150
+ show_current_perimeter_response = OuterboundsCommandResponse()
151
+
152
+ show_current_perimeter_step = CommandStatus(
153
+ "ShowCurrentPerimeter",
154
+ OuterboundsCommandStatus.OK,
155
+ "Current Perimeter Fetch Successful.",
156
+ )
157
+
158
+ ob_config_dict = get_ob_config_or_fail_command(
159
+ config_dir,
160
+ profile,
161
+ output,
162
+ show_current_perimeter_response,
163
+ show_current_perimeter_step,
164
+ )
165
+
166
+ perimeters = get_perimeters_from_api_or_fail_command(
167
+ config_dir,
168
+ profile,
169
+ output,
170
+ show_current_perimeter_response,
171
+ show_current_perimeter_step,
172
+ )
173
+ confirm_user_has_access_to_perimeter_or_fail(
174
+ ob_config_dict["OB_CURRENT_PERIMETER"],
175
+ perimeters,
176
+ output,
177
+ show_current_perimeter_response,
178
+ show_current_perimeter_step,
179
+ )
180
+
181
+ click.secho(
182
+ "Current Perimeter: {}".format(ob_config_dict["OB_CURRENT_PERIMETER"]),
183
+ fg="green",
184
+ err=True,
185
+ )
186
+
187
+ show_current_perimeter_response.add_or_update_data(
188
+ "current_perimeter", ob_config_dict["OB_CURRENT_PERIMETER"]
189
+ )
190
+
191
+ if output == "json":
192
+ click.echo(json.dumps(show_current_perimeter_response.as_dict(), indent=4))
193
+
194
+
195
+ @cli.command(help="List all available perimeters", hidden=True)
196
+ @click.option(
197
+ "-d",
198
+ "--config-dir",
199
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
200
+ help="Path to Metaflow configuration directory",
201
+ show_default=True,
202
+ )
203
+ @click.option(
204
+ "-p",
205
+ "--profile",
206
+ default=os.environ.get("METAFLOW_PROFILE", ""),
207
+ help="The named metaflow profile in which your workstation exists",
208
+ )
209
+ @click.option(
210
+ "-o",
211
+ "--output",
212
+ default="",
213
+ help="Show output in the specified format.",
214
+ type=click.Choice(["json", ""]),
215
+ )
216
+ def list_perimeters(config_dir=None, profile=None, output=""):
217
+ list_perimeters_response = OuterboundsCommandResponse()
218
+
219
+ list_perimeters_step = CommandStatus(
220
+ "ListPerimeters", OuterboundsCommandStatus.OK, "Perimeter Fetch Successful."
221
+ )
222
+
223
+ ob_config_dict = get_ob_config_or_fail_command(
224
+ config_dir, profile, output, list_perimeters_response, list_perimeters_step
225
+ )
226
+ active_perimeter = ob_config_dict["OB_CURRENT_PERIMETER"]
227
+
228
+ perimeters = get_perimeters_from_api_or_fail_command(
229
+ config_dir, profile, output, list_perimeters_response, list_perimeters_step
230
+ )
231
+
232
+ perimeter_list = []
233
+ for perimeter in perimeters.values():
234
+ status = "OK"
235
+ perimeter_list.append(
236
+ {
237
+ "id": perimeter["perimeter"],
238
+ "active": perimeter["perimeter"] == active_perimeter,
239
+ "status": status,
240
+ }
241
+ )
242
+ if perimeter["perimeter"] != active_perimeter:
243
+ click.secho("Perimeter: {}".format(perimeter["perimeter"]), err=True)
244
+ else:
245
+ click.secho(
246
+ "Perimeter: {} (active)".format(perimeter["perimeter"]),
247
+ fg="green",
248
+ err=True,
249
+ )
250
+
251
+ list_perimeters_response.add_or_update_data("perimeters", perimeter_list)
252
+
253
+ if output == "json":
254
+ click.echo(json.dumps(list_perimeters_response.as_dict(), indent=4))
255
+
256
+
257
+ def get_list_perimeters_api_response(config_dir, profile):
258
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(config_dir, profile)
259
+ api_url = metaflowconfig.get_sanitized_url_from_config(
260
+ config_dir, profile, "OBP_API_SERVER"
261
+ )
262
+ perimeters_response = requests.get(
263
+ f"{api_url}/v1/me/perimeters?privilege=Execute",
264
+ headers={"x-api-key": metaflow_token},
265
+ )
266
+ perimeters_response.raise_for_status()
267
+ return perimeters_response.json()["perimeters"]
268
+
269
+
270
+ def get_ob_config_file_path(config_dir: str, profile: str) -> str:
271
+ # If OBP_CONFIG_DIR is set, use that, otherwise use METAFLOW_HOME
272
+ # If neither are set, use ~/.metaflowconfig
273
+ obp_config_dir = path.expanduser(os.environ.get("OBP_CONFIG_DIR", config_dir))
274
+
275
+ ob_config_filename = f"ob_config_{profile}.json" if profile else "ob_config.json"
276
+ return os.path.expanduser(os.path.join(obp_config_dir, ob_config_filename))
277
+
278
+
279
+ def get_perimeters_from_api_or_fail_command(
280
+ config_dir: str,
281
+ profile: str,
282
+ output: str,
283
+ command_response: OuterboundsCommandResponse,
284
+ command_step: CommandStatus,
285
+ ) -> Dict[str, Dict[str, str]]:
286
+ try:
287
+ perimeters = get_list_perimeters_api_response(config_dir, profile)
288
+ except:
289
+ click.secho(
290
+ "Failed to fetch perimeters from API.",
291
+ fg="red",
292
+ err=True,
293
+ )
294
+ command_step.update(
295
+ status=OuterboundsCommandStatus.FAIL,
296
+ reason="Failed to fetch perimeters from API",
297
+ mitigation="",
298
+ )
299
+ command_response.add_step(command_step)
300
+ if output == "json":
301
+ click.echo(json.dumps(command_response.as_dict(), indent=4))
302
+ sys.exit(1)
303
+ return {p["perimeter"]: p for p in perimeters}
304
+
305
+
306
+ def get_ob_config_or_fail_command(
307
+ config_dir: str,
308
+ profile: str,
309
+ output: str,
310
+ command_response: OuterboundsCommandResponse,
311
+ command_step: CommandStatus,
312
+ ) -> Dict[str, str]:
313
+ path_to_config = get_ob_config_file_path(config_dir, profile)
314
+
315
+ if not os.path.exists(path_to_config):
316
+ click.secho(
317
+ "Config file not found at {}".format(path_to_config), fg="red", err=True
318
+ )
319
+ command_step.update(
320
+ status=OuterboundsCommandStatus.FAIL,
321
+ reason="Config file not found",
322
+ mitigation="Please make sure the config file exists at {}".format(
323
+ path_to_config
324
+ ),
325
+ )
326
+ command_response.add_step(command_step)
327
+ if output == "json":
328
+ click.echo(json.dumps(command_response.as_dict(), indent=4))
329
+ sys.exit(1)
330
+
331
+ with open(path_to_config, "r") as file:
332
+ ob_config_dict = json.load(file)
333
+
334
+ if "OB_CURRENT_PERIMETER" not in ob_config_dict:
335
+ click.secho(
336
+ "OB_CURRENT_PERIMETER not found in Config file: {}".format(path_to_config),
337
+ fg="red",
338
+ err=True,
339
+ )
340
+ command_step.update(
341
+ status=OuterboundsCommandStatus.FAIL,
342
+ reason="OB_CURRENT_PERIMETER not found in Config file: {}",
343
+ mitigation="",
344
+ )
345
+ command_response.add_step(command_step)
346
+ if output == "json":
347
+ click.echo(json.dumps(command_response.as_dict(), indent=4))
348
+ sys.exit(1)
349
+
350
+ return ob_config_dict
351
+
352
+
353
+ def confirm_user_has_access_to_perimeter_or_fail(
354
+ perimeter_id: str,
355
+ perimeters: Dict[str, Any],
356
+ output: str,
357
+ command_response: OuterboundsCommandResponse,
358
+ command_step: CommandStatus,
359
+ ):
360
+ if perimeter_id not in perimeters:
361
+ click.secho(
362
+ f"You do not have access to perimeter {perimeter_id} or it does not exist.",
363
+ fg="red",
364
+ err=True,
365
+ )
366
+ command_step.update(
367
+ status=OuterboundsCommandStatus.FAIL,
368
+ reason=f"You do not have access to perimeter {perimeter_id} or it does not exist.",
369
+ mitigation="",
370
+ )
371
+ command_response.add_step(command_step)
372
+ if output == "json":
373
+ click.echo(json.dumps(command_response.as_dict(), indent=4))
374
+ sys.exit(1)
@@ -110,8 +110,10 @@ def configure_cloud_workstation(config_dir=None, profile=None, binary=None, outp
110
110
  kubeconfig_configure_step = CommandStatus(
111
111
  "ConfigureKubeConfig", OuterboundsCommandStatus.OK, "Kubeconfig is configured"
112
112
  )
113
-
114
113
  try:
114
+ if not profile:
115
+ profile = metaflowconfig.get_metaflow_profile()
116
+
115
117
  metaflow_token = metaflowconfig.get_metaflow_token_from_config(
116
118
  config_dir, profile
117
119
  )
@@ -194,8 +196,25 @@ def configure_cloud_workstation(config_dir=None, profile=None, binary=None, outp
194
196
  default="",
195
197
  help="The named metaflow profile in which your workstation exists",
196
198
  )
197
- def list_workstations(config_dir=None, profile=None):
199
+ @click.option(
200
+ "-o",
201
+ "--output",
202
+ default="json",
203
+ help="Show output in the specified format.",
204
+ type=click.Choice(["json"]),
205
+ )
206
+ def list_workstations(config_dir=None, profile=None, output="json"):
207
+ list_response = OuterboundsCommandResponse()
208
+ list_step = CommandStatus(
209
+ "listWorkstations",
210
+ OuterboundsCommandStatus.OK,
211
+ "Workstation list successfully fetched!",
212
+ )
213
+ list_response.add_or_update_data("workstations", [])
214
+
198
215
  try:
216
+ if not profile:
217
+ profile = metaflowconfig.get_metaflow_profile()
199
218
  metaflow_token = metaflowconfig.get_metaflow_token_from_config(
200
219
  config_dir, profile
201
220
  )
@@ -205,17 +224,23 @@ def list_workstations(config_dir=None, profile=None):
205
224
  workstations_response = requests.get(
206
225
  f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
207
226
  )
208
- try:
209
- workstations_response.raise_for_status()
210
- click.echo(json.dumps(workstations_response.json(), indent=4))
211
- except HTTPError:
212
- click.secho("Failed to generate workstation token.", fg="red")
213
- click.secho(
214
- "Error: {}".format(json.dumps(workstations_response.json(), indent=4))
215
- )
227
+ workstations_response.raise_for_status()
228
+ list_response.add_or_update_data(
229
+ "workstations", workstations_response.json()["workstations"]
230
+ )
231
+ if output == "json":
232
+ click.echo(json.dumps(list_response.as_dict(), indent=4))
216
233
  except Exception as e:
217
- click.secho("Failed to list workstations", fg="red")
218
- click.secho("Error: {}".format(str(e)))
234
+ list_step.update(
235
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations", ""
236
+ )
237
+ list_response.add_step(list_step)
238
+ if output == "json":
239
+ list_response.add_or_update_data("error", str(e))
240
+ click.echo(json.dumps(list_response.as_dict(), indent=4))
241
+ else:
242
+ click.secho("Failed to list workstations", fg="red", err=True)
243
+ click.secho("Error: {}".format(str(e)), fg="red", err=True)
219
244
 
220
245
 
221
246
  @cli.command(help="Hibernate workstation", hidden=True)
@@ -243,6 +268,8 @@ def hibernate_workstation(config_dir=None, profile=None, workstation=None):
243
268
  click.secho("Please specify a workstation ID", fg="red")
244
269
  return
245
270
  try:
271
+ if not profile:
272
+ profile = metaflowconfig.get_metaflow_profile()
246
273
  metaflow_token = metaflowconfig.get_metaflow_token_from_config(
247
274
  config_dir, profile
248
275
  )
@@ -267,7 +294,7 @@ def hibernate_workstation(config_dir=None, profile=None, workstation=None):
267
294
  )
268
295
  except Exception as e:
269
296
  click.secho("Failed to hibernate workstation", fg="red")
270
- click.secho("Error: {}".format(str(e)))
297
+ click.secho("Error: {}".format(str(e)), fg="red")
271
298
 
272
299
 
273
300
  @cli.command(help="Restart workstation to the int", hidden=True)
@@ -295,6 +322,9 @@ def restart_workstation(config_dir=None, profile=None, workstation=None):
295
322
  click.secho("Please specify a workstation ID", fg="red")
296
323
  return
297
324
  try:
325
+ if not profile:
326
+ profile = metaflowconfig.get_metaflow_profile()
327
+
298
328
  metaflow_token = metaflowconfig.get_metaflow_token_from_config(
299
329
  config_dir, profile
300
330
  )
@@ -319,7 +349,7 @@ def restart_workstation(config_dir=None, profile=None, workstation=None):
319
349
  )
320
350
  except Exception as e:
321
351
  click.secho("Failed to restart workstation", fg="red")
322
- click.secho("Error: {}".format(str(e)))
352
+ click.secho("Error: {}".format(str(e)), fg="red")
323
353
 
324
354
 
325
355
  @cli.command(help="Install dependencies needed by workstations", hidden=True)
@@ -1,11 +1,13 @@
1
1
  import json
2
2
  import os
3
3
  import requests
4
+ from os import path
5
+ import requests
4
6
 
5
7
 
6
- def init_config() -> dict:
7
- profile = os.environ.get("METAFLOW_PROFILE")
8
- config_dir = os.path.expanduser(
8
+ def init_config(config_dir="", profile="") -> dict:
9
+ profile = profile or os.environ.get("METAFLOW_PROFILE")
10
+ config_dir = config_dir or os.path.expanduser(
9
11
  os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")
10
12
  )
11
13
 
@@ -44,13 +46,10 @@ def get_metaflow_token_from_config(config_dir: str, profile: str) -> str:
44
46
  config_dir (str): Path to the config directory
45
47
  profile (str): The named metaflow profile
46
48
  """
47
- config_filename = f"config_{profile}.json" if profile else "config.json"
48
- config_path = os.path.join(config_dir, config_filename)
49
- with open(config_path) as json_file:
50
- config = json.load(json_file)
51
- if config is None or "METAFLOW_SERVICE_AUTH_KEY" not in config:
52
- raise Exception("METAFLOW_SERVICE_AUTH_KEY not found in config file")
53
- return config["METAFLOW_SERVICE_AUTH_KEY"]
49
+ config = init_config(config_dir, profile)
50
+ if config is None or "METAFLOW_SERVICE_AUTH_KEY" not in config:
51
+ raise Exception("METAFLOW_SERVICE_AUTH_KEY not found in config file")
52
+ return config["METAFLOW_SERVICE_AUTH_KEY"]
54
53
 
55
54
 
56
55
  def get_sanitized_url_from_config(config_dir: str, profile: str, key: str) -> str:
@@ -62,16 +61,12 @@ def get_sanitized_url_from_config(config_dir: str, profile: str, key: str) -> st
62
61
  profile (str): The named metaflow profile
63
62
  key (str): The key to look up in the config file
64
63
  """
65
- config_filename = f"config_{profile}.json" if profile else "config.json"
66
- config_path = os.path.join(config_dir, config_filename)
67
-
68
- with open(config_path) as json_file:
69
- config = json.load(json_file)
70
- if key not in config:
71
- raise Exception(f"Key {key} not found in config file {config_path}")
72
- url_in_config = config[key]
73
- if not url_in_config.startswith("https://"):
74
- url_in_config = f"https://{url_in_config}"
64
+ config = init_config(config_dir, profile)
65
+ if key not in config:
66
+ raise Exception(f"Key {key} not found in config")
67
+ url_in_config = config[key]
68
+ if not url_in_config.startswith("https://"):
69
+ url_in_config = f"https://{url_in_config}"
75
70
 
76
- url_in_config = url_in_config.rstrip("/")
77
- return url_in_config
71
+ url_in_config = url_in_config.rstrip("/")
72
+ return url_in_config
@@ -37,10 +37,14 @@ class OuterboundsCommandResponse:
37
37
  self._message = ""
38
38
  self._steps = []
39
39
  self.metadata = {}
40
+ self._data = {}
40
41
 
41
42
  def add_or_update_metadata(self, key, value):
42
43
  self.metadata[key] = value
43
44
 
45
+ def add_or_update_data(self, key, value):
46
+ self._data[key] = value
47
+
44
48
  def add_step(self, step: CommandStatus):
45
49
  self._steps.append(step)
46
50
  self._process_step_status(step)
@@ -59,10 +63,11 @@ class OuterboundsCommandResponse:
59
63
  self._message = "We found one or more warnings with your installation."
60
64
 
61
65
  def as_dict(self):
66
+ self._data["steps"] = [step.as_dict() for step in self._steps]
62
67
  return {
63
68
  "status": self.status.value,
64
69
  "code": self._code,
65
70
  "message": self._message,
66
- "steps": [step.as_dict() for step in self._steps],
67
71
  "metadata": self.metadata,
72
+ "data": self._data,
68
73
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: outerbounds
3
- Version: 0.3.53
3
+ Version: 0.3.53rc1
4
4
  Summary: More Data Science, Less Administration
5
5
  License: Proprietary
6
6
  Keywords: data science,machine learning,MLOps
@@ -0,0 +1,15 @@
1
+ outerbounds/__init__.py,sha256=GPdaubvAYF8pOFWJ3b-sPMKCpyfpteWVMZWkmaYhxRw,32
2
+ outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
3
+ outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
4
+ outerbounds/command_groups/cli.py,sha256=H4LxcYTmsY9DQUrReSRLjvbg9s9Ro7s-eUrcMqEJ_9A,261
5
+ outerbounds/command_groups/local_setup_cli.py,sha256=DwQYfTnK_kb63oQM0A8cAJeLRCTLOcyEwjkBL42O1fQ,29414
6
+ outerbounds/command_groups/perimeters_cli.py,sha256=9tOql42d00KfHpZYkLLGEAOiy8iRbIzsknldCyICwU0,12063
7
+ outerbounds/command_groups/workstations_cli.py,sha256=f3gwHMZPHzeOcGj5VfC5tZZA18JQhFzy2LRGzqAosOk,19286
8
+ outerbounds/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ outerbounds/utils/kubeconfig.py,sha256=l1mUP1j9VIq3fsffi5bJ1Nk-hYlwd1dIqkpj7DvVS1E,7936
10
+ outerbounds/utils/metaflowconfig.py,sha256=HgaDmK3F97rppfGUdysS1Zppe28ERTLV_HcB5IuPpV4,2631
11
+ outerbounds/utils/schema.py,sha256=Ht_Yf5uoKO0m36WXHZLSPmWPH6EFWXfZDQsiAUquc5k,2160
12
+ outerbounds-0.3.53rc1.dist-info/METADATA,sha256=uUkhPz_gBBdZGk4mA3B5PrwediF87wcbiMRxb9bPe_w,1364
13
+ outerbounds-0.3.53rc1.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
14
+ outerbounds-0.3.53rc1.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
15
+ outerbounds-0.3.53rc1.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- outerbounds/__init__.py,sha256=GPdaubvAYF8pOFWJ3b-sPMKCpyfpteWVMZWkmaYhxRw,32
2
- outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
3
- outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
4
- outerbounds/command_groups/cli.py,sha256=61VsBlPG2ykP_786eCyllqeM8DMhPAOfj2FhktrSd7k,207
5
- outerbounds/command_groups/local_setup_cli.py,sha256=g_kkrlDGzYvZTm184pW6QwotpkcqBamB14kH_Kv8TbM,28685
6
- outerbounds/command_groups/workstations_cli.py,sha256=VgydQzCas3mlAFyzZuanjl1E8Zh7pBrbKbbP6t6N2WU,18237
7
- outerbounds/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- outerbounds/utils/kubeconfig.py,sha256=l1mUP1j9VIq3fsffi5bJ1Nk-hYlwd1dIqkpj7DvVS1E,7936
9
- outerbounds/utils/metaflowconfig.py,sha256=6u9D4x-pQVCPKnmGkTg9uSSHrq4mGnWQl7TurwyV2e8,2945
10
- outerbounds/utils/schema.py,sha256=nBuarFbdZu0LGhG0YkJ6pEIvdglfM_TO_W_Db2vksb0,2017
11
- outerbounds-0.3.53.dist-info/METADATA,sha256=Q0iMl6HsByY3wbjR129VFxJJz7WpETZ2oAI2vACaYtw,1361
12
- outerbounds-0.3.53.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
13
- outerbounds-0.3.53.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
14
- outerbounds-0.3.53.dist-info/RECORD,,