ob-metaflow-extensions 1.2.2__tar.gz → 1.2.3__tar.gz

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.
Files changed (128) hide show
  1. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/PKG-INFO +1 -1
  2. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/app_deploy_decorator.py +41 -7
  3. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/_state_machine.py +4 -0
  4. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/capsule.py +16 -1
  5. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/__init__.py +1 -1
  6. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py +14 -3
  7. ob_metaflow_extensions-1.2.3/metaflow_extensions/outerbounds/plugins/apps/core/deployer.py +309 -0
  8. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/ob_metaflow_extensions.egg-info/PKG-INFO +1 -1
  9. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/setup.py +1 -1
  10. ob_metaflow_extensions-1.2.2/metaflow_extensions/outerbounds/plugins/apps/core/deployer.py +0 -132
  11. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/MANIFEST.in +0 -0
  12. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/README.md +0 -0
  13. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/__init__.py +0 -0
  14. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/config/__init__.py +0 -0
  15. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/__init__.py +0 -0
  16. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/__init__.py +0 -0
  17. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/app_cli.py +0 -0
  18. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/app_utils.py +0 -0
  19. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/consts.py +0 -0
  20. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/__init__.py +0 -0
  21. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/_vendor/__init__.py +0 -0
  22. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/_vendor/spinner/__init__.py +0 -0
  23. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/_vendor/spinner/spinners.py +0 -0
  24. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/app_cli.py +0 -0
  25. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/app_config.py +0 -0
  26. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/artifacts.py +0 -0
  27. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/click_importer.py +0 -0
  28. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/code_package/__init__.py +0 -0
  29. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/code_package/code_packager.py +0 -0
  30. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/code_package/examples.py +0 -0
  31. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/cli_generator.py +0 -0
  32. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/config_utils.py +0 -0
  33. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/schema_export.py +0 -0
  34. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/typed_configs.py +0 -0
  35. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config/typed_init_generator.py +0 -0
  36. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/config_schema.yaml +0 -0
  37. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/dependencies.py +0 -0
  38. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/experimental/__init__.py +0 -0
  39. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/perimeters.py +0 -0
  40. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/secrets.py +0 -0
  41. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/utils.py +0 -0
  42. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/core/validations.py +0 -0
  43. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/deploy_decorator.py +0 -0
  44. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/apps/supervisord_utils.py +0 -0
  45. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/auth_server.py +0 -0
  46. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/aws/__init__.py +0 -0
  47. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/aws/assume_role.py +0 -0
  48. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/aws/assume_role_decorator.py +0 -0
  49. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/card_utilities/__init__.py +0 -0
  50. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/card_utilities/async_cards.py +0 -0
  51. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/card_utilities/extra_components.py +0 -0
  52. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/card_utilities/injector.py +0 -0
  53. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/checkpoint_datastores/__init__.py +0 -0
  54. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/checkpoint_datastores/coreweave.py +0 -0
  55. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/checkpoint_datastores/nebius.py +0 -0
  56. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/__init__.py +0 -0
  57. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/baker.py +0 -0
  58. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/docker_environment.py +0 -0
  59. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery.py +0 -0
  60. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_cli.py +0 -0
  61. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_decorator.py +0 -0
  62. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/kubernetes/__init__.py +0 -0
  63. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/kubernetes/kubernetes_client.py +0 -0
  64. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/kubernetes/pod_killer.py +0 -0
  65. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nim/card.py +0 -0
  66. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nim/nim_decorator.py +0 -0
  67. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nim/nim_manager.py +0 -0
  68. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nim/utils.py +0 -0
  69. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/__init__.py +0 -0
  70. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/constants.py +0 -0
  71. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/exceptions.py +0 -0
  72. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/heartbeat_store.py +0 -0
  73. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf.py +0 -0
  74. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf_cli.py +0 -0
  75. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/nvcf_decorator.py +0 -0
  76. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvcf/utils.py +0 -0
  77. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/__init__.py +0 -0
  78. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/exceptions.py +0 -0
  79. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/nvct.py +0 -0
  80. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/nvct_cli.py +0 -0
  81. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/nvct_decorator.py +0 -0
  82. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/nvct_runner.py +0 -0
  83. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/nvct/utils.py +0 -0
  84. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/ollama/__init__.py +0 -0
  85. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/ollama/constants.py +0 -0
  86. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/ollama/exceptions.py +0 -0
  87. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/ollama/ollama.py +0 -0
  88. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/ollama/status_card.py +0 -0
  89. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/perimeters.py +0 -0
  90. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/profilers/deco_injector.py +0 -0
  91. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/profilers/gpu_profile_decorator.py +0 -0
  92. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/secrets/__init__.py +0 -0
  93. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/secrets/secrets.py +0 -0
  94. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowflake/__init__.py +0 -0
  95. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowflake/snowflake.py +0 -0
  96. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/__init__.py +0 -0
  97. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark.py +0 -0
  98. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_cli.py +0 -0
  99. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_client.py +0 -0
  100. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_decorator.py +0 -0
  101. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_exceptions.py +0 -0
  102. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_job.py +0 -0
  103. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/snowpark/snowpark_service_spec.py +0 -0
  104. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/tensorboard/__init__.py +0 -0
  105. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/torchtune/__init__.py +0 -0
  106. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/vllm/__init__.py +0 -0
  107. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/vllm/constants.py +0 -0
  108. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/vllm/exceptions.py +0 -0
  109. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/vllm/status_card.py +0 -0
  110. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/plugins/vllm/vllm_manager.py +0 -0
  111. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/profilers/__init__.py +0 -0
  112. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/profilers/gpu.py +0 -0
  113. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/remote_config.py +0 -0
  114. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/__init__.py +0 -0
  115. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py +0 -0
  116. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/ob_internal.py +0 -0
  117. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py +0 -0
  118. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py +0 -0
  119. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py +0 -0
  120. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/ollama/__init__.py +0 -0
  121. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py +0 -0
  122. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/torchtune/__init__.py +0 -0
  123. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/metaflow_extensions/outerbounds/toplevel/plugins/vllm/__init__.py +0 -0
  124. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/ob_metaflow_extensions.egg-info/SOURCES.txt +0 -0
  125. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/ob_metaflow_extensions.egg-info/dependency_links.txt +0 -0
  126. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/ob_metaflow_extensions.egg-info/requires.txt +0 -0
  127. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/ob_metaflow_extensions.egg-info/top_level.txt +0 -0
  128. {ob_metaflow_extensions-1.2.2 → ob_metaflow_extensions-1.2.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob_metaflow_extensions
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -26,6 +26,9 @@ class AppDeployDecorator(StepDecorator):
26
26
  package_url = None
27
27
  package_sha = None
28
28
 
29
+ MAX_ENTROPY = 6
30
+ MAX_NAME_LENGTH = 15 - MAX_ENTROPY - 1 # -1 for the hyphen
31
+
29
32
  def step_init(self, flow, graph, step, decos, environment, flow_datastore, logger):
30
33
  self.logger = logger
31
34
  self.environment = environment
@@ -37,6 +40,23 @@ class AppDeployDecorator(StepDecorator):
37
40
  "METAFLOW_CODE_SHA", self.package_sha
38
41
  )
39
42
 
43
+ def _extract_project_info(self):
44
+ project = current.get("project_name")
45
+ branch = current.get("branch_name")
46
+ is_production = current.get("is_production")
47
+ return project, branch, is_production
48
+
49
+ def _resolve_default_image(self, flow):
50
+ # TODO : Resolve the default image over here.
51
+ pass
52
+
53
+ def _resolve_default_name_prefix(self, flow, step_name):
54
+ # TODO: Only tweek MAX_NAME_LENGTH as backend support allows longer names.
55
+ base_prefix = (flow.name + "-" + step_name).lower()
56
+ if len(base_prefix) > self.MAX_NAME_LENGTH:
57
+ base_prefix = "mf-app"
58
+ return base_prefix
59
+
40
60
  def task_pre_step(
41
61
  self,
42
62
  step_name,
@@ -58,22 +78,36 @@ class AppDeployDecorator(StepDecorator):
58
78
  "METAFLOW_CODE_URL or METAFLOW_CODE_SHA is not set. "
59
79
  "Please set METAFLOW_CODE_URL and METAFLOW_CODE_SHA in your environment."
60
80
  )
61
- default_name = "-".join(current.pathspec.split("/")).lower()
62
81
  image = os.environ.get("FASTBAKERY_IMAGE", None)
63
82
 
64
- hash_key = hashlib.sha256(package_url.encode()).hexdigest()[:6]
65
-
66
- default_name = (
67
- (current.flow_name + "-" + current.step_name)[:12] + "-" + hash_key
68
- ).lower()
83
+ # TODO [Apps] - This is temporary. Backend will support longer names in the future.
84
+ default_name = self._resolve_default_name_prefix(flow, step_name)
85
+ project, branch, is_production = self._extract_project_info()
86
+ project_info = {}
87
+ if project is not None:
88
+ project_info["metaflow/project"] = project
89
+ project_info["metaflow/branch"] = branch
90
+ project_info["metaflow/is_production"] = is_production
91
+
92
+ default_tags = {
93
+ "metaflow/flow_name": flow.name,
94
+ "metaflow/step_name": step_name,
95
+ "metaflow/run_id": run_id,
96
+ "metaflow/task_id": task_id,
97
+ "metaflow/retry_count": retry_count,
98
+ "metaflow/pathspec": current.pathspec,
99
+ **project_info,
100
+ }
69
101
 
70
102
  AppDeployer._set_state(
71
103
  perimeter,
72
104
  api_server,
73
105
  code_package_url=package_url,
74
106
  code_package_key=package_sha,
75
- name=default_name,
107
+ name_prefix=default_name,
76
108
  image=image,
109
+ max_entropy=self.MAX_ENTROPY,
110
+ default_tags=[{k: str(v)} for k, v in default_tags.items()],
77
111
  )
78
112
  current._update_env(
79
113
  {
@@ -189,6 +189,10 @@ class CurrentWorkerInfo(TypedDict):
189
189
  crashlooping: int
190
190
 
191
191
 
192
+ class LogLine(TypedDict):
193
+ message: str
194
+
195
+
192
196
  class DEPLOYMENT_READY_CONDITIONS:
193
197
  """
194
198
  Deployment ready conditions define what is considered a successful completion of the current deployment instance.
@@ -18,6 +18,7 @@ from ._state_machine import (
18
18
  WorkerStatus,
19
19
  CapsuleStatus,
20
20
  DEPLOYMENT_READY_CONDITIONS,
21
+ LogLine,
21
22
  )
22
23
 
23
24
 
@@ -508,7 +509,7 @@ class CapsuleApi:
508
509
 
509
510
  def logs(
510
511
  self, capsule_id: str, worker_id: str, previous: bool = False
511
- ) -> List[str]:
512
+ ) -> List[LogLine]:
512
513
  _url = os.path.join(self._base_url, capsule_id, "workers", worker_id, "logs")
513
514
  options = None
514
515
  if previous:
@@ -530,6 +531,20 @@ class CapsuleApi:
530
531
  message="Capsule JSON decode failed",
531
532
  )
532
533
 
534
+ def patch(self, capsule_id: str, patch_input: dict):
535
+ capsule_response = self.get(capsule_id)
536
+ if "spec" not in capsule_response or len(capsule_response.get("spec", {})) == 0:
537
+ raise CapsuleApiException(
538
+ self._base_url,
539
+ "patch",
540
+ 403,
541
+ "Capsule response of incorrect format",
542
+ )
543
+
544
+ spec = capsule_response.get("spec")
545
+ spec.update(patch_input)
546
+ return self.create(spec)
547
+
533
548
 
534
549
  def list_and_filter_capsules(
535
550
  capsule_api: CapsuleApi, project, branch, name, tags, auth_type, capsule_id
@@ -9,4 +9,4 @@ from .config_utils import (
9
9
  RequiredFieldMissingException,
10
10
  )
11
11
  from . import schema_export
12
- from .typed_configs import TypedCoreConfig
12
+ from .typed_configs import TypedCoreConfig, TypedDict
@@ -527,6 +527,10 @@ class PackageConfig(metaclass=ConfigMeta):
527
527
  )
528
528
 
529
529
 
530
+ def everything_is_string(*args):
531
+ return all(isinstance(arg, str) for arg in args)
532
+
533
+
530
534
  class BasicAppValidations:
531
535
  @staticmethod
532
536
  def name(name):
@@ -534,7 +538,7 @@ class BasicAppValidations:
534
538
  return True
535
539
  regex = r"^[a-z0-9-]+$" # Only allow lowercase letters, numbers, and hyphens
536
540
  validator = BasicValidations(CoreConfig, "name")
537
- return validator.length_validation(20, name) and validator.regex_validation(
541
+ return validator.length_validation(15, name) and validator.regex_validation(
538
542
  regex, name
539
543
  )
540
544
 
@@ -548,12 +552,19 @@ class BasicAppValidations:
548
552
  def tags(tags):
549
553
  if tags is None:
550
554
  return True
551
- if not all(isinstance(tag, dict) and len(tag) == 1 for tag in tags):
555
+ if not all(
556
+ isinstance(tag, dict)
557
+ and len(tag) == 1
558
+ and all(
559
+ [everything_is_string(*tag.keys()), everything_is_string(*tag.values())]
560
+ )
561
+ for tag in tags
562
+ ):
552
563
  raise ConfigValidationFailedException(
553
564
  field_name="tags",
554
565
  field_info=CoreConfig._get_field(CoreConfig, "tags"), # type: ignore
555
566
  current_value=tags,
556
- message="Tags must be a list of dictionaries with one key. Currently they are set to %s "
567
+ message="Tags must be a list of dictionaries with one key and the value must be a string. Currently they are set to %s "
557
568
  % (str(tags)),
558
569
  )
559
570
  return True
@@ -0,0 +1,309 @@
1
+ from .config import TypedCoreConfig, TypedDict
2
+ from .perimeters import PerimeterExtractor
3
+ from .capsule import CapsuleApi
4
+ import json
5
+ from ._state_machine import DEPLOYMENT_READY_CONDITIONS, LogLine
6
+ from .app_config import AppConfig, AppConfigError
7
+ from .capsule import CapsuleDeployer, list_and_filter_capsules
8
+ from functools import partial
9
+ import sys
10
+ import uuid
11
+ from typing import Type, Dict, List
12
+ from datetime import datetime
13
+
14
+
15
+ class AppDeployer(TypedCoreConfig):
16
+ """ """
17
+
18
+ __init__ = TypedCoreConfig.__init__
19
+
20
+ _app_config: AppConfig
21
+
22
+ _state = {}
23
+
24
+ __state_items = [
25
+ "perimeter",
26
+ "api_url",
27
+ "code_package_url",
28
+ "code_package_key",
29
+ "image",
30
+ "project",
31
+ "branch",
32
+ ]
33
+
34
+ @property
35
+ def _deploy_config(self) -> AppConfig:
36
+ if not hasattr(self, "_app_config"):
37
+ self._app_config = AppConfig(self._config)
38
+ return self._app_config
39
+
40
+ # Things that need to be set before deploy
41
+ @classmethod
42
+ def _set_state(
43
+ cls,
44
+ perimeter: str,
45
+ api_url: str,
46
+ code_package_url: str = None,
47
+ code_package_key: str = None,
48
+ name_prefix: str = None,
49
+ image: str = None,
50
+ max_entropy: int = 4,
51
+ default_tags: List[Dict[str, str]] = None,
52
+ project: str = None,
53
+ branch: str = None,
54
+ ):
55
+ cls._state["perimeter"] = perimeter
56
+ cls._state["api_url"] = api_url
57
+ cls._state["code_package_url"] = code_package_url
58
+ cls._state["code_package_key"] = code_package_key
59
+ cls._state["name_prefix"] = name_prefix
60
+ cls._state["image"] = image
61
+ cls._state["max_entropy"] = max_entropy
62
+ cls._state["default_tags"] = default_tags
63
+ cls._state["project"] = project
64
+ cls._state["branch"] = branch
65
+
66
+ assert (
67
+ max_entropy > 0
68
+ ), "max_entropy must be greater than 0. Since AppDeployer's deploy fn can be called many time inside a step itself."
69
+
70
+ def deploy(
71
+ self,
72
+ readiness_condition=DEPLOYMENT_READY_CONDITIONS.ATLEAST_ONE_RUNNING,
73
+ max_wait_time=600,
74
+ readiness_wait_time=10,
75
+ logger_fn=partial(print, file=sys.stderr),
76
+ status_file=None,
77
+ no_loader=False,
78
+ **kwargs,
79
+ ) -> "DeployedApp":
80
+
81
+ # Name setting from top level if none is set in the code
82
+ if self._deploy_config._core_config.name is None:
83
+ name = self._state[
84
+ "name_prefix"
85
+ ] # for now the name-prefix cannot be very large.
86
+ entropy = uuid.uuid4().hex[: self._state["max_entropy"]]
87
+ self._deploy_config._core_config.name = f"{name}-{entropy}"
88
+
89
+ if len(self._state["default_tags"]) > 0:
90
+ self._deploy_config._core_config.tags = (
91
+ self._deploy_config._core_config.tags or []
92
+ ) + self._state["default_tags"]
93
+
94
+ self._deploy_config.commit()
95
+ # Set any state that might have been passed down from the top level
96
+ for k in self.__state_items:
97
+ if self._deploy_config.get_state(k) is None:
98
+ self._deploy_config.set_state(k, self._state[k])
99
+
100
+ capsule = CapsuleDeployer(
101
+ self._deploy_config,
102
+ self._state["api_url"],
103
+ create_timeout=max_wait_time,
104
+ debug_dir=None,
105
+ success_terminal_state_condition=readiness_condition,
106
+ readiness_wait_time=readiness_wait_time,
107
+ logger_fn=logger_fn,
108
+ )
109
+
110
+ currently_present_capsules = list_and_filter_capsules(
111
+ capsule.capsule_api,
112
+ None,
113
+ None,
114
+ capsule.name,
115
+ None,
116
+ None,
117
+ None,
118
+ )
119
+
120
+ force_upgrade = self._deploy_config.get_state("force_upgrade", False)
121
+
122
+ if len(currently_present_capsules) > 0:
123
+ # Only update the capsule if there is no upgrade in progress
124
+ # Only update a "already updating" capsule if the `--force-upgrade` flag is provided.
125
+ _curr_cap = currently_present_capsules[0]
126
+ this_capsule_is_being_updated = _curr_cap.get("status", {}).get(
127
+ "updateInProgress", False
128
+ )
129
+
130
+ if this_capsule_is_being_updated and not force_upgrade:
131
+ _upgrader = _curr_cap.get("metadata", {}).get("lastModifiedBy", None)
132
+ message = f"{capsule.capsule_type} is currently being upgraded"
133
+ if _upgrader:
134
+ message = (
135
+ f"{capsule.capsule_type} is currently being upgraded. Upgrade was launched by {_upgrader}. "
136
+ "If you wish to force upgrade, you can do so by providing the `--force-upgrade` flag."
137
+ )
138
+ raise AppConfigError(message)
139
+
140
+ logger_fn(
141
+ f"🚀 {'' if not force_upgrade else 'Force'} Upgrading {capsule.capsule_type.lower()} `{capsule.name}`....",
142
+ )
143
+ else:
144
+ logger_fn(
145
+ f"🚀 Deploying {capsule.capsule_type.lower()} `{capsule.name}`....",
146
+ )
147
+
148
+ capsule.create()
149
+ final_status = capsule.wait_for_terminal_state()
150
+ return DeployedApp(
151
+ final_status["id"],
152
+ final_status["auth_type"],
153
+ final_status["public_url"],
154
+ final_status["available_replicas"],
155
+ final_status["name"],
156
+ final_status["deployed_version"],
157
+ final_status["deployed_at"],
158
+ )
159
+
160
+
161
+ class DeployedApp:
162
+ def __init__(
163
+ self,
164
+ _id: str,
165
+ capsule_type: str,
166
+ public_url: str,
167
+ available_replicas: int,
168
+ name: str,
169
+ deployed_version: str,
170
+ deployed_at: str,
171
+ ):
172
+ self._id = _id
173
+ self._capsule_type = capsule_type
174
+ self._public_url = public_url
175
+ self._available_replicas = available_replicas
176
+ self._name = name
177
+ self._deployed_version = deployed_version
178
+ self._deployed_at = deployed_at
179
+
180
+ def _get_capsule_api(self) -> CapsuleApi:
181
+ perimeter, api_server = PerimeterExtractor.during_metaflow_execution()
182
+ return CapsuleApi(api_server, perimeter)
183
+
184
+ def logs(self, previous=False) -> Dict[str, List[LogLine]]:
185
+ """
186
+ Returns a dictionary of worker_id to logs.
187
+ If `previous` is True, it will return the logs from the previous execution of the workers. Useful when debugging a crashlooping worker.
188
+ """
189
+ capsule_api = self._get_capsule_api()
190
+ # extract workers from capsule
191
+ workers = capsule_api.get_workers(self._id)
192
+ # get logs from workers
193
+ logs = {
194
+ # worker_id: logs
195
+ }
196
+ for worker in workers:
197
+ # TODO: Handle exceptions better over here.
198
+ logs[worker["workerId"]] = capsule_api.logs(
199
+ self._id, worker["workerId"], previous=previous
200
+ )
201
+ return logs
202
+
203
+ def info(self) -> dict:
204
+ """
205
+ Returns a dictionary representing the deployed app.
206
+ """
207
+ capsule_api = self._get_capsule_api()
208
+ capsule = capsule_api.get(self._id)
209
+ return capsule
210
+
211
+ def scale_to_zero(self):
212
+ """
213
+ Scales the DeployedApp to 0 replicas.
214
+ """
215
+ capsule_api = self._get_capsule_api()
216
+ return capsule_api.patch(
217
+ self._id,
218
+ {
219
+ "autoscalingConfig": {
220
+ "minReplicas": 0,
221
+ "maxReplicas": 0,
222
+ }
223
+ },
224
+ )
225
+
226
+ def delete(self):
227
+ """
228
+ Deletes the DeployedApp.
229
+ """
230
+ capsule_api = self._get_capsule_api()
231
+ return capsule_api.delete(self._id)
232
+
233
+ @property
234
+ def id(self) -> str:
235
+ return self._id
236
+
237
+ @property
238
+ def auth_style(self) -> str:
239
+ # TODO : Fix naming here.
240
+ return self._capsule_type
241
+
242
+ @property
243
+ def public_url(self) -> str:
244
+ return self._public_url
245
+
246
+ @property
247
+ def available_replicas(self) -> int:
248
+ return self._available_replicas
249
+
250
+ @property
251
+ def name(self) -> str:
252
+ return self._name
253
+
254
+ @property
255
+ def deployed_version(self) -> str:
256
+ return self._deployed_version
257
+
258
+ def to_dict(self) -> dict:
259
+ return {
260
+ "id": self._id,
261
+ "auth_style": self.auth_style, # TODO : Fix naming here.
262
+ "public_url": self._public_url,
263
+ "available_replicas": self._available_replicas,
264
+ "name": self._name,
265
+ "deployed_version": self._deployed_version,
266
+ "deployed_at": self._deployed_at,
267
+ }
268
+
269
+ @classmethod
270
+ def from_dict(cls, data: dict):
271
+ return cls(
272
+ _id=data["id"],
273
+ capsule_type=data["capsule_type"],
274
+ public_url=data["public_url"],
275
+ available_replicas=data["available_replicas"],
276
+ name=data["name"],
277
+ deployed_version=data["deployed_version"],
278
+ deployed_at=data["deployed_at"],
279
+ )
280
+
281
+ @property
282
+ def deployed_at(self) -> datetime:
283
+ return datetime.fromisoformat(self._deployed_at)
284
+
285
+ def __repr__(self) -> str:
286
+ return (
287
+ f"DeployedApp(id='{self._id}', "
288
+ f"name='{self._name}', "
289
+ f"public_url='{self._public_url}', "
290
+ f"available_replicas={self._available_replicas}, "
291
+ f"deployed_version='{self._deployed_version}')"
292
+ )
293
+
294
+
295
+ class apps:
296
+
297
+ _name_prefix = None
298
+
299
+ @classmethod
300
+ def set_name_prefix(cls, name_prefix: str):
301
+ cls._name_prefix = name_prefix
302
+
303
+ @property
304
+ def name_prefix(self) -> str:
305
+ return self._name_prefix
306
+
307
+ @property
308
+ def Deployer(self) -> Type[AppDeployer]:
309
+ return AppDeployer
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -2,7 +2,7 @@ from setuptools import setup, find_namespace_packages
2
2
  from pathlib import Path
3
3
 
4
4
 
5
- version = "1.2.2"
5
+ version = "1.2.3"
6
6
  this_directory = Path(__file__).parent
7
7
  long_description = (this_directory / "README.md").read_text()
8
8
 
@@ -1,132 +0,0 @@
1
- from .config import TypedCoreConfig
2
-
3
- from ._state_machine import DEPLOYMENT_READY_CONDITIONS
4
- from .app_config import AppConfig, AppConfigError
5
- from .capsule import CapsuleDeployer, list_and_filter_capsules
6
- from functools import partial
7
- import sys
8
- from typing import Type
9
-
10
-
11
- class AppDeployer(TypedCoreConfig):
12
- """ """
13
-
14
- __init__ = TypedCoreConfig.__init__
15
-
16
- _app_config: AppConfig
17
-
18
- _state = {}
19
-
20
- @property
21
- def app_config(self) -> AppConfig:
22
- if not hasattr(self, "_app_config"):
23
- self._app_config = AppConfig(self._config)
24
- return self._app_config
25
-
26
- # Things that need to be set before deploy
27
- @classmethod
28
- def _set_state(
29
- cls,
30
- perimeter: str,
31
- api_url: str,
32
- code_package_url: str = None,
33
- code_package_key: str = None,
34
- name: str = None,
35
- image: str = None,
36
- ):
37
- cls._state["perimeter"] = perimeter
38
- cls._state["api_url"] = api_url
39
- cls._state["code_package_url"] = code_package_url
40
- cls._state["code_package_key"] = code_package_key
41
- cls._state["name"] = name
42
- cls._state["image"] = image
43
-
44
- def deploy(
45
- self,
46
- readiness_condition=DEPLOYMENT_READY_CONDITIONS.ATLEAST_ONE_RUNNING,
47
- max_wait_time=600,
48
- readiness_wait_time=10,
49
- logger_fn=partial(print, file=sys.stderr),
50
- status_file=None,
51
- no_loader=False,
52
- **kwargs,
53
- ):
54
- # Name setting from top level if none is set in the code
55
- if self.app_config._core_config.name is None:
56
- self.app_config._core_config.name = self._state["name"]
57
-
58
- self.app_config.commit()
59
-
60
- # Set any state that might have been passed down from the top level
61
- for k, v in self._state.items():
62
- if self.app_config.get_state(k) is None:
63
- self.app_config.set_state(k, v)
64
-
65
- capsule = CapsuleDeployer(
66
- self.app_config,
67
- self._state["api_url"],
68
- create_timeout=max_wait_time,
69
- debug_dir=None,
70
- success_terminal_state_condition=readiness_condition,
71
- readiness_wait_time=readiness_wait_time,
72
- logger_fn=logger_fn,
73
- )
74
-
75
- currently_present_capsules = list_and_filter_capsules(
76
- capsule.capsule_api,
77
- None,
78
- None,
79
- capsule.name,
80
- None,
81
- None,
82
- None,
83
- )
84
-
85
- force_upgrade = self.app_config.get_state("force_upgrade", False)
86
-
87
- if len(currently_present_capsules) > 0:
88
- # Only update the capsule if there is no upgrade in progress
89
- # Only update a "already updating" capsule if the `--force-upgrade` flag is provided.
90
- _curr_cap = currently_present_capsules[0]
91
- this_capsule_is_being_updated = _curr_cap.get("status", {}).get(
92
- "updateInProgress", False
93
- )
94
-
95
- if this_capsule_is_being_updated and not force_upgrade:
96
- _upgrader = _curr_cap.get("metadata", {}).get("lastModifiedBy", None)
97
- message = f"{capsule.capsule_type} is currently being upgraded"
98
- if _upgrader:
99
- message = (
100
- f"{capsule.capsule_type} is currently being upgraded. Upgrade was launched by {_upgrader}. "
101
- "If you wish to force upgrade, you can do so by providing the `--force-upgrade` flag."
102
- )
103
- raise AppConfigError(message)
104
-
105
- logger_fn(
106
- f"🚀 {'' if not force_upgrade else 'Force'} Upgrading {capsule.capsule_type.lower()} `{capsule.name}`....",
107
- )
108
- else:
109
- logger_fn(
110
- f"🚀 Deploying {capsule.capsule_type.lower()} `{capsule.name}`....",
111
- )
112
-
113
- capsule.create()
114
- final_status = capsule.wait_for_terminal_state()
115
- return final_status
116
-
117
-
118
- class apps:
119
-
120
- _name_prefix = None
121
-
122
- @classmethod
123
- def set_name_prefix(cls, name_prefix: str):
124
- cls._name_prefix = name_prefix
125
-
126
- @property
127
- def name_prefix(self) -> str:
128
- return self._name_prefix
129
-
130
- @property
131
- def Deployer(self) -> Type[AppDeployer]:
132
- return AppDeployer