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 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.20rc0
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: kubeconfig (>=1.1.1,<2.0.0)
19
- Requires-Dist: ob-metaflow (==2.8.0.1)
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,,