mage-ai 0.9.69__py3-none-any.whl → 0.9.70__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.
Potentially problematic release.
This version of mage-ai might be problematic. Click here for more details.
- mage_ai/api/policies/BackfillPolicy.py +1 -0
- mage_ai/api/policies/PipelinePolicy.py +1 -0
- mage_ai/api/policies/WorkspacePolicy.py +1 -0
- mage_ai/api/presenters/BackfillPresenter.py +1 -0
- mage_ai/api/resources/GitBranchResource.py +56 -23
- mage_ai/api/resources/GitCustomBranchResource.py +29 -1
- mage_ai/api/resources/OauthResource.py +1 -1
- mage_ai/api/resources/PipelineResource.py +11 -5
- mage_ai/api/resources/PipelineRunResource.py +41 -4
- mage_ai/api/resources/PipelineScheduleResource.py +4 -0
- mage_ai/api/resources/PullRequestResource.py +6 -4
- mage_ai/api/resources/WorkspaceResource.py +5 -4
- mage_ai/cache/block_action_object/__init__.py +1 -1
- mage_ai/cluster_manager/kubernetes/workload_manager.py +52 -1
- mage_ai/cluster_manager/workspace/base.py +6 -0
- mage_ai/cluster_manager/workspace/kubernetes.py +22 -1
- mage_ai/command_center/applications/utils.py +2 -2
- mage_ai/command_center/presenters/text.py +1 -1
- mage_ai/data_preparation/executors/k8s_block_executor.py +30 -7
- mage_ai/data_preparation/executors/k8s_pipeline_executor.py +30 -7
- mage_ai/data_preparation/executors/streaming_pipeline_executor.py +1 -1
- mage_ai/data_preparation/git/__init__.py +50 -22
- mage_ai/data_preparation/git/api.py +62 -7
- mage_ai/data_preparation/git/utils.py +45 -21
- mage_ai/data_preparation/models/block/__init__.py +24 -5
- mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
- mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
- mage_ai/data_preparation/models/block/remote/__init__.py +0 -0
- mage_ai/data_preparation/models/block/remote/models.py +58 -0
- mage_ai/data_preparation/models/block/utils.py +38 -0
- mage_ai/data_preparation/models/constants.py +2 -0
- mage_ai/data_preparation/models/global_data_product/__init__.py +12 -0
- mage_ai/data_preparation/models/pipeline.py +9 -5
- mage_ai/data_preparation/models/triggers/__init__.py +4 -2
- mage_ai/data_preparation/storage/local_storage.py +12 -6
- mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
- mage_ai/orchestration/db/models/oauth.py +10 -9
- mage_ai/orchestration/db/models/schedules.py +21 -0
- mage_ai/orchestration/notification/sender.py +37 -15
- mage_ai/orchestration/pipeline_scheduler_original.py +32 -25
- mage_ai/orchestration/triggers/api.py +29 -1
- mage_ai/orchestration/triggers/global_data_product.py +9 -4
- mage_ai/orchestration/triggers/utils.py +10 -1
- mage_ai/orchestration/utils/resources.py +3 -0
- mage_ai/server/api/downloads.py +4 -1
- mage_ai/server/api/runs.py +151 -0
- mage_ai/server/constants.py +1 -1
- mage_ai/server/frontend_dist/404.html +6 -6
- mage_ai/server/frontend_dist/_next/static/{_krrrgup_C-dPOpX36S8I → RhDiJSkcjCsh4xxX4BFBk}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/{frontend_dist_base_path_template/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist/block-layout.html +2 -2
- mage_ai/server/frontend_dist/compute.html +2 -2
- mage_ai/server/frontend_dist/files.html +2 -2
- mage_ai/server/frontend_dist/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-data-products.html +2 -2
- mage_ai/server/frontend_dist/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/index.html +2 -2
- mage_ai/server/frontend_dist/manage/files.html +2 -2
- mage_ai/server/frontend_dist/manage/settings.html +2 -2
- mage_ai/server/frontend_dist/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist/manage/users.html +2 -2
- mage_ai/server/frontend_dist/manage.html +2 -2
- mage_ai/server/frontend_dist/oauth.html +2 -2
- mage_ai/server/frontend_dist/overview.html +2 -2
- mage_ai/server/frontend_dist/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist/pipelines.html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist/settings.html +2 -2
- mage_ai/server/frontend_dist/sign-in.html +6 -6
- mage_ai/server/frontend_dist/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist/templates.html +2 -2
- mage_ai/server/frontend_dist/terminal.html +2 -2
- mage_ai/server/frontend_dist/test.html +2 -2
- mage_ai/server/frontend_dist/triggers.html +2 -2
- mage_ai/server/frontend_dist/version-control.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/404.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/_next/static/{KLL5mirre9d7_ZeEpaw3s → TdpLLFome13qvM0gXvpHs}/_buildManifest.js +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-b3502f3f1aa92ac7.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/{3548-fa0792ddb88f4646.js → 3548-9d26185b3fb663b1.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-b9b85ba10667e654.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-8b8e100079ab69e1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-2a69553d8c6eeb53.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-4bfc84ff07d7656f.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/pipeline-runs-3edc6270c5b0e962.js → frontend_dist_base_path_template/_next/static/chunks/pages/pipeline-runs-6d183f91a2ff6668.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-7181b086c93784d2.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-38e1fbcfbfc1014e.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-b645a6d13ab9fe3a.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-eb11c5390c982b49.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/{triggers-1bdfda8edc9cf4a8.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-32985f3f7c7dd3ab.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-349af617d05f001b.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-60d01d3887e31136.js +1 -0
- mage_ai/server/{frontend_dist/_next/static/chunks/pages/triggers-9cba3211434a8966.js → frontend_dist_base_path_template/_next/static/chunks/pages/triggers-a599c6ac89be8c8d.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-3433c8b22e8342aa.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/block-layout.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/compute.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-data-products.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/index.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/files.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/[user].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users/new.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/manage.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/oauth.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/overview.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipeline-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/backfills.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/dashboard.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/edit.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/logs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors/block-runtime.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/monitors.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs/[run].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/runs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/syncs.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline]/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines/[pipeline].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/pipelines.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/platform/global-hooks.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/account/profile.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/platform/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/permissions.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/preferences.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/roles.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/sync-data.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings/workspace/users.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/settings.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/sign-in.html +6 -6
- mage_ai/server/frontend_dist_base_path_template/templates/[...slug].html +2 -2
- mage_ai/server/frontend_dist_base_path_template/templates.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/terminal.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/test.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/triggers.html +2 -2
- mage_ai/server/frontend_dist_base_path_template/version-control.html +2 -2
- mage_ai/server/scheduler_manager.py +3 -1
- mage_ai/server/server.py +35 -12
- mage_ai/server/utils/output_display.py +2 -2
- mage_ai/services/aws/ecs/ecs.py +1 -0
- mage_ai/services/k8s/config.py +4 -4
- mage_ai/services/k8s/utils.py +97 -0
- mage_ai/shared/parsers.py +6 -1
- mage_ai/tests/api/operations/base/mixins.py +1 -1
- mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
- mage_ai/tests/authentication/oauth/test_utils.py +1 -1
- mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
- mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
- mage_ai/tests/server/test_server.py +19 -0
- mage_ai/tests/services/k8s/test_job_manager.py +9 -6
- mage_ai/version_control/branch/utils.py +2 -1
- mage_ai/version_control/models.py +3 -2
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/METADATA +2 -2
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/RECORD +217 -212
- mage_ai/server/frontend_dist/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/1557-df144fbd8b2208c3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7966-f07b2913f7326b50.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/9624-59b2f803f9c88cd6.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/_app-d9c89527266296f7.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/manage-852d403c7bda21b3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills/[...slug]-ff4bd7a8ec3bab40.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/backfills-a8b61d8d239fd16f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/edit-e1dd1ed71d26c10d.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/triggers/[...slug]-f028ef3880ed856c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-503049734a8b082f.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-5b26eeda8aed8a7b.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-8b793b3b696a2cd3.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/version-control-5753fac7c1bfdc88.js +0 -1
- /mage_ai/server/frontend_dist/_next/static/{_krrrgup_C-dPOpX36S8I → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{KLL5mirre9d7_ZeEpaw3s → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.69.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
|
@@ -6,19 +6,25 @@ from jinja2 import Template
|
|
|
6
6
|
from mage_ai.data_preparation.executors.pipeline_executor import PipelineExecutor
|
|
7
7
|
from mage_ai.data_preparation.models.pipeline import Pipeline
|
|
8
8
|
from mage_ai.data_preparation.shared.utils import get_template_vars
|
|
9
|
+
from mage_ai.orchestration.db import safe_db_query
|
|
10
|
+
from mage_ai.orchestration.db.models.schedules import PipelineRun
|
|
9
11
|
from mage_ai.services.k8s.config import K8sExecutorConfig
|
|
10
12
|
from mage_ai.services.k8s.constants import DEFAULT_NAMESPACE
|
|
11
13
|
from mage_ai.services.k8s.job_manager import JobManager as K8sJobManager
|
|
12
14
|
from mage_ai.shared.hash import merge_dict
|
|
15
|
+
from mage_ai.shared.utils import clean_name
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
class K8sPipelineExecutor(PipelineExecutor):
|
|
16
19
|
def __init__(self, pipeline: Pipeline, execution_partition: str = None):
|
|
17
20
|
super().__init__(pipeline, execution_partition=execution_partition)
|
|
18
|
-
self.
|
|
21
|
+
self.executor_config_dict = self.pipeline.repo_config.k8s_executor_config or dict()
|
|
19
22
|
if self.pipeline.executor_config is not None:
|
|
20
|
-
self.
|
|
21
|
-
|
|
23
|
+
self.executor_config_dict = merge_dict(
|
|
24
|
+
self.executor_config_dict,
|
|
25
|
+
self.pipeline.executor_config,
|
|
26
|
+
)
|
|
27
|
+
self.executor_config = K8sExecutorConfig.load(config=self.executor_config_dict)
|
|
22
28
|
|
|
23
29
|
def cancel(
|
|
24
30
|
self,
|
|
@@ -63,10 +69,7 @@ class K8sPipelineExecutor(PipelineExecutor):
|
|
|
63
69
|
) -> K8sJobManager:
|
|
64
70
|
if global_vars is None:
|
|
65
71
|
global_vars = dict()
|
|
66
|
-
|
|
67
|
-
job_name_prefix = 'data-prep'
|
|
68
|
-
else:
|
|
69
|
-
job_name_prefix = self.executor_config.job_name_prefix
|
|
72
|
+
job_name_prefix = self._get_job_name_prefix(pipeline_run_id)
|
|
70
73
|
|
|
71
74
|
if self.executor_config.namespace:
|
|
72
75
|
|
|
@@ -83,3 +86,23 @@ class K8sPipelineExecutor(PipelineExecutor):
|
|
|
83
86
|
logging_tags=kwargs.get('tags', dict()),
|
|
84
87
|
namespace=namespace,
|
|
85
88
|
)
|
|
89
|
+
|
|
90
|
+
@safe_db_query
|
|
91
|
+
def _get_job_name_prefix(
|
|
92
|
+
self,
|
|
93
|
+
pipeline_run_id,
|
|
94
|
+
):
|
|
95
|
+
if not self.executor_config.job_name_prefix:
|
|
96
|
+
job_name_prefix = 'data-prep'
|
|
97
|
+
else:
|
|
98
|
+
job_name_prefix = self.executor_config.job_name_prefix
|
|
99
|
+
if not pipeline_run_id:
|
|
100
|
+
return job_name_prefix
|
|
101
|
+
|
|
102
|
+
if '{trigger_name}' in job_name_prefix:
|
|
103
|
+
pipeline_run = PipelineRun.query.get(pipeline_run_id)
|
|
104
|
+
trigger = pipeline_run.pipeline_schedule
|
|
105
|
+
job_name_prefix = job_name_prefix.format(
|
|
106
|
+
trigger_name=clean_name(trigger.name).replace('_', '-'))
|
|
107
|
+
|
|
108
|
+
return job_name_prefix
|
|
@@ -84,7 +84,7 @@ class StreamingPipelineExecutor(PipelineExecutor):
|
|
|
84
84
|
# 1. Support multiple sources and sinks
|
|
85
85
|
# 2. Support flink pipeline
|
|
86
86
|
|
|
87
|
-
tags = self.build_tags(**kwargs)
|
|
87
|
+
tags = self.build_tags(pipeline_run_id=pipeline_run_id, **kwargs)
|
|
88
88
|
self.logging_tags = tags
|
|
89
89
|
if build_block_output_stdout:
|
|
90
90
|
stdout_logger = logging.getLogger('streaming_pipeline_executor')
|
|
@@ -19,9 +19,11 @@ from mage_ai.data_preparation.git.utils import (
|
|
|
19
19
|
check_connection_async,
|
|
20
20
|
create_ssh_keys,
|
|
21
21
|
get_access_token,
|
|
22
|
+
get_oauth_access_token_for_user,
|
|
22
23
|
get_provider_from_remote_url,
|
|
23
24
|
poll_process_with_timeout,
|
|
24
25
|
run_command,
|
|
26
|
+
validate_authentication_for_remote_url,
|
|
25
27
|
)
|
|
26
28
|
from mage_ai.data_preparation.preferences import get_preferences
|
|
27
29
|
from mage_ai.data_preparation.sync import AuthType, GitConfig
|
|
@@ -79,8 +81,7 @@ class Git:
|
|
|
79
81
|
if setup_repo:
|
|
80
82
|
self.__setup_repo()
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
self.__set_git_config()
|
|
84
|
+
self.__set_git_config()
|
|
84
85
|
|
|
85
86
|
if self.remote_repo_link and self.repo:
|
|
86
87
|
try:
|
|
@@ -162,6 +163,9 @@ class Git:
|
|
|
162
163
|
|
|
163
164
|
git_config = GitConfig.load(config=config)
|
|
164
165
|
|
|
166
|
+
if auth_type is None:
|
|
167
|
+
auth_type = git_config.auth_type
|
|
168
|
+
|
|
165
169
|
return Git(
|
|
166
170
|
auth_type=auth_type,
|
|
167
171
|
git_config=git_config,
|
|
@@ -182,8 +186,11 @@ class Git:
|
|
|
182
186
|
|
|
183
187
|
return [branch.name for branch in self.repo.branches]
|
|
184
188
|
|
|
185
|
-
async def check_connection(self) -> None:
|
|
186
|
-
|
|
189
|
+
async def check_connection(self, remote_url: str = None) -> None:
|
|
190
|
+
if remote_url:
|
|
191
|
+
await validate_authentication_for_remote_url(self.repo.git, remote_url)
|
|
192
|
+
else:
|
|
193
|
+
await check_connection_async(self.repo.git, self.origin.name)
|
|
187
194
|
|
|
188
195
|
def _remote_command(func: Callable) -> None:
|
|
189
196
|
"""
|
|
@@ -224,19 +231,19 @@ class Git:
|
|
|
224
231
|
elif self.auth_type == AuthType.HTTPS:
|
|
225
232
|
token = self.get_access_token()
|
|
226
233
|
url_original = list(self.origin.urls)[0]
|
|
234
|
+
remote_repo_link = self.remote_repo_link
|
|
227
235
|
if self.git_config and self.remote_repo_link and token:
|
|
228
|
-
|
|
236
|
+
remote_repo_link = build_authenticated_remote_url(
|
|
229
237
|
self.remote_repo_link,
|
|
230
238
|
self.git_config.username,
|
|
231
239
|
token,
|
|
232
240
|
)
|
|
233
|
-
self.origin.set_url(
|
|
241
|
+
self.origin.set_url(remote_repo_link)
|
|
234
242
|
try:
|
|
235
|
-
asyncio.run(self.check_connection())
|
|
243
|
+
asyncio.run(self.check_connection(remote_url=remote_repo_link))
|
|
236
244
|
return func(self, *args, **kwargs)
|
|
237
245
|
finally:
|
|
238
246
|
self.origin.set_url(url_original)
|
|
239
|
-
self.remote_repo_link = url_original
|
|
240
247
|
return wrapper
|
|
241
248
|
|
|
242
249
|
def add_remote(self, name: str, url: str) -> None:
|
|
@@ -432,6 +439,10 @@ class Git:
|
|
|
432
439
|
self.repo.git.reset('--hard', f'{remote.name}/{branch}')
|
|
433
440
|
self.__pip_install()
|
|
434
441
|
|
|
442
|
+
@_remote_command
|
|
443
|
+
def fetch(self) -> None:
|
|
444
|
+
self.origin.fetch()
|
|
445
|
+
|
|
435
446
|
@_remote_command
|
|
436
447
|
def push(self) -> None:
|
|
437
448
|
self.repo.git.push(
|
|
@@ -564,7 +575,7 @@ class Git:
|
|
|
564
575
|
if len(remote_refs) == 0 and user:
|
|
565
576
|
from mage_ai.data_preparation.git import api
|
|
566
577
|
|
|
567
|
-
access_token =
|
|
578
|
+
access_token = get_oauth_access_token_for_user(user)
|
|
568
579
|
if access_token:
|
|
569
580
|
|
|
570
581
|
if remote_exists:
|
|
@@ -685,15 +696,22 @@ class Git:
|
|
|
685
696
|
elif remote:
|
|
686
697
|
# For remote branches, switch to the local branch if it exists. Otherwise create a new
|
|
687
698
|
# branch using the remote branch as the starting point.
|
|
688
|
-
|
|
689
|
-
branch = branch[len(remote) + 1:]
|
|
690
|
-
if branch in self.repo.heads:
|
|
691
|
-
self.repo.git.switch(branch)
|
|
692
|
-
else:
|
|
693
|
-
self.repo.git.switch('-c', branch, f'{remote}/{branch}')
|
|
699
|
+
self._switch_remote_branch(branch, remote)
|
|
694
700
|
else:
|
|
695
701
|
self.repo.git.switch('-c', branch)
|
|
696
702
|
|
|
703
|
+
@_remote_command
|
|
704
|
+
def _switch_remote_branch(self, branch: str, remote: str) -> None:
|
|
705
|
+
self.switch_remote_branch(branch, remote)
|
|
706
|
+
|
|
707
|
+
def switch_remote_branch(self, branch: str, remote: str) -> None:
|
|
708
|
+
if branch.startswith(remote):
|
|
709
|
+
branch = branch[len(remote) + 1:]
|
|
710
|
+
if branch in self.repo.heads:
|
|
711
|
+
self.repo.git.switch(branch)
|
|
712
|
+
else:
|
|
713
|
+
self.repo.git.switch('-c', branch, f'{remote}/{branch}')
|
|
714
|
+
|
|
697
715
|
@_remote_command
|
|
698
716
|
def clone(self, sync_submodules: bool = False) -> None:
|
|
699
717
|
from git import Repo
|
|
@@ -729,13 +747,23 @@ class Git:
|
|
|
729
747
|
shutil.rmtree(tmp_path)
|
|
730
748
|
|
|
731
749
|
def __set_git_config(self):
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
750
|
+
from git.config import GitConfigParser
|
|
751
|
+
if self.git_config:
|
|
752
|
+
if self.git_config.username:
|
|
753
|
+
self.repo.config_writer().set_value(
|
|
754
|
+
'user', 'name', self.git_config.username).release()
|
|
755
|
+
if self.git_config.email:
|
|
756
|
+
self.repo.config_writer().set_value(
|
|
757
|
+
'user', 'email', self.git_config.email).release()
|
|
758
|
+
|
|
759
|
+
# This GitConfigParser is created in the same way that GitPython creates a
|
|
760
|
+
# global config parser. We create it this way to avoid needing to create a
|
|
761
|
+
# git.Repo object.
|
|
762
|
+
global_config = GitConfigParser(
|
|
763
|
+
os.path.normpath(os.path.expanduser("~/.gitconfig")),
|
|
764
|
+
read_only=False,
|
|
765
|
+
)
|
|
766
|
+
global_config.set_value(
|
|
739
767
|
'safe', 'directory', Path(self.repo_path).as_posix()).release()
|
|
740
768
|
|
|
741
769
|
def __pip_install(self) -> None:
|
|
@@ -3,9 +3,6 @@ import shutil
|
|
|
3
3
|
import uuid
|
|
4
4
|
from typing import Dict
|
|
5
5
|
|
|
6
|
-
from git.remote import RemoteProgress
|
|
7
|
-
from git.repo.base import Repo
|
|
8
|
-
|
|
9
6
|
from mage_ai.authentication.oauth.constants import ProviderName, get_ghe_hostname
|
|
10
7
|
from mage_ai.authentication.oauth.utils import access_tokens_for_client
|
|
11
8
|
from mage_ai.data_preparation.git.clients.base import Client as GitClient
|
|
@@ -33,13 +30,51 @@ def get_access_token_for_user(user: User, provider: str = None) -> Oauth2AccessT
|
|
|
33
30
|
return access_tokens[0]
|
|
34
31
|
|
|
35
32
|
|
|
33
|
+
def switch_branch(
|
|
34
|
+
remote_name: str,
|
|
35
|
+
remote_url: str,
|
|
36
|
+
branch_name: str,
|
|
37
|
+
token: str,
|
|
38
|
+
config_overwrite: Dict = None,
|
|
39
|
+
user: User = None,
|
|
40
|
+
):
|
|
41
|
+
from mage_ai.data_preparation.git import Git
|
|
42
|
+
provider = get_provider_from_remote_url(remote_url)
|
|
43
|
+
username = get_username(token, user=user, provider=provider)
|
|
44
|
+
|
|
45
|
+
url = build_authenticated_remote_url(remote_url, username, token)
|
|
46
|
+
git_manager = Git.get_manager(user=user, config_overwrite=config_overwrite)
|
|
47
|
+
|
|
48
|
+
remote = git_manager.repo.remotes[remote_name]
|
|
49
|
+
url_original = list(remote.urls)[0]
|
|
50
|
+
remote.set_url(url)
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
git_manager.switch_remote_branch(branch_name, remote_name)
|
|
54
|
+
except Exception as err:
|
|
55
|
+
raise err
|
|
56
|
+
finally:
|
|
57
|
+
try:
|
|
58
|
+
remote.set_url(url_original)
|
|
59
|
+
except Exception as err:
|
|
60
|
+
print('WARNING (mage_ai.data_preparation.git.api):')
|
|
61
|
+
print(err)
|
|
62
|
+
|
|
63
|
+
|
|
36
64
|
def fetch(
|
|
37
65
|
remote_name: str,
|
|
38
66
|
remote_url: str,
|
|
39
67
|
token: str,
|
|
40
68
|
user: User = None,
|
|
41
69
|
config_overwrite: Dict = None,
|
|
42
|
-
)
|
|
70
|
+
):
|
|
71
|
+
"""
|
|
72
|
+
Returns:
|
|
73
|
+
git.remote.RemoteProgress: Custom progress object that can be used to monitor the
|
|
74
|
+
fetch progress.
|
|
75
|
+
"""
|
|
76
|
+
from git.remote import RemoteProgress
|
|
77
|
+
|
|
43
78
|
from mage_ai.data_preparation.git import Git
|
|
44
79
|
|
|
45
80
|
custom_progress = RemoteProgress()
|
|
@@ -74,7 +109,14 @@ def pull(
|
|
|
74
109
|
token: str,
|
|
75
110
|
user: User = None,
|
|
76
111
|
config_overwrite: Dict = None,
|
|
77
|
-
)
|
|
112
|
+
):
|
|
113
|
+
"""
|
|
114
|
+
Returns:
|
|
115
|
+
git.remote.RemoteProgress: Custom progress object that can be used to monitor the
|
|
116
|
+
pull progress.
|
|
117
|
+
"""
|
|
118
|
+
from git.remote import RemoteProgress
|
|
119
|
+
|
|
78
120
|
from mage_ai.data_preparation.git import Git
|
|
79
121
|
|
|
80
122
|
custom_progress = RemoteProgress()
|
|
@@ -110,7 +152,13 @@ def push_raw(
|
|
|
110
152
|
token: str,
|
|
111
153
|
user: User = None,
|
|
112
154
|
**kwargs,
|
|
113
|
-
)
|
|
155
|
+
):
|
|
156
|
+
"""
|
|
157
|
+
Returns:
|
|
158
|
+
git.remote.RemoteProgress: Custom progress object that can be used to monitor the
|
|
159
|
+
push progress.
|
|
160
|
+
"""
|
|
161
|
+
from git.remote import RemoteProgress
|
|
114
162
|
custom_progress = RemoteProgress()
|
|
115
163
|
provider = get_provider_from_remote_url(remote_url)
|
|
116
164
|
username = get_username(token, user=user, provider=provider)
|
|
@@ -142,7 +190,12 @@ def push(
|
|
|
142
190
|
token: str,
|
|
143
191
|
user: User = None,
|
|
144
192
|
config_overwrite: Dict = None,
|
|
145
|
-
)
|
|
193
|
+
):
|
|
194
|
+
"""
|
|
195
|
+
Returns:
|
|
196
|
+
git.remote.RemoteProgress: Custom progress object that can be used to monitor the
|
|
197
|
+
push progress.
|
|
198
|
+
"""
|
|
146
199
|
from mage_ai.data_preparation.git import Git
|
|
147
200
|
|
|
148
201
|
git_manager = Git.get_manager(user=user, config_overwrite=config_overwrite)
|
|
@@ -213,6 +266,8 @@ def clone(
|
|
|
213
266
|
tmp_path = f'{git_manager.repo_path}_{uuid.uuid4().hex}'
|
|
214
267
|
os.mkdir(tmp_path)
|
|
215
268
|
try:
|
|
269
|
+
from git.repo.base import Repo
|
|
270
|
+
|
|
216
271
|
# Clone to a tmp folder first, then copy the folder to the actual repo path. Git
|
|
217
272
|
# won't allow you to clone to a folder that is not empty.
|
|
218
273
|
Repo.clone_from(
|
|
@@ -6,16 +6,20 @@ from typing import Callable
|
|
|
6
6
|
from urllib.parse import urlparse, urlsplit, urlunsplit
|
|
7
7
|
|
|
8
8
|
from mage_ai.authentication.oauth.constants import ProviderName, get_ghe_hostname
|
|
9
|
+
from mage_ai.authentication.oauth.utils import access_tokens_for_client
|
|
9
10
|
from mage_ai.data_preparation.git.constants import (
|
|
10
11
|
DEFAULT_KNOWN_HOSTS_FILE,
|
|
11
12
|
DEFAULT_SSH_KEY_DIRECTORY,
|
|
12
13
|
)
|
|
14
|
+
from mage_ai.data_preparation.repo_manager import get_project_uuid
|
|
13
15
|
from mage_ai.data_preparation.shared.secrets import get_secret_value
|
|
14
16
|
from mage_ai.data_preparation.sync import AuthType, GitConfig
|
|
15
|
-
from mage_ai.
|
|
17
|
+
from mage_ai.orchestration.db.models.oauth import Oauth2AccessToken, User
|
|
18
|
+
from mage_ai.settings import get_bool_value, get_settings_value
|
|
16
19
|
from mage_ai.settings.keys import (
|
|
17
20
|
BITBUCKET_HOST,
|
|
18
21
|
GIT_ACCESS_TOKEN,
|
|
22
|
+
GIT_OVERWRITE_WITH_PROJECT_SETTINGS,
|
|
19
23
|
GIT_SSH_PRIVATE_KEY,
|
|
20
24
|
GIT_SSH_PUBLIC_KEY,
|
|
21
25
|
GITLAB_HOST,
|
|
@@ -52,19 +56,23 @@ def get_provider_from_remote_url(remote_url: str) -> str:
|
|
|
52
56
|
return ProviderName.GITHUB
|
|
53
57
|
|
|
54
58
|
|
|
55
|
-
def create_ssh_keys(
|
|
59
|
+
def create_ssh_keys(
|
|
60
|
+
git_config: GitConfig, repo_path: str, overwrite: bool = False
|
|
61
|
+
) -> str:
|
|
56
62
|
if not os.path.exists(DEFAULT_SSH_KEY_DIRECTORY):
|
|
57
63
|
os.mkdir(DEFAULT_SSH_KEY_DIRECTORY, 0o700)
|
|
58
64
|
pubk_secret_name = git_config.ssh_public_key_secret_name
|
|
65
|
+
overwrite_with_project_settings = get_bool_value(
|
|
66
|
+
get_settings_value(GIT_OVERWRITE_WITH_PROJECT_SETTINGS)
|
|
67
|
+
)
|
|
59
68
|
if pubk_secret_name:
|
|
60
69
|
public_key_file = os.path.join(
|
|
61
|
-
DEFAULT_SSH_KEY_DIRECTORY,
|
|
62
|
-
f'id_rsa_{pubk_secret_name}.pub'
|
|
70
|
+
DEFAULT_SSH_KEY_DIRECTORY, f'id_rsa_{pubk_secret_name}.pub'
|
|
63
71
|
)
|
|
64
72
|
if not os.path.exists(public_key_file) or overwrite:
|
|
65
73
|
try:
|
|
66
74
|
public_key = get_settings_value(GIT_SSH_PUBLIC_KEY)
|
|
67
|
-
if not public_key:
|
|
75
|
+
if not public_key or overwrite_with_project_settings:
|
|
68
76
|
public_key = get_secret_value(
|
|
69
77
|
pubk_secret_name,
|
|
70
78
|
repo_name=repo_path,
|
|
@@ -80,13 +88,12 @@ def create_ssh_keys(git_config: GitConfig, repo_path: str, overwrite: bool = Fal
|
|
|
80
88
|
private_key_file = os.path.join(DEFAULT_SSH_KEY_DIRECTORY, 'id_rsa')
|
|
81
89
|
if pk_secret_name:
|
|
82
90
|
custom_private_key_file = os.path.join(
|
|
83
|
-
DEFAULT_SSH_KEY_DIRECTORY,
|
|
84
|
-
f'id_rsa_{pk_secret_name}'
|
|
91
|
+
DEFAULT_SSH_KEY_DIRECTORY, f'id_rsa_{pk_secret_name}'
|
|
85
92
|
)
|
|
86
93
|
if not os.path.exists(custom_private_key_file) or overwrite:
|
|
87
94
|
try:
|
|
88
95
|
private_key = get_settings_value(GIT_SSH_PRIVATE_KEY)
|
|
89
|
-
if not private_key:
|
|
96
|
+
if not private_key or overwrite_with_project_settings:
|
|
90
97
|
private_key = get_secret_value(
|
|
91
98
|
pk_secret_name,
|
|
92
99
|
repo_name=repo_path,
|
|
@@ -142,11 +149,16 @@ def add_host_to_known_hosts(remote_repo_link: str):
|
|
|
142
149
|
|
|
143
150
|
def get_access_token(git_config, repo_path: str = None) -> str:
|
|
144
151
|
token = get_settings_value(GIT_ACCESS_TOKEN)
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
overwrite_with_project_settings = get_bool_value(
|
|
153
|
+
get_settings_value(GIT_OVERWRITE_WITH_PROJECT_SETTINGS)
|
|
154
|
+
)
|
|
155
|
+
if git_config and git_config.access_token_secret_name:
|
|
156
|
+
token_from_secrets = get_secret_value(
|
|
147
157
|
git_config.access_token_secret_name,
|
|
148
158
|
repo_name=repo_path or get_repo_path(),
|
|
149
159
|
)
|
|
160
|
+
if not token or overwrite_with_project_settings:
|
|
161
|
+
token = token_from_secrets
|
|
150
162
|
|
|
151
163
|
return token
|
|
152
164
|
|
|
@@ -179,10 +191,7 @@ async def poll_process_with_timeout(
|
|
|
179
191
|
if return_code is not None:
|
|
180
192
|
out, err = proc.communicate()
|
|
181
193
|
if return_code != 0:
|
|
182
|
-
message = (
|
|
183
|
-
err.decode('UTF-8') if err
|
|
184
|
-
else error_message
|
|
185
|
-
)
|
|
194
|
+
message = err.decode('UTF-8') if err else error_message
|
|
186
195
|
raise ChildProcessError(message)
|
|
187
196
|
else:
|
|
188
197
|
return out.decode('UTF-8') if out else None
|
|
@@ -205,17 +214,16 @@ async def check_connection_async(git, remote_name: str) -> None:
|
|
|
205
214
|
'Error connecting to remote, make sure your access token or SSH key is '
|
|
206
215
|
'set up properly.'
|
|
207
216
|
),
|
|
217
|
+
timeout=5,
|
|
208
218
|
)
|
|
209
219
|
|
|
210
220
|
|
|
211
221
|
async def validate_authentication_for_remote_url(git, remote_url: str) -> None:
|
|
212
222
|
proc = git.ls_remote(remote_url, as_process=True)
|
|
213
223
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
error_message='Error connecting to remote, make sure your access is valid.',
|
|
218
|
-
)
|
|
224
|
+
await poll_process_with_timeout(
|
|
225
|
+
proc,
|
|
226
|
+
error_message='Error connecting to remote, make sure your access is valid.',
|
|
219
227
|
)
|
|
220
228
|
|
|
221
229
|
|
|
@@ -258,14 +266,30 @@ def execute_on_remote_branch(func: Callable, branch) -> None:
|
|
|
258
266
|
if hostname:
|
|
259
267
|
await __add_host_to_known_hosts(remote_name)
|
|
260
268
|
else:
|
|
261
|
-
raise TimeoutError(
|
|
269
|
+
raise TimeoutError(
|
|
270
|
+
"""
|
|
262
271
|
Connecting to remote timed out, make sure your SSH key is set up properly
|
|
263
272
|
and your repository host is added as a known host. More information
|
|
264
273
|
here: https://docs.mage.ai/developing-in-the-cloud/setting-up-git#5-add-github-com-to-known-hosts
|
|
265
|
-
"""
|
|
274
|
+
"""
|
|
275
|
+
)
|
|
266
276
|
return await func(*args, **kwargs)
|
|
267
277
|
else:
|
|
268
278
|
await check_connection_async(git, remote_name)
|
|
269
279
|
return await func(*args, **kwargs)
|
|
270
280
|
|
|
271
281
|
return wrapper
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def get_oauth_client_id(provider: str) -> str:
|
|
285
|
+
return f'{provider}_{get_project_uuid()}'
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def get_oauth_access_token_for_user(
|
|
289
|
+
user: User, provider: str = None
|
|
290
|
+
) -> Oauth2AccessToken:
|
|
291
|
+
if not provider:
|
|
292
|
+
provider = ProviderName.GHE if get_ghe_hostname() else ProviderName.GITHUB
|
|
293
|
+
access_tokens = access_tokens_for_client(get_oauth_client_id(provider), user=user)
|
|
294
|
+
if access_tokens:
|
|
295
|
+
return access_tokens[0]
|
|
@@ -1122,6 +1122,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
|
|
|
1122
1122
|
data_integration_runtime_settings: Dict = None,
|
|
1123
1123
|
execution_partition_previous: str = None,
|
|
1124
1124
|
metadata: Dict = None,
|
|
1125
|
+
override_outputs: bool = True,
|
|
1125
1126
|
**kwargs,
|
|
1126
1127
|
) -> Dict:
|
|
1127
1128
|
if logging_tags is None:
|
|
@@ -1213,7 +1214,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
|
|
|
1213
1214
|
DX_PRINTER.critical(
|
|
1214
1215
|
block=self,
|
|
1215
1216
|
execution_partition=execution_partition,
|
|
1216
|
-
override_outputs=
|
|
1217
|
+
override_outputs=override_outputs,
|
|
1217
1218
|
dynamic_block_uuid=dynamic_block_uuid,
|
|
1218
1219
|
__uuid='store_variables',
|
|
1219
1220
|
)
|
|
@@ -1221,7 +1222,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
|
|
|
1221
1222
|
self.store_variables(
|
|
1222
1223
|
variable_mapping,
|
|
1223
1224
|
execution_partition=execution_partition,
|
|
1224
|
-
override_outputs=
|
|
1225
|
+
override_outputs=override_outputs,
|
|
1225
1226
|
spark=self.__get_spark_session_from_global_vars(
|
|
1226
1227
|
global_vars=global_vars,
|
|
1227
1228
|
),
|
|
@@ -1616,7 +1617,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
|
|
|
1616
1617
|
|
|
1617
1618
|
block_function = self._validate_execution(decorated_functions, input_vars)
|
|
1618
1619
|
if block_function is not None:
|
|
1619
|
-
if logger
|
|
1620
|
+
if logger:
|
|
1620
1621
|
global_vars['logger'] = logger
|
|
1621
1622
|
|
|
1622
1623
|
track_spark = from_notebook and self.should_track_spark()
|
|
@@ -1780,6 +1781,7 @@ class Block(DataIntegrationMixin, SparkBlock, ProjectPlatformAccessible):
|
|
|
1780
1781
|
global_vars=global_vars,
|
|
1781
1782
|
metadata=metadata,
|
|
1782
1783
|
upstream_block_uuids_override=upstream_block_uuids_override,
|
|
1784
|
+
current_block=self,
|
|
1783
1785
|
)
|
|
1784
1786
|
|
|
1785
1787
|
return variables
|
|
@@ -2386,8 +2388,17 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
|
|
|
2386
2388
|
|
|
2387
2389
|
def get_executor_type(self) -> str:
|
|
2388
2390
|
if self.executor_type:
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
+
block_executor_type = Template(self.executor_type).render(**get_template_vars())
|
|
2392
|
+
else:
|
|
2393
|
+
block_executor_type = None
|
|
2394
|
+
if not block_executor_type or block_executor_type == ExecutorType.LOCAL_PYTHON:
|
|
2395
|
+
# If block executor_type is not set, fall back to pipeline level executor_type
|
|
2396
|
+
if self.pipeline:
|
|
2397
|
+
pipeline_executor_type = self.pipeline.get_executor_type()
|
|
2398
|
+
else:
|
|
2399
|
+
pipeline_executor_type = None
|
|
2400
|
+
block_executor_type = pipeline_executor_type or block_executor_type
|
|
2401
|
+
return block_executor_type
|
|
2391
2402
|
|
|
2392
2403
|
def get_pipelines_from_cache(self, block_cache: BlockCache = None) -> List[Dict]:
|
|
2393
2404
|
if block_cache is None:
|
|
@@ -2994,6 +3005,8 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
|
|
|
2994
3005
|
global_vars: Dict = None,
|
|
2995
3006
|
dynamic_block_index: int = None,
|
|
2996
3007
|
) -> Dict:
|
|
3008
|
+
from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
|
|
3009
|
+
|
|
2997
3010
|
"""
|
|
2998
3011
|
Enriches the provided global variables dictionary with additional context, Spark session,
|
|
2999
3012
|
environment, configuration, and an empty context dictionary.
|
|
@@ -3040,6 +3053,12 @@ df = get_variable('{self.pipeline.uuid}', '{self.uuid}', 'df')
|
|
|
3040
3053
|
if dynamic_block_index is not None:
|
|
3041
3054
|
global_vars['dynamic_block_index'] = dynamic_block_index
|
|
3042
3055
|
|
|
3056
|
+
# Remote blocks
|
|
3057
|
+
if global_vars.get('remote_blocks'):
|
|
3058
|
+
global_vars['remote_blocks'] = [RemoteBlock.load(
|
|
3059
|
+
**remote_block_dict,
|
|
3060
|
+
).get_outputs() for remote_block_dict in global_vars['remote_blocks']]
|
|
3061
|
+
|
|
3043
3062
|
self.global_vars = global_vars
|
|
3044
3063
|
|
|
3045
3064
|
return global_vars
|
|
@@ -9,7 +9,10 @@ import pandas as pd
|
|
|
9
9
|
from mage_ai.data_preparation.models.block.dynamic.variables import (
|
|
10
10
|
get_outputs_for_dynamic_block,
|
|
11
11
|
)
|
|
12
|
-
from mage_ai.data_preparation.models.constants import
|
|
12
|
+
from mage_ai.data_preparation.models.constants import (
|
|
13
|
+
DATAFRAME_ANALYSIS_MAX_COLUMNS,
|
|
14
|
+
DATAFRAME_SAMPLE_COUNT_PREVIEW,
|
|
15
|
+
)
|
|
13
16
|
from mage_ai.server.kernel_output_parser import DataType
|
|
14
17
|
from mage_ai.shared.array import find
|
|
15
18
|
from mage_ai.shared.custom_logger import DX_PRINTER
|
|
@@ -245,7 +248,9 @@ def transform_dataframe_for_display(dataframe: pd.DataFrame) -> Dict:
|
|
|
245
248
|
data = dict(
|
|
246
249
|
columns=columns_to_display,
|
|
247
250
|
rows=json.loads(
|
|
248
|
-
dataframe[columns_to_display].to_json(
|
|
251
|
+
dataframe[columns_to_display][:DATAFRAME_SAMPLE_COUNT_PREVIEW].to_json(
|
|
252
|
+
orient='split',
|
|
253
|
+
)
|
|
249
254
|
)['data'],
|
|
250
255
|
index=list(dataframe.index),
|
|
251
256
|
shape=[row_count, column_count],
|
|
@@ -253,7 +258,7 @@ def transform_dataframe_for_display(dataframe: pd.DataFrame) -> Dict:
|
|
|
253
258
|
else:
|
|
254
259
|
data = dict(
|
|
255
260
|
columns=['col0'],
|
|
256
|
-
rows=[[dataframe]],
|
|
261
|
+
rows=[[dataframe[:DATAFRAME_SAMPLE_COUNT_PREVIEW]]],
|
|
257
262
|
index=[0],
|
|
258
263
|
shape=[1, 1],
|
|
259
264
|
)
|
|
@@ -413,7 +418,7 @@ def transform_output_for_display_dynamic_child(
|
|
|
413
418
|
df = pd.concat([df, df_inner], axis=1)
|
|
414
419
|
|
|
415
420
|
df = limit_output(df, sample_count)
|
|
416
|
-
if 1 == len(set(df.columns)):
|
|
421
|
+
if isinstance(df, pd.DataFrame) and 1 == len(set(df.columns)):
|
|
417
422
|
df.columns = [f'{col}_{idx}' for idx, col in enumerate(df.columns)]
|
|
418
423
|
|
|
419
424
|
return transform_dataframe_for_display(df)
|
|
@@ -2,6 +2,7 @@ from logging import Logger
|
|
|
2
2
|
from typing import Dict, List
|
|
3
3
|
|
|
4
4
|
from mage_ai.data_preparation.models.block import Block
|
|
5
|
+
from mage_ai.data_preparation.models.block.remote.models import RemoteBlock
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class GlobalDataProductBlock(Block):
|
|
@@ -37,13 +38,35 @@ class GlobalDataProductBlock(Block):
|
|
|
37
38
|
# Avoid circular dependency of Pipeline class
|
|
38
39
|
from mage_ai.orchestration.triggers import global_data_product as trigger
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
global_data_product = self.get_global_data_product()
|
|
42
|
+
|
|
43
|
+
pipeline_run = trigger.trigger_and_check_status(
|
|
44
|
+
global_data_product,
|
|
42
45
|
block=self,
|
|
43
46
|
from_notebook=from_notebook,
|
|
44
47
|
logger=logger,
|
|
45
48
|
logging_tags=logging_tags,
|
|
49
|
+
poll_interval=30,
|
|
50
|
+
remote_blocks=global_vars.get('remote_blocks'),
|
|
46
51
|
variables=global_vars.get('variables') if global_vars else None,
|
|
47
52
|
)
|
|
48
53
|
|
|
54
|
+
if pipeline_run and pipeline_run.pipeline:
|
|
55
|
+
pipeline = pipeline_run.pipeline
|
|
56
|
+
|
|
57
|
+
arr = []
|
|
58
|
+
|
|
59
|
+
for block in global_data_product.get_blocks():
|
|
60
|
+
if block and block.uuid:
|
|
61
|
+
arr.append(RemoteBlock.load(
|
|
62
|
+
block_uuid=block.uuid,
|
|
63
|
+
execution_partition=pipeline_run.execution_partition,
|
|
64
|
+
pipeline_uuid=pipeline.uuid,
|
|
65
|
+
repo_path=pipeline.repo_path,
|
|
66
|
+
))
|
|
67
|
+
|
|
68
|
+
return [
|
|
69
|
+
dict(remote_blocks=arr),
|
|
70
|
+
]
|
|
71
|
+
|
|
49
72
|
return []
|
|
File without changes
|