mlrun 1.3.3rc1__py3-none-any.whl → 1.4.0__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 mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +3 -3
- mlrun/__main__.py +79 -37
- mlrun/api/__init__.py +1 -1
- mlrun/api/api/__init__.py +1 -1
- mlrun/api/api/api.py +4 -4
- mlrun/api/api/deps.py +10 -21
- mlrun/api/api/endpoints/__init__.py +1 -1
- mlrun/api/api/endpoints/artifacts.py +64 -36
- mlrun/api/api/endpoints/auth.py +4 -4
- mlrun/api/api/endpoints/background_tasks.py +11 -11
- mlrun/api/api/endpoints/client_spec.py +5 -5
- mlrun/api/api/endpoints/clusterization_spec.py +6 -4
- mlrun/api/api/endpoints/feature_store.py +124 -115
- mlrun/api/api/endpoints/files.py +22 -14
- mlrun/api/api/endpoints/frontend_spec.py +28 -21
- mlrun/api/api/endpoints/functions.py +142 -87
- mlrun/api/api/endpoints/grafana_proxy.py +89 -442
- mlrun/api/api/endpoints/healthz.py +20 -7
- mlrun/api/api/endpoints/hub.py +320 -0
- mlrun/api/api/endpoints/internal/__init__.py +1 -1
- mlrun/api/api/endpoints/internal/config.py +1 -1
- mlrun/api/api/endpoints/internal/memory_reports.py +9 -9
- mlrun/api/api/endpoints/logs.py +11 -11
- mlrun/api/api/endpoints/model_endpoints.py +74 -70
- mlrun/api/api/endpoints/operations.py +13 -9
- mlrun/api/api/endpoints/pipelines.py +93 -88
- mlrun/api/api/endpoints/projects.py +35 -35
- mlrun/api/api/endpoints/runs.py +69 -27
- mlrun/api/api/endpoints/runtime_resources.py +28 -28
- mlrun/api/api/endpoints/schedules.py +98 -41
- mlrun/api/api/endpoints/secrets.py +37 -32
- mlrun/api/api/endpoints/submit.py +12 -12
- mlrun/api/api/endpoints/tags.py +20 -22
- mlrun/api/api/utils.py +251 -42
- mlrun/api/constants.py +1 -1
- mlrun/api/crud/__init__.py +18 -15
- mlrun/api/crud/artifacts.py +10 -10
- mlrun/api/crud/client_spec.py +4 -4
- mlrun/api/crud/clusterization_spec.py +3 -3
- mlrun/api/crud/feature_store.py +54 -46
- mlrun/api/crud/functions.py +3 -3
- mlrun/api/crud/hub.py +312 -0
- mlrun/api/crud/logs.py +11 -9
- mlrun/api/crud/model_monitoring/__init__.py +3 -3
- mlrun/api/crud/model_monitoring/grafana.py +435 -0
- mlrun/api/crud/model_monitoring/model_endpoints.py +352 -129
- mlrun/api/crud/notifications.py +149 -0
- mlrun/api/crud/pipelines.py +67 -52
- mlrun/api/crud/projects.py +51 -23
- mlrun/api/crud/runs.py +7 -5
- mlrun/api/crud/runtime_resources.py +13 -13
- mlrun/api/{db/filedb → crud/runtimes}/__init__.py +1 -1
- mlrun/api/crud/runtimes/nuclio/__init__.py +14 -0
- mlrun/api/crud/runtimes/nuclio/function.py +505 -0
- mlrun/api/crud/runtimes/nuclio/helpers.py +310 -0
- mlrun/api/crud/secrets.py +88 -46
- mlrun/api/crud/tags.py +5 -5
- mlrun/api/db/__init__.py +1 -1
- mlrun/api/db/base.py +102 -54
- mlrun/api/db/init_db.py +2 -3
- mlrun/api/db/session.py +4 -12
- mlrun/api/db/sqldb/__init__.py +1 -1
- mlrun/api/db/sqldb/db.py +439 -196
- mlrun/api/db/sqldb/helpers.py +1 -1
- mlrun/api/db/sqldb/models/__init__.py +3 -3
- mlrun/api/db/sqldb/models/models_mysql.py +82 -64
- mlrun/api/db/sqldb/models/models_sqlite.py +76 -64
- mlrun/api/db/sqldb/session.py +27 -20
- mlrun/api/initial_data.py +82 -24
- mlrun/api/launcher.py +196 -0
- mlrun/api/main.py +91 -22
- mlrun/api/middlewares.py +6 -5
- mlrun/api/migrations_mysql/env.py +1 -1
- mlrun/api/migrations_mysql/versions/28383af526f3_market_place_to_hub.py +40 -0
- mlrun/api/migrations_mysql/versions/32bae1b0e29c_increase_timestamp_fields_precision.py +1 -1
- mlrun/api/migrations_mysql/versions/4903aef6a91d_tag_foreign_key_and_cascades.py +1 -1
- mlrun/api/migrations_mysql/versions/5f1351c88a19_adding_background_tasks_table.py +1 -1
- mlrun/api/migrations_mysql/versions/88e656800d6a_add_requested_logs_column_and_index_to_.py +1 -1
- mlrun/api/migrations_mysql/versions/9d16de5f03a7_adding_data_versions_table.py +1 -1
- mlrun/api/migrations_mysql/versions/b86f5b53f3d7_adding_name_and_updated_to_runs_table.py +1 -1
- mlrun/api/migrations_mysql/versions/c4af40b0bf61_init.py +1 -1
- mlrun/api/migrations_mysql/versions/c905d15bd91d_notifications.py +72 -0
- mlrun/api/migrations_mysql/versions/ee041e8fdaa0_adding_next_run_time_column_to_schedule_.py +1 -1
- mlrun/api/migrations_sqlite/env.py +1 -1
- mlrun/api/migrations_sqlite/versions/11f8dd2dc9fe_init.py +1 -1
- mlrun/api/migrations_sqlite/versions/1c954f8cb32d_schedule_last_run_uri.py +1 -1
- mlrun/api/migrations_sqlite/versions/2b6d23c715aa_adding_feature_sets.py +1 -1
- mlrun/api/migrations_sqlite/versions/4acd9430b093_market_place_to_hub.py +77 -0
- mlrun/api/migrations_sqlite/versions/6401142f2d7c_adding_next_run_time_column_to_schedule_.py +1 -1
- mlrun/api/migrations_sqlite/versions/64d90a1a69bc_adding_background_tasks_table.py +1 -1
- mlrun/api/migrations_sqlite/versions/803438ecd005_add_requested_logs_column_to_runs.py +1 -1
- mlrun/api/migrations_sqlite/versions/863114f0c659_refactoring_feature_set.py +1 -1
- mlrun/api/migrations_sqlite/versions/959ae00528ad_notifications.py +63 -0
- mlrun/api/migrations_sqlite/versions/accf9fc83d38_adding_data_versions_table.py +1 -1
- mlrun/api/migrations_sqlite/versions/b68e8e897a28_schedule_labels.py +1 -1
- mlrun/api/migrations_sqlite/versions/bcd0c1f9720c_adding_project_labels.py +1 -1
- mlrun/api/migrations_sqlite/versions/cf21882f938e_schedule_id.py +1 -1
- mlrun/api/migrations_sqlite/versions/d781f58f607f_tag_object_name_string.py +1 -1
- mlrun/api/migrations_sqlite/versions/deac06871ace_adding_marketplace_sources_table.py +1 -1
- mlrun/api/migrations_sqlite/versions/e1dd5983c06b_schedule_concurrency_limit.py +1 -1
- mlrun/api/migrations_sqlite/versions/e5594ed3ab53_adding_name_and_updated_to_runs_table.py +1 -1
- mlrun/api/migrations_sqlite/versions/f4249b4ba6fa_adding_feature_vectors.py +1 -1
- mlrun/api/migrations_sqlite/versions/f7b5a1a03629_adding_feature_labels.py +1 -1
- mlrun/api/schemas/__init__.py +216 -138
- mlrun/api/utils/__init__.py +1 -1
- mlrun/api/utils/asyncio.py +1 -1
- mlrun/api/utils/auth/__init__.py +1 -1
- mlrun/api/utils/auth/providers/__init__.py +1 -1
- mlrun/api/utils/auth/providers/base.py +7 -7
- mlrun/api/utils/auth/providers/nop.py +6 -7
- mlrun/api/utils/auth/providers/opa.py +17 -17
- mlrun/api/utils/auth/verifier.py +36 -34
- mlrun/api/utils/background_tasks.py +24 -24
- mlrun/{builder.py → api/utils/builder.py} +216 -123
- mlrun/api/utils/clients/__init__.py +1 -1
- mlrun/api/utils/clients/chief.py +19 -4
- mlrun/api/utils/clients/iguazio.py +106 -60
- mlrun/api/utils/clients/log_collector.py +1 -1
- mlrun/api/utils/clients/nuclio.py +23 -23
- mlrun/api/utils/clients/protocols/grpc.py +2 -2
- mlrun/api/utils/db/__init__.py +1 -1
- mlrun/api/utils/db/alembic.py +1 -1
- mlrun/api/utils/db/backup.py +1 -1
- mlrun/api/utils/db/mysql.py +24 -25
- mlrun/api/utils/db/sql_collation.py +1 -1
- mlrun/api/utils/db/sqlite_migration.py +2 -2
- mlrun/api/utils/events/__init__.py +14 -0
- mlrun/api/utils/events/base.py +57 -0
- mlrun/api/utils/events/events_factory.py +41 -0
- mlrun/api/utils/events/iguazio.py +217 -0
- mlrun/api/utils/events/nop.py +55 -0
- mlrun/api/utils/helpers.py +16 -13
- mlrun/api/utils/memory_reports.py +1 -1
- mlrun/api/utils/periodic.py +6 -3
- mlrun/api/utils/projects/__init__.py +1 -1
- mlrun/api/utils/projects/follower.py +33 -33
- mlrun/api/utils/projects/leader.py +36 -34
- mlrun/api/utils/projects/member.py +27 -27
- mlrun/api/utils/projects/remotes/__init__.py +1 -1
- mlrun/api/utils/projects/remotes/follower.py +13 -13
- mlrun/api/utils/projects/remotes/leader.py +10 -10
- mlrun/api/utils/projects/remotes/nop_follower.py +27 -21
- mlrun/api/utils/projects/remotes/nop_leader.py +17 -16
- mlrun/api/utils/scheduler.py +140 -51
- mlrun/api/utils/singletons/__init__.py +1 -1
- mlrun/api/utils/singletons/db.py +9 -15
- mlrun/api/utils/singletons/k8s.py +677 -5
- mlrun/api/utils/singletons/logs_dir.py +1 -1
- mlrun/api/utils/singletons/project_member.py +1 -1
- mlrun/api/utils/singletons/scheduler.py +1 -1
- mlrun/artifacts/__init__.py +2 -2
- mlrun/artifacts/base.py +8 -2
- mlrun/artifacts/dataset.py +5 -3
- mlrun/artifacts/manager.py +7 -1
- mlrun/artifacts/model.py +15 -4
- mlrun/artifacts/plots.py +1 -1
- mlrun/common/__init__.py +1 -1
- mlrun/common/constants.py +15 -0
- mlrun/common/model_monitoring.py +209 -0
- mlrun/common/schemas/__init__.py +167 -0
- mlrun/{api → common}/schemas/artifact.py +13 -14
- mlrun/{api → common}/schemas/auth.py +10 -8
- mlrun/{api → common}/schemas/background_task.py +3 -3
- mlrun/{api → common}/schemas/client_spec.py +1 -1
- mlrun/{api → common}/schemas/clusterization_spec.py +3 -3
- mlrun/{api → common}/schemas/constants.py +21 -8
- mlrun/common/schemas/events.py +36 -0
- mlrun/{api → common}/schemas/feature_store.py +2 -1
- mlrun/{api → common}/schemas/frontend_spec.py +7 -6
- mlrun/{api → common}/schemas/function.py +5 -5
- mlrun/{api → common}/schemas/http.py +3 -3
- mlrun/common/schemas/hub.py +134 -0
- mlrun/{api → common}/schemas/k8s.py +3 -3
- mlrun/{api → common}/schemas/memory_reports.py +1 -1
- mlrun/common/schemas/model_endpoints.py +342 -0
- mlrun/common/schemas/notification.py +57 -0
- mlrun/{api → common}/schemas/object.py +6 -6
- mlrun/{api → common}/schemas/pipeline.py +3 -3
- mlrun/{api → common}/schemas/project.py +6 -5
- mlrun/common/schemas/regex.py +24 -0
- mlrun/common/schemas/runs.py +30 -0
- mlrun/{api → common}/schemas/runtime_resource.py +3 -3
- mlrun/{api → common}/schemas/schedule.py +19 -7
- mlrun/{api → common}/schemas/secret.py +3 -3
- mlrun/{api → common}/schemas/tag.py +2 -2
- mlrun/common/types.py +25 -0
- mlrun/config.py +152 -20
- mlrun/data_types/__init__.py +7 -2
- mlrun/data_types/data_types.py +4 -2
- mlrun/data_types/infer.py +1 -1
- mlrun/data_types/spark.py +10 -3
- mlrun/datastore/__init__.py +10 -3
- mlrun/datastore/azure_blob.py +1 -1
- mlrun/datastore/base.py +185 -53
- mlrun/datastore/datastore.py +1 -1
- mlrun/datastore/filestore.py +1 -1
- mlrun/datastore/google_cloud_storage.py +1 -1
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +1 -1
- mlrun/datastore/s3.py +1 -1
- mlrun/datastore/sources.py +192 -70
- mlrun/datastore/spark_udf.py +44 -0
- mlrun/datastore/store_resources.py +4 -4
- mlrun/datastore/targets.py +115 -45
- mlrun/datastore/utils.py +127 -5
- mlrun/datastore/v3io.py +1 -1
- mlrun/datastore/wasbfs/__init__.py +1 -1
- mlrun/datastore/wasbfs/fs.py +1 -1
- mlrun/db/__init__.py +7 -5
- mlrun/db/base.py +112 -68
- mlrun/db/httpdb.py +445 -277
- mlrun/db/nopdb.py +491 -0
- mlrun/db/sqldb.py +112 -65
- mlrun/errors.py +6 -1
- mlrun/execution.py +44 -22
- mlrun/feature_store/__init__.py +1 -1
- mlrun/feature_store/api.py +143 -95
- mlrun/feature_store/common.py +16 -20
- mlrun/feature_store/feature_set.py +42 -12
- mlrun/feature_store/feature_vector.py +32 -21
- mlrun/feature_store/ingestion.py +9 -12
- mlrun/feature_store/retrieval/__init__.py +3 -2
- mlrun/feature_store/retrieval/base.py +388 -66
- mlrun/feature_store/retrieval/dask_merger.py +63 -151
- mlrun/feature_store/retrieval/job.py +30 -12
- mlrun/feature_store/retrieval/local_merger.py +40 -133
- mlrun/feature_store/retrieval/spark_merger.py +129 -127
- mlrun/feature_store/retrieval/storey_merger.py +173 -0
- mlrun/feature_store/steps.py +132 -15
- mlrun/features.py +8 -3
- mlrun/frameworks/__init__.py +1 -1
- mlrun/frameworks/_common/__init__.py +1 -1
- mlrun/frameworks/_common/artifacts_library.py +1 -1
- mlrun/frameworks/_common/mlrun_interface.py +1 -1
- mlrun/frameworks/_common/model_handler.py +1 -1
- mlrun/frameworks/_common/plan.py +1 -1
- mlrun/frameworks/_common/producer.py +1 -1
- mlrun/frameworks/_common/utils.py +1 -1
- mlrun/frameworks/_dl_common/__init__.py +1 -1
- mlrun/frameworks/_dl_common/loggers/__init__.py +1 -1
- mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +1 -1
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +1 -1
- mlrun/frameworks/_dl_common/model_handler.py +1 -1
- mlrun/frameworks/_dl_common/utils.py +1 -1
- mlrun/frameworks/_ml_common/__init__.py +1 -1
- mlrun/frameworks/_ml_common/artifacts_library.py +1 -1
- mlrun/frameworks/_ml_common/loggers/__init__.py +1 -1
- mlrun/frameworks/_ml_common/loggers/logger.py +1 -1
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
- mlrun/frameworks/_ml_common/model_handler.py +1 -1
- mlrun/frameworks/_ml_common/pkl_model_server.py +13 -1
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/__init__.py +1 -1
- mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +1 -6
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +1 -1
- mlrun/frameworks/_ml_common/producer.py +1 -1
- mlrun/frameworks/_ml_common/utils.py +1 -1
- mlrun/frameworks/auto_mlrun/__init__.py +1 -1
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +1 -1
- mlrun/frameworks/huggingface/__init__.py +1 -1
- mlrun/frameworks/huggingface/model_server.py +1 -1
- mlrun/frameworks/lgbm/__init__.py +1 -1
- mlrun/frameworks/lgbm/callbacks/__init__.py +1 -1
- mlrun/frameworks/lgbm/callbacks/callback.py +1 -1
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +1 -1
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +1 -1
- mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -1
- mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -1
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +1 -1
- mlrun/frameworks/lgbm/mlrun_interfaces/model_mlrun_interface.py +1 -1
- mlrun/frameworks/lgbm/model_handler.py +1 -1
- mlrun/frameworks/lgbm/model_server.py +1 -1
- mlrun/frameworks/lgbm/utils.py +1 -1
- mlrun/frameworks/onnx/__init__.py +1 -1
- mlrun/frameworks/onnx/dataset.py +1 -1
- mlrun/frameworks/onnx/mlrun_interface.py +1 -1
- mlrun/frameworks/onnx/model_handler.py +1 -1
- mlrun/frameworks/onnx/model_server.py +1 -1
- mlrun/frameworks/parallel_coordinates.py +1 -1
- mlrun/frameworks/pytorch/__init__.py +1 -1
- mlrun/frameworks/pytorch/callbacks/__init__.py +1 -1
- mlrun/frameworks/pytorch/callbacks/callback.py +1 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +1 -1
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +1 -1
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +1 -1
- mlrun/frameworks/pytorch/callbacks_handler.py +1 -1
- mlrun/frameworks/pytorch/mlrun_interface.py +1 -1
- mlrun/frameworks/pytorch/model_handler.py +1 -1
- mlrun/frameworks/pytorch/model_server.py +1 -1
- mlrun/frameworks/pytorch/utils.py +1 -1
- mlrun/frameworks/sklearn/__init__.py +1 -1
- mlrun/frameworks/sklearn/estimator.py +1 -1
- mlrun/frameworks/sklearn/metric.py +1 -1
- mlrun/frameworks/sklearn/metrics_library.py +1 -1
- mlrun/frameworks/sklearn/mlrun_interface.py +1 -1
- mlrun/frameworks/sklearn/model_handler.py +1 -1
- mlrun/frameworks/sklearn/utils.py +1 -1
- mlrun/frameworks/tf_keras/__init__.py +1 -1
- mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -1
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +1 -1
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +1 -1
- mlrun/frameworks/tf_keras/mlrun_interface.py +1 -1
- mlrun/frameworks/tf_keras/model_handler.py +1 -1
- mlrun/frameworks/tf_keras/model_server.py +1 -1
- mlrun/frameworks/tf_keras/utils.py +1 -1
- mlrun/frameworks/xgboost/__init__.py +1 -1
- mlrun/frameworks/xgboost/mlrun_interface.py +1 -1
- mlrun/frameworks/xgboost/model_handler.py +1 -1
- mlrun/frameworks/xgboost/utils.py +1 -1
- mlrun/k8s_utils.py +14 -765
- mlrun/kfpops.py +14 -17
- mlrun/launcher/__init__.py +13 -0
- mlrun/launcher/base.py +406 -0
- mlrun/launcher/client.py +159 -0
- mlrun/launcher/factory.py +50 -0
- mlrun/launcher/local.py +276 -0
- mlrun/launcher/remote.py +178 -0
- mlrun/lists.py +10 -2
- mlrun/mlutils/__init__.py +1 -1
- mlrun/mlutils/data.py +1 -1
- mlrun/mlutils/models.py +1 -1
- mlrun/mlutils/plots.py +1 -1
- mlrun/model.py +252 -14
- mlrun/model_monitoring/__init__.py +41 -0
- mlrun/model_monitoring/features_drift_table.py +1 -1
- mlrun/model_monitoring/helpers.py +123 -38
- mlrun/model_monitoring/model_endpoint.py +144 -0
- mlrun/model_monitoring/model_monitoring_batch.py +310 -259
- mlrun/model_monitoring/stores/__init__.py +106 -0
- mlrun/model_monitoring/stores/kv_model_endpoint_store.py +448 -0
- mlrun/model_monitoring/stores/model_endpoint_store.py +147 -0
- mlrun/model_monitoring/stores/models/__init__.py +23 -0
- mlrun/model_monitoring/stores/models/base.py +18 -0
- mlrun/model_monitoring/stores/models/mysql.py +100 -0
- mlrun/model_monitoring/stores/models/sqlite.py +98 -0
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +370 -0
- mlrun/model_monitoring/stream_processing_fs.py +239 -271
- mlrun/package/__init__.py +163 -0
- mlrun/package/context_handler.py +325 -0
- mlrun/package/errors.py +47 -0
- mlrun/package/packager.py +298 -0
- mlrun/{runtimes/package → package/packagers}/__init__.py +3 -1
- mlrun/package/packagers/default_packager.py +422 -0
- mlrun/package/packagers/numpy_packagers.py +612 -0
- mlrun/package/packagers/pandas_packagers.py +968 -0
- mlrun/package/packagers/python_standard_library_packagers.py +616 -0
- mlrun/package/packagers_manager.py +786 -0
- mlrun/package/utils/__init__.py +53 -0
- mlrun/package/utils/_archiver.py +226 -0
- mlrun/package/utils/_formatter.py +211 -0
- mlrun/package/utils/_pickler.py +234 -0
- mlrun/package/utils/_supported_format.py +71 -0
- mlrun/package/utils/log_hint_utils.py +93 -0
- mlrun/package/utils/type_hint_utils.py +298 -0
- mlrun/platforms/__init__.py +1 -1
- mlrun/platforms/iguazio.py +34 -2
- mlrun/platforms/other.py +1 -1
- mlrun/projects/__init__.py +1 -1
- mlrun/projects/operations.py +14 -9
- mlrun/projects/pipelines.py +31 -13
- mlrun/projects/project.py +762 -238
- mlrun/render.py +49 -19
- mlrun/run.py +57 -326
- mlrun/runtimes/__init__.py +3 -9
- mlrun/runtimes/base.py +247 -784
- mlrun/runtimes/constants.py +1 -1
- mlrun/runtimes/daskjob.py +45 -41
- mlrun/runtimes/funcdoc.py +43 -7
- mlrun/runtimes/function.py +66 -656
- mlrun/runtimes/function_reference.py +1 -1
- mlrun/runtimes/generators.py +1 -1
- mlrun/runtimes/kubejob.py +99 -116
- mlrun/runtimes/local.py +59 -66
- mlrun/runtimes/mpijob/__init__.py +1 -1
- mlrun/runtimes/mpijob/abstract.py +13 -15
- mlrun/runtimes/mpijob/v1.py +3 -1
- mlrun/runtimes/mpijob/v1alpha1.py +1 -1
- mlrun/runtimes/nuclio.py +1 -1
- mlrun/runtimes/pod.py +51 -26
- mlrun/runtimes/remotesparkjob.py +3 -1
- mlrun/runtimes/serving.py +12 -4
- mlrun/runtimes/sparkjob/__init__.py +1 -2
- mlrun/runtimes/sparkjob/abstract.py +44 -31
- mlrun/runtimes/sparkjob/spark3job.py +11 -9
- mlrun/runtimes/utils.py +61 -42
- mlrun/secrets.py +16 -18
- mlrun/serving/__init__.py +3 -2
- mlrun/serving/merger.py +1 -1
- mlrun/serving/remote.py +1 -1
- mlrun/serving/routers.py +39 -42
- mlrun/serving/server.py +23 -13
- mlrun/serving/serving_wrapper.py +1 -1
- mlrun/serving/states.py +172 -39
- mlrun/serving/utils.py +1 -1
- mlrun/serving/v1_serving.py +1 -1
- mlrun/serving/v2_serving.py +29 -21
- mlrun/utils/__init__.py +1 -2
- mlrun/utils/async_http.py +8 -1
- mlrun/utils/azure_vault.py +1 -1
- mlrun/utils/clones.py +2 -2
- mlrun/utils/condition_evaluator.py +65 -0
- mlrun/utils/db.py +52 -0
- mlrun/utils/helpers.py +188 -13
- mlrun/utils/http.py +89 -54
- mlrun/utils/logger.py +48 -8
- mlrun/utils/model_monitoring.py +132 -100
- mlrun/utils/notifications/__init__.py +1 -1
- mlrun/utils/notifications/notification/__init__.py +8 -6
- mlrun/utils/notifications/notification/base.py +20 -14
- mlrun/utils/notifications/notification/console.py +7 -4
- mlrun/utils/notifications/notification/git.py +36 -19
- mlrun/utils/notifications/notification/ipython.py +10 -8
- mlrun/utils/notifications/notification/slack.py +18 -13
- mlrun/utils/notifications/notification_pusher.py +377 -56
- mlrun/utils/regex.py +6 -1
- mlrun/utils/singleton.py +1 -1
- mlrun/utils/v3io_clients.py +1 -1
- mlrun/utils/vault.py +270 -269
- mlrun/utils/version/__init__.py +1 -1
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +1 -1
- {mlrun-1.3.3rc1.dist-info → mlrun-1.4.0.dist-info}/METADATA +16 -10
- mlrun-1.4.0.dist-info/RECORD +434 -0
- mlrun/api/api/endpoints/marketplace.py +0 -257
- mlrun/api/crud/marketplace.py +0 -221
- mlrun/api/crud/model_monitoring/model_endpoint_store.py +0 -847
- mlrun/api/db/filedb/db.py +0 -518
- mlrun/api/schemas/marketplace.py +0 -128
- mlrun/api/schemas/model_endpoints.py +0 -185
- mlrun/db/filedb.py +0 -891
- mlrun/feature_store/retrieval/online.py +0 -92
- mlrun/model_monitoring/constants.py +0 -67
- mlrun/runtimes/package/context_handler.py +0 -711
- mlrun/runtimes/sparkjob/spark2job.py +0 -59
- mlrun-1.3.3rc1.dist-info/RECORD +0 -381
- {mlrun-1.3.3rc1.dist-info → mlrun-1.4.0.dist-info}/LICENSE +0 -0
- {mlrun-1.3.3rc1.dist-info → mlrun-1.4.0.dist-info}/WHEEL +0 -0
- {mlrun-1.3.3rc1.dist-info → mlrun-1.4.0.dist-info}/entry_points.txt +0 -0
- {mlrun-1.3.3rc1.dist-info → mlrun-1.4.0.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023 Iguazio
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
import datetime
|
|
15
15
|
import getpass
|
|
16
16
|
import glob
|
|
17
|
+
import http
|
|
18
|
+
import importlib.util as imputil
|
|
17
19
|
import json
|
|
20
|
+
import os.path
|
|
18
21
|
import pathlib
|
|
19
22
|
import shutil
|
|
20
23
|
import tempfile
|
|
@@ -31,21 +34,25 @@ import git.exc
|
|
|
31
34
|
import inflection
|
|
32
35
|
import kfp
|
|
33
36
|
import nuclio
|
|
37
|
+
import requests
|
|
34
38
|
import yaml
|
|
39
|
+
from deprecated import deprecated
|
|
35
40
|
|
|
36
|
-
import mlrun.
|
|
41
|
+
import mlrun.common.model_monitoring as model_monitoring_constants
|
|
42
|
+
import mlrun.common.schemas
|
|
37
43
|
import mlrun.db
|
|
38
44
|
import mlrun.errors
|
|
45
|
+
import mlrun.runtimes
|
|
46
|
+
import mlrun.runtimes.pod
|
|
47
|
+
import mlrun.runtimes.utils
|
|
39
48
|
import mlrun.utils.regex
|
|
40
|
-
from mlrun.runtimes import RuntimeKinds
|
|
41
49
|
|
|
42
50
|
from ..artifacts import Artifact, ArtifactProducer, DatasetArtifact, ModelArtifact
|
|
43
51
|
from ..artifacts.manager import ArtifactManager, dict_to_artifact, extend_artifact_path
|
|
44
52
|
from ..datastore import store_manager
|
|
45
53
|
from ..features import Feature
|
|
46
|
-
from ..model import EntrypointParam, ModelObj
|
|
54
|
+
from ..model import EntrypointParam, ImageBuilder, ModelObj
|
|
47
55
|
from ..run import code_to_function, get_object, import_function, new_function
|
|
48
|
-
from ..runtimes.utils import add_code_metadata
|
|
49
56
|
from ..secrets import SecretsStore
|
|
50
57
|
from ..utils import (
|
|
51
58
|
is_ipython,
|
|
@@ -57,7 +64,6 @@ from ..utils import (
|
|
|
57
64
|
)
|
|
58
65
|
from ..utils.clones import clone_git, clone_tgz, clone_zip, get_repo_url
|
|
59
66
|
from ..utils.helpers import ensure_git_branch, resolve_git_reference_from_source
|
|
60
|
-
from ..utils.model_monitoring import set_project_model_monitoring_credentials
|
|
61
67
|
from ..utils.notifications import CustomNotificationPusher, NotificationTypes
|
|
62
68
|
from .operations import (
|
|
63
69
|
BuildStatus,
|
|
@@ -109,15 +115,20 @@ def new_project(
|
|
|
109
115
|
subpath: str = None,
|
|
110
116
|
save: bool = True,
|
|
111
117
|
overwrite: bool = False,
|
|
118
|
+
parameters: dict = None,
|
|
112
119
|
) -> "MlrunProject":
|
|
113
120
|
"""Create a new MLRun project, optionally load it from a yaml/zip/git template
|
|
114
121
|
|
|
122
|
+
A new project is created and returned, you can customize the project by placing a project_setup.py file
|
|
123
|
+
in the project root dir, it will be executed upon project creation or loading.
|
|
124
|
+
|
|
125
|
+
|
|
115
126
|
example::
|
|
116
127
|
|
|
117
|
-
# create a project with local and
|
|
128
|
+
# create a project with local and hub functions, a workflow, and an artifact
|
|
118
129
|
project = mlrun.new_project("myproj", "./", init_git=True, description="my new project")
|
|
119
130
|
project.set_function('prep_data.py', 'prep-data', image='mlrun/mlrun', handler='prep_data')
|
|
120
|
-
project.set_function('hub://
|
|
131
|
+
project.set_function('hub://auto-trainer', 'train')
|
|
121
132
|
project.set_artifact('data', Artifact(target_path=data_url))
|
|
122
133
|
project.set_workflow('main', "./myflow.py")
|
|
123
134
|
project.save()
|
|
@@ -135,6 +146,16 @@ def new_project(
|
|
|
135
146
|
project.run("main", watch=True)
|
|
136
147
|
|
|
137
148
|
|
|
149
|
+
example using project_setup.py to init the project objects::
|
|
150
|
+
|
|
151
|
+
def setup(project):
|
|
152
|
+
project.set_function('prep_data.py', 'prep-data', image='mlrun/mlrun', handler='prep_data')
|
|
153
|
+
project.set_function('hub://auto-trainer', 'train')
|
|
154
|
+
project.set_artifact('data', Artifact(target_path=data_url))
|
|
155
|
+
project.set_workflow('main', "./myflow.py")
|
|
156
|
+
return project
|
|
157
|
+
|
|
158
|
+
|
|
138
159
|
:param name: project name
|
|
139
160
|
:param context: project local directory path (default value = "./")
|
|
140
161
|
:param init_git: if True, will git init the context dir
|
|
@@ -147,6 +168,7 @@ def new_project(
|
|
|
147
168
|
:param save: whether to save the created project in the DB
|
|
148
169
|
:param overwrite: overwrite project using 'cascade' deletion strategy (deletes project resources)
|
|
149
170
|
if project with name exists
|
|
171
|
+
:param parameters: key/value pairs to add to the project.spec.params
|
|
150
172
|
|
|
151
173
|
:returns: project object
|
|
152
174
|
"""
|
|
@@ -154,6 +176,10 @@ def new_project(
|
|
|
154
176
|
name = _add_username_to_project_name_if_needed(name, user_project)
|
|
155
177
|
|
|
156
178
|
if from_template:
|
|
179
|
+
if subpath:
|
|
180
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
181
|
+
"Unsupported option, cannot use subpath argument with project templates"
|
|
182
|
+
)
|
|
157
183
|
if from_template.endswith(".yaml"):
|
|
158
184
|
project = _load_project_file(from_template, name, secrets)
|
|
159
185
|
elif from_template.startswith("git://"):
|
|
@@ -169,7 +195,13 @@ def new_project(
|
|
|
169
195
|
# Remove original owner name for avoiding possible conflicts
|
|
170
196
|
project.spec.owner = None
|
|
171
197
|
else:
|
|
172
|
-
project = MlrunProject(
|
|
198
|
+
project = MlrunProject.from_dict(
|
|
199
|
+
{
|
|
200
|
+
"metadata": {
|
|
201
|
+
"name": name,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
)
|
|
173
205
|
project.spec.context = context
|
|
174
206
|
project.spec.subpath = subpath or project.spec.subpath
|
|
175
207
|
|
|
@@ -182,14 +214,20 @@ def new_project(
|
|
|
182
214
|
project.spec.origin_url = url
|
|
183
215
|
if description:
|
|
184
216
|
project.spec.description = description
|
|
217
|
+
if parameters:
|
|
218
|
+
# Enable setting project parameters at load time, can be used to customize the project_setup
|
|
219
|
+
for key, val in parameters.items():
|
|
220
|
+
project.spec.params[key] = val
|
|
185
221
|
|
|
186
222
|
_set_as_current_default_project(project)
|
|
187
223
|
|
|
188
224
|
if save and mlrun.mlconf.dbpath:
|
|
189
225
|
if overwrite:
|
|
190
|
-
logger.info(
|
|
226
|
+
logger.info(
|
|
227
|
+
"Overwriting project (by deleting and then creating)", name=name
|
|
228
|
+
)
|
|
191
229
|
_delete_project_from_db(
|
|
192
|
-
name, secrets, mlrun.
|
|
230
|
+
name, secrets, mlrun.common.schemas.DeletionStrategy.cascade
|
|
193
231
|
)
|
|
194
232
|
|
|
195
233
|
try:
|
|
@@ -200,12 +238,17 @@ def new_project(
|
|
|
200
238
|
"Use overwrite=True to overwrite the existing project."
|
|
201
239
|
) from exc
|
|
202
240
|
logger.info(
|
|
203
|
-
|
|
241
|
+
"Created and saved project",
|
|
242
|
+
name=name,
|
|
204
243
|
from_template=from_template,
|
|
205
244
|
overwrite=overwrite,
|
|
206
245
|
context=context,
|
|
207
246
|
save=save,
|
|
208
247
|
)
|
|
248
|
+
|
|
249
|
+
# Hook for initializing the project using a project_setup script
|
|
250
|
+
project = project.setup(save and mlrun.mlconf.dbpath)
|
|
251
|
+
|
|
209
252
|
return project
|
|
210
253
|
|
|
211
254
|
|
|
@@ -220,10 +263,15 @@ def load_project(
|
|
|
220
263
|
user_project: bool = False,
|
|
221
264
|
save: bool = True,
|
|
222
265
|
sync_functions: bool = False,
|
|
266
|
+
parameters: dict = None,
|
|
223
267
|
) -> "MlrunProject":
|
|
224
268
|
"""Load an MLRun project from git or tar or dir
|
|
225
269
|
|
|
226
|
-
|
|
270
|
+
MLRun looks for a project.yaml file with project definition and objects in the project root path
|
|
271
|
+
and use it to initialize the project, in addition it runs the project_setup.py file (if it exists)
|
|
272
|
+
for further customization.
|
|
273
|
+
|
|
274
|
+
Usage example::
|
|
227
275
|
|
|
228
276
|
# Load the project and run the 'main' workflow.
|
|
229
277
|
# When using git as the url source the context directory must be an empty or
|
|
@@ -231,6 +279,21 @@ def load_project(
|
|
|
231
279
|
project = load_project("./demo_proj", "git://github.com/mlrun/project-demo.git")
|
|
232
280
|
project.run("main", arguments={'data': data_url})
|
|
233
281
|
|
|
282
|
+
|
|
283
|
+
project_setup.py example::
|
|
284
|
+
|
|
285
|
+
def setup(project):
|
|
286
|
+
train_function = project.set_function(
|
|
287
|
+
"src/trainer.py",
|
|
288
|
+
name="mpi-training",
|
|
289
|
+
kind="mpijob",
|
|
290
|
+
image="mlrun/ml-models",
|
|
291
|
+
)
|
|
292
|
+
# Set the number of replicas for the training from the project parameter
|
|
293
|
+
train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
|
|
294
|
+
return project
|
|
295
|
+
|
|
296
|
+
|
|
234
297
|
:param context: project local directory path (default value = "./")
|
|
235
298
|
:param url: name (in DB) or git or tar.gz or .zip sources archive path e.g.:
|
|
236
299
|
git://github.com/mlrun/demo-xgb-project.git
|
|
@@ -246,6 +309,7 @@ def load_project(
|
|
|
246
309
|
:param user_project: add the current user name to the project name (for db:// prefixes)
|
|
247
310
|
:param save: whether to save the created project and artifact in the DB
|
|
248
311
|
:param sync_functions: sync the project's functions into the project object (will be saved to the DB if save=True)
|
|
312
|
+
:param parameters: key/value pairs to add to the project.spec.params
|
|
249
313
|
|
|
250
314
|
:returns: project object
|
|
251
315
|
"""
|
|
@@ -271,13 +335,18 @@ def load_project(
|
|
|
271
335
|
clone_tgz(url, context, secrets, clone)
|
|
272
336
|
elif url.endswith(".zip"):
|
|
273
337
|
clone_zip(url, context, secrets, clone)
|
|
274
|
-
|
|
338
|
+
elif url.startswith("db://") or "://" not in url:
|
|
275
339
|
project = _load_project_from_db(url, secrets, user_project)
|
|
276
340
|
project.spec.context = context
|
|
277
341
|
if not path.isdir(context):
|
|
278
342
|
makedirs(context)
|
|
279
343
|
project.spec.subpath = subpath or project.spec.subpath
|
|
280
344
|
from_db = True
|
|
345
|
+
else:
|
|
346
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
347
|
+
"Unsupported url scheme, supported schemes are: git://, db:// or "
|
|
348
|
+
".zip/.tar.gz/.yaml file path (could be local or remote) or project name which will be loaded from DB"
|
|
349
|
+
)
|
|
281
350
|
|
|
282
351
|
if not repo:
|
|
283
352
|
repo, url = init_repo(context, url, init_git)
|
|
@@ -287,6 +356,12 @@ def load_project(
|
|
|
287
356
|
|
|
288
357
|
if not project.metadata.name:
|
|
289
358
|
raise ValueError("project name must be specified")
|
|
359
|
+
|
|
360
|
+
if parameters:
|
|
361
|
+
# Enable setting project parameters at load time, can be used to customize the project_setup
|
|
362
|
+
for key, val in parameters.items():
|
|
363
|
+
project.spec.params[key] = val
|
|
364
|
+
|
|
290
365
|
if not from_db:
|
|
291
366
|
project.spec.source = url or project.spec.source
|
|
292
367
|
project.spec.origin_url = url or project.spec.origin_url
|
|
@@ -301,14 +376,18 @@ def load_project(
|
|
|
301
376
|
except Exception:
|
|
302
377
|
pass
|
|
303
378
|
|
|
304
|
-
|
|
379
|
+
to_save = save and mlrun.mlconf.dbpath
|
|
380
|
+
if to_save:
|
|
305
381
|
project.save()
|
|
382
|
+
|
|
383
|
+
# Hook for initializing the project using a project_setup script
|
|
384
|
+
project = project.setup(to_save)
|
|
385
|
+
|
|
386
|
+
if to_save:
|
|
306
387
|
project.register_artifacts()
|
|
307
|
-
if sync_functions:
|
|
308
|
-
project.sync_functions(names=project.get_function_names(), save=True)
|
|
309
388
|
|
|
310
|
-
|
|
311
|
-
project.sync_functions(
|
|
389
|
+
if sync_functions:
|
|
390
|
+
project.sync_functions(save=to_save)
|
|
312
391
|
|
|
313
392
|
_set_as_current_default_project(project)
|
|
314
393
|
|
|
@@ -326,31 +405,55 @@ def get_or_create_project(
|
|
|
326
405
|
user_project: bool = False,
|
|
327
406
|
from_template: str = None,
|
|
328
407
|
save: bool = True,
|
|
408
|
+
parameters: dict = None,
|
|
329
409
|
) -> "MlrunProject":
|
|
330
410
|
"""Load a project from MLRun DB, or create/import if doesnt exist
|
|
331
411
|
|
|
332
|
-
|
|
412
|
+
MLRun looks for a project.yaml file with project definition and objects in the project root path
|
|
413
|
+
and use it to initialize the project, in addition it runs the project_setup.py file (if it exists)
|
|
414
|
+
for further customization.
|
|
415
|
+
|
|
416
|
+
Usage example::
|
|
333
417
|
|
|
334
418
|
# load project from the DB (if exist) or the source repo
|
|
335
419
|
project = get_or_create_project("myproj", "./", "git://github.com/mlrun/demo-xgb-project.git")
|
|
336
420
|
project.pull("development") # pull the latest code from git
|
|
337
421
|
project.run("main", arguments={'data': data_url}) # run the workflow "main"
|
|
338
422
|
|
|
423
|
+
|
|
424
|
+
project_setup.py example::
|
|
425
|
+
|
|
426
|
+
def setup(project):
|
|
427
|
+
train_function = project.set_function(
|
|
428
|
+
"src/trainer.py",
|
|
429
|
+
name="mpi-training",
|
|
430
|
+
kind="mpijob",
|
|
431
|
+
image="mlrun/ml-models",
|
|
432
|
+
)
|
|
433
|
+
# Set the number of replicas for the training from the project parameter
|
|
434
|
+
train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
|
|
435
|
+
return project
|
|
436
|
+
|
|
437
|
+
|
|
339
438
|
:param name: project name
|
|
340
439
|
:param context: project local directory path (default value = "./")
|
|
341
440
|
:param url: name (in DB) or git or tar.gz or .zip sources archive path e.g.:
|
|
342
441
|
git://github.com/mlrun/demo-xgb-project.git
|
|
343
442
|
http://mysite/archived-project.zip
|
|
344
443
|
:param secrets: key:secret dict or SecretsStore used to download sources
|
|
345
|
-
:param init_git: if True, will
|
|
444
|
+
:param init_git: if True, will execute `git init` on the context dir
|
|
346
445
|
:param subpath: project subpath (within the archive/context)
|
|
347
446
|
:param clone: if True, always clone (delete any existing content)
|
|
348
447
|
:param user_project: add the current username to the project name (for db:// prefixes)
|
|
349
448
|
:param from_template: path to project YAML file that will be used as from_template (for new projects)
|
|
350
449
|
:param save: whether to save the created project in the DB
|
|
450
|
+
:param parameters: key/value pairs to add to the project.spec.params
|
|
451
|
+
|
|
351
452
|
:returns: project object
|
|
352
453
|
"""
|
|
353
454
|
context = context or "./"
|
|
455
|
+
spec_path = path.join(context, subpath or "", "project.yaml")
|
|
456
|
+
load_from_path = url or path.isfile(spec_path)
|
|
354
457
|
try:
|
|
355
458
|
# load project from the DB.
|
|
356
459
|
# use `name` as `url` as we load the project from the DB
|
|
@@ -365,51 +468,107 @@ def get_or_create_project(
|
|
|
365
468
|
user_project=user_project,
|
|
366
469
|
# only loading project from db so no need to save it
|
|
367
470
|
save=False,
|
|
471
|
+
parameters=parameters,
|
|
368
472
|
)
|
|
369
|
-
logger.info(
|
|
473
|
+
logger.info("Project loaded successfully", project_name=name)
|
|
370
474
|
return project
|
|
371
475
|
|
|
372
476
|
except mlrun.errors.MLRunNotFoundError:
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
477
|
+
logger.debug("Project not found in db", project_name=name)
|
|
478
|
+
|
|
479
|
+
# do not nest under "try" or else the exceptions raised below will be logged along with the "not found" message
|
|
480
|
+
if load_from_path:
|
|
481
|
+
# loads a project from archive or local project.yaml
|
|
482
|
+
logger.info("Loading project from path", project_name=name, path=url or context)
|
|
483
|
+
project = load_project(
|
|
484
|
+
context,
|
|
485
|
+
url,
|
|
486
|
+
name,
|
|
487
|
+
secrets=secrets,
|
|
488
|
+
init_git=init_git,
|
|
489
|
+
subpath=subpath,
|
|
490
|
+
clone=clone,
|
|
491
|
+
user_project=user_project,
|
|
492
|
+
save=save,
|
|
493
|
+
parameters=parameters,
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
logger.info(
|
|
497
|
+
"Project loaded successfully",
|
|
498
|
+
project_name=name,
|
|
499
|
+
path=url or context,
|
|
500
|
+
stored_in_db=save,
|
|
501
|
+
)
|
|
502
|
+
return project
|
|
503
|
+
|
|
504
|
+
# create a new project
|
|
505
|
+
project = new_project(
|
|
506
|
+
name,
|
|
507
|
+
context,
|
|
508
|
+
init_git=init_git,
|
|
509
|
+
user_project=user_project,
|
|
510
|
+
from_template=from_template,
|
|
511
|
+
secrets=secrets,
|
|
512
|
+
subpath=subpath,
|
|
513
|
+
save=save,
|
|
514
|
+
parameters=parameters,
|
|
515
|
+
)
|
|
516
|
+
logger.info("Project created successfully", project_name=name, stored_in_db=save)
|
|
517
|
+
return project
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _run_project_setup(
|
|
521
|
+
project: "MlrunProject", setup_file_path: str, save: bool = False
|
|
522
|
+
):
|
|
523
|
+
"""Run the project setup file if found
|
|
524
|
+
|
|
525
|
+
When loading a project MLRun will look for a project_setup.py file, if it is found
|
|
526
|
+
it will execute the setup(project) handler, which can enrich the project with additional
|
|
527
|
+
objects, functions, artifacts, etc.
|
|
528
|
+
|
|
529
|
+
Example::
|
|
530
|
+
|
|
531
|
+
def setup(project):
|
|
532
|
+
train_function = project.set_function(
|
|
533
|
+
"src/trainer.py",
|
|
534
|
+
name="mpi-training",
|
|
535
|
+
kind="mpijob",
|
|
536
|
+
image="mlrun/ml-models",
|
|
402
537
|
)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
538
|
+
# Set the number of replicas for the training from the project parameter
|
|
539
|
+
train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
|
|
540
|
+
return project
|
|
541
|
+
|
|
542
|
+
"""
|
|
543
|
+
if not path.exists(setup_file_path):
|
|
407
544
|
return project
|
|
545
|
+
spec = imputil.spec_from_file_location("workflow", setup_file_path)
|
|
546
|
+
if spec is None:
|
|
547
|
+
raise ImportError(f"cannot import project setup file in {setup_file_path}")
|
|
548
|
+
mod = imputil.module_from_spec(spec)
|
|
549
|
+
spec.loader.exec_module(mod)
|
|
550
|
+
|
|
551
|
+
if hasattr(mod, "setup"):
|
|
552
|
+
try:
|
|
553
|
+
project = getattr(mod, "setup")(project)
|
|
554
|
+
except Exception as exc:
|
|
555
|
+
logger.error(
|
|
556
|
+
"Failed to run project_setup script",
|
|
557
|
+
setup_file_path=setup_file_path,
|
|
558
|
+
exc=mlrun.errors.err_to_str(exc),
|
|
559
|
+
)
|
|
560
|
+
raise exc
|
|
561
|
+
if save:
|
|
562
|
+
project.save()
|
|
563
|
+
else:
|
|
564
|
+
logger.warn("skipping setup, setup() handler was not found in project_setup.py")
|
|
565
|
+
return project
|
|
408
566
|
|
|
409
567
|
|
|
410
568
|
def _load_project_dir(context, name="", subpath=""):
|
|
411
569
|
subpath_str = subpath or ""
|
|
412
570
|
fpath = path.join(context, subpath_str, "project.yaml")
|
|
571
|
+
setup_file_path = path.join(context, subpath_str, "project_setup.py")
|
|
413
572
|
if path.isfile(fpath):
|
|
414
573
|
with open(fpath) as fp:
|
|
415
574
|
data = fp.read()
|
|
@@ -419,10 +578,19 @@ def _load_project_dir(context, name="", subpath=""):
|
|
|
419
578
|
|
|
420
579
|
elif path.isfile(path.join(context, subpath_str, "function.yaml")):
|
|
421
580
|
func = import_function(path.join(context, subpath_str, "function.yaml"))
|
|
422
|
-
project = MlrunProject(
|
|
423
|
-
|
|
424
|
-
|
|
581
|
+
project = MlrunProject.from_dict(
|
|
582
|
+
{
|
|
583
|
+
"metadata": {
|
|
584
|
+
"name": func.metadata.project,
|
|
585
|
+
},
|
|
586
|
+
"spec": {
|
|
587
|
+
"functions": [{"url": "function.yaml", "name": func.metadata.name}],
|
|
588
|
+
},
|
|
589
|
+
}
|
|
425
590
|
)
|
|
591
|
+
elif path.exists(setup_file_path):
|
|
592
|
+
# If there is a setup script do not force having project.yaml file
|
|
593
|
+
project = MlrunProject()
|
|
426
594
|
else:
|
|
427
595
|
raise mlrun.errors.MLRunNotFoundError(
|
|
428
596
|
"project or function YAML not found in path"
|
|
@@ -526,11 +694,13 @@ class ProjectSpec(ModelObj):
|
|
|
526
694
|
goals=None,
|
|
527
695
|
load_source_on_run=None,
|
|
528
696
|
default_requirements: typing.Union[str, typing.List[str]] = None,
|
|
529
|
-
desired_state=mlrun.
|
|
697
|
+
desired_state=mlrun.common.schemas.ProjectState.online.value,
|
|
530
698
|
owner=None,
|
|
531
699
|
disable_auto_mount=None,
|
|
532
700
|
workdir=None,
|
|
533
701
|
default_image=None,
|
|
702
|
+
build=None,
|
|
703
|
+
custom_packagers: typing.List[typing.Tuple[str, bool]] = None,
|
|
534
704
|
):
|
|
535
705
|
self.repo = None
|
|
536
706
|
|
|
@@ -564,6 +734,13 @@ class ProjectSpec(ModelObj):
|
|
|
564
734
|
self.disable_auto_mount = disable_auto_mount
|
|
565
735
|
self.default_image = default_image
|
|
566
736
|
|
|
737
|
+
self.build = build
|
|
738
|
+
|
|
739
|
+
# A list of custom packagers to include when running the functions of the project. A custom packager is stored
|
|
740
|
+
# in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
|
|
741
|
+
# whether it is mandatory for a run (raise exception on collection error) or not.
|
|
742
|
+
self.custom_packagers = custom_packagers or []
|
|
743
|
+
|
|
567
744
|
@property
|
|
568
745
|
def source(self) -> str:
|
|
569
746
|
"""source url or git repo"""
|
|
@@ -573,8 +750,6 @@ class ProjectSpec(ModelObj):
|
|
|
573
750
|
if url:
|
|
574
751
|
self._source = url
|
|
575
752
|
|
|
576
|
-
if self._source in [".", "./"]:
|
|
577
|
-
return path.abspath(self.context)
|
|
578
753
|
return self._source
|
|
579
754
|
|
|
580
755
|
@source.setter
|
|
@@ -730,6 +905,54 @@ class ProjectSpec(ModelObj):
|
|
|
730
905
|
if key in self._artifacts:
|
|
731
906
|
del self._artifacts[key]
|
|
732
907
|
|
|
908
|
+
@property
|
|
909
|
+
def build(self) -> ImageBuilder:
|
|
910
|
+
return self._build
|
|
911
|
+
|
|
912
|
+
@build.setter
|
|
913
|
+
def build(self, build):
|
|
914
|
+
self._build = self._verify_dict(build, "build", ImageBuilder)
|
|
915
|
+
|
|
916
|
+
def add_custom_packager(self, packager: str, is_mandatory: bool):
|
|
917
|
+
"""
|
|
918
|
+
Add a custom packager from the custom packagers list.
|
|
919
|
+
|
|
920
|
+
:param packager: The packager module path to add. For example, if a packager `MyPackager` is in the
|
|
921
|
+
project's source at my_module.py, then the module path is: "my_module.MyPackager".
|
|
922
|
+
:param is_mandatory: Whether this packager must be collected during a run. If False, failing to collect it won't
|
|
923
|
+
raise an error during the packagers collection phase.
|
|
924
|
+
"""
|
|
925
|
+
# TODO: enable importing packagers from the hub.
|
|
926
|
+
if packager in [
|
|
927
|
+
custom_packager[0] for custom_packager in self.custom_packagers
|
|
928
|
+
]:
|
|
929
|
+
logger.warn(
|
|
930
|
+
f"The packager's module path '{packager}' is already registered in the project."
|
|
931
|
+
)
|
|
932
|
+
return
|
|
933
|
+
self.custom_packagers.append((packager, is_mandatory))
|
|
934
|
+
|
|
935
|
+
def remove_custom_packager(self, packager: str):
|
|
936
|
+
"""
|
|
937
|
+
Remove a custom packager from the custom packagers list.
|
|
938
|
+
|
|
939
|
+
:param packager: The packager module path to remove.
|
|
940
|
+
|
|
941
|
+
:raise MLRunInvalidArgumentError: In case the packager was not in the list.
|
|
942
|
+
"""
|
|
943
|
+
# Look for the packager tuple in the list to remove it:
|
|
944
|
+
packager_tuple: typing.Tuple[str, bool] = None
|
|
945
|
+
for custom_packager in self.custom_packagers:
|
|
946
|
+
if custom_packager[0] == packager:
|
|
947
|
+
packager_tuple = custom_packager
|
|
948
|
+
|
|
949
|
+
# If not found, raise an error, otherwise remove:
|
|
950
|
+
if packager_tuple is None:
|
|
951
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
952
|
+
f"The packager module path '{packager}' is not registered in the project, hence it cannot be removed."
|
|
953
|
+
)
|
|
954
|
+
self.custom_packagers.remove(packager_tuple)
|
|
955
|
+
|
|
733
956
|
def _source_repo(self):
|
|
734
957
|
src = self.source
|
|
735
958
|
if src:
|
|
@@ -769,6 +992,7 @@ class MlrunProject(ModelObj):
|
|
|
769
992
|
|
|
770
993
|
def __init__(
|
|
771
994
|
self,
|
|
995
|
+
# TODO: remove all arguments except metadata and spec in 1.6.0
|
|
772
996
|
name=None,
|
|
773
997
|
description=None,
|
|
774
998
|
params=None,
|
|
@@ -777,7 +1001,7 @@ class MlrunProject(ModelObj):
|
|
|
777
1001
|
artifacts=None,
|
|
778
1002
|
artifact_path=None,
|
|
779
1003
|
conda=None,
|
|
780
|
-
# all except these
|
|
1004
|
+
# all except these metadata and spec are for backwards compatibility with MlrunProjectLegacy
|
|
781
1005
|
metadata=None,
|
|
782
1006
|
spec=None,
|
|
783
1007
|
default_requirements: typing.Union[str, typing.List[str]] = None,
|
|
@@ -789,6 +1013,26 @@ class MlrunProject(ModelObj):
|
|
|
789
1013
|
self._status = None
|
|
790
1014
|
self.status = None
|
|
791
1015
|
|
|
1016
|
+
if any(
|
|
1017
|
+
[
|
|
1018
|
+
name,
|
|
1019
|
+
description,
|
|
1020
|
+
params,
|
|
1021
|
+
functions,
|
|
1022
|
+
workflows,
|
|
1023
|
+
artifacts,
|
|
1024
|
+
artifact_path,
|
|
1025
|
+
conda,
|
|
1026
|
+
default_requirements,
|
|
1027
|
+
]
|
|
1028
|
+
):
|
|
1029
|
+
# TODO: remove in 1.6.0 along with all arguments except metadata and spec
|
|
1030
|
+
warnings.warn(
|
|
1031
|
+
"Project constructor arguments are deprecated in 1.4.0 and will be removed in 1.6.0,"
|
|
1032
|
+
" use metadata and spec instead",
|
|
1033
|
+
FutureWarning,
|
|
1034
|
+
)
|
|
1035
|
+
|
|
792
1036
|
# Handling the fields given in the legacy way
|
|
793
1037
|
self.metadata.name = name or self.metadata.name
|
|
794
1038
|
self.spec.description = description or self.spec.description
|
|
@@ -866,20 +1110,28 @@ class MlrunProject(ModelObj):
|
|
|
866
1110
|
def source(self, source):
|
|
867
1111
|
self.spec.source = source
|
|
868
1112
|
|
|
869
|
-
def set_source(
|
|
1113
|
+
def set_source(
|
|
1114
|
+
self,
|
|
1115
|
+
source: str = "",
|
|
1116
|
+
pull_at_runtime: bool = False,
|
|
1117
|
+
workdir: Optional[str] = None,
|
|
1118
|
+
):
|
|
870
1119
|
"""set the project source code path(can be git/tar/zip archive)
|
|
871
1120
|
|
|
872
|
-
:param source:
|
|
873
|
-
|
|
874
|
-
|
|
1121
|
+
:param source: valid absolute path or URL to git, zip, or tar file, (or None for current) e.g.
|
|
1122
|
+
git://github.com/mlrun/something.git
|
|
1123
|
+
http://some/url/file.zip
|
|
1124
|
+
note path source must exist on the image or exist locally when run is local
|
|
1125
|
+
(it is recommended to use 'workdir' when source is a filepath instead)
|
|
875
1126
|
:param pull_at_runtime: load the archive into the container at job runtime vs on build/deploy
|
|
876
|
-
:param workdir:
|
|
1127
|
+
:param workdir: workdir path relative to the context dir or absolute
|
|
877
1128
|
"""
|
|
1129
|
+
mlrun.utils.helpers.validate_builder_source(source, pull_at_runtime, workdir)
|
|
1130
|
+
|
|
878
1131
|
self.spec.load_source_on_run = pull_at_runtime
|
|
879
1132
|
self.spec.source = source or self.spec.source
|
|
880
1133
|
|
|
881
1134
|
if self.spec.source.startswith("git://"):
|
|
882
|
-
|
|
883
1135
|
source, reference, branch = resolve_git_reference_from_source(source)
|
|
884
1136
|
if not branch and not reference:
|
|
885
1137
|
logger.warn(
|
|
@@ -892,20 +1144,23 @@ class MlrunProject(ModelObj):
|
|
|
892
1144
|
self.sync_functions()
|
|
893
1145
|
|
|
894
1146
|
def get_artifact_uri(
|
|
895
|
-
self, key: str, category: str = "artifact", tag: str = None
|
|
1147
|
+
self, key: str, category: str = "artifact", tag: str = None, iter: int = None
|
|
896
1148
|
) -> str:
|
|
897
1149
|
"""return the project artifact uri (store://..) from the artifact key
|
|
898
1150
|
|
|
899
1151
|
example::
|
|
900
1152
|
|
|
901
|
-
uri = project.get_artifact_uri("my_model", category="model", tag="prod")
|
|
1153
|
+
uri = project.get_artifact_uri("my_model", category="model", tag="prod", iter=0)
|
|
902
1154
|
|
|
903
1155
|
:param key: artifact key/name
|
|
904
1156
|
:param category: artifact category (artifact, model, feature-vector, ..)
|
|
905
1157
|
:param tag: artifact version tag, default to latest version
|
|
1158
|
+
:param iter: iteration number, default to no iteration
|
|
906
1159
|
"""
|
|
907
1160
|
uri = f"store://{category}s/{self.metadata.name}/{key}"
|
|
908
|
-
if
|
|
1161
|
+
if iter is not None:
|
|
1162
|
+
uri = f"{uri}#{iter}"
|
|
1163
|
+
if tag is not None:
|
|
909
1164
|
uri = f"{uri}:{tag}"
|
|
910
1165
|
return uri
|
|
911
1166
|
|
|
@@ -989,7 +1244,7 @@ class MlrunProject(ModelObj):
|
|
|
989
1244
|
engine=None,
|
|
990
1245
|
args_schema: typing.List[EntrypointParam] = None,
|
|
991
1246
|
handler=None,
|
|
992
|
-
schedule: typing.Union[str, mlrun.
|
|
1247
|
+
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
993
1248
|
ttl=None,
|
|
994
1249
|
**args,
|
|
995
1250
|
):
|
|
@@ -1008,11 +1263,24 @@ class MlrunProject(ModelObj):
|
|
|
1008
1263
|
:param ttl: pipeline ttl in secs (after that the pods will be removed)
|
|
1009
1264
|
:param args: argument values (key=value, ..)
|
|
1010
1265
|
"""
|
|
1011
|
-
|
|
1012
|
-
|
|
1266
|
+
|
|
1267
|
+
# validate the provided workflow_path
|
|
1268
|
+
if mlrun.utils.helpers.is_file_path_invalid(
|
|
1269
|
+
self.spec.get_code_path(), workflow_path
|
|
1270
|
+
):
|
|
1271
|
+
raise ValueError(
|
|
1272
|
+
f"Invalid 'workflow_path': '{workflow_path}'. Please provide a valid URL/path to a file."
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1013
1275
|
if embed:
|
|
1014
|
-
if
|
|
1015
|
-
|
|
1276
|
+
if (
|
|
1277
|
+
self.context
|
|
1278
|
+
and not workflow_path.startswith("/")
|
|
1279
|
+
# since the user may provide a path the includes the context,
|
|
1280
|
+
# we need to make sure we don't add it twice
|
|
1281
|
+
and not workflow_path.startswith(self.context)
|
|
1282
|
+
):
|
|
1283
|
+
workflow_path = path.join(self.context, workflow_path)
|
|
1016
1284
|
with open(workflow_path, "r") as fp:
|
|
1017
1285
|
txt = fp.read()
|
|
1018
1286
|
workflow = {"name": name, "code": txt}
|
|
@@ -1087,11 +1355,13 @@ class MlrunProject(ModelObj):
|
|
|
1087
1355
|
artifact_path = mlrun.utils.helpers.fill_artifact_path_template(
|
|
1088
1356
|
self.spec.artifact_path or mlrun.mlconf.artifact_path, self.metadata.name
|
|
1089
1357
|
)
|
|
1358
|
+
# TODO: To correctly maintain the list of artifacts from an exported project,
|
|
1359
|
+
# we need to maintain the different trees that generated them
|
|
1090
1360
|
producer = ArtifactProducer(
|
|
1091
1361
|
"project",
|
|
1092
1362
|
self.metadata.name,
|
|
1093
1363
|
self.metadata.name,
|
|
1094
|
-
tag=self._get_hexsha() or
|
|
1364
|
+
tag=self._get_hexsha() or str(uuid.uuid4()),
|
|
1095
1365
|
)
|
|
1096
1366
|
for artifact_dict in self.spec.artifacts:
|
|
1097
1367
|
if _is_imported_artifact(artifact_dict):
|
|
@@ -1436,12 +1706,15 @@ class MlrunProject(ModelObj):
|
|
|
1436
1706
|
with open(f"{temp_dir}/_body", "rb") as fp:
|
|
1437
1707
|
artifact.spec._body = fp.read()
|
|
1438
1708
|
artifact.target_path = ""
|
|
1709
|
+
|
|
1710
|
+
# if the dataitem is not a file, it means we downloaded it from a remote source to a temp file,
|
|
1711
|
+
# so we need to remove it after we're done with it
|
|
1712
|
+
dataitem.remove_local()
|
|
1713
|
+
|
|
1439
1714
|
return self.log_artifact(
|
|
1440
1715
|
artifact, local_path=temp_dir, artifact_path=artifact_path
|
|
1441
1716
|
)
|
|
1442
1717
|
|
|
1443
|
-
if dataitem.kind != "file":
|
|
1444
|
-
remove(item_file)
|
|
1445
1718
|
else:
|
|
1446
1719
|
raise ValueError("unsupported file suffix, use .yaml, .json, or .zip")
|
|
1447
1720
|
|
|
@@ -1471,6 +1744,21 @@ class MlrunProject(ModelObj):
|
|
|
1471
1744
|
self.__dict__.update(project.__dict__)
|
|
1472
1745
|
return project
|
|
1473
1746
|
|
|
1747
|
+
def setup(self, save: bool = True) -> "MlrunProject":
|
|
1748
|
+
"""Run the project setup file if found
|
|
1749
|
+
|
|
1750
|
+
When loading a project MLRun will look for a project_setup.py file, if it is found
|
|
1751
|
+
it will execute the setup(project) handler, which can enrich the project with additional
|
|
1752
|
+
objects, functions, artifacts, etc.
|
|
1753
|
+
|
|
1754
|
+
:param save: save the project after the setup
|
|
1755
|
+
"""
|
|
1756
|
+
# Hook for initializing the project using a project_setup script
|
|
1757
|
+
setup_file_path = path.join(
|
|
1758
|
+
self.context, self.spec.subpath or "", "project_setup.py"
|
|
1759
|
+
)
|
|
1760
|
+
return _run_project_setup(self, setup_file_path, save)
|
|
1761
|
+
|
|
1474
1762
|
def set_function(
|
|
1475
1763
|
self,
|
|
1476
1764
|
func: typing.Union[str, mlrun.runtimes.BaseRuntime] = None,
|
|
@@ -1481,6 +1769,7 @@ class MlrunProject(ModelObj):
|
|
|
1481
1769
|
with_repo: bool = None,
|
|
1482
1770
|
tag: str = None,
|
|
1483
1771
|
requirements: typing.Union[str, typing.List[str]] = None,
|
|
1772
|
+
requirements_file: str = "",
|
|
1484
1773
|
) -> mlrun.runtimes.BaseRuntime:
|
|
1485
1774
|
"""update or add a function object to the project
|
|
1486
1775
|
|
|
@@ -1489,7 +1778,7 @@ class MlrunProject(ModelObj):
|
|
|
1489
1778
|
|
|
1490
1779
|
object (s3://, v3io://, ..)
|
|
1491
1780
|
MLRun DB e.g. db://project/func:ver
|
|
1492
|
-
functions hub/market: e.g. hub://
|
|
1781
|
+
functions hub/market: e.g. hub://auto-trainer:master
|
|
1493
1782
|
|
|
1494
1783
|
examples::
|
|
1495
1784
|
|
|
@@ -1508,16 +1797,20 @@ class MlrunProject(ModelObj):
|
|
|
1508
1797
|
# by providing a path to a pip requirements file
|
|
1509
1798
|
proj.set_function('my.py', requirements="requirements.txt")
|
|
1510
1799
|
|
|
1511
|
-
:param func:
|
|
1512
|
-
:param name:
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
:param
|
|
1519
|
-
:param
|
|
1520
|
-
:param
|
|
1800
|
+
:param func: function object or spec/code url, None refers to current Notebook
|
|
1801
|
+
:param name: name of the function (under the project), can be specified with a tag to support
|
|
1802
|
+
versions (e.g. myfunc:v1)
|
|
1803
|
+
:param kind: runtime kind e.g. job, nuclio, spark, dask, mpijob
|
|
1804
|
+
default: job
|
|
1805
|
+
:param image: docker image to be used, can also be specified in
|
|
1806
|
+
the function object/yaml
|
|
1807
|
+
:param handler: default function handler to invoke (can only be set with .py/.ipynb files)
|
|
1808
|
+
:param with_repo: add (clone) the current repo to the build source
|
|
1809
|
+
:param tag: function version tag (none for 'latest', can only be set with .py/.ipynb files)
|
|
1810
|
+
if tag is specified and name is empty, the function key (under the project)
|
|
1811
|
+
will be enriched with the tag value. (i.e. 'function-name:tag')
|
|
1812
|
+
:param requirements: a list of python packages
|
|
1813
|
+
:param requirements_file: path to a python requirements file
|
|
1521
1814
|
|
|
1522
1815
|
:returns: project object
|
|
1523
1816
|
"""
|
|
@@ -1536,6 +1829,7 @@ class MlrunProject(ModelObj):
|
|
|
1536
1829
|
func = path.relpath(func, self.spec.context)
|
|
1537
1830
|
|
|
1538
1831
|
func = func or ""
|
|
1832
|
+
name = mlrun.utils.normalize_name(name) if name else name
|
|
1539
1833
|
if isinstance(func, str):
|
|
1540
1834
|
# in hub or db functions name defaults to the function name
|
|
1541
1835
|
if not name and not (func.startswith("db://") or func.startswith("hub://")):
|
|
@@ -1551,10 +1845,14 @@ class MlrunProject(ModelObj):
|
|
|
1551
1845
|
"requirements": requirements,
|
|
1552
1846
|
}
|
|
1553
1847
|
func = {k: v for k, v in function_dict.items() if v}
|
|
1554
|
-
|
|
1555
|
-
|
|
1848
|
+
resolved_function_name, function_object = _init_function_from_dict(
|
|
1849
|
+
func, self
|
|
1850
|
+
)
|
|
1851
|
+
func["name"] = resolved_function_name
|
|
1556
1852
|
elif hasattr(func, "to_dict"):
|
|
1557
|
-
|
|
1853
|
+
resolved_function_name, function_object = _init_function_from_obj(
|
|
1854
|
+
func, self, name=name
|
|
1855
|
+
)
|
|
1558
1856
|
if handler:
|
|
1559
1857
|
raise ValueError(
|
|
1560
1858
|
"default handler cannot be set for existing function object"
|
|
@@ -1562,15 +1860,23 @@ class MlrunProject(ModelObj):
|
|
|
1562
1860
|
if image:
|
|
1563
1861
|
function_object.spec.image = image
|
|
1564
1862
|
if with_repo:
|
|
1863
|
+
# mark source to be enriched before run with project source (enrich_function_object)
|
|
1565
1864
|
function_object.spec.build.source = "./"
|
|
1566
1865
|
if requirements:
|
|
1567
|
-
function_object.with_requirements(
|
|
1568
|
-
|
|
1866
|
+
function_object.with_requirements(
|
|
1867
|
+
requirements, requirements_file=requirements_file
|
|
1868
|
+
)
|
|
1869
|
+
if not resolved_function_name:
|
|
1569
1870
|
raise ValueError("function name must be specified")
|
|
1570
1871
|
else:
|
|
1571
1872
|
raise ValueError("func must be a function url or object")
|
|
1572
1873
|
|
|
1573
|
-
|
|
1874
|
+
# if function name was not explicitly provided,
|
|
1875
|
+
# we use the resolved name (from the function object) and add the tag
|
|
1876
|
+
if tag and not name and ":" not in resolved_function_name:
|
|
1877
|
+
resolved_function_name = f"{resolved_function_name}:{tag}"
|
|
1878
|
+
|
|
1879
|
+
self.spec.set_function(resolved_function_name, function_object, func)
|
|
1574
1880
|
return function_object
|
|
1575
1881
|
|
|
1576
1882
|
def remove_function(self, name):
|
|
@@ -1587,33 +1893,65 @@ class MlrunProject(ModelObj):
|
|
|
1587
1893
|
enrich=False,
|
|
1588
1894
|
ignore_cache=False,
|
|
1589
1895
|
copy_function=True,
|
|
1896
|
+
tag: str = "",
|
|
1590
1897
|
) -> mlrun.runtimes.BaseRuntime:
|
|
1591
1898
|
"""get function object by name
|
|
1592
1899
|
|
|
1593
|
-
:param key:
|
|
1594
|
-
:param sync:
|
|
1595
|
-
:param enrich:
|
|
1596
|
-
:param ignore_cache:
|
|
1597
|
-
:param copy_function:
|
|
1900
|
+
:param key: name of key for search
|
|
1901
|
+
:param sync: will reload/reinit the function from the project spec
|
|
1902
|
+
:param enrich: add project info/config/source info to the function object
|
|
1903
|
+
:param ignore_cache: read the function object from the DB (ignore the local cache)
|
|
1904
|
+
:param copy_function: return a copy of the function object
|
|
1905
|
+
:param tag: provide if the function key is tagged under the project (function was set with a tag)
|
|
1598
1906
|
|
|
1599
1907
|
:returns: function object
|
|
1600
1908
|
"""
|
|
1601
|
-
if
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
self.
|
|
1909
|
+
if tag and ":" not in key:
|
|
1910
|
+
key = f"{key}:{tag}"
|
|
1911
|
+
|
|
1912
|
+
function, err = self._get_function(
|
|
1913
|
+
mlrun.utils.normalize_name(key), sync, ignore_cache
|
|
1914
|
+
)
|
|
1915
|
+
if not function and "_" in key:
|
|
1916
|
+
function, err = self._get_function(key, sync, ignore_cache)
|
|
1917
|
+
|
|
1918
|
+
if not function:
|
|
1919
|
+
raise err
|
|
1920
|
+
|
|
1609
1921
|
if enrich:
|
|
1610
1922
|
function = enrich_function_object(
|
|
1611
1923
|
self, function, copy_function=copy_function
|
|
1612
1924
|
)
|
|
1613
1925
|
self.spec._function_objects[key] = function
|
|
1926
|
+
|
|
1614
1927
|
return function
|
|
1615
1928
|
|
|
1616
|
-
def
|
|
1929
|
+
def _get_function(self, key, sync, ignore_cache):
|
|
1930
|
+
"""
|
|
1931
|
+
Function can be retrieved from the project spec (cache) or from the database.
|
|
1932
|
+
In sync mode, we first perform a sync of the function_objects from the function_definitions,
|
|
1933
|
+
and then returning it from the function_objects (if exists).
|
|
1934
|
+
When not in sync mode, we verify and return from the function objects directly.
|
|
1935
|
+
In ignore_cache mode, we query the function from the database rather than from the project spec.
|
|
1936
|
+
"""
|
|
1937
|
+
if key in self.spec._function_objects and not sync and not ignore_cache:
|
|
1938
|
+
function = self.spec._function_objects[key]
|
|
1939
|
+
|
|
1940
|
+
elif key in self.spec._function_definitions and not ignore_cache:
|
|
1941
|
+
self.sync_functions([key])
|
|
1942
|
+
function = self.spec._function_objects[key]
|
|
1943
|
+
else:
|
|
1944
|
+
try:
|
|
1945
|
+
function = get_db_function(self, key)
|
|
1946
|
+
self.spec._function_objects[key] = function
|
|
1947
|
+
except requests.HTTPError as exc:
|
|
1948
|
+
if exc.response.status_code != http.HTTPStatus.NOT_FOUND.value:
|
|
1949
|
+
raise exc
|
|
1950
|
+
return None, exc
|
|
1951
|
+
|
|
1952
|
+
return function, None
|
|
1953
|
+
|
|
1954
|
+
def get_function_objects(self) -> FunctionsDict:
|
|
1617
1955
|
""" "get a virtual dict with all the project functions ready for use in a pipeline"""
|
|
1618
1956
|
self.sync_functions()
|
|
1619
1957
|
return FunctionsDict(self)
|
|
@@ -1712,7 +2050,7 @@ class MlrunProject(ModelObj):
|
|
|
1712
2050
|
if not names:
|
|
1713
2051
|
names = self.spec._function_definitions.keys()
|
|
1714
2052
|
funcs = {}
|
|
1715
|
-
origin = add_code_metadata(self.spec.context)
|
|
2053
|
+
origin = mlrun.runtimes.utils.add_code_metadata(self.spec.context)
|
|
1716
2054
|
for name in names:
|
|
1717
2055
|
f = self.spec._function_definitions.get(name)
|
|
1718
2056
|
if not f:
|
|
@@ -1779,7 +2117,7 @@ class MlrunProject(ModelObj):
|
|
|
1779
2117
|
self,
|
|
1780
2118
|
secrets: dict = None,
|
|
1781
2119
|
file_path: str = None,
|
|
1782
|
-
provider: typing.Union[str, mlrun.
|
|
2120
|
+
provider: typing.Union[str, mlrun.common.schemas.SecretProviderName] = None,
|
|
1783
2121
|
):
|
|
1784
2122
|
"""set project secrets from dict or secrets env file
|
|
1785
2123
|
when using a secrets file it should have lines in the form KEY=VALUE, comment line start with "#"
|
|
@@ -1806,18 +2144,21 @@ class MlrunProject(ModelObj):
|
|
|
1806
2144
|
"must specify secrets OR file_path"
|
|
1807
2145
|
)
|
|
1808
2146
|
if file_path:
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
2147
|
+
if path.isfile(file_path):
|
|
2148
|
+
secrets = dotenv.dotenv_values(file_path)
|
|
2149
|
+
if None in secrets.values():
|
|
2150
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
2151
|
+
"env file lines must be in the form key=value"
|
|
2152
|
+
)
|
|
2153
|
+
else:
|
|
2154
|
+
raise mlrun.errors.MLRunNotFoundError(f"{file_path} does not exist")
|
|
1814
2155
|
# drop V3IO paths/credentials and MLrun service API address
|
|
1815
2156
|
env_vars = {
|
|
1816
2157
|
key: val
|
|
1817
2158
|
for key, val in secrets.items()
|
|
1818
2159
|
if key != "MLRUN_DBPATH" and not key.startswith("V3IO_")
|
|
1819
2160
|
}
|
|
1820
|
-
provider = provider or mlrun.
|
|
2161
|
+
provider = provider or mlrun.common.schemas.SecretProviderName.kubernetes
|
|
1821
2162
|
mlrun.db.get_run_db().create_project_secrets(
|
|
1822
2163
|
self.metadata.name, provider=provider, secrets=env_vars
|
|
1823
2164
|
)
|
|
@@ -1862,7 +2203,9 @@ class MlrunProject(ModelObj):
|
|
|
1862
2203
|
ttl: int = None,
|
|
1863
2204
|
engine: str = None,
|
|
1864
2205
|
local: bool = None,
|
|
1865
|
-
schedule: typing.Union[
|
|
2206
|
+
schedule: typing.Union[
|
|
2207
|
+
str, mlrun.common.schemas.ScheduleCronTrigger, bool
|
|
2208
|
+
] = None,
|
|
1866
2209
|
timeout: int = None,
|
|
1867
2210
|
overwrite: bool = False,
|
|
1868
2211
|
source: str = None,
|
|
@@ -1938,7 +2281,10 @@ class MlrunProject(ModelObj):
|
|
|
1938
2281
|
|
|
1939
2282
|
self.sync_functions(always=sync)
|
|
1940
2283
|
if not self.spec._function_objects:
|
|
1941
|
-
raise ValueError(
|
|
2284
|
+
raise ValueError(
|
|
2285
|
+
"There are no functions in the project."
|
|
2286
|
+
" Make sure you've set your functions with project.set_function()."
|
|
2287
|
+
)
|
|
1942
2288
|
|
|
1943
2289
|
if not name and not workflow_path and not workflow_handler:
|
|
1944
2290
|
if self.spec.workflows:
|
|
@@ -1951,9 +2297,9 @@ class MlrunProject(ModelObj):
|
|
|
1951
2297
|
else:
|
|
1952
2298
|
workflow_spec = self.spec._workflows[name].copy()
|
|
1953
2299
|
workflow_spec.merge_args(arguments)
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2300
|
+
workflow_spec.cleanup_ttl = (
|
|
2301
|
+
cleanup_ttl or ttl or workflow_spec.cleanup_ttl or workflow_spec.ttl
|
|
2302
|
+
)
|
|
1957
2303
|
workflow_spec.run_local = local
|
|
1958
2304
|
|
|
1959
2305
|
name = f"{self.metadata.name}-{name}" if name else self.metadata.name
|
|
@@ -2039,14 +2385,43 @@ class MlrunProject(ModelObj):
|
|
|
2039
2385
|
notifiers=notifiers,
|
|
2040
2386
|
)
|
|
2041
2387
|
|
|
2388
|
+
# TODO: remove in 1.6.0
|
|
2389
|
+
@deprecated(
|
|
2390
|
+
version="1.4.0",
|
|
2391
|
+
reason="'clear_context' will be removed in 1.6.0, this can cause unexpected issues",
|
|
2392
|
+
category=FutureWarning,
|
|
2393
|
+
)
|
|
2042
2394
|
def clear_context(self):
|
|
2043
2395
|
"""delete all files and clear the context dir"""
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
and
|
|
2047
|
-
|
|
2048
|
-
)
|
|
2049
|
-
|
|
2396
|
+
warnings.warn(
|
|
2397
|
+
"This method deletes all files and clears the context directory or subpath (if defined)!"
|
|
2398
|
+
" Please keep in mind that this method can produce unexpected outcomes and is not recommended,"
|
|
2399
|
+
" it will be deprecated in 1.6.0."
|
|
2400
|
+
)
|
|
2401
|
+
# clear only if the context path exists and not relative
|
|
2402
|
+
if self.spec.context and os.path.isabs(self.spec.context):
|
|
2403
|
+
|
|
2404
|
+
# if a subpath is defined, will empty the subdir instead of the entire context
|
|
2405
|
+
if self.spec.subpath:
|
|
2406
|
+
path_to_clear = path.join(self.spec.context, self.spec.subpath)
|
|
2407
|
+
logger.info(f"Subpath is defined, Clearing path: {path_to_clear}")
|
|
2408
|
+
else:
|
|
2409
|
+
path_to_clear = self.spec.context
|
|
2410
|
+
logger.info(
|
|
2411
|
+
f"Subpath is not defined, Clearing context: {path_to_clear}"
|
|
2412
|
+
)
|
|
2413
|
+
if path.exists(path_to_clear) and path.isdir(path_to_clear):
|
|
2414
|
+
shutil.rmtree(path_to_clear)
|
|
2415
|
+
else:
|
|
2416
|
+
logger.warn(
|
|
2417
|
+
f"Attempt to clear {path_to_clear} failed. Path either does not exist or is not a directory."
|
|
2418
|
+
" Please ensure that your context or subdpath are properly defined."
|
|
2419
|
+
)
|
|
2420
|
+
else:
|
|
2421
|
+
logger.warn(
|
|
2422
|
+
"Your context path is a relative path;"
|
|
2423
|
+
" in order to avoid unexpected results, we do not allow the deletion of relative paths."
|
|
2424
|
+
)
|
|
2050
2425
|
|
|
2051
2426
|
def save(self, filepath=None, store=True):
|
|
2052
2427
|
"""export project to yaml file and save project in database
|
|
@@ -2105,15 +2480,43 @@ class MlrunProject(ModelObj):
|
|
|
2105
2480
|
mlrun.get_dataitem(filepath).upload(tmp_path)
|
|
2106
2481
|
remove(tmp_path)
|
|
2107
2482
|
|
|
2108
|
-
def set_model_monitoring_credentials(
|
|
2483
|
+
def set_model_monitoring_credentials(
|
|
2484
|
+
self,
|
|
2485
|
+
access_key: str = None,
|
|
2486
|
+
endpoint_store_connection: str = None,
|
|
2487
|
+
stream_path: str = None,
|
|
2488
|
+
):
|
|
2109
2489
|
"""Set the credentials that will be used by the project's model monitoring
|
|
2110
2490
|
infrastructure functions.
|
|
2111
|
-
The supplied credentials must have data access
|
|
2112
2491
|
|
|
2113
|
-
:param access_key:
|
|
2492
|
+
:param access_key: Model Monitoring access key for managing user permissions
|
|
2493
|
+
:param endpoint_store_connection: Endpoint store connection string
|
|
2494
|
+
:param stream_path: Path to the model monitoring stream
|
|
2114
2495
|
"""
|
|
2115
|
-
|
|
2116
|
-
|
|
2496
|
+
|
|
2497
|
+
secrets_dict = {}
|
|
2498
|
+
if access_key:
|
|
2499
|
+
secrets_dict[
|
|
2500
|
+
model_monitoring_constants.ProjectSecretKeys.ACCESS_KEY
|
|
2501
|
+
] = access_key
|
|
2502
|
+
|
|
2503
|
+
if endpoint_store_connection:
|
|
2504
|
+
secrets_dict[
|
|
2505
|
+
model_monitoring_constants.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION
|
|
2506
|
+
] = endpoint_store_connection
|
|
2507
|
+
|
|
2508
|
+
if stream_path:
|
|
2509
|
+
if stream_path.startswith("kafka://") and "?topic" in stream_path:
|
|
2510
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
2511
|
+
"Custom kafka topic is not allowed"
|
|
2512
|
+
)
|
|
2513
|
+
secrets_dict[
|
|
2514
|
+
model_monitoring_constants.ProjectSecretKeys.STREAM_PATH
|
|
2515
|
+
] = stream_path
|
|
2516
|
+
|
|
2517
|
+
self.set_secrets(
|
|
2518
|
+
secrets=secrets_dict,
|
|
2519
|
+
provider=mlrun.common.schemas.SecretProviderName.kubernetes,
|
|
2117
2520
|
)
|
|
2118
2521
|
|
|
2119
2522
|
def run_function(
|
|
@@ -2134,18 +2537,19 @@ class MlrunProject(ModelObj):
|
|
|
2134
2537
|
verbose: bool = None,
|
|
2135
2538
|
selector: str = None,
|
|
2136
2539
|
auto_build: bool = None,
|
|
2137
|
-
schedule: typing.Union[str, mlrun.
|
|
2540
|
+
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
2138
2541
|
artifact_path: str = None,
|
|
2542
|
+
notifications: typing.List[mlrun.model.Notification] = None,
|
|
2139
2543
|
returns: Optional[List[Union[str, Dict[str, str]]]] = None,
|
|
2140
2544
|
) -> typing.Union[mlrun.model.RunObject, kfp.dsl.ContainerOp]:
|
|
2141
2545
|
"""Run a local or remote task as part of a local/kubeflow pipeline
|
|
2142
2546
|
|
|
2143
2547
|
example (use with project)::
|
|
2144
2548
|
|
|
2145
|
-
# create a project with two functions (local and from
|
|
2549
|
+
# create a project with two functions (local and from hub)
|
|
2146
2550
|
project = mlrun.new_project(project_name, "./proj")
|
|
2147
2551
|
project.set_function("mycode.py", "myfunc", image="mlrun/mlrun")
|
|
2148
|
-
project.set_function("hub://
|
|
2552
|
+
project.set_function("hub://auto-trainer", "train")
|
|
2149
2553
|
|
|
2150
2554
|
# run functions (refer to them by name)
|
|
2151
2555
|
run1 = project.run_function("myfunc", params={"x": 7})
|
|
@@ -2177,6 +2581,7 @@ class MlrunProject(ModelObj):
|
|
|
2177
2581
|
see this link for help:
|
|
2178
2582
|
https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#module-apscheduler.triggers.cron
|
|
2179
2583
|
:param artifact_path: path to store artifacts, when running in a workflow this will be set automatically
|
|
2584
|
+
:param notifications: list of notifications to push when the run is completed
|
|
2180
2585
|
:param returns: List of log hints - configurations for how to log the returning values from the
|
|
2181
2586
|
handler's run (as artifacts or results). The list's length must be equal to the amount
|
|
2182
2587
|
of returning objects. A log hint may be given as:
|
|
@@ -2211,6 +2616,7 @@ class MlrunProject(ModelObj):
|
|
|
2211
2616
|
auto_build=auto_build,
|
|
2212
2617
|
schedule=schedule,
|
|
2213
2618
|
artifact_path=artifact_path,
|
|
2619
|
+
notifications=notifications,
|
|
2214
2620
|
returns=returns,
|
|
2215
2621
|
)
|
|
2216
2622
|
|
|
@@ -2219,28 +2625,30 @@ class MlrunProject(ModelObj):
|
|
|
2219
2625
|
function: typing.Union[str, mlrun.runtimes.BaseRuntime],
|
|
2220
2626
|
with_mlrun: bool = None,
|
|
2221
2627
|
skip_deployed: bool = False,
|
|
2222
|
-
image=None,
|
|
2223
|
-
base_image=None,
|
|
2628
|
+
image: str = None,
|
|
2629
|
+
base_image: str = None,
|
|
2224
2630
|
commands: list = None,
|
|
2225
|
-
secret_name=None,
|
|
2631
|
+
secret_name: str = None,
|
|
2226
2632
|
requirements: typing.Union[str, typing.List[str]] = None,
|
|
2227
|
-
mlrun_version_specifier=None,
|
|
2633
|
+
mlrun_version_specifier: str = None,
|
|
2228
2634
|
builder_env: dict = None,
|
|
2229
2635
|
overwrite_build_params: bool = False,
|
|
2636
|
+
requirements_file: str = None,
|
|
2230
2637
|
) -> typing.Union[BuildStatus, kfp.dsl.ContainerOp]:
|
|
2231
2638
|
"""deploy ML function, build container with its dependencies
|
|
2232
2639
|
|
|
2233
|
-
:param function:
|
|
2234
|
-
:param with_mlrun:
|
|
2235
|
-
:param skip_deployed:
|
|
2236
|
-
:param image:
|
|
2237
|
-
:param base_image:
|
|
2238
|
-
:param commands:
|
|
2239
|
-
:param secret_name:
|
|
2240
|
-
:param requirements:
|
|
2640
|
+
:param function: name of the function (in the project) or function object
|
|
2641
|
+
:param with_mlrun: add the current mlrun package to the container build
|
|
2642
|
+
:param skip_deployed: skip the build if we already have an image for the function
|
|
2643
|
+
:param image: target image name/path
|
|
2644
|
+
:param base_image: base image name/path (commands and source code will be added to it)
|
|
2645
|
+
:param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
|
|
2646
|
+
:param secret_name: k8s secret for accessing the docker registry
|
|
2647
|
+
:param requirements: list of python packages, defaults to None
|
|
2648
|
+
:param requirements_file: pip requirements file path, defaults to None
|
|
2241
2649
|
:param mlrun_version_specifier: which mlrun package version to include (if not current)
|
|
2242
|
-
:param builder_env:
|
|
2243
|
-
|
|
2650
|
+
:param builder_env: Kaniko builder pod env vars dict (for config/credentials)
|
|
2651
|
+
e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
|
|
2244
2652
|
:param overwrite_build_params: overwrite the function build parameters with the provided ones, or attempt to
|
|
2245
2653
|
add to existing parameters
|
|
2246
2654
|
"""
|
|
@@ -2253,12 +2661,141 @@ class MlrunProject(ModelObj):
|
|
|
2253
2661
|
commands=commands,
|
|
2254
2662
|
secret_name=secret_name,
|
|
2255
2663
|
requirements=requirements,
|
|
2664
|
+
requirements_file=requirements_file,
|
|
2256
2665
|
mlrun_version_specifier=mlrun_version_specifier,
|
|
2257
2666
|
builder_env=builder_env,
|
|
2258
2667
|
project_object=self,
|
|
2259
2668
|
overwrite_build_params=overwrite_build_params,
|
|
2260
2669
|
)
|
|
2261
2670
|
|
|
2671
|
+
def build_config(
|
|
2672
|
+
self,
|
|
2673
|
+
image: str = None,
|
|
2674
|
+
set_as_default: bool = False,
|
|
2675
|
+
with_mlrun: bool = None,
|
|
2676
|
+
base_image: str = None,
|
|
2677
|
+
commands: list = None,
|
|
2678
|
+
secret_name: str = None,
|
|
2679
|
+
requirements: typing.Union[str, typing.List[str]] = None,
|
|
2680
|
+
overwrite_build_params: bool = False,
|
|
2681
|
+
requirements_file: str = None,
|
|
2682
|
+
):
|
|
2683
|
+
"""specify builder configuration for the project
|
|
2684
|
+
|
|
2685
|
+
:param image: target image name/path. If not specified the project's existing `default_image` name will be
|
|
2686
|
+
used. If not set, the `mlconf.default_project_image_name` value will be used
|
|
2687
|
+
:param set_as_default: set `image` to be the project's default image (default False)
|
|
2688
|
+
:param with_mlrun: add the current mlrun package to the container build
|
|
2689
|
+
:param base_image: base image name/path
|
|
2690
|
+
:param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
|
|
2691
|
+
:param secret_name: k8s secret for accessing the docker registry
|
|
2692
|
+
:param requirements: a list of packages to install on the built image
|
|
2693
|
+
:param requirements_file: requirements file to install on the built image
|
|
2694
|
+
:param overwrite_build_params: overwrite existing build configuration (default False)
|
|
2695
|
+
|
|
2696
|
+
* False: the new params are merged with the existing (currently merge is applied to requirements and
|
|
2697
|
+
commands)
|
|
2698
|
+
* True: the existing params are replaced by the new ones
|
|
2699
|
+
"""
|
|
2700
|
+
default_image_name = mlrun.mlconf.default_project_image_name.format(
|
|
2701
|
+
name=self.name
|
|
2702
|
+
)
|
|
2703
|
+
image = image or self.default_image or default_image_name
|
|
2704
|
+
|
|
2705
|
+
self.spec.build.build_config(
|
|
2706
|
+
image=image,
|
|
2707
|
+
base_image=base_image,
|
|
2708
|
+
commands=commands,
|
|
2709
|
+
secret=secret_name,
|
|
2710
|
+
with_mlrun=with_mlrun,
|
|
2711
|
+
requirements=requirements,
|
|
2712
|
+
requirements_file=requirements_file,
|
|
2713
|
+
overwrite=overwrite_build_params,
|
|
2714
|
+
)
|
|
2715
|
+
|
|
2716
|
+
if set_as_default and image != self.default_image:
|
|
2717
|
+
self.set_default_image(image)
|
|
2718
|
+
|
|
2719
|
+
def build_image(
|
|
2720
|
+
self,
|
|
2721
|
+
image: str = None,
|
|
2722
|
+
set_as_default: bool = True,
|
|
2723
|
+
with_mlrun: bool = None,
|
|
2724
|
+
skip_deployed: bool = False,
|
|
2725
|
+
base_image: str = None,
|
|
2726
|
+
commands: list = None,
|
|
2727
|
+
secret_name: str = None,
|
|
2728
|
+
requirements: typing.Union[str, typing.List[str]] = None,
|
|
2729
|
+
mlrun_version_specifier: str = None,
|
|
2730
|
+
builder_env: dict = None,
|
|
2731
|
+
overwrite_build_params: bool = False,
|
|
2732
|
+
requirements_file: str = None,
|
|
2733
|
+
) -> typing.Union[BuildStatus, kfp.dsl.ContainerOp]:
|
|
2734
|
+
"""Builder docker image for the project, based on the project's build config. Parameters allow to override
|
|
2735
|
+
the build config.
|
|
2736
|
+
|
|
2737
|
+
:param image: target image name/path. If not specified the project's existing `default_image` name will be
|
|
2738
|
+
used. If not set, the `mlconf.default_project_image_name` value will be used
|
|
2739
|
+
:param set_as_default: set `image` to be the project's default image (default False)
|
|
2740
|
+
:param with_mlrun: add the current mlrun package to the container build
|
|
2741
|
+
:param skip_deployed: skip the build if we already have the image specified built
|
|
2742
|
+
:param base_image: base image name/path (commands and source code will be added to it)
|
|
2743
|
+
:param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
|
|
2744
|
+
:param secret_name: k8s secret for accessing the docker registry
|
|
2745
|
+
:param requirements: list of python packages, defaults to None
|
|
2746
|
+
:param requirements_file: pip requirements file path, defaults to None
|
|
2747
|
+
:param mlrun_version_specifier: which mlrun package version to include (if not current)
|
|
2748
|
+
:param builder_env: Kaniko builder pod env vars dict (for config/credentials)
|
|
2749
|
+
e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
|
|
2750
|
+
:param overwrite_build_params: overwrite existing build configuration (default False)
|
|
2751
|
+
|
|
2752
|
+
* False: the new params are merged with the existing (currently merge is applied to requirements and
|
|
2753
|
+
commands)
|
|
2754
|
+
* True: the existing params are replaced by the new ones
|
|
2755
|
+
"""
|
|
2756
|
+
|
|
2757
|
+
self.build_config(
|
|
2758
|
+
image=image,
|
|
2759
|
+
set_as_default=set_as_default,
|
|
2760
|
+
base_image=base_image,
|
|
2761
|
+
commands=commands,
|
|
2762
|
+
secret_name=secret_name,
|
|
2763
|
+
with_mlrun=with_mlrun,
|
|
2764
|
+
requirements=requirements,
|
|
2765
|
+
requirements_file=requirements_file,
|
|
2766
|
+
overwrite_build_params=overwrite_build_params,
|
|
2767
|
+
)
|
|
2768
|
+
|
|
2769
|
+
function = mlrun.new_function("mlrun--project--image--builder", kind="job")
|
|
2770
|
+
|
|
2771
|
+
build = self.spec.build
|
|
2772
|
+
result = self.build_function(
|
|
2773
|
+
function=function,
|
|
2774
|
+
with_mlrun=build.with_mlrun,
|
|
2775
|
+
image=build.image,
|
|
2776
|
+
base_image=build.base_image,
|
|
2777
|
+
commands=build.commands,
|
|
2778
|
+
secret_name=build.secret,
|
|
2779
|
+
requirements=build.requirements,
|
|
2780
|
+
skip_deployed=skip_deployed,
|
|
2781
|
+
overwrite_build_params=overwrite_build_params,
|
|
2782
|
+
mlrun_version_specifier=mlrun_version_specifier,
|
|
2783
|
+
builder_env=builder_env,
|
|
2784
|
+
)
|
|
2785
|
+
|
|
2786
|
+
try:
|
|
2787
|
+
mlrun.db.get_run_db(secrets=self._secrets).delete_function(
|
|
2788
|
+
name=function.metadata.name
|
|
2789
|
+
)
|
|
2790
|
+
except Exception as exc:
|
|
2791
|
+
logger.warning(
|
|
2792
|
+
f"Image was successfully built, but failed to delete temporary function {function.metadata.name}."
|
|
2793
|
+
" To remove the function, attempt to manually delete it.",
|
|
2794
|
+
exc=repr(exc),
|
|
2795
|
+
)
|
|
2796
|
+
|
|
2797
|
+
return result
|
|
2798
|
+
|
|
2262
2799
|
def deploy_function(
|
|
2263
2800
|
self,
|
|
2264
2801
|
function: typing.Union[str, mlrun.runtimes.BaseRuntime],
|
|
@@ -2315,7 +2852,7 @@ class MlrunProject(ModelObj):
|
|
|
2315
2852
|
iter: int = None,
|
|
2316
2853
|
best_iteration: bool = False,
|
|
2317
2854
|
kind: str = None,
|
|
2318
|
-
category: typing.Union[str, mlrun.
|
|
2855
|
+
category: typing.Union[str, mlrun.common.schemas.ArtifactCategories] = None,
|
|
2319
2856
|
) -> mlrun.lists.ArtifactList:
|
|
2320
2857
|
"""List artifacts filtered by various parameters.
|
|
2321
2858
|
|
|
@@ -2329,8 +2866,9 @@ class MlrunProject(ModelObj):
|
|
|
2329
2866
|
# check different artifact versions for a specific artifact, return as objects list
|
|
2330
2867
|
result_versions = project.list_artifacts('results', tag='*').to_objects()
|
|
2331
2868
|
|
|
2332
|
-
:param name: Name of artifacts to retrieve. Name is used as a like query, and is not
|
|
2333
|
-
that querying for
|
|
2869
|
+
:param name: Name of artifacts to retrieve. Name with '~' prefix is used as a like query, and is not
|
|
2870
|
+
case-sensitive. This means that querying for ``~name`` may return artifacts named
|
|
2871
|
+
``my_Name_1`` or ``surname``.
|
|
2334
2872
|
:param tag: Return artifacts assigned this tag.
|
|
2335
2873
|
:param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
|
|
2336
2874
|
a list of "label=value" (match label key and value) or "label" (match just label key) strings.
|
|
@@ -2376,8 +2914,9 @@ class MlrunProject(ModelObj):
|
|
|
2376
2914
|
latest_models = project.list_models('', tag='latest')
|
|
2377
2915
|
|
|
2378
2916
|
|
|
2379
|
-
:param name: Name of artifacts to retrieve. Name is used as a like query, and is not
|
|
2380
|
-
that querying for
|
|
2917
|
+
:param name: Name of artifacts to retrieve. Name with '~' prefix is used as a like query, and is not
|
|
2918
|
+
case-sensitive. This means that querying for ``~name`` may return artifacts named
|
|
2919
|
+
``my_Name_1`` or ``surname``.
|
|
2381
2920
|
:param tag: Return artifacts assigned this tag.
|
|
2382
2921
|
:param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
|
|
2383
2922
|
a list of "label=value" (match label key and value) or "label" (match just label key) strings.
|
|
@@ -2423,17 +2962,17 @@ class MlrunProject(ModelObj):
|
|
|
2423
2962
|
|
|
2424
2963
|
def list_runs(
|
|
2425
2964
|
self,
|
|
2426
|
-
name=None,
|
|
2427
|
-
uid=None,
|
|
2428
|
-
labels=None,
|
|
2429
|
-
state=None,
|
|
2430
|
-
sort=True,
|
|
2431
|
-
last=0,
|
|
2432
|
-
iter=False,
|
|
2433
|
-
start_time_from: datetime.datetime = None,
|
|
2434
|
-
start_time_to: datetime.datetime = None,
|
|
2435
|
-
last_update_time_from: datetime.datetime = None,
|
|
2436
|
-
last_update_time_to: datetime.datetime = None,
|
|
2965
|
+
name: Optional[str] = None,
|
|
2966
|
+
uid: Optional[Union[str, List[str]]] = None,
|
|
2967
|
+
labels: Optional[Union[str, List[str]]] = None,
|
|
2968
|
+
state: Optional[str] = None,
|
|
2969
|
+
sort: bool = True,
|
|
2970
|
+
last: int = 0,
|
|
2971
|
+
iter: bool = False,
|
|
2972
|
+
start_time_from: Optional[datetime.datetime] = None,
|
|
2973
|
+
start_time_to: Optional[datetime.datetime] = None,
|
|
2974
|
+
last_update_time_from: Optional[datetime.datetime] = None,
|
|
2975
|
+
last_update_time_to: Optional[datetime.datetime] = None,
|
|
2437
2976
|
**kwargs,
|
|
2438
2977
|
) -> mlrun.lists.RunList:
|
|
2439
2978
|
"""Retrieve a list of runs, filtered by various options.
|
|
@@ -2447,6 +2986,10 @@ class MlrunProject(ModelObj):
|
|
|
2447
2986
|
# return a list of runs matching the name and label and compare
|
|
2448
2987
|
runs = project.list_runs(name='download', labels='owner=admin')
|
|
2449
2988
|
runs.compare()
|
|
2989
|
+
|
|
2990
|
+
# multi-label filter can also be provided
|
|
2991
|
+
runs = project.list_runs(name='download', labels=["kind=job", "owner=admin"])
|
|
2992
|
+
|
|
2450
2993
|
# If running in Jupyter, can use the .show() function to display the results
|
|
2451
2994
|
project.list_runs(name='').show()
|
|
2452
2995
|
|
|
@@ -2454,8 +2997,8 @@ class MlrunProject(ModelObj):
|
|
|
2454
2997
|
:param name: Name of the run to retrieve.
|
|
2455
2998
|
:param uid: Unique ID of the run.
|
|
2456
2999
|
:param project: Project that the runs belongs to.
|
|
2457
|
-
:param labels: List runs that have
|
|
2458
|
-
applied
|
|
3000
|
+
:param labels: List runs that have specific labels assigned. a single or multi label filter can be
|
|
3001
|
+
applied.
|
|
2459
3002
|
:param state: List only runs whose state is specified.
|
|
2460
3003
|
:param sort: Whether to sort the result according to their start time. Otherwise, results will be
|
|
2461
3004
|
returned by their internal order in the DB (order will not be guaranteed).
|
|
@@ -2484,13 +3027,53 @@ class MlrunProject(ModelObj):
|
|
|
2484
3027
|
**kwargs,
|
|
2485
3028
|
)
|
|
2486
3029
|
|
|
3030
|
+
def get_custom_packagers(self) -> typing.List[typing.Tuple[str, bool]]:
|
|
3031
|
+
"""
|
|
3032
|
+
Get the custom packagers registered in the project.
|
|
3033
|
+
|
|
3034
|
+
:return: A list of the custom packagers module paths.
|
|
3035
|
+
"""
|
|
3036
|
+
# Return a copy so the user won't be able to edit the list by the reference returned (no need for deep copy as
|
|
3037
|
+
# tuples do not support item assignment):
|
|
3038
|
+
return self.spec.custom_packagers.copy()
|
|
3039
|
+
|
|
3040
|
+
def add_custom_packager(self, packager: str, is_mandatory: bool):
|
|
3041
|
+
"""
|
|
3042
|
+
Add a custom packager from the custom packagers list. All project's custom packagers are added to each project
|
|
3043
|
+
function.
|
|
3044
|
+
|
|
3045
|
+
**Notice** that in order to run a function with the custom packagers included, you must set a source for the
|
|
3046
|
+
project (using the `project.set_source` method) with the parameter `pull_at_runtime=True` so the source code of
|
|
3047
|
+
the packagers will be able to be imported.
|
|
3048
|
+
|
|
3049
|
+
:param packager: The packager module path to add. For example, if a packager `MyPackager` is in the
|
|
3050
|
+
project's source at my_module.py, then the module path is: "my_module.MyPackager".
|
|
3051
|
+
:param is_mandatory: Whether this packager must be collected during a run. If False, failing to collect it won't
|
|
3052
|
+
raise an error during the packagers collection phase.
|
|
3053
|
+
"""
|
|
3054
|
+
self.spec.add_custom_packager(packager=packager, is_mandatory=is_mandatory)
|
|
3055
|
+
|
|
3056
|
+
def remove_custom_packager(self, packager: str):
|
|
3057
|
+
"""
|
|
3058
|
+
Remove a custom packager from the custom packagers list.
|
|
3059
|
+
|
|
3060
|
+
:param packager: The packager module path to remove.
|
|
3061
|
+
|
|
3062
|
+
:raise MLRunInvalidArgumentError: In case the packager was not in the list.
|
|
3063
|
+
"""
|
|
3064
|
+
self.spec.remove_custom_packager(packager=packager)
|
|
3065
|
+
|
|
2487
3066
|
|
|
2488
3067
|
def _set_as_current_default_project(project: MlrunProject):
|
|
2489
3068
|
mlrun.mlconf.default_project = project.metadata.name
|
|
2490
3069
|
pipeline_context.set(project)
|
|
2491
3070
|
|
|
2492
3071
|
|
|
2493
|
-
def _init_function_from_dict(
|
|
3072
|
+
def _init_function_from_dict(
|
|
3073
|
+
f: dict,
|
|
3074
|
+
project: MlrunProject,
|
|
3075
|
+
name: typing.Optional[str] = None,
|
|
3076
|
+
) -> typing.Tuple[str, mlrun.runtimes.BaseRuntime]:
|
|
2494
3077
|
name = name or f.get("name", "")
|
|
2495
3078
|
url = f.get("url", "")
|
|
2496
3079
|
kind = f.get("kind", "")
|
|
@@ -2556,6 +3139,7 @@ def _init_function_from_dict(f, project, name=None):
|
|
|
2556
3139
|
raise ValueError(f"unsupported function url:handler {url}:{handler} or no spec")
|
|
2557
3140
|
|
|
2558
3141
|
if with_repo:
|
|
3142
|
+
# mark source to be enriched before run with project source (enrich_function_object)
|
|
2559
3143
|
func.spec.build.source = "./"
|
|
2560
3144
|
if requirements:
|
|
2561
3145
|
func.with_requirements(requirements)
|
|
@@ -2563,7 +3147,11 @@ def _init_function_from_dict(f, project, name=None):
|
|
|
2563
3147
|
return _init_function_from_obj(func, project, name)
|
|
2564
3148
|
|
|
2565
3149
|
|
|
2566
|
-
def _init_function_from_obj(
|
|
3150
|
+
def _init_function_from_obj(
|
|
3151
|
+
func: mlrun.runtimes.BaseRuntime,
|
|
3152
|
+
project: MlrunProject,
|
|
3153
|
+
name: typing.Optional[str] = None,
|
|
3154
|
+
) -> typing.Tuple[str, mlrun.runtimes.BaseRuntime]:
|
|
2567
3155
|
build = func.spec.build
|
|
2568
3156
|
if project.spec.origin_url:
|
|
2569
3157
|
origin = project.spec.origin_url
|
|
@@ -2580,76 +3168,12 @@ def _init_function_from_obj(func, project, name=None):
|
|
|
2580
3168
|
return name or func.metadata.name, func
|
|
2581
3169
|
|
|
2582
3170
|
|
|
2583
|
-
def _init_function_from_dict_legacy(f, project):
|
|
2584
|
-
name = f.get("name", "")
|
|
2585
|
-
url = f.get("url", "")
|
|
2586
|
-
kind = f.get("kind", "")
|
|
2587
|
-
image = f.get("image", None)
|
|
2588
|
-
with_repo = f.get("with_repo", False)
|
|
2589
|
-
|
|
2590
|
-
if with_repo and not project.source:
|
|
2591
|
-
raise ValueError("project source must be specified when cloning context")
|
|
2592
|
-
|
|
2593
|
-
in_context = False
|
|
2594
|
-
if not url and "spec" not in f:
|
|
2595
|
-
raise ValueError("function missing a url or a spec")
|
|
2596
|
-
# We are not using the project method to obtain an absolute path here,
|
|
2597
|
-
# because legacy projects are built differently, and we cannot rely on them to have a spec
|
|
2598
|
-
if url and "://" not in url:
|
|
2599
|
-
if project.context and not url.startswith("/"):
|
|
2600
|
-
url = path.join(project.context, url)
|
|
2601
|
-
in_context = True
|
|
2602
|
-
if not path.isfile(url):
|
|
2603
|
-
raise OSError(f"{url} not found")
|
|
2604
|
-
|
|
2605
|
-
if "spec" in f:
|
|
2606
|
-
func = new_function(name, runtime=f["spec"])
|
|
2607
|
-
elif is_yaml_path(url) or url.startswith("db://") or url.startswith("hub://"):
|
|
2608
|
-
func = import_function(url)
|
|
2609
|
-
if image:
|
|
2610
|
-
func.spec.image = image
|
|
2611
|
-
elif url.endswith(".ipynb"):
|
|
2612
|
-
func = code_to_function(name, filename=url, image=image, kind=kind)
|
|
2613
|
-
elif url.endswith(".py"):
|
|
2614
|
-
if not image:
|
|
2615
|
-
raise ValueError(
|
|
2616
|
-
"image must be provided with py code files, "
|
|
2617
|
-
"use function object for more control/settings"
|
|
2618
|
-
)
|
|
2619
|
-
if in_context and with_repo:
|
|
2620
|
-
func = new_function(name, command=url, image=image, kind=kind or "job")
|
|
2621
|
-
else:
|
|
2622
|
-
func = code_to_function(name, filename=url, image=image, kind=kind or "job")
|
|
2623
|
-
else:
|
|
2624
|
-
raise ValueError(f"unsupported function url {url} or no spec")
|
|
2625
|
-
|
|
2626
|
-
if with_repo:
|
|
2627
|
-
func.spec.build.source = "./"
|
|
2628
|
-
|
|
2629
|
-
return _init_function_from_obj_legacy(func, project, name)
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
def _init_function_from_obj_legacy(func, project, name=None):
|
|
2633
|
-
build = func.spec.build
|
|
2634
|
-
if project.origin_url:
|
|
2635
|
-
origin = project.origin_url
|
|
2636
|
-
try:
|
|
2637
|
-
if project.repo:
|
|
2638
|
-
origin += "#" + project.repo.head.commit.hexsha
|
|
2639
|
-
except Exception:
|
|
2640
|
-
pass
|
|
2641
|
-
build.code_origin = origin
|
|
2642
|
-
if project.name:
|
|
2643
|
-
func.metadata.project = project.name
|
|
2644
|
-
if project.tag:
|
|
2645
|
-
func.metadata.tag = project.tag
|
|
2646
|
-
return name or func.metadata.name, func
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
3171
|
def _has_module(handler, kind):
|
|
2650
3172
|
if not handler:
|
|
2651
3173
|
return False
|
|
2652
|
-
return (
|
|
3174
|
+
return (
|
|
3175
|
+
kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes() and ":" in handler
|
|
3176
|
+
) or "." in handler
|
|
2653
3177
|
|
|
2654
3178
|
|
|
2655
3179
|
def _is_imported_artifact(artifact):
|