outerbounds 0.3.20rc0__py3-none-any.whl → 0.3.21__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.
- outerbounds/__init__.py +1 -286
- {outerbounds-0.3.20rc0.dist-info → outerbounds-0.3.21.dist-info}/METADATA +3 -7
- outerbounds-0.3.21.dist-info/RECORD +6 -0
- outerbounds-0.3.20rc0.dist-info/RECORD +0 -6
- {outerbounds-0.3.20rc0.dist-info → outerbounds-0.3.21.dist-info}/WHEEL +0 -0
- {outerbounds-0.3.20rc0.dist-info → outerbounds-0.3.21.dist-info}/entry_points.txt +0 -0
outerbounds/__init__.py
CHANGED
@@ -5,16 +5,10 @@ from pathlib import Path
|
|
5
5
|
import json
|
6
6
|
import os
|
7
7
|
import re
|
8
|
-
import string
|
9
8
|
import sys
|
10
9
|
import zlib
|
10
|
+
|
11
11
|
import click
|
12
|
-
from kubeconfig import KubeConfig
|
13
|
-
import yaml
|
14
|
-
import requests
|
15
|
-
import base64
|
16
|
-
import datetime
|
17
|
-
import hashlib
|
18
12
|
|
19
13
|
|
20
14
|
class CheckException(Exception):
|
@@ -397,282 +391,3 @@ def configure(encoded_config=None, config_dir=None, profile=None, echo=None):
|
|
397
391
|
except Exception as e:
|
398
392
|
click.secho("Writing the configuration file '{}' failed.".format(writer.path()))
|
399
393
|
click.secho("Error: {}".format(str(e)))
|
400
|
-
|
401
|
-
|
402
|
-
def get_k8s_token_response(config_dir, profile):
|
403
|
-
config_path = path.join(config_dir, "config.json")
|
404
|
-
if profile != "":
|
405
|
-
config_path = path.join(config_dir, "config_{}.json".format(profile))
|
406
|
-
with open(config_path) as json_file:
|
407
|
-
config = json.load(json_file)
|
408
|
-
metaflow_token = config["METAFLOW_SERVICE_AUTH_KEY"]
|
409
|
-
|
410
|
-
if config["OBP_AUTH_SERVER"].startswith("https://"):
|
411
|
-
auth_endpoint = config["OBP_AUTH_SERVER"]
|
412
|
-
else:
|
413
|
-
auth_endpoint = "https://{}".format(config["OBP_AUTH_SERVER"])
|
414
|
-
|
415
|
-
if auth_endpoint.endswith("/"):
|
416
|
-
auth_endpoint = auth_endpoint[:-1]
|
417
|
-
|
418
|
-
generate_token_url = "{}/generate/k8s".format(auth_endpoint)
|
419
|
-
headers = {"x-api-key": metaflow_token}
|
420
|
-
response = requests.get(generate_token_url, headers=headers)
|
421
|
-
if response.status_code == 200:
|
422
|
-
return json.loads(response.content)
|
423
|
-
else:
|
424
|
-
raise Exception(
|
425
|
-
"Failed to get response. Error: {}".format(response.status_code)
|
426
|
-
)
|
427
|
-
|
428
|
-
|
429
|
-
@cli.command(help="Generate a token to use your cloud workstation")
|
430
|
-
@click.option(
|
431
|
-
"-d",
|
432
|
-
"--config-dir",
|
433
|
-
default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
|
434
|
-
help="Path to Metaflow configuration directory",
|
435
|
-
show_default=True,
|
436
|
-
)
|
437
|
-
@click.option(
|
438
|
-
"-p",
|
439
|
-
"--profile",
|
440
|
-
default="",
|
441
|
-
help="The named metaflow profile in which your workstation exists",
|
442
|
-
)
|
443
|
-
def generate_workstation_token(config_dir=None, profile=None):
|
444
|
-
try:
|
445
|
-
token = get_k8s_token_response(config_dir, profile)["token"]
|
446
|
-
token_data = base64.b64decode(token.split(".")[1] + "==")
|
447
|
-
exec_creds = {
|
448
|
-
"kind": "ExecCredential",
|
449
|
-
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
450
|
-
"spec": {},
|
451
|
-
"status": {
|
452
|
-
"token": token,
|
453
|
-
"expirationTimestamp": datetime.datetime.fromtimestamp(
|
454
|
-
json.loads(token_data)["exp"], datetime.timezone.utc
|
455
|
-
).isoformat(),
|
456
|
-
},
|
457
|
-
}
|
458
|
-
click.echo(json.dumps(exec_creds))
|
459
|
-
except Exception as e:
|
460
|
-
click.secho("Failed to generate workstation token.", fg="red")
|
461
|
-
click.secho("Error: {}".format(str(e)))
|
462
|
-
|
463
|
-
|
464
|
-
@cli.command(help="Configure a cloud workstation")
|
465
|
-
@click.option(
|
466
|
-
"-d",
|
467
|
-
"--config-dir",
|
468
|
-
default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
|
469
|
-
help="Path to Metaflow configuration directory",
|
470
|
-
show_default=True,
|
471
|
-
)
|
472
|
-
@click.option(
|
473
|
-
"-p",
|
474
|
-
"--profile",
|
475
|
-
default="",
|
476
|
-
help="The named metaflow profile in which your workstation exists",
|
477
|
-
)
|
478
|
-
def configure_cloud_workstation(config_dir=None, profile=None):
|
479
|
-
k8s_response = get_k8s_token_response(config_dir, profile)
|
480
|
-
token_data = base64.b64decode(k8s_response["token"].split(".")[1] + "==")
|
481
|
-
ws_namespace = "ws-{}".format(
|
482
|
-
hashlib.md5(bytes(json.loads(token_data)["username"], "utf-8")).hexdigest()
|
483
|
-
)
|
484
|
-
|
485
|
-
kube_config_path = path.expanduser(os.environ.get("KUBECONFIG", "~/.kube/config"))
|
486
|
-
kube_config = KubeConfig(kube_config_path)
|
487
|
-
kube_config.set_context(
|
488
|
-
"outerbounds-workstations", "outerbounds-cluster", ws_namespace, "obp-user"
|
489
|
-
)
|
490
|
-
|
491
|
-
kube_config.set_cluster(
|
492
|
-
"outerbounds-cluster",
|
493
|
-
server=k8s_response["endpoint"],
|
494
|
-
insecure_skip_tls_verify=True,
|
495
|
-
)
|
496
|
-
|
497
|
-
with open(kube_config_path, "r") as f:
|
498
|
-
kube_yaml = yaml.safe_load(f)
|
499
|
-
|
500
|
-
gen_creds_args = ["generate-workstation-token"]
|
501
|
-
if profile != "":
|
502
|
-
gen_creds_args.append("--profile")
|
503
|
-
gen_creds_args.append(profile)
|
504
|
-
|
505
|
-
gen_creds_args.append("--config-dir")
|
506
|
-
gen_creds_args.append(config_dir)
|
507
|
-
|
508
|
-
user_exec_creds = {
|
509
|
-
"exec": {
|
510
|
-
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
511
|
-
"command": "outerbounds",
|
512
|
-
"args": gen_creds_args,
|
513
|
-
"env": None,
|
514
|
-
"interactiveMode": "Never",
|
515
|
-
"provideClusterInfo": False,
|
516
|
-
}
|
517
|
-
}
|
518
|
-
|
519
|
-
user_updated = False
|
520
|
-
|
521
|
-
if kube_yaml["users"] is None:
|
522
|
-
kube_yaml["users"] = []
|
523
|
-
|
524
|
-
for user in kube_yaml["users"]:
|
525
|
-
if user["name"] == "obp-user":
|
526
|
-
user["user"] = user_exec_creds
|
527
|
-
user_updated = True
|
528
|
-
|
529
|
-
if not user_updated:
|
530
|
-
kube_yaml["users"].append({"name": "obp-user", "user": user_exec_creds})
|
531
|
-
|
532
|
-
with open(kube_config_path, "w") as f:
|
533
|
-
yaml.safe_dump(kube_yaml, f)
|
534
|
-
|
535
|
-
kube_config.use_context("outerbounds-workstations")
|
536
|
-
|
537
|
-
|
538
|
-
def prepare_workstations_api_request(config_dir, profile, api_path):
|
539
|
-
config_path = path.join(config_dir, "config.json")
|
540
|
-
if profile != "":
|
541
|
-
config_path = path.join(config_dir, "config_{}.json".format(profile))
|
542
|
-
with open(config_path) as json_file:
|
543
|
-
config = json.load(json_file)
|
544
|
-
metaflow_token = config["METAFLOW_SERVICE_AUTH_KEY"]
|
545
|
-
|
546
|
-
if config["OBP_AUTH_SERVER"].startswith("https://"):
|
547
|
-
auth_endpoint = config["OBP_AUTH_SERVER"]
|
548
|
-
else:
|
549
|
-
auth_endpoint = "https://{}".format(config["OBP_AUTH_SERVER"])
|
550
|
-
|
551
|
-
if auth_endpoint.endswith("/"):
|
552
|
-
auth_endpoint = auth_endpoint[:-1]
|
553
|
-
|
554
|
-
api_endpoint = auth_endpoint.replace("auth", "api")
|
555
|
-
endpoint_url = "{}/{}".format(api_endpoint, api_path)
|
556
|
-
headers = {"x-api-key": metaflow_token}
|
557
|
-
return endpoint_url, headers
|
558
|
-
|
559
|
-
|
560
|
-
@cli.command(help="List all existing workstations")
|
561
|
-
@click.option(
|
562
|
-
"-d",
|
563
|
-
"--config-dir",
|
564
|
-
default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
|
565
|
-
help="Path to Metaflow configuration directory",
|
566
|
-
show_default=True,
|
567
|
-
)
|
568
|
-
@click.option(
|
569
|
-
"-p",
|
570
|
-
"--profile",
|
571
|
-
default="",
|
572
|
-
help="The named metaflow profile in which your workstation exists",
|
573
|
-
)
|
574
|
-
def list_workstations(config_dir=None, profile=None):
|
575
|
-
try:
|
576
|
-
list_workstations_path = "v1/workstations"
|
577
|
-
endpoint_url, headers = prepare_workstations_api_request(
|
578
|
-
config_dir, profile, list_workstations_path
|
579
|
-
)
|
580
|
-
response = requests.get(endpoint_url, headers=headers)
|
581
|
-
if response.status_code == 200:
|
582
|
-
# Print pretty JSON
|
583
|
-
click.echo(json.dumps(response.json(), indent=4))
|
584
|
-
else:
|
585
|
-
click.secho("Failed to list workstations", fg="red")
|
586
|
-
click.secho("Error: {}".format(json.dumps(response.json(), indent=4)))
|
587
|
-
except Exception as e:
|
588
|
-
click.secho("Failed to list workstations", fg="red")
|
589
|
-
click.secho("Error: {}".format(str(e)))
|
590
|
-
|
591
|
-
|
592
|
-
@cli.command(help="Hibernate workstation")
|
593
|
-
@click.option(
|
594
|
-
"-d",
|
595
|
-
"--config-dir",
|
596
|
-
default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
|
597
|
-
help="Path to Metaflow configuration directory",
|
598
|
-
show_default=True,
|
599
|
-
)
|
600
|
-
@click.option(
|
601
|
-
"-p",
|
602
|
-
"--profile",
|
603
|
-
default="",
|
604
|
-
help="The named metaflow profile in which your workstation exists",
|
605
|
-
)
|
606
|
-
@click.option(
|
607
|
-
"-w",
|
608
|
-
"--workstation",
|
609
|
-
default="",
|
610
|
-
help="The ID of the workstation to hibernate",
|
611
|
-
)
|
612
|
-
def hibernate_workstation(config_dir=None, profile=None, workstation=None):
|
613
|
-
if workstation is None or workstation == "":
|
614
|
-
click.secho("Please specify a workstation ID", fg="red")
|
615
|
-
return
|
616
|
-
try:
|
617
|
-
list_workstations_path = "v1/workstations/hibernate/{}".format(workstation)
|
618
|
-
endpoint_url, headers = prepare_workstations_api_request(
|
619
|
-
config_dir, profile, list_workstations_path
|
620
|
-
)
|
621
|
-
response = requests.put(endpoint_url, headers=headers)
|
622
|
-
if response.status_code == 200:
|
623
|
-
response_json = response.json()
|
624
|
-
if len(response_json) > 0:
|
625
|
-
click.echo(json.dumps(response_json, indent=4))
|
626
|
-
else:
|
627
|
-
click.secho("Success", fg="green", bold=True)
|
628
|
-
else:
|
629
|
-
click.secho("Failed to hibernate workstation", fg="red")
|
630
|
-
click.secho("Error: {}".format(json.dumps(response.json(), indent=4)))
|
631
|
-
except Exception as e:
|
632
|
-
click.secho("Failed to hibernate workstation", fg="red")
|
633
|
-
click.secho("Error: {}".format(str(e)))
|
634
|
-
|
635
|
-
|
636
|
-
@cli.command(help="Restart workstation")
|
637
|
-
@click.option(
|
638
|
-
"-d",
|
639
|
-
"--config-dir",
|
640
|
-
default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
|
641
|
-
help="Path to Metaflow configuration directory",
|
642
|
-
show_default=True,
|
643
|
-
)
|
644
|
-
@click.option(
|
645
|
-
"-p",
|
646
|
-
"--profile",
|
647
|
-
default="",
|
648
|
-
help="The named metaflow profile in which your workstation exists",
|
649
|
-
)
|
650
|
-
@click.option(
|
651
|
-
"-w",
|
652
|
-
"--workstation",
|
653
|
-
default="",
|
654
|
-
help="The ID of the workstation to restart",
|
655
|
-
)
|
656
|
-
def restart_workstation(config_dir=None, profile=None, workstation=None):
|
657
|
-
if workstation is None or workstation == "":
|
658
|
-
click.secho("Please specify a workstation ID", fg="red")
|
659
|
-
return
|
660
|
-
try:
|
661
|
-
list_workstations_path = "v1/workstations/restart/{}".format(workstation)
|
662
|
-
endpoint_url, headers = prepare_workstations_api_request(
|
663
|
-
config_dir, profile, list_workstations_path
|
664
|
-
)
|
665
|
-
response = requests.put(endpoint_url, headers=headers)
|
666
|
-
if response.status_code == 200:
|
667
|
-
# Print pretty JSON
|
668
|
-
response_json = response.json()
|
669
|
-
if len(response_json) > 0:
|
670
|
-
click.echo(json.dumps(response_json, indent=4))
|
671
|
-
else:
|
672
|
-
click.secho("Success", fg="green", bold=True)
|
673
|
-
else:
|
674
|
-
click.secho("Failed to hibernate workstation", fg="red")
|
675
|
-
click.secho("Error: {}".format(json.dumps(response.json(), indent=4)))
|
676
|
-
except Exception as e:
|
677
|
-
click.secho("Failed to hibernate workstation", fg="red")
|
678
|
-
click.secho("Error: {}".format(str(e)))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: outerbounds
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.21
|
4
4
|
Summary: More Data Science, Less Administration
|
5
5
|
License: Proprietary
|
6
6
|
Keywords: data science,machine learning,MLOps
|
@@ -13,14 +13,10 @@ Classifier: Programming Language :: Python :: 3.8
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.9
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
16
|
-
Requires-Dist: PyYAML (>=6.0,<7.0)
|
17
16
|
Requires-Dist: click (>=8.1.3,<9.0.0)
|
18
|
-
Requires-Dist:
|
19
|
-
Requires-Dist: ob-metaflow (==
|
20
|
-
Requires-Dist: ob-metaflow-extensions (==1.1.16)
|
17
|
+
Requires-Dist: ob-metaflow (==2.8.4.2)
|
18
|
+
Requires-Dist: ob-metaflow-extensions (==1.1.19)
|
21
19
|
Requires-Dist: opentelemetry-distro (==0.37b0)
|
22
20
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.16.0)
|
23
21
|
Requires-Dist: opentelemetry-instrumentation-requests (==0.37b0)
|
24
|
-
Requires-Dist: types-PyYAML (>=6.0.12.8,<7.0.0.0)
|
25
|
-
Requires-Dist: types-requests (>=2.28.11.15,<3.0.0.0)
|
26
22
|
Project-URL: Documentation, https://docs.metaflow.org
|
@@ -0,0 +1,6 @@
|
|
1
|
+
outerbounds/__init__.py,sha256=8DqpohQHW-bb7fMvLEHjnM5DlcLGRw7IthmARDfciKY,13620
|
2
|
+
outerbounds/cli_main.py,sha256=k9CgQa-3xvfVcIK_ja2WKsKtwjQma9kYJyQY7LagNe4,56
|
3
|
+
outerbounds-0.3.21.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
|
4
|
+
outerbounds-0.3.21.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
|
5
|
+
outerbounds-0.3.21.dist-info/METADATA,sha256=V3Ja9j2HBR2NzsYKOQUsxKSfSBCdB5szScQIzR3knTQ,924
|
6
|
+
outerbounds-0.3.21.dist-info/RECORD,,
|
@@ -1,6 +0,0 @@
|
|
1
|
-
outerbounds/__init__.py,sha256=nbc4Q5XfGhJ5IH6TpQhbRxq4RycpCZdza884KItq0Ak,23393
|
2
|
-
outerbounds/cli_main.py,sha256=k9CgQa-3xvfVcIK_ja2WKsKtwjQma9kYJyQY7LagNe4,56
|
3
|
-
outerbounds-0.3.20rc0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
|
4
|
-
outerbounds-0.3.20rc0.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
|
5
|
-
outerbounds-0.3.20rc0.dist-info/METADATA,sha256=Jipw5msg0tZxYBUJ5JNpmM5S-C4EarDySSmtgjXt0GA,1109
|
6
|
-
outerbounds-0.3.20rc0.dist-info/RECORD,,
|
File without changes
|
File without changes
|