outerbounds 0.3.20rc0__py3-none-any.whl → 0.3.21__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|