mage-ai 0.9.68__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/SeedResource.py +2 -1
- mage_ai/api/resources/SessionResource.py +13 -1
- mage_ai/api/resources/WorkspaceResource.py +5 -4
- mage_ai/authentication/permissions/constants.py +2 -0
- mage_ai/authentication/permissions/seed.py +32 -21
- mage_ai/authentication/providers/active_directory.py +4 -3
- mage_ai/authentication/providers/okta.py +22 -83
- 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 +78 -8
- 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 +31 -8
- mage_ai/data_preparation/models/block/data_integration/mixins.py +16 -5
- mage_ai/data_preparation/models/block/dynamic/child.py +3 -0
- mage_ai/data_preparation/models/block/dynamic/utils.py +9 -4
- mage_ai/data_preparation/models/block/dynamic/variables.py +2 -2
- mage_ai/data_preparation/models/block/extension/utils.py +1 -0
- mage_ai/data_preparation/models/block/global_data_product/__init__.py +25 -2
- mage_ai/data_preparation/models/block/integration/__init__.py +1 -1
- 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/sql/__init__.py +1 -1
- 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 +31 -11
- mage_ai/data_preparation/models/triggers/__init__.py +4 -2
- mage_ai/data_preparation/models/utils.py +6 -0
- mage_ai/data_preparation/models/variable.py +18 -4
- mage_ai/data_preparation/repo_manager.py +3 -2
- mage_ai/data_preparation/shared/utils.py +1 -1
- mage_ai/data_preparation/storage/local_storage.py +12 -6
- mage_ai/data_preparation/templates/data_exporters/mysql.py +2 -2
- mage_ai/data_preparation/templates/data_exporters/oracledb.py +27 -0
- mage_ai/data_preparation/templates/repo/metadata.yaml +1 -0
- mage_ai/io/bigquery.py +131 -58
- mage_ai/io/mysql.py +38 -6
- mage_ai/io/snowflake.py +152 -29
- mage_ai/orchestration/db/migrations/versions/42a14d6143f1_update_token_column_type.py +54 -0
- mage_ai/orchestration/db/models/oauth.py +14 -13
- mage_ai/orchestration/db/models/schedules.py +30 -2
- mage_ai/orchestration/job_manager.py +6 -0
- mage_ai/orchestration/notification/sender.py +37 -15
- mage_ai/orchestration/pipeline_scheduler_original.py +48 -31
- mage_ai/orchestration/queue/celery_queue.py +8 -1
- mage_ai/orchestration/queue/process_queue.py +67 -4
- mage_ai/orchestration/queue/queue.py +8 -0
- 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/{i8pymuJDTVHdWjUP1QSh1 → 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/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- 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/index-4e12783b064c1cfe.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-a66b4c7641ae03eb.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]/settings-59aca25a5b1d3998.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-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- 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/platform/settings-c2e9ef989c8bfa73.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/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.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/{CKCvjsYCf2imD2X8zAOBf → 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/2717-d9200be634dd6766.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-9d26185b3fb663b1.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6d708c6b2153ea08.js +1 -0
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-8a23dd8360593e7a.js +1 -0
- 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/index-4e12783b064c1cfe.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-a66b4c7641ae03eb.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]/settings-59aca25a5b1d3998.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-cb88fd075a357fcf.js → triggers-4612d15a65c35912.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines-3591d035bb3bb2b8.js +1 -0
- 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/platform/settings-c2e9ef989c8bfa73.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_base_path_template/_next/static/chunks/pages/settings/workspace/{users-86814e581acaf5db.js → users-a4db8710f703c729.js} +1 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-09414a8b66fb6f06.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 +9 -0
- mage_ai/server/server.py +47 -17
- mage_ai/server/utils/output_display.py +2 -2
- mage_ai/server/websocket_server.py +1 -0
- 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/settings/keys/auth.py +2 -0
- mage_ai/settings/server.py +1 -1
- mage_ai/shared/parsers.py +6 -1
- mage_ai/streaming/sources/influxdb.py +2 -0
- mage_ai/streaming/sources/kafka.py +1 -1
- mage_ai/tests/api/endpoints/mixins.py +10 -9
- mage_ai/tests/api/endpoints/test_seeds.py +24 -0
- mage_ai/tests/api/operations/base/mixins.py +1 -1
- mage_ai/tests/api/operations/test_sessions.py +53 -2
- mage_ai/tests/api/resources/test_pipeline_resource.py +2 -2
- mage_ai/tests/authentication/oauth/test_utils.py +1 -1
- mage_ai/tests/authentication/providers/test_okta.py +43 -0
- mage_ai/tests/data_preparation/models/block/test_global_data_product.py +2 -0
- mage_ai/tests/orchestration/db/models/test_oauth.py +3 -3
- mage_ai/tests/orchestration/queue/test_process_queue.py +1 -0
- mage_ai/tests/orchestration/triggers/test_global_data_product.py +138 -136
- mage_ai/tests/server/test_server.py +27 -4
- 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.68.dist-info → mage_ai-0.9.70.dist-info}/METADATA +5 -5
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/RECORD +272 -264
- mage_ai/server/frontend_dist/_next/static/chunks/1557-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/7361-18d9d8be96e1ce97.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-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/index-13760bb72d823b69.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-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.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/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist/_next/static/chunks/pages/sign-in-f59d34429fe022ee.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-01f0843dc6ac4971.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/2717-b5f9575799b594d5.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/3548-961ff79ca70038c7.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/5699-6efc749f2f8ddd20.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/7361-18d9d8be96e1ce97.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-08790743315de36a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/index-13760bb72d823b69.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-bd0aff5a5ed8888c.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/pipelines/[pipeline]/settings-d1ee961387c58b7f.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/pipelines-ceb06e1616ee9610.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/preferences-8ff16ef9566e911a.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/platform/settings-74d76300942dcee8.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/preferences-d7a8bc51bb7a1082.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/settings/workspace/sync-data-79a4cf66a623e667.js +0 -1
- mage_ai/server/frontend_dist_base_path_template/_next/static/chunks/pages/sign-in-f59d34429fe022ee.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/{i8pymuJDTVHdWjUP1QSh1 → RhDiJSkcjCsh4xxX4BFBk}/_ssgManifest.js +0 -0
- /mage_ai/server/frontend_dist_base_path_template/_next/static/{CKCvjsYCf2imD2X8zAOBf → TdpLLFome13qvM0gXvpHs}/_ssgManifest.js +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/LICENSE +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/WHEEL +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/entry_points.txt +0 -0
- {mage_ai-0.9.68.dist-info → mage_ai-0.9.70.dist-info}/top_level.txt +0 -0
|
@@ -1,104 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
import uuid
|
|
3
|
-
from typing import Awaitable, Dict
|
|
4
|
-
|
|
5
|
-
import aiohttp
|
|
6
|
-
from aiohttp import BasicAuth
|
|
1
|
+
from urllib.parse import unquote, urlparse
|
|
7
2
|
|
|
8
3
|
from mage_ai.authentication.oauth.constants import ProviderName
|
|
9
|
-
from mage_ai.authentication.providers.
|
|
10
|
-
from mage_ai.authentication.providers.sso import SsoProvider
|
|
11
|
-
from mage_ai.authentication.providers.utils import get_base_url
|
|
4
|
+
from mage_ai.authentication.providers.oidc import OidcProvider
|
|
12
5
|
from mage_ai.settings import get_settings_value
|
|
13
6
|
from mage_ai.settings.keys import OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_DOMAIN_URL
|
|
14
7
|
|
|
15
8
|
|
|
16
|
-
class OktaProvider(
|
|
9
|
+
class OktaProvider(
|
|
10
|
+
OidcProvider
|
|
11
|
+
): # Okta configuration uses OIDC so we can just subclass the OidcProvider
|
|
17
12
|
provider = ProviderName.OKTA
|
|
18
13
|
|
|
19
14
|
def __init__(self):
|
|
20
15
|
self.hostname = get_settings_value(OKTA_DOMAIN_URL)
|
|
21
16
|
self.client_id = get_settings_value(OKTA_CLIENT_ID)
|
|
22
17
|
self.client_secret = get_settings_value(OKTA_CLIENT_SECRET)
|
|
18
|
+
self.parsed_url = urlparse(unquote(self.hostname))
|
|
19
|
+
if not self.parsed_url.scheme:
|
|
20
|
+
self.parsed_url = urlparse(unquote(f'https://{self.hostname}'))
|
|
23
21
|
self.__validate()
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
self.discovery_url = (
|
|
24
|
+
f'https://{self.parsed_url.netloc}/.well-known/openid-configuration'
|
|
25
|
+
)
|
|
26
|
+
self.discover()
|
|
27
27
|
|
|
28
28
|
def __validate(self):
|
|
29
|
-
if not self.
|
|
30
|
-
raise
|
|
29
|
+
if not self.parsed_url.netloc:
|
|
30
|
+
raise ValueError(
|
|
31
31
|
'Okta hostname is empty. '
|
|
32
|
-
'Make sure the OKTA_DOMAIN_URL environment variable is set.'
|
|
32
|
+
'Make sure the OKTA_DOMAIN_URL environment variable is set.'
|
|
33
|
+
)
|
|
33
34
|
if not self.client_id:
|
|
34
|
-
raise
|
|
35
|
+
raise ValueError(
|
|
35
36
|
'Okta client id is empty. '
|
|
36
|
-
'Make sure the OKTA_CLIENT_ID environment variable is set.'
|
|
37
|
+
'Make sure the OKTA_CLIENT_ID environment variable is set.'
|
|
38
|
+
)
|
|
37
39
|
if not self.client_secret:
|
|
38
|
-
raise
|
|
40
|
+
raise ValueError(
|
|
39
41
|
'Okta client secret is empty. '
|
|
40
|
-
'Make sure the OKTA_CLIENT_SECRET environment variable is set.'
|
|
41
|
-
|
|
42
|
-
def get_auth_url_response(self, redirect_uri: str = None, **kwargs) -> Dict:
|
|
43
|
-
base_url = get_base_url(redirect_uri)
|
|
44
|
-
redirect_uri_query = dict(
|
|
45
|
-
provider=self.provider,
|
|
46
|
-
redirect_uri=redirect_uri,
|
|
47
|
-
)
|
|
48
|
-
query = dict(
|
|
49
|
-
client_id=self.client_id,
|
|
50
|
-
redirect_uri=urllib.parse.quote_plus(
|
|
51
|
-
f'{base_url}/oauth',
|
|
52
|
-
),
|
|
53
|
-
response_mode='query',
|
|
54
|
-
response_type='code',
|
|
55
|
-
scope='openid email profile',
|
|
56
|
-
state=uuid.uuid4().hex,
|
|
57
|
-
)
|
|
58
|
-
query_strings = []
|
|
59
|
-
for k, v in query.items():
|
|
60
|
-
query_strings.append(f'{k}={v}')
|
|
61
|
-
|
|
62
|
-
return dict(
|
|
63
|
-
url=f"{self.hostname}/oauth2/default/v1/authorize?{'&'.join(query_strings)}",
|
|
64
|
-
redirect_query_params=redirect_uri_query,
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
async def get_access_token_response(self, code: str, **kwargs) -> Awaitable[Dict]:
|
|
68
|
-
base_url = get_base_url(kwargs.get('redirect_uri'))
|
|
69
|
-
data = dict()
|
|
70
|
-
async with aiohttp.ClientSession() as session:
|
|
71
|
-
async with session.post(
|
|
72
|
-
f'{self.hostname}/oauth2/default/v1/token',
|
|
73
|
-
headers={
|
|
74
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
75
|
-
},
|
|
76
|
-
data=dict(
|
|
77
|
-
grant_type='authorization_code',
|
|
78
|
-
code=code,
|
|
79
|
-
redirect_uri=f'{base_url}/oauth',
|
|
80
|
-
),
|
|
81
|
-
auth=BasicAuth(self.client_id, self.client_secret),
|
|
82
|
-
timeout=20,
|
|
83
|
-
) as response:
|
|
84
|
-
data = await response.json()
|
|
85
|
-
|
|
86
|
-
return data
|
|
87
|
-
|
|
88
|
-
async def get_user_info(self, access_token: str = None, **kwargs) -> Awaitable[Dict]:
|
|
89
|
-
if access_token is None:
|
|
90
|
-
raise Exception('Access token is required to fetch user info.')
|
|
91
|
-
async with aiohttp.ClientSession() as session:
|
|
92
|
-
async with session.get(
|
|
93
|
-
f'{self.hostname}/oauth2/default/v1/userinfo',
|
|
94
|
-
headers={
|
|
95
|
-
'Authorization': f'Bearer {access_token}'
|
|
96
|
-
},
|
|
97
|
-
timeout=10,
|
|
98
|
-
) as response:
|
|
99
|
-
userinfo_resp = await response.json()
|
|
100
|
-
|
|
101
|
-
return dict(
|
|
102
|
-
email=userinfo_resp.get('email'),
|
|
103
|
-
username=userinfo_resp.get('sub'),
|
|
104
|
-
)
|
|
42
|
+
'Make sure the OKTA_CLIENT_SECRET environment variable is set.'
|
|
43
|
+
)
|
|
@@ -294,7 +294,7 @@ class BlockActionObjectCache(BaseCache):
|
|
|
294
294
|
)
|
|
295
295
|
|
|
296
296
|
content = None
|
|
297
|
-
with open(block_file_absolute_path, 'r') as f:
|
|
297
|
+
with open(block_file_absolute_path, 'r', encoding='utf-8') as f:
|
|
298
298
|
content = f.read()
|
|
299
299
|
|
|
300
300
|
mapping[OBJECT_TYPE_BLOCK_FILE][key] = dict(
|
|
@@ -477,6 +477,57 @@ class WorkloadManager:
|
|
|
477
477
|
|
|
478
478
|
return k8s_service
|
|
479
479
|
|
|
480
|
+
def patch_workload(
|
|
481
|
+
self,
|
|
482
|
+
name: str,
|
|
483
|
+
workspace_config: KubernetesWorkspaceConfig,
|
|
484
|
+
update_workspace_settings: bool = False,
|
|
485
|
+
) -> None:
|
|
486
|
+
"""
|
|
487
|
+
Update workload for k8s. Currently the only fields that can be updated are
|
|
488
|
+
container_config and workspace settings.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
name (str): name of the workload/workspace
|
|
492
|
+
workspace_config (KubernetesWorkspaceConfig): new workspace config
|
|
493
|
+
update_workspace_settings (bool): whether to update workspace settings with
|
|
494
|
+
the current pod's environment variables.
|
|
495
|
+
"""
|
|
496
|
+
container_config_yaml = workspace_config.container_config
|
|
497
|
+
container_config = dict()
|
|
498
|
+
if isinstance(container_config_yaml, str):
|
|
499
|
+
container_config = yaml.full_load(container_config_yaml)
|
|
500
|
+
|
|
501
|
+
if update_workspace_settings:
|
|
502
|
+
env_vars = self.__populate_env_vars(
|
|
503
|
+
name,
|
|
504
|
+
container_config=container_config,
|
|
505
|
+
)
|
|
506
|
+
container_config['env'] = env_vars
|
|
507
|
+
|
|
508
|
+
mage_container_config = {
|
|
509
|
+
'name': f'{name}-container',
|
|
510
|
+
**container_config,
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
stateful_set_template_spec = {
|
|
514
|
+
'containers': [mage_container_config],
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
updated_stateful_set = {
|
|
518
|
+
'spec': {
|
|
519
|
+
'template': {
|
|
520
|
+
'spec': stateful_set_template_spec,
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
self.apps_client.patch_namespaced_stateful_set(
|
|
526
|
+
name,
|
|
527
|
+
namespace=self.namespace,
|
|
528
|
+
body=updated_stateful_set,
|
|
529
|
+
)
|
|
530
|
+
|
|
480
531
|
def add_service_to_ingress_paths(
|
|
481
532
|
self,
|
|
482
533
|
ingress_name: str,
|
|
@@ -712,7 +763,7 @@ class WorkloadManager:
|
|
|
712
763
|
def __populate_env_vars(
|
|
713
764
|
self,
|
|
714
765
|
name,
|
|
715
|
-
project_type: str =
|
|
766
|
+
project_type: str = None,
|
|
716
767
|
project_uuid: str = None,
|
|
717
768
|
container_config: Dict = None,
|
|
718
769
|
initial_metadata: Dict = None,
|
|
@@ -148,6 +148,12 @@ class Workspace(abc.ABC):
|
|
|
148
148
|
"""
|
|
149
149
|
raise NotImplementedError('Initialize method not implemented')
|
|
150
150
|
|
|
151
|
+
def update(self, payload: Dict, **kwargs):
|
|
152
|
+
"""
|
|
153
|
+
Update the workspace configuration.
|
|
154
|
+
"""
|
|
155
|
+
raise NotImplementedError('Update method not implemented')
|
|
156
|
+
|
|
151
157
|
@abc.abstractmethod
|
|
152
158
|
@safe_db_query
|
|
153
159
|
def delete(self, **kwargs):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import Dict
|
|
2
3
|
|
|
3
4
|
import yaml
|
|
4
5
|
|
|
@@ -7,7 +8,7 @@ from mage_ai.cluster_manager.constants import KUBE_NAMESPACE, ClusterType
|
|
|
7
8
|
from mage_ai.cluster_manager.kubernetes.workload_manager import WorkloadManager
|
|
8
9
|
from mage_ai.cluster_manager.workspace.base import Workspace
|
|
9
10
|
from mage_ai.data_preparation.repo_manager import ProjectType, get_project_type
|
|
10
|
-
from mage_ai.shared.hash import merge_dict
|
|
11
|
+
from mage_ai.shared.hash import extract, merge_dict
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class KubernetesWorkspace(Workspace):
|
|
@@ -82,6 +83,26 @@ class KubernetesWorkspace(Workspace):
|
|
|
82
83
|
|
|
83
84
|
return cls(name)
|
|
84
85
|
|
|
86
|
+
def update(self, payload: Dict, **kwargs):
|
|
87
|
+
update_workspace_settings = payload.pop('update_workspace_settings', False)
|
|
88
|
+
extracted_payload = extract(payload, [
|
|
89
|
+
'container_config',
|
|
90
|
+
])
|
|
91
|
+
updated_config = merge_dict(
|
|
92
|
+
self.config.to_dict(),
|
|
93
|
+
extracted_payload,
|
|
94
|
+
)
|
|
95
|
+
workspace_config = KubernetesWorkspaceConfig.load(
|
|
96
|
+
config=updated_config
|
|
97
|
+
)
|
|
98
|
+
self.workload_manager.patch_workload(
|
|
99
|
+
self.name,
|
|
100
|
+
workspace_config,
|
|
101
|
+
update_workspace_settings=update_workspace_settings,
|
|
102
|
+
)
|
|
103
|
+
with open(self.config_path, 'w', encoding='utf-8') as fp:
|
|
104
|
+
yaml.dump(workspace_config.to_dict(), fp)
|
|
105
|
+
|
|
85
106
|
def delete(self, **kwargs):
|
|
86
107
|
try:
|
|
87
108
|
self.workload_manager.delete_workload(
|
|
@@ -17,7 +17,7 @@ from mage_ai.version_control.models import File
|
|
|
17
17
|
|
|
18
18
|
"""
|
|
19
19
|
Icons:
|
|
20
|
-
|
|
20
|
+
Text Editor (file browser): ChurnV3
|
|
21
21
|
Portal Terminal (terminal): RankingV3
|
|
22
22
|
Version Control (file diffs): ForecastV3
|
|
23
23
|
|
|
@@ -65,7 +65,7 @@ async def build_application_arcane_library() -> Dict:
|
|
|
65
65
|
return build_generic(model_class=File, item_dict=dict(
|
|
66
66
|
item_type=ItemType.DETAIL,
|
|
67
67
|
object_type=ObjectType.FILE,
|
|
68
|
-
title='
|
|
68
|
+
title='Text editor',
|
|
69
69
|
description='browse and edit files across all projects',
|
|
70
70
|
subtitle='Browser / Editor',
|
|
71
71
|
applications=[
|
|
@@ -5,5 +5,5 @@ def application_title(uuid: ApplicationExpansionUUID) -> str:
|
|
|
5
5
|
if ApplicationExpansionUUID.VersionControlFileDiffs == uuid:
|
|
6
6
|
return 'version control file diffs'
|
|
7
7
|
if ApplicationExpansionUUID.ArcaneLibrary == uuid:
|
|
8
|
-
return 'the
|
|
8
|
+
return 'the Text Editor'
|
|
9
9
|
return f'the {uuid} application'
|
|
@@ -4,10 +4,13 @@ from jinja2 import Template
|
|
|
4
4
|
|
|
5
5
|
from mage_ai.data_preparation.executors.block_executor import BlockExecutor
|
|
6
6
|
from mage_ai.data_preparation.shared.utils import get_template_vars
|
|
7
|
+
from mage_ai.orchestration.db import safe_db_query
|
|
8
|
+
from mage_ai.orchestration.db.models.schedules import BlockRun
|
|
7
9
|
from mage_ai.services.k8s.config import K8sExecutorConfig
|
|
8
10
|
from mage_ai.services.k8s.constants import DEFAULT_NAMESPACE
|
|
9
11
|
from mage_ai.services.k8s.job_manager import JobManager as K8sJobManager
|
|
10
12
|
from mage_ai.shared.hash import merge_dict
|
|
13
|
+
from mage_ai.shared.utils import clean_name
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
class K8sBlockExecutor(BlockExecutor):
|
|
@@ -15,10 +18,13 @@ class K8sBlockExecutor(BlockExecutor):
|
|
|
15
18
|
|
|
16
19
|
def __init__(self, pipeline, block_uuid: str, execution_partition: str = None, **kwargs):
|
|
17
20
|
super().__init__(pipeline, block_uuid, execution_partition=execution_partition)
|
|
18
|
-
self.
|
|
21
|
+
self.executor_config_dict = self.pipeline.repo_config.k8s_executor_config or dict()
|
|
19
22
|
if self.block.executor_config is not None:
|
|
20
|
-
self.
|
|
21
|
-
|
|
23
|
+
self.executor_config_dict = merge_dict(
|
|
24
|
+
self.executor_config_dict,
|
|
25
|
+
self.block.executor_config,
|
|
26
|
+
)
|
|
27
|
+
self.executor_config = K8sExecutorConfig.load(config=self.executor_config_dict)
|
|
22
28
|
|
|
23
29
|
def _execute(
|
|
24
30
|
self,
|
|
@@ -26,10 +32,7 @@ class K8sBlockExecutor(BlockExecutor):
|
|
|
26
32
|
global_vars: Dict = None,
|
|
27
33
|
**kwargs,
|
|
28
34
|
) -> None:
|
|
29
|
-
|
|
30
|
-
job_name_prefix = 'data-prep'
|
|
31
|
-
else:
|
|
32
|
-
job_name_prefix = self.executor_config.job_name_prefix
|
|
35
|
+
job_name_prefix = self._get_job_name_prefix(block_run_id)
|
|
33
36
|
|
|
34
37
|
if self.executor_config.namespace:
|
|
35
38
|
namespace = Template(self.executor_config.namespace).render(
|
|
@@ -54,3 +57,23 @@ class K8sBlockExecutor(BlockExecutor):
|
|
|
54
57
|
cmd,
|
|
55
58
|
k8s_config=self.executor_config,
|
|
56
59
|
)
|
|
60
|
+
|
|
61
|
+
@safe_db_query
|
|
62
|
+
def _get_job_name_prefix(
|
|
63
|
+
self,
|
|
64
|
+
block_run_id,
|
|
65
|
+
):
|
|
66
|
+
if not self.executor_config.job_name_prefix:
|
|
67
|
+
job_name_prefix = 'data-prep'
|
|
68
|
+
else:
|
|
69
|
+
job_name_prefix = self.executor_config.job_name_prefix
|
|
70
|
+
if not block_run_id:
|
|
71
|
+
return job_name_prefix
|
|
72
|
+
|
|
73
|
+
if '{trigger_name}' in job_name_prefix:
|
|
74
|
+
block_run = BlockRun.query.get(block_run_id)
|
|
75
|
+
trigger = block_run.pipeline_run.pipeline_schedule
|
|
76
|
+
job_name_prefix = job_name_prefix.format(
|
|
77
|
+
trigger_name=clean_name(trigger.name).replace('_', '-'))
|
|
78
|
+
|
|
79
|
+
return job_name_prefix
|
|
@@ -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
|
|
@@ -2,9 +2,12 @@ import asyncio
|
|
|
2
2
|
import copy
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
|
+
import traceback
|
|
5
6
|
from contextlib import redirect_stderr, redirect_stdout
|
|
7
|
+
from datetime import datetime
|
|
6
8
|
from typing import Callable, Dict, List, Union
|
|
7
9
|
|
|
10
|
+
import pytz
|
|
8
11
|
import yaml
|
|
9
12
|
from jinja2 import Template
|
|
10
13
|
|
|
@@ -12,9 +15,14 @@ from mage_ai.data_preparation.executors.pipeline_executor import PipelineExecuto
|
|
|
12
15
|
from mage_ai.data_preparation.logging.logger import DictLogger
|
|
13
16
|
from mage_ai.data_preparation.models.constants import BlockLanguage, BlockType
|
|
14
17
|
from mage_ai.data_preparation.models.pipeline import Pipeline
|
|
18
|
+
from mage_ai.data_preparation.shared.retry import RetryConfig
|
|
15
19
|
from mage_ai.data_preparation.shared.stream import StreamToLogger
|
|
16
20
|
from mage_ai.data_preparation.shared.utils import get_template_vars
|
|
21
|
+
from mage_ai.orchestration.db import safe_db_query
|
|
22
|
+
from mage_ai.orchestration.db.models.schedules import PipelineRun
|
|
17
23
|
from mage_ai.shared.hash import merge_dict
|
|
24
|
+
from mage_ai.shared.retry import retry
|
|
25
|
+
from mage_ai.usage_statistics.logger import UsageStatisticLogger
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
class StreamingPipelineExecutor(PipelineExecutor):
|
|
@@ -22,6 +30,7 @@ class StreamingPipelineExecutor(PipelineExecutor):
|
|
|
22
30
|
super().__init__(pipeline, **kwargs)
|
|
23
31
|
# TODO: Support custom log destination for streaming pipelines
|
|
24
32
|
self.parse_and_validate_blocks()
|
|
33
|
+
self.retry_metadata = dict(attempts=0)
|
|
25
34
|
|
|
26
35
|
def parse_and_validate_blocks(self):
|
|
27
36
|
"""
|
|
@@ -67,13 +76,16 @@ class StreamingPipelineExecutor(PipelineExecutor):
|
|
|
67
76
|
self,
|
|
68
77
|
build_block_output_stdout: Callable[..., object] = None,
|
|
69
78
|
global_vars: Dict = None,
|
|
79
|
+
pipeline_run_id: int = None,
|
|
80
|
+
retry_config: Dict = None,
|
|
70
81
|
**kwargs,
|
|
71
82
|
) -> None:
|
|
72
83
|
# TODOs:
|
|
73
84
|
# 1. Support multiple sources and sinks
|
|
74
85
|
# 2. Support flink pipeline
|
|
75
86
|
|
|
76
|
-
tags = self.build_tags(**kwargs)
|
|
87
|
+
tags = self.build_tags(pipeline_run_id=pipeline_run_id, **kwargs)
|
|
88
|
+
self.logging_tags = tags
|
|
77
89
|
if build_block_output_stdout:
|
|
78
90
|
stdout_logger = logging.getLogger('streaming_pipeline_executor')
|
|
79
91
|
self.logger = DictLogger(stdout_logger)
|
|
@@ -82,24 +94,53 @@ class StreamingPipelineExecutor(PipelineExecutor):
|
|
|
82
94
|
self.logger = DictLogger(self.logger_manager.logger, logging_tags=tags)
|
|
83
95
|
stdout = StreamToLogger(self.logger, logging_tags=tags)
|
|
84
96
|
try:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
if retry_config is None:
|
|
98
|
+
retry_config = self.pipeline.retry_config or dict()
|
|
99
|
+
infinite_retries = False if retry_config else False
|
|
100
|
+
|
|
101
|
+
if type(retry_config) is not RetryConfig:
|
|
102
|
+
retry_config = RetryConfig.load(config=retry_config)
|
|
103
|
+
|
|
104
|
+
@retry(
|
|
105
|
+
retries=retry_config.retries,
|
|
106
|
+
delay=retry_config.delay,
|
|
107
|
+
max_delay=retry_config.max_delay,
|
|
108
|
+
exponential_backoff=retry_config.exponential_backoff,
|
|
109
|
+
logger=self.logger,
|
|
110
|
+
logging_tags=self.logging_tags,
|
|
111
|
+
retry_metadata=self.retry_metadata,
|
|
112
|
+
)
|
|
113
|
+
def __execute_with_retry():
|
|
114
|
+
with redirect_stdout(stdout):
|
|
115
|
+
with redirect_stderr(stdout):
|
|
116
|
+
self.__execute_in_python(
|
|
117
|
+
build_block_output_stdout=build_block_output_stdout,
|
|
118
|
+
global_vars=global_vars,
|
|
119
|
+
pipeline_run_id=pipeline_run_id,
|
|
120
|
+
)
|
|
121
|
+
__execute_with_retry()
|
|
91
122
|
except Exception as e:
|
|
92
123
|
if not build_block_output_stdout:
|
|
93
124
|
self.logger.exception(
|
|
94
125
|
f'Failed to execute streaming pipeline {self.pipeline.uuid}',
|
|
95
126
|
**merge_dict(dict(error=e), tags),
|
|
96
127
|
)
|
|
128
|
+
if not infinite_retries:
|
|
129
|
+
# If pipeline retry config is present, fail the pipeline after the retries
|
|
130
|
+
self.__update_pipeline_run_status(
|
|
131
|
+
pipeline_run_id,
|
|
132
|
+
PipelineRun.PipelineRunStatus.FAILED,
|
|
133
|
+
error=e,
|
|
134
|
+
)
|
|
135
|
+
|
|
97
136
|
raise e
|
|
98
137
|
|
|
99
138
|
def __execute_in_python(
|
|
100
139
|
self,
|
|
101
140
|
build_block_output_stdout: Callable[..., object] = None,
|
|
102
|
-
global_vars: Dict = None
|
|
141
|
+
global_vars: Dict = None,
|
|
142
|
+
pipeline_run_id: int = None,
|
|
143
|
+
|
|
103
144
|
):
|
|
104
145
|
from mage_ai.streaming.sinks.sink_factory import SinkFactory
|
|
105
146
|
from mage_ai.streaming.sources.base import SourceConsumeMethod
|
|
@@ -227,6 +268,35 @@ class StreamingPipelineExecutor(PipelineExecutor):
|
|
|
227
268
|
for sink in sinks_by_uuid.values():
|
|
228
269
|
sink.destroy()
|
|
229
270
|
|
|
271
|
+
@safe_db_query
|
|
272
|
+
def __update_pipeline_run_status(
|
|
273
|
+
self,
|
|
274
|
+
pipeline_run_id: int,
|
|
275
|
+
status: PipelineRun.PipelineRunStatus,
|
|
276
|
+
error: Exception = None,
|
|
277
|
+
):
|
|
278
|
+
if not pipeline_run_id or not status:
|
|
279
|
+
return
|
|
280
|
+
pipeline_run = PipelineRun.query.get(pipeline_run_id)
|
|
281
|
+
pipeline_run.update(
|
|
282
|
+
status=status,
|
|
283
|
+
completed_at=datetime.now(tz=pytz.UTC),
|
|
284
|
+
)
|
|
285
|
+
if status == PipelineRun.PipelineRunStatus.FAILED:
|
|
286
|
+
asyncio.run(UsageStatisticLogger().pipeline_run_ended(pipeline_run))
|
|
287
|
+
error_msg = None
|
|
288
|
+
stacktrace = None
|
|
289
|
+
if error is not None:
|
|
290
|
+
error_msg = str(error)
|
|
291
|
+
stacktrace = traceback.format_exc()
|
|
292
|
+
notification_sender = self.pipeline.get_notification_sender()
|
|
293
|
+
notification_sender.send_pipeline_run_failure_message(
|
|
294
|
+
pipeline=self.pipeline,
|
|
295
|
+
pipeline_run=pipeline_run,
|
|
296
|
+
error=error_msg,
|
|
297
|
+
stacktrace=stacktrace,
|
|
298
|
+
)
|
|
299
|
+
|
|
230
300
|
def __execute_in_flink(self):
|
|
231
301
|
"""
|
|
232
302
|
TODO: Implement this method
|