outerbounds 0.3.87__py3-none-any.whl → 0.3.87rc0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,511 @@
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
+ from outerbounds._vendor import click
15
+ import requests
16
+ from requests.exceptions import HTTPError
17
+
18
+ from ..utils import metaflowconfig
19
+ from ..utils.schema import (
20
+ CommandStatus,
21
+ OuterboundsCommandResponse,
22
+ OuterboundsCommandStatus,
23
+ )
24
+
25
+
26
+ @click.group()
27
+ def cli(**kwargs):
28
+ pass
29
+
30
+
31
+ @click.group(help="Manage perimeters")
32
+ def app(**kwargs):
33
+ pass
34
+
35
+
36
+ @app.command(help="Start an app using a port and a name")
37
+ @click.option(
38
+ "-d",
39
+ "--config-dir",
40
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
41
+ help="Path to Metaflow configuration directory",
42
+ show_default=True,
43
+ )
44
+ @click.option(
45
+ "-p",
46
+ "--profile",
47
+ default=os.environ.get("METAFLOW_PROFILE", ""),
48
+ help="The named metaflow profile in which your workstation exists",
49
+ )
50
+ @click.option(
51
+ "--port",
52
+ required=True,
53
+ help="Port number where you want to start your app.",
54
+ type=int,
55
+ )
56
+ @click.option(
57
+ "--name",
58
+ required=True,
59
+ help="Name of your app",
60
+ type=str,
61
+ )
62
+ @click.option(
63
+ "-o",
64
+ "--output",
65
+ default="",
66
+ help="Show output in the specified format.",
67
+ type=click.Choice(["json", ""]),
68
+ )
69
+ def start(config_dir=None, profile=None, port=-1, name="", output=""):
70
+ start_app_response = OuterboundsCommandResponse()
71
+
72
+ validate_workstation_step = CommandStatus(
73
+ "ValidateRunningOnWorkstation",
74
+ OuterboundsCommandStatus.OK,
75
+ "Command is being run on a workstation.",
76
+ )
77
+
78
+ list_workstations_step = CommandStatus(
79
+ "ListWorkstations",
80
+ OuterboundsCommandStatus.OK,
81
+ "List of workstations fetched.",
82
+ )
83
+
84
+ validate_port_exists = CommandStatus(
85
+ "ValidatePortExists",
86
+ OuterboundsCommandStatus.OK,
87
+ "Port exists on workstation",
88
+ )
89
+
90
+ start_app_step = CommandStatus(
91
+ "StartApp",
92
+ OuterboundsCommandStatus.OK,
93
+ f"App {name} started on port {port}!",
94
+ )
95
+
96
+ if "WORKSTATION_ID" not in os.environ:
97
+ validate_workstation_step.update(
98
+ OuterboundsCommandStatus.FAIL,
99
+ "All outerbounds app commands can only be run from a workstation.",
100
+ "",
101
+ )
102
+ start_app_response.add_step(validate_workstation_step)
103
+ click.secho(
104
+ "All outerbounds app commands can only be run from a workstation.",
105
+ fg="red",
106
+ err=True,
107
+ )
108
+
109
+ if output == "json":
110
+ click.echo(json.dumps(start_app_response.as_dict(), indent=4))
111
+ return
112
+
113
+ try:
114
+ try:
115
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
116
+ config_dir, profile
117
+ )
118
+ api_url = metaflowconfig.get_sanitized_url_from_config(
119
+ config_dir, profile, "OBP_API_SERVER"
120
+ )
121
+
122
+ workstations_response = requests.get(
123
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
124
+ )
125
+ workstations_response.raise_for_status()
126
+ start_app_response.add_step(list_workstations_step)
127
+ except:
128
+ click.secho("Failed to list workstations!", fg="red", err=True)
129
+ list_workstations_step.update(
130
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
131
+ )
132
+ start_app_response.add_step(list_workstations_step)
133
+ if output == "json":
134
+ click.echo(json.dumps(start_app_response.as_dict(), indent=4))
135
+ return
136
+
137
+ workstations_json = workstations_response.json()["workstations"]
138
+ for workstation in workstations_json:
139
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
140
+ if "named_ports" in workstation["spec"]:
141
+ for named_port in workstation["spec"]["named_ports"]:
142
+ if int(named_port["port"]) == port:
143
+ start_app_response.add_step(validate_port_exists)
144
+ if named_port["enabled"] and named_port["name"] == name:
145
+ click.secho(
146
+ f"App {name} started on port {port}!",
147
+ fg="green",
148
+ err=True,
149
+ )
150
+ start_app_response.add_step(start_app_step)
151
+ if output == "json":
152
+ click.echo(
153
+ json.dumps(
154
+ start_app_response.as_dict(), indent=4
155
+ )
156
+ )
157
+ return
158
+ else:
159
+ try:
160
+ response = requests.put(
161
+ f"{api_url}/v1/workstations/update/{os.environ['WORKSTATION_ID']}/namedports",
162
+ headers={"x-api-key": metaflow_token},
163
+ json={
164
+ "port": port,
165
+ "name": name,
166
+ "enabled": True,
167
+ },
168
+ )
169
+
170
+ response.raise_for_status()
171
+ click.secho(
172
+ f"App {name} started on port {port}!",
173
+ fg="green",
174
+ err=True,
175
+ )
176
+ except:
177
+ click.secho(
178
+ f"Failed to start app {name} on port {port}!",
179
+ fg="red",
180
+ err=True,
181
+ )
182
+ start_app_step.update(
183
+ OuterboundsCommandStatus.FAIL,
184
+ f"Failed to start app {name} on port {port}!",
185
+ "",
186
+ )
187
+
188
+ start_app_response.add_step(start_app_step)
189
+ if output == "json":
190
+ click.echo(
191
+ json.dumps(
192
+ start_app_response.as_dict(), indent=4
193
+ )
194
+ )
195
+ return
196
+
197
+ click.secho(
198
+ f"Port {port} not found on workstation {os.environ['WORKSTATION_ID']}",
199
+ fg="red",
200
+ err=True,
201
+ )
202
+ validate_port_exists.update(
203
+ OuterboundsCommandStatus.FAIL,
204
+ f"Port {port} not found on workstation {os.environ['WORKSTATION_ID']}",
205
+ "",
206
+ )
207
+ start_app_response.add_step(validate_port_exists)
208
+ if output == "json":
209
+ click.echo(json.dumps(start_app_response.as_dict(), indent=4))
210
+ except Exception as e:
211
+ click.secho(f"Failed to start app {name} on port {port}!", fg="red", err=True)
212
+ start_app_step.update(
213
+ OuterboundsCommandStatus.FAIL,
214
+ f"Failed to start app {name} on port {port}!",
215
+ "",
216
+ )
217
+ start_app_response.add_step(start_app_step)
218
+ if output == "json":
219
+ click.secho(json.dumps(start_app_response.as_dict(), indent=4))
220
+
221
+
222
+ @app.command(help="Stop an app using its port number")
223
+ @click.option(
224
+ "-d",
225
+ "--config-dir",
226
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
227
+ help="Path to Metaflow configuration directory",
228
+ show_default=True,
229
+ )
230
+ @click.option(
231
+ "-p",
232
+ "--profile",
233
+ default=os.environ.get("METAFLOW_PROFILE", ""),
234
+ help="The named metaflow profile in which your workstation exists",
235
+ )
236
+ @click.option(
237
+ "--port",
238
+ required=True,
239
+ help="Port number where you want to start your app.",
240
+ type=int,
241
+ )
242
+ @click.option(
243
+ "-o",
244
+ "--output",
245
+ default="",
246
+ help="Show output in the specified format.",
247
+ type=click.Choice(["json", ""]),
248
+ )
249
+ def stop(config_dir=None, profile=None, port=-1, output=""):
250
+ stop_app_response = OuterboundsCommandResponse()
251
+
252
+ validate_workstation_step = CommandStatus(
253
+ "ValidateRunningOnWorkstation",
254
+ OuterboundsCommandStatus.OK,
255
+ "Command is being run on a workstation.",
256
+ )
257
+
258
+ list_workstations_step = CommandStatus(
259
+ "ListWorkstations",
260
+ OuterboundsCommandStatus.OK,
261
+ "List of workstations fetched.",
262
+ )
263
+
264
+ validate_port_exists = CommandStatus(
265
+ "ValidatePortExists",
266
+ OuterboundsCommandStatus.OK,
267
+ "Port exists on workstation",
268
+ )
269
+
270
+ stop_app_step = CommandStatus(
271
+ "StopApp",
272
+ OuterboundsCommandStatus.OK,
273
+ f"App stopped on port {port}!",
274
+ )
275
+
276
+ if "WORKSTATION_ID" not in os.environ:
277
+ validate_workstation_step.update(
278
+ OuterboundsCommandStatus.FAIL,
279
+ "All outerbounds app commands can only be run from a workstation.",
280
+ "",
281
+ )
282
+ stop_app_response.add_step(validate_workstation_step)
283
+ click.secho(
284
+ "All outerbounds app commands can only be run from a workstation.",
285
+ fg="red",
286
+ err=True,
287
+ )
288
+
289
+ if output == "json":
290
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
291
+ return
292
+
293
+ try:
294
+ try:
295
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
296
+ config_dir, profile
297
+ )
298
+ api_url = metaflowconfig.get_sanitized_url_from_config(
299
+ config_dir, profile, "OBP_API_SERVER"
300
+ )
301
+
302
+ workstations_response = requests.get(
303
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
304
+ )
305
+ workstations_response.raise_for_status()
306
+ stop_app_response.add_step(list_workstations_step)
307
+ except:
308
+ click.secho("Failed to list workstations!", fg="red", err=True)
309
+ list_workstations_step.update(
310
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
311
+ )
312
+ stop_app_response.add_step(list_workstations_step)
313
+ if output == "json":
314
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
315
+ return
316
+
317
+ workstations_json = workstations_response.json()["workstations"]
318
+ for workstation in workstations_json:
319
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
320
+ if "named_ports" in workstation["spec"]:
321
+ for named_port in workstation["spec"]["named_ports"]:
322
+ if int(named_port["port"]) == port:
323
+ stop_app_response.add_step(validate_port_exists)
324
+ if not named_port["enabled"]:
325
+ click.secho(
326
+ f"App stopped on port {port}!", fg="green", err=True
327
+ )
328
+ stop_app_response.add_step(stop_app_step)
329
+ if output == "json":
330
+ click.echo(
331
+ json.dumps(
332
+ stop_app_response.as_dict(), indent=4
333
+ )
334
+ )
335
+ return
336
+ else:
337
+ try:
338
+ response = requests.put(
339
+ f"{api_url}/v1/workstations/update/{os.environ['WORKSTATION_ID']}/namedports",
340
+ headers={"x-api-key": metaflow_token},
341
+ json={
342
+ "port": port,
343
+ "name": named_port["name"],
344
+ "enabled": False,
345
+ },
346
+ )
347
+ response.raise_for_status()
348
+ click.secho(
349
+ f"App stopped on port {port}!",
350
+ fg="green",
351
+ err=True,
352
+ )
353
+ except:
354
+ click.secho(
355
+ f"Failed to stop app on port {port}!",
356
+ fg="red",
357
+ err=True,
358
+ )
359
+ stop_app_step.update(
360
+ OuterboundsCommandStatus.FAIL,
361
+ f"Failed to stop app on port {port}!",
362
+ "",
363
+ )
364
+
365
+ stop_app_response.add_step(stop_app_step)
366
+ if output == "json":
367
+ click.echo(
368
+ json.dumps(
369
+ stop_app_response.as_dict(), indent=4
370
+ )
371
+ )
372
+ return
373
+
374
+ click.secho(
375
+ f"Port {port} not found on workstation {os.environ['WORKSTATION_ID']}",
376
+ fg="red",
377
+ err=True,
378
+ )
379
+ validate_port_exists.update(
380
+ OuterboundsCommandStatus.FAIL,
381
+ f"Port {port} not found on workstation {os.environ['WORKSTATION_ID']}",
382
+ "",
383
+ )
384
+ stop_app_response.add_step(validate_port_exists)
385
+ if output == "json":
386
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
387
+ except Exception as e:
388
+ click.secho(f"Failed to stop app on port {port}!", fg="red", err=True)
389
+ stop_app_step.update(
390
+ OuterboundsCommandStatus.FAIL, f"Failed to stop on port {port}!", ""
391
+ )
392
+ stop_app_response.add_step(stop_app_step)
393
+ if output == "json":
394
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
395
+
396
+
397
+ @app.command(help="Stop an app using its port number")
398
+ @click.option(
399
+ "-d",
400
+ "--config-dir",
401
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
402
+ help="Path to Metaflow configuration directory",
403
+ show_default=True,
404
+ )
405
+ @click.option(
406
+ "-p",
407
+ "--profile",
408
+ default=os.environ.get("METAFLOW_PROFILE", ""),
409
+ help="The named metaflow profile in which your workstation exists",
410
+ )
411
+ @click.option(
412
+ "-o",
413
+ "--output",
414
+ default="",
415
+ help="Show output in the specified format.",
416
+ type=click.Choice(["json", ""]),
417
+ )
418
+ def list(config_dir=None, profile=None, output=""):
419
+ list_app_response = OuterboundsCommandResponse()
420
+
421
+ validate_workstation_step = CommandStatus(
422
+ "ValidateRunningOnWorkstation",
423
+ OuterboundsCommandStatus.OK,
424
+ "Command is being run on a workstation.",
425
+ )
426
+
427
+ list_workstations_step = CommandStatus(
428
+ "ListWorkstations",
429
+ OuterboundsCommandStatus.OK,
430
+ "List of workstations fetched.",
431
+ )
432
+
433
+ if "WORKSTATION_ID" not in os.environ:
434
+ validate_workstation_step.update(
435
+ OuterboundsCommandStatus.FAIL,
436
+ "All outerbounds app commands can only be run from a workstation.",
437
+ "",
438
+ )
439
+ list_app_response.add_step(validate_workstation_step)
440
+ click.secho(
441
+ "All outerbounds app commands can only be run from a workstation.",
442
+ fg="red",
443
+ err=True,
444
+ )
445
+
446
+ if output == "json":
447
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
448
+ return
449
+
450
+ try:
451
+ try:
452
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
453
+ config_dir, profile
454
+ )
455
+ api_url = metaflowconfig.get_sanitized_url_from_config(
456
+ config_dir, profile, "OBP_API_SERVER"
457
+ )
458
+
459
+ workstations_response = requests.get(
460
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
461
+ )
462
+ workstations_response.raise_for_status()
463
+ list_app_response.add_step(list_workstations_step)
464
+ except:
465
+ click.secho("Failed to list workstations!", fg="red", err=True)
466
+ list_workstations_step.update(
467
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
468
+ )
469
+ list_app_response.add_step(list_workstations_step)
470
+ if output == "json":
471
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
472
+ return
473
+
474
+ workstations_json = workstations_response.json()["workstations"]
475
+ for workstation in workstations_json:
476
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
477
+ if "named_ports" in workstation["spec"]:
478
+ for named_port in workstation["spec"]["named_ports"]:
479
+ if named_port["enabled"]:
480
+ click.secho(
481
+ f"App Name: {named_port['name']}", fg="green", err=True
482
+ )
483
+ click.secho(
484
+ f"App Port on Workstation: {named_port['port']}",
485
+ fg="green",
486
+ err=True,
487
+ )
488
+ click.secho(f"App Status: Deployed", fg="green", err=True)
489
+ click.secho(
490
+ f"App URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{named_port['name']}/",
491
+ fg="green",
492
+ err=True,
493
+ )
494
+ else:
495
+ click.secho(
496
+ f"App Port on Workstation: {named_port['port']}",
497
+ fg="yellow",
498
+ err=True,
499
+ )
500
+ click.secho(
501
+ f"App Status: Not Deployed", fg="yellow", err=True
502
+ )
503
+
504
+ click.echo("\n", err=True)
505
+ except Exception as e:
506
+ click.secho(f"Failed to list apps!", fg="red", err=True)
507
+ if output == "json":
508
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
509
+
510
+
511
+ cli.add_command(app, name="app")
@@ -2,11 +2,17 @@ from outerbounds._vendor import click
2
2
  from . import local_setup_cli
3
3
  from . import workstations_cli
4
4
  from . import perimeters_cli
5
+ from . import apps_cli
5
6
 
6
7
 
7
8
  @click.command(
8
9
  cls=click.CommandCollection,
9
- sources=[local_setup_cli.cli, workstations_cli.cli, perimeters_cli.cli],
10
+ sources=[
11
+ local_setup_cli.cli,
12
+ workstations_cli.cli,
13
+ perimeters_cli.cli,
14
+ apps_cli.cli,
15
+ ],
10
16
  )
11
17
  def cli(**kwargs):
12
18
  pass
@@ -59,14 +59,14 @@ class OuterboundsCommandResponse:
59
59
  if step.status == OuterboundsCommandStatus.FAIL:
60
60
  self.status = OuterboundsCommandStatus.FAIL
61
61
  self._code = 500
62
- self._message = "We found one or more errors with your installation."
62
+ self._message = "Encountered an error when trying to run command."
63
63
  elif (
64
64
  step.status == OuterboundsCommandStatus.WARN
65
65
  and self.status != OuterboundsCommandStatus.FAIL
66
66
  ):
67
67
  self.status = OuterboundsCommandStatus.WARN
68
68
  self._code = 200
69
- self._message = "We found one or more warnings with your installation."
69
+ self._message = "Encountered one or more warnings when running the command."
70
70
 
71
71
  def as_dict(self):
72
72
  self._data["steps"] = [step.as_dict() for step in self._steps]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: outerbounds
3
- Version: 0.3.87
3
+ Version: 0.3.87rc0
4
4
  Summary: More Data Science, Less Administration
5
5
  License: Proprietary
6
6
  Keywords: data science,machine learning,MLOps
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.8
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Provides-Extra: apps
17
18
  Provides-Extra: azure
18
19
  Provides-Extra: gcp
19
20
  Requires-Dist: azure-identity (>=1.15.0,<2.0.0) ; extra == "azure"
@@ -41,16 +41,17 @@ outerbounds/_vendor/yaml/serializer.py,sha256=8wFZRy9SsQSktF_f9OOroroqsh4qVUe53r
41
41
  outerbounds/_vendor/yaml/tokens.py,sha256=JBSu38wihGr4l73JwbfMA7Ks1-X84g8-NskTz7KwPmA,2578
42
42
  outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
43
43
  outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
44
- outerbounds/command_groups/cli.py,sha256=sorDdQvmTPqIwfvgtuNLILelimXu5CknFnWQFsYFGHs,286
44
+ outerbounds/command_groups/apps_cli.py,sha256=924nZKqZuE0kokMfz4woSI8hx47l7ss7-831ETccUXE,19340
45
+ outerbounds/command_groups/cli.py,sha256=3NyxnczfANtxKh-KnJHGWAEC5akdXux_hylfGXTs2A0,362
45
46
  outerbounds/command_groups/local_setup_cli.py,sha256=somQMeLgRSK8BAje2rN6LeY-lszXmwBpNLvDCk293h8,36554
46
47
  outerbounds/command_groups/perimeters_cli.py,sha256=vU1LykO9T2Xj4Fn8hyhaCUR4q454q1aVoU4XhhrgAS4,12781
47
48
  outerbounds/command_groups/workstations_cli.py,sha256=V5Jbj1cVb4IRllI7fOgNgL6OekRpuFDv6CEhDb4xC6w,22016
48
49
  outerbounds/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
50
  outerbounds/utils/kubeconfig.py,sha256=yvcyRXGR4AhQuqUDqmbGxEOHw5ixMFV0AZIDg1LI_Qo,7981
50
51
  outerbounds/utils/metaflowconfig.py,sha256=Ja1eufYm4UANRNpk7qlxg_UUudToDOVCjaWg88N-xhQ,3538
51
- outerbounds/utils/schema.py,sha256=cNlgjmteLPbDzSEUSQDsq8txdhMGyezSmM83jU3aa0w,2329
52
+ outerbounds/utils/schema.py,sha256=lMUr9kNgn9wy-sO_t_Tlxmbt63yLeN4b0xQXbDUDj4A,2331
52
53
  outerbounds/vendor.py,sha256=gRLRJNXtZBeUpPEog0LOeIsl6GosaFFbCxUvR4bW6IQ,5093
53
- outerbounds-0.3.87.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
54
- outerbounds-0.3.87.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
55
- outerbounds-0.3.87.dist-info/METADATA,sha256=EFZdu76YljG2LKp8ZQZcKcyWAsVg_O_m335BmfcfKjo,1632
56
- outerbounds-0.3.87.dist-info/RECORD,,
54
+ outerbounds-0.3.87rc0.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
55
+ outerbounds-0.3.87rc0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
56
+ outerbounds-0.3.87rc0.dist-info/METADATA,sha256=av38VP4Z6_FUni7QysM7NjtLY2vzM_6vnGA8juq8rFM,1656
57
+ outerbounds-0.3.87rc0.dist-info/RECORD,,