sagemaker-core 1.0.62__py3-none-any.whl → 2.3.1__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.
- sagemaker/__init__.py +2 -0
- sagemaker/core/__init__.py +16 -0
- sagemaker/core/_studio.py +116 -0
- sagemaker/core/_version.py +11 -0
- sagemaker/core/accept_types.py +131 -0
- sagemaker/core/analytics.py +744 -0
- sagemaker/core/apiutils/__init__.py +13 -0
- sagemaker/core/apiutils/_base_types.py +228 -0
- sagemaker/core/apiutils/_boto_functions.py +130 -0
- sagemaker/core/apiutils/_utils.py +34 -0
- sagemaker/core/base_deserializers.py +35 -0
- sagemaker/core/base_serializers.py +35 -0
- sagemaker/core/clarify/__init__.py +2898 -0
- sagemaker/core/collection.py +467 -0
- sagemaker/core/common_utils.py +2399 -0
- sagemaker/core/compute_resource_requirements/__init__.py +18 -0
- sagemaker/core/compute_resource_requirements/resource_requirements.py +94 -0
- sagemaker/core/config/__init__.py +181 -0
- sagemaker/core/config/config.py +238 -0
- sagemaker/core/config/config_manager.py +595 -0
- sagemaker/core/config/config_schema.py +1220 -0
- sagemaker/core/config/config_utils.py +297 -0
- {sagemaker_core/main → sagemaker/core}/config_schema.py +408 -3
- sagemaker/core/constants.py +73 -0
- sagemaker/core/content_types.py +137 -0
- sagemaker/core/debugger/__init__.py +39 -0
- sagemaker/core/debugger/debugger.py +945 -0
- sagemaker/core/debugger/framework_profile.py +292 -0
- sagemaker/core/debugger/metrics_config.py +468 -0
- sagemaker/core/debugger/profiler.py +42 -0
- sagemaker/core/debugger/profiler_config.py +190 -0
- sagemaker/core/debugger/profiler_constants.py +40 -0
- sagemaker/core/debugger/utils.py +148 -0
- sagemaker/core/deprecations.py +254 -0
- sagemaker/core/deserializers/__init__.py +10 -0
- sagemaker/core/deserializers/base.py +424 -0
- sagemaker/core/deserializers/implementations.py +157 -0
- sagemaker/core/drift_check_baselines.py +106 -0
- sagemaker/core/enums.py +51 -0
- sagemaker/core/environment_variables.py +101 -0
- sagemaker/core/exceptions.py +108 -0
- sagemaker/core/experiments/__init__.py +53 -0
- sagemaker/core/experiments/_api_types.py +251 -0
- sagemaker/core/experiments/_environment.py +124 -0
- sagemaker/core/experiments/_helper.py +294 -0
- sagemaker/core/experiments/_metrics.py +333 -0
- sagemaker/core/experiments/_run_context.py +58 -0
- sagemaker/core/experiments/_utils.py +216 -0
- sagemaker/core/experiments/experiment.py +247 -0
- sagemaker/core/experiments/run.py +970 -0
- sagemaker/core/experiments/trial.py +296 -0
- sagemaker/core/experiments/trial_component.py +387 -0
- sagemaker/core/explainer/__init__.py +24 -0
- sagemaker/core/explainer/clarify_explainer_config.py +298 -0
- sagemaker/core/explainer/explainer_config.py +44 -0
- sagemaker/core/fw_utils.py +1220 -0
- sagemaker/core/git_utils.py +415 -0
- sagemaker/core/helper/pipeline_variable.py +82 -0
- sagemaker/core/helper/session_helper.py +2977 -0
- sagemaker/core/hyperparameters.py +172 -0
- sagemaker/core/image_retriever/__init__.py +3 -0
- sagemaker/core/image_retriever/image_retriever.py +640 -0
- sagemaker/core/image_retriever/image_retriever_utils.py +509 -0
- sagemaker/core/image_retriever/test.py +7 -0
- sagemaker/core/image_uri_config/autogluon.json +1335 -0
- sagemaker/core/image_uri_config/blazingtext.json +50 -0
- sagemaker/core/image_uri_config/chainer.json +104 -0
- sagemaker/core/image_uri_config/clarify.json +39 -0
- sagemaker/core/image_uri_config/coach-mxnet.json +70 -0
- sagemaker/core/image_uri_config/coach-tensorflow.json +186 -0
- sagemaker/core/image_uri_config/data-wrangler.json +91 -0
- sagemaker/core/image_uri_config/debugger.json +34 -0
- sagemaker/core/image_uri_config/detailed-profiler.json +18 -0
- sagemaker/core/image_uri_config/djl-deepspeed.json +385 -0
- sagemaker/core/image_uri_config/djl-fastertransformer.json +167 -0
- sagemaker/core/image_uri_config/djl-lmi.json +136 -0
- sagemaker/core/image_uri_config/djl-neuronx.json +258 -0
- sagemaker/core/image_uri_config/djl-tensorrtllm.json +262 -0
- sagemaker/core/image_uri_config/factorization-machines.json +50 -0
- sagemaker/core/image_uri_config/forecasting-deepar.json +50 -0
- sagemaker/core/image_uri_config/huggingface-llm-neuronx.json +770 -0
- sagemaker/core/image_uri_config/huggingface-llm.json +1267 -0
- sagemaker/core/image_uri_config/huggingface-neuron.json +52 -0
- sagemaker/core/image_uri_config/huggingface-neuronx.json +686 -0
- sagemaker/core/image_uri_config/huggingface-tei-cpu.json +298 -0
- sagemaker/core/image_uri_config/huggingface-tei.json +298 -0
- sagemaker/core/image_uri_config/huggingface-training-compiler.json +195 -0
- sagemaker/core/image_uri_config/huggingface-vllm-neuronx.json +38 -0
- sagemaker/core/image_uri_config/huggingface.json +2287 -0
- sagemaker/core/image_uri_config/hyperpod-recipes-neuron.json +52 -0
- sagemaker/core/image_uri_config/image-classification-neo.json +43 -0
- sagemaker/core/image_uri_config/image-classification.json +50 -0
- sagemaker/core/image_uri_config/inferentia-mxnet.json +88 -0
- sagemaker/core/image_uri_config/inferentia-pytorch.json +127 -0
- sagemaker/core/image_uri_config/inferentia-tensorflow.json +88 -0
- sagemaker/core/image_uri_config/instance_gpu_info.json +782 -0
- sagemaker/core/image_uri_config/ipinsights.json +50 -0
- sagemaker/core/image_uri_config/kmeans.json +50 -0
- sagemaker/core/image_uri_config/knn.json +50 -0
- sagemaker/core/image_uri_config/lda.json +26 -0
- sagemaker/core/image_uri_config/linear-learner.json +50 -0
- sagemaker/core/image_uri_config/model-monitor.json +42 -0
- sagemaker/core/image_uri_config/mxnet.json +1154 -0
- sagemaker/core/image_uri_config/neo-mxnet.json +64 -0
- sagemaker/core/image_uri_config/neo-pytorch.json +341 -0
- sagemaker/core/image_uri_config/neo-tensorflow.json +109 -0
- sagemaker/core/image_uri_config/ntm.json +50 -0
- sagemaker/core/image_uri_config/object-detection.json +50 -0
- sagemaker/core/image_uri_config/object2vec.json +50 -0
- sagemaker/core/image_uri_config/pca.json +50 -0
- sagemaker/core/image_uri_config/pytorch-neuron.json +43 -0
- sagemaker/core/image_uri_config/pytorch-smp.json +218 -0
- sagemaker/core/image_uri_config/pytorch-training-compiler.json +80 -0
- sagemaker/core/image_uri_config/pytorch.json +3101 -0
- sagemaker/core/image_uri_config/randomcutforest.json +50 -0
- sagemaker/core/image_uri_config/ray-pytorch.json +46 -0
- sagemaker/core/image_uri_config/ray-tensorflow.json +194 -0
- sagemaker/core/image_uri_config/sagemaker-base-python.json +46 -0
- sagemaker/core/image_uri_config/sagemaker-distribution.json +37 -0
- sagemaker/core/image_uri_config/sagemaker-geospatial.json +13 -0
- sagemaker/core/image_uri_config/sagemaker-tritonserver.json +252 -0
- sagemaker/core/image_uri_config/semantic-segmentation.json +50 -0
- sagemaker/core/image_uri_config/seq2seq.json +50 -0
- sagemaker/core/image_uri_config/sklearn.json +494 -0
- sagemaker/core/image_uri_config/spark.json +280 -0
- sagemaker/core/image_uri_config/sparkml-serving.json +97 -0
- sagemaker/core/image_uri_config/stabilityai.json +53 -0
- sagemaker/core/image_uri_config/tensorflow.json +5086 -0
- sagemaker/core/image_uri_config/vw.json +25 -0
- sagemaker/core/image_uri_config/xgboost-neo.json +43 -0
- sagemaker/core/image_uri_config/xgboost.json +972 -0
- sagemaker/core/image_uris.py +816 -0
- sagemaker/core/inference_config.py +144 -0
- sagemaker/core/inference_recommender/__init__.py +18 -0
- sagemaker/core/inference_recommender/inference_recommender_mixin.py +622 -0
- sagemaker/core/inputs.py +366 -0
- sagemaker/core/instance_group.py +61 -0
- sagemaker/core/instance_types.py +164 -0
- sagemaker/core/instance_types_gpu_info.py +43 -0
- sagemaker/core/interactive_apps/__init__.py +41 -0
- sagemaker/core/interactive_apps/base_interactive_app.py +204 -0
- sagemaker/core/interactive_apps/detail_profiler_app.py +139 -0
- sagemaker/core/interactive_apps/tensorboard.py +149 -0
- sagemaker/core/iterators.py +197 -0
- sagemaker/core/job.py +380 -0
- sagemaker/core/jumpstart/__init__.py +156 -0
- sagemaker/core/jumpstart/accessors.py +390 -0
- sagemaker/core/jumpstart/artifacts/__init__.py +69 -0
- sagemaker/core/jumpstart/artifacts/environment_variables.py +252 -0
- sagemaker/core/jumpstart/artifacts/hyperparameters.py +120 -0
- sagemaker/core/jumpstart/artifacts/image_uris.py +139 -0
- sagemaker/core/jumpstart/artifacts/incremental_training.py +87 -0
- sagemaker/core/jumpstart/artifacts/instance_types.py +223 -0
- sagemaker/core/jumpstart/artifacts/kwargs.py +289 -0
- sagemaker/core/jumpstart/artifacts/metric_definitions.py +117 -0
- sagemaker/core/jumpstart/artifacts/model_packages.py +202 -0
- sagemaker/core/jumpstart/artifacts/model_uris.py +252 -0
- sagemaker/core/jumpstart/artifacts/payloads.py +96 -0
- sagemaker/core/jumpstart/artifacts/predictors.py +540 -0
- sagemaker/core/jumpstart/artifacts/resource_names.py +86 -0
- sagemaker/core/jumpstart/artifacts/resource_requirements.py +162 -0
- sagemaker/core/jumpstart/artifacts/script_uris.py +172 -0
- sagemaker/core/jumpstart/cache.py +663 -0
- sagemaker/core/jumpstart/configs.py +50 -0
- sagemaker/core/jumpstart/constants.py +198 -0
- sagemaker/core/jumpstart/deserializers.py +81 -0
- sagemaker/core/jumpstart/document.py +76 -0
- sagemaker/core/jumpstart/enums.py +168 -0
- sagemaker/core/jumpstart/exceptions.py +236 -0
- sagemaker/core/jumpstart/factory/utils.py +833 -0
- sagemaker/core/jumpstart/filters.py +597 -0
- sagemaker/core/jumpstart/hub/constants.py +16 -0
- sagemaker/core/jumpstart/hub/hub.py +291 -0
- sagemaker/core/jumpstart/hub/interfaces.py +936 -0
- sagemaker/core/jumpstart/hub/parser_utils.py +70 -0
- sagemaker/core/jumpstart/hub/parsers.py +288 -0
- sagemaker/core/jumpstart/hub/types.py +35 -0
- sagemaker/core/jumpstart/hub/utils.py +260 -0
- sagemaker/core/jumpstart/models.py +501 -0
- sagemaker/core/jumpstart/notebook_utils.py +575 -0
- sagemaker/core/jumpstart/parameters.py +20 -0
- sagemaker/core/jumpstart/payload_utils.py +239 -0
- sagemaker/core/jumpstart/region_config.json +171 -0
- sagemaker/core/jumpstart/search.py +171 -0
- sagemaker/core/jumpstart/serializers.py +81 -0
- sagemaker/core/jumpstart/session_utils.py +234 -0
- sagemaker/core/jumpstart/types.py +3044 -0
- sagemaker/core/jumpstart/utils.py +1731 -0
- sagemaker/core/jumpstart/validators.py +257 -0
- sagemaker/core/lambda_helper.py +312 -0
- sagemaker/core/lineage/__init__.py +42 -0
- sagemaker/core/lineage/_api_types.py +239 -0
- sagemaker/core/lineage/_utils.py +49 -0
- sagemaker/core/lineage/action.py +345 -0
- sagemaker/core/lineage/artifact.py +646 -0
- sagemaker/core/lineage/association.py +190 -0
- sagemaker/core/lineage/context.py +505 -0
- sagemaker/core/lineage/lineage_trial_component.py +191 -0
- sagemaker/core/lineage/query.py +732 -0
- sagemaker/core/lineage/visualizer.py +346 -0
- sagemaker/core/local/__init__.py +18 -0
- sagemaker/core/local/data.py +423 -0
- sagemaker/core/local/entities.py +678 -0
- sagemaker/core/local/exceptions.py +17 -0
- sagemaker/core/local/image.py +1243 -0
- sagemaker/core/local/local_session.py +739 -0
- sagemaker/core/local/utils.py +246 -0
- sagemaker/core/logs.py +181 -0
- sagemaker/core/metadata_properties.py +56 -0
- sagemaker/core/metric_definitions.py +91 -0
- sagemaker/core/mlflow/__init__.py +38 -0
- sagemaker/core/mlflow/forward_sagemaker_metrics.py +44 -0
- sagemaker/core/model_card/__init__.py +26 -0
- sagemaker/core/model_life_cycle.py +51 -0
- sagemaker/core/model_metrics.py +160 -0
- sagemaker/core/model_monitor/__init__.py +66 -0
- sagemaker/core/model_monitor/clarify_model_monitoring.py +1497 -0
- sagemaker/core/model_monitor/cron_expression_generator.py +82 -0
- sagemaker/core/model_monitor/data_capture_config.py +115 -0
- sagemaker/core/model_monitor/data_quality_monitoring_config.py +66 -0
- sagemaker/core/model_monitor/dataset_format.py +102 -0
- sagemaker/core/model_monitor/model_monitoring.py +4266 -0
- sagemaker/core/model_monitor/monitoring_alert.py +76 -0
- sagemaker/core/model_monitor/monitoring_files.py +506 -0
- sagemaker/core/model_monitor/utils.py +793 -0
- sagemaker/core/model_registry.py +480 -0
- sagemaker/core/model_uris.py +97 -0
- sagemaker/core/modules/__init__.py +19 -0
- sagemaker/core/modules/configs.py +239 -0
- sagemaker/core/modules/constants.py +37 -0
- sagemaker/core/modules/distributed.py +182 -0
- sagemaker/core/modules/local_core/local_container.py +605 -0
- sagemaker/core/modules/templates.py +83 -0
- sagemaker/core/modules/train/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/common/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/common/utils.py +205 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/basic_script_driver.py +81 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/mpi_driver.py +123 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/mpi_utils.py +302 -0
- sagemaker/core/modules/train/container_drivers/distributed_drivers/torchrun_driver.py +129 -0
- sagemaker/core/modules/train/container_drivers/scripts/__init__.py +14 -0
- sagemaker/core/modules/train/container_drivers/scripts/environment.py +305 -0
- sagemaker/core/modules/train/sm_recipes/__init__.py +0 -0
- sagemaker/core/modules/train/sm_recipes/utils.py +330 -0
- sagemaker/core/modules/types.py +19 -0
- sagemaker/core/modules/utils.py +194 -0
- sagemaker/core/network.py +185 -0
- sagemaker/core/parameter.py +173 -0
- sagemaker/core/payloads.py +185 -0
- sagemaker/core/processing.py +1599 -0
- sagemaker/core/remote_function/__init__.py +19 -0
- sagemaker/core/remote_function/checkpoint_location.py +47 -0
- sagemaker/core/remote_function/client.py +1310 -0
- sagemaker/core/remote_function/core/__init__.py +0 -0
- sagemaker/core/remote_function/core/_custom_dispatch_table.py +72 -0
- sagemaker/core/remote_function/core/pipeline_variables.py +347 -0
- sagemaker/core/remote_function/core/serialization.py +410 -0
- sagemaker/core/remote_function/core/stored_function.py +223 -0
- sagemaker/core/remote_function/custom_file_filter.py +128 -0
- sagemaker/core/remote_function/errors.py +102 -0
- sagemaker/core/remote_function/invoke_function.py +167 -0
- sagemaker/core/remote_function/job.py +2121 -0
- sagemaker/core/remote_function/logging_config.py +38 -0
- sagemaker/core/remote_function/runtime_environment/__init__.py +14 -0
- sagemaker/core/remote_function/runtime_environment/bootstrap_runtime_environment.py +605 -0
- sagemaker/core/remote_function/runtime_environment/mpi_utils_remote.py +252 -0
- sagemaker/core/remote_function/runtime_environment/runtime_environment_manager.py +554 -0
- sagemaker/core/remote_function/runtime_environment/spark_app.py +18 -0
- sagemaker/core/remote_function/spark_config.py +149 -0
- sagemaker/core/resource_requirements.py +168 -0
- {sagemaker_core/main → sagemaker/core}/resources.py +19098 -10895
- sagemaker/core/s3/__init__.py +41 -0
- sagemaker/core/s3/client.py +367 -0
- sagemaker/core/s3/utils.py +175 -0
- sagemaker/core/script_uris.py +93 -0
- sagemaker/core/serializers/__init__.py +11 -0
- sagemaker/core/serializers/base.py +510 -0
- sagemaker/core/serializers/implementations.py +159 -0
- sagemaker/core/serializers/utils.py +223 -0
- sagemaker/core/serverless_inference_config.py +63 -0
- sagemaker/core/session_settings.py +55 -0
- sagemaker/core/shapes/__init__.py +3 -0
- sagemaker/core/shapes/model_card_shapes.py +159 -0
- {sagemaker_core/main → sagemaker/core/shapes}/shapes.py +5810 -1806
- sagemaker/core/spark/__init__.py +16 -0
- sagemaker/core/spark/defaults.py +16 -0
- sagemaker/core/spark/processing.py +1380 -0
- sagemaker/core/telemetry/__init__.py +23 -0
- sagemaker/core/telemetry/constants.py +82 -0
- sagemaker/core/telemetry/telemetry_logging.py +285 -0
- sagemaker/core/tools/__init__.py +1 -0
- {sagemaker_core → sagemaker/core}/tools/codegen.py +4 -4
- {sagemaker_core → sagemaker/core}/tools/constants.py +23 -15
- {sagemaker_core → sagemaker/core}/tools/data_extractor.py +1 -1
- {sagemaker_core → sagemaker/core}/tools/method.py +1 -1
- sagemaker/core/tools/model_card/generate_model_card_from_schema.py +562 -0
- {sagemaker_core → sagemaker/core}/tools/resources_codegen.py +165 -98
- {sagemaker_core → sagemaker/core}/tools/resources_extractor.py +5 -13
- {sagemaker_core → sagemaker/core}/tools/shapes_codegen.py +16 -17
- {sagemaker_core → sagemaker/core}/tools/shapes_extractor.py +29 -67
- {sagemaker_core → sagemaker/core}/tools/templates.py +39 -17
- sagemaker/core/training/__init__.py +14 -0
- sagemaker/core/training/configs.py +345 -0
- sagemaker/core/training/constants.py +37 -0
- sagemaker/core/training/utils.py +77 -0
- sagemaker/core/training_compiler/__init__.py +16 -0
- sagemaker/core/training_compiler/config.py +197 -0
- sagemaker/core/training_compiler_config.py +197 -0
- sagemaker/core/transformer.py +793 -0
- sagemaker/core/user_agent.py +76 -0
- sagemaker/core/utilities/__init__.py +24 -0
- sagemaker/core/utilities/cache.py +169 -0
- sagemaker/core/utilities/search_expression.py +133 -0
- sagemaker/core/utils/__init__.py +48 -0
- sagemaker/core/utils/code_injection/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/codec.py +2 -2
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/shape_dag.py +5979 -176
- {sagemaker_core/main → sagemaker/core/utils}/exceptions.py +8 -8
- sagemaker_core/main/default_configs_helper.py → sagemaker/core/utils/intelligent_defaults_helper.py +5 -6
- {sagemaker_core/main → sagemaker/core/utils}/logs.py +1 -2
- {sagemaker_core/main → sagemaker/core/utils}/utils.py +27 -22
- sagemaker/core/workflow/__init__.py +152 -0
- sagemaker/core/workflow/conditions.py +313 -0
- sagemaker/core/workflow/entities.py +58 -0
- sagemaker/core/workflow/execution_variables.py +89 -0
- sagemaker/core/workflow/functions.py +193 -0
- sagemaker/core/workflow/parameters.py +222 -0
- sagemaker/core/workflow/pipeline_context.py +394 -0
- sagemaker/core/workflow/pipeline_definition_config.py +31 -0
- sagemaker/core/workflow/properties.py +285 -0
- sagemaker/core/workflow/step_outputs.py +65 -0
- sagemaker/core/workflow/utilities.py +514 -0
- sagemaker/lineage/__init__.py +33 -0
- sagemaker/lineage/action.py +28 -0
- sagemaker/lineage/artifact.py +28 -0
- sagemaker/lineage/context.py +28 -0
- sagemaker/lineage/lineage_trial_component.py +28 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/METADATA +28 -9
- sagemaker_core-2.3.1.dist-info/RECORD +351 -0
- sagemaker_core-2.3.1.dist-info/top_level.txt +1 -0
- sagemaker_core/_version.py +0 -3
- sagemaker_core/helper/session_helper.py +0 -769
- sagemaker_core/resources/__init__.py +0 -1
- sagemaker_core/shapes/__init__.py +0 -1
- sagemaker_core/tools/__init__.py +0 -1
- sagemaker_core-1.0.62.dist-info/RECORD +0 -35
- sagemaker_core-1.0.62.dist-info/top_level.txt +0 -1
- {sagemaker_core → sagemaker/core/helper}/__init__.py +0 -0
- {sagemaker_core/helper → sagemaker/core/jumpstart/factory}/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/jumpstart/hub}/__init__.py +0 -0
- {sagemaker_core/main/code_injection → sagemaker/core/modules/local_core}/__init__.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/base.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/code_injection/constants.py +0 -0
- {sagemaker_core/main → sagemaker/core/utils}/user_agent.py +0 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/WHEEL +0 -0
- {sagemaker_core-1.0.62.dist-info → sagemaker_core-2.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,769 +0,0 @@
|
|
|
1
|
-
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"). You
|
|
4
|
-
# may not use this file except in compliance with the License. A copy of
|
|
5
|
-
# the License is located at
|
|
6
|
-
#
|
|
7
|
-
# http://aws.amazon.com/apache2.0/
|
|
8
|
-
#
|
|
9
|
-
# or in the "license" file accompanying this file. This file is
|
|
10
|
-
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
11
|
-
# ANY KIND, either express or implied. See the License for the specific
|
|
12
|
-
# language governing permissions and limitations under the License.
|
|
13
|
-
from __future__ import absolute_import, annotations, print_function
|
|
14
|
-
|
|
15
|
-
import json
|
|
16
|
-
import logging
|
|
17
|
-
import os
|
|
18
|
-
import re
|
|
19
|
-
from functools import reduce
|
|
20
|
-
from typing import Dict, Optional
|
|
21
|
-
|
|
22
|
-
import boto3
|
|
23
|
-
import botocore
|
|
24
|
-
import botocore.config
|
|
25
|
-
from botocore.exceptions import ClientError
|
|
26
|
-
|
|
27
|
-
# Setting LOGGER for backward compatibility, in case users import it...
|
|
28
|
-
logger = LOGGER = logging.getLogger("sagemaker")
|
|
29
|
-
|
|
30
|
-
NOTEBOOK_METADATA_FILE = "/opt/ml/metadata/resource-metadata.json"
|
|
31
|
-
MODEL_MONITOR_ONE_TIME_SCHEDULE = "NOW"
|
|
32
|
-
_STATUS_CODE_TABLE = {
|
|
33
|
-
"COMPLETED": "Completed",
|
|
34
|
-
"INPROGRESS": "InProgress",
|
|
35
|
-
"IN_PROGRESS": "InProgress",
|
|
36
|
-
"FAILED": "Failed",
|
|
37
|
-
"STOPPED": "Stopped",
|
|
38
|
-
"STOPPING": "Stopping",
|
|
39
|
-
"STARTING": "Starting",
|
|
40
|
-
"PENDING": "Pending",
|
|
41
|
-
}
|
|
42
|
-
EP_LOGGER_POLL = 10
|
|
43
|
-
DEFAULT_EP_POLL = 30
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class LogState(object):
|
|
47
|
-
"""Placeholder docstring"""
|
|
48
|
-
|
|
49
|
-
STARTING = 1
|
|
50
|
-
WAIT_IN_PROGRESS = 2
|
|
51
|
-
TAILING = 3
|
|
52
|
-
JOB_COMPLETE = 4
|
|
53
|
-
COMPLETE = 5
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
class Session(object): # pylint: disable=too-many-public-methods
|
|
57
|
-
"""Manage interactions with the Amazon SageMaker APIs and any other AWS services needed.
|
|
58
|
-
|
|
59
|
-
This class provides convenient methods for manipulating entities and resources that Amazon
|
|
60
|
-
SageMaker uses, such as training jobs, endpoints, and input datasets in S3.
|
|
61
|
-
AWS service calls are delegated to an underlying Boto3 session, which by default
|
|
62
|
-
is initialized using the AWS configuration chain. When you make an Amazon SageMaker API call
|
|
63
|
-
that accesses an S3 bucket location and one is not specified, the ``Session`` creates a default
|
|
64
|
-
bucket based on a naming convention which includes the current AWS account ID.
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
def __init__(
|
|
68
|
-
self,
|
|
69
|
-
boto_session=None,
|
|
70
|
-
sagemaker_client=None,
|
|
71
|
-
sagemaker_runtime_client=None,
|
|
72
|
-
sagemaker_featurestore_runtime_client=None,
|
|
73
|
-
default_bucket=None,
|
|
74
|
-
sagemaker_metrics_client=None,
|
|
75
|
-
default_bucket_prefix: str = None,
|
|
76
|
-
):
|
|
77
|
-
"""Initialize a SageMaker ``Session``.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
boto_session (boto3.session.Session): The underlying Boto3 session which AWS service
|
|
81
|
-
calls are delegated to (default: None). If not provided, one is created with
|
|
82
|
-
default AWS configuration chain.
|
|
83
|
-
sagemaker_client (boto3.SageMaker.Client): Client which makes Amazon SageMaker service
|
|
84
|
-
calls other than ``InvokeEndpoint`` (default: None). Estimators created using this
|
|
85
|
-
``Session`` use this client. If not provided, one will be created using this
|
|
86
|
-
instance's ``boto_session``.
|
|
87
|
-
sagemaker_runtime_client (boto3.SageMakerRuntime.Client): Client which makes
|
|
88
|
-
``InvokeEndpoint`` calls to Amazon SageMaker (default: None). Predictors created
|
|
89
|
-
using this ``Session`` use this client. If not provided, one will be created using
|
|
90
|
-
this instance's ``boto_session``.
|
|
91
|
-
sagemaker_featurestore_runtime_client (boto3.SageMakerFeatureStoreRuntime.Client):
|
|
92
|
-
Client which makes SageMaker FeatureStore record related calls to Amazon SageMaker
|
|
93
|
-
(default: None). If not provided, one will be created using
|
|
94
|
-
this instance's ``boto_session``.
|
|
95
|
-
default_bucket (str): The default Amazon S3 bucket to be used by this session.
|
|
96
|
-
This will be created the next time an Amazon S3 bucket is needed (by calling
|
|
97
|
-
:func:`default_bucket`).
|
|
98
|
-
If not provided, it will be fetched from the sagemaker_config. If not configured
|
|
99
|
-
there either, a default bucket will be created based on the following format:
|
|
100
|
-
"sagemaker-{region}-{aws-account-id}".
|
|
101
|
-
Example: "sagemaker-my-custom-bucket".
|
|
102
|
-
sagemaker_metrics_client (boto3.SageMakerMetrics.Client):
|
|
103
|
-
Client which makes SageMaker Metrics related calls to Amazon SageMaker
|
|
104
|
-
(default: None). If not provided, one will be created using
|
|
105
|
-
this instance's ``boto_session``.
|
|
106
|
-
default_bucket_prefix (str): The default prefix to use for S3 Object Keys. (default:
|
|
107
|
-
None). If provided and where applicable, it will be used by the SDK to construct
|
|
108
|
-
default S3 URIs, in the format:
|
|
109
|
-
`s3://{default_bucket}/{default_bucket_prefix}/<rest of object key>`
|
|
110
|
-
This parameter can also be specified via `{sagemaker_config}` instead of here. If
|
|
111
|
-
not provided here or within `{sagemaker_config}`, default S3 URIs will have the
|
|
112
|
-
format: `s3://{default_bucket}/<rest of object key>`
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
# sagemaker_config is validated and initialized inside :func:`_initialize`,
|
|
116
|
-
# so if default_bucket is None and the sagemaker_config has a default S3 bucket configured,
|
|
117
|
-
# _default_bucket_name_override will be set again inside :func:`_initialize`.
|
|
118
|
-
self.endpoint_arn = None
|
|
119
|
-
self._default_bucket = None
|
|
120
|
-
self._default_bucket_name_override = default_bucket
|
|
121
|
-
# this may also be set again inside :func:`_initialize` if it is None
|
|
122
|
-
self.default_bucket_prefix = default_bucket_prefix
|
|
123
|
-
self._default_bucket_set_by_sdk = False
|
|
124
|
-
|
|
125
|
-
self.s3_resource = None
|
|
126
|
-
self.s3_client = None
|
|
127
|
-
self.resource_groups_client = None
|
|
128
|
-
self.resource_group_tagging_client = None
|
|
129
|
-
self._config = None
|
|
130
|
-
self.lambda_client = None
|
|
131
|
-
|
|
132
|
-
self._initialize(
|
|
133
|
-
boto_session=boto_session,
|
|
134
|
-
sagemaker_client=sagemaker_client,
|
|
135
|
-
sagemaker_runtime_client=sagemaker_runtime_client,
|
|
136
|
-
sagemaker_featurestore_runtime_client=sagemaker_featurestore_runtime_client,
|
|
137
|
-
sagemaker_metrics_client=sagemaker_metrics_client,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
def _initialize(
|
|
141
|
-
self,
|
|
142
|
-
boto_session,
|
|
143
|
-
sagemaker_client,
|
|
144
|
-
sagemaker_runtime_client,
|
|
145
|
-
sagemaker_featurestore_runtime_client,
|
|
146
|
-
sagemaker_metrics_client,
|
|
147
|
-
):
|
|
148
|
-
"""Initialize this SageMaker Session.
|
|
149
|
-
|
|
150
|
-
Creates or uses a boto_session, sagemaker_client and sagemaker_runtime_client.
|
|
151
|
-
Sets the region_name.
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
self.boto_session = boto_session or boto3.DEFAULT_SESSION or boto3.Session()
|
|
155
|
-
|
|
156
|
-
self._region_name = self.boto_session.region_name
|
|
157
|
-
if self._region_name is None:
|
|
158
|
-
raise ValueError(
|
|
159
|
-
"Must setup local AWS configuration with a region supported by SageMaker."
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
# Make use of user_agent_extra field of the botocore_config object
|
|
163
|
-
# to append SageMaker Python SDK specific user_agent suffix
|
|
164
|
-
# to the current User-Agent header value from boto3
|
|
165
|
-
# This config will also make sure that user_agent never fails to log the User-Agent string
|
|
166
|
-
# even if boto User-Agent header format is updated in the future
|
|
167
|
-
# Ref: https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html
|
|
168
|
-
|
|
169
|
-
# Create sagemaker_client with the botocore_config object
|
|
170
|
-
# This config is customized to append SageMaker Python SDK specific user_agent suffix
|
|
171
|
-
self.sagemaker_client = sagemaker_client or self.boto_session.client("sagemaker")
|
|
172
|
-
|
|
173
|
-
if sagemaker_runtime_client is not None:
|
|
174
|
-
self.sagemaker_runtime_client = sagemaker_runtime_client
|
|
175
|
-
else:
|
|
176
|
-
config = botocore.config.Config(read_timeout=80)
|
|
177
|
-
self.sagemaker_runtime_client = self.boto_session.client(
|
|
178
|
-
"runtime.sagemaker", config=config
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
if sagemaker_featurestore_runtime_client:
|
|
182
|
-
self.sagemaker_featurestore_runtime_client = sagemaker_featurestore_runtime_client
|
|
183
|
-
else:
|
|
184
|
-
self.sagemaker_featurestore_runtime_client = self.boto_session.client(
|
|
185
|
-
"sagemaker-featurestore-runtime"
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
if sagemaker_metrics_client:
|
|
189
|
-
self.sagemaker_metrics_client = sagemaker_metrics_client
|
|
190
|
-
else:
|
|
191
|
-
self.sagemaker_metrics_client = self.boto_session.client("sagemaker-metrics")
|
|
192
|
-
|
|
193
|
-
self.s3_client = self.boto_session.client("s3", region_name=self.boto_region_name)
|
|
194
|
-
self.s3_resource = self.boto_session.resource("s3", region_name=self.boto_region_name)
|
|
195
|
-
|
|
196
|
-
self.local_mode = False
|
|
197
|
-
|
|
198
|
-
def account_id(self) -> str:
|
|
199
|
-
"""Get the AWS account id of the caller.
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
AWS account ID.
|
|
203
|
-
"""
|
|
204
|
-
region = self.boto_session.region_name
|
|
205
|
-
sts_client = self.boto_session.client(
|
|
206
|
-
"sts", region_name=region, endpoint_url=sts_regional_endpoint(region)
|
|
207
|
-
)
|
|
208
|
-
return sts_client.get_caller_identity()["Account"]
|
|
209
|
-
|
|
210
|
-
@property
|
|
211
|
-
def config(self) -> Dict | None:
|
|
212
|
-
"""The config for the local mode, unused in a normal session"""
|
|
213
|
-
return self._config
|
|
214
|
-
|
|
215
|
-
@config.setter
|
|
216
|
-
def config(self, value: Dict | None):
|
|
217
|
-
"""The config for the local mode, unused in a normal session"""
|
|
218
|
-
self._config = value
|
|
219
|
-
|
|
220
|
-
@property
|
|
221
|
-
def boto_region_name(self):
|
|
222
|
-
"""Placeholder docstring"""
|
|
223
|
-
return self._region_name
|
|
224
|
-
|
|
225
|
-
def get_caller_identity_arn(self):
|
|
226
|
-
"""Returns the ARN user or role whose credentials are used to call the API.
|
|
227
|
-
|
|
228
|
-
Returns:
|
|
229
|
-
str: The ARN user or role
|
|
230
|
-
"""
|
|
231
|
-
if os.path.exists(NOTEBOOK_METADATA_FILE):
|
|
232
|
-
with open(NOTEBOOK_METADATA_FILE, "rb") as f:
|
|
233
|
-
metadata = json.loads(f.read())
|
|
234
|
-
instance_name = metadata.get("ResourceName")
|
|
235
|
-
domain_id = metadata.get("DomainId")
|
|
236
|
-
user_profile_name = metadata.get("UserProfileName")
|
|
237
|
-
execution_role_arn = metadata.get("ExecutionRoleArn")
|
|
238
|
-
try:
|
|
239
|
-
if domain_id is None:
|
|
240
|
-
instance_desc = self.sagemaker_client.describe_notebook_instance(
|
|
241
|
-
NotebookInstanceName=instance_name
|
|
242
|
-
)
|
|
243
|
-
return instance_desc["RoleArn"]
|
|
244
|
-
|
|
245
|
-
# find execution role from the metadata file if present
|
|
246
|
-
if execution_role_arn is not None:
|
|
247
|
-
return execution_role_arn
|
|
248
|
-
|
|
249
|
-
user_profile_desc = self.sagemaker_client.describe_user_profile(
|
|
250
|
-
DomainId=domain_id, UserProfileName=user_profile_name
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
# First, try to find role in userSettings
|
|
254
|
-
if user_profile_desc.get("UserSettings", {}).get("ExecutionRole"):
|
|
255
|
-
return user_profile_desc["UserSettings"]["ExecutionRole"]
|
|
256
|
-
|
|
257
|
-
# If not found, fallback to the domain
|
|
258
|
-
domain_desc = self.sagemaker_client.describe_domain(DomainId=domain_id)
|
|
259
|
-
return domain_desc["DefaultUserSettings"]["ExecutionRole"]
|
|
260
|
-
except ClientError:
|
|
261
|
-
logger.debug(
|
|
262
|
-
"Couldn't call 'describe_notebook_instance' to get the Role "
|
|
263
|
-
"ARN of the instance %s.",
|
|
264
|
-
instance_name,
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
assumed_role = self.boto_session.client(
|
|
268
|
-
"sts",
|
|
269
|
-
region_name=self.boto_region_name,
|
|
270
|
-
endpoint_url=sts_regional_endpoint(self.boto_region_name),
|
|
271
|
-
).get_caller_identity()["Arn"]
|
|
272
|
-
|
|
273
|
-
role = re.sub(r"^(.+)sts::(\d+):assumed-role/(.+?)/.*$", r"\1iam::\2:role/\3", assumed_role)
|
|
274
|
-
|
|
275
|
-
# Call IAM to get the role's path
|
|
276
|
-
role_name = role[role.rfind("/") + 1 :]
|
|
277
|
-
try:
|
|
278
|
-
role = self.boto_session.client("iam").get_role(RoleName=role_name)["Role"]["Arn"]
|
|
279
|
-
except ClientError:
|
|
280
|
-
logger.warning(
|
|
281
|
-
"Couldn't call 'get_role' to get Role ARN from role name %s to get Role path.",
|
|
282
|
-
role_name,
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
# This conditional has been present since the inception of SageMaker
|
|
286
|
-
# Guessing this conditional's purpose was to handle lack of IAM permissions
|
|
287
|
-
# https://github.com/aws/sagemaker-python-sdk/issues/2089#issuecomment-791802713
|
|
288
|
-
if "AmazonSageMaker-ExecutionRole" in assumed_role:
|
|
289
|
-
logger.warning(
|
|
290
|
-
"Assuming role was created in SageMaker AWS console, "
|
|
291
|
-
"as the name contains `AmazonSageMaker-ExecutionRole`. "
|
|
292
|
-
"Defaulting to Role ARN with service-role in path. "
|
|
293
|
-
"If this Role ARN is incorrect, please add "
|
|
294
|
-
"IAM read permissions to your role or supply the "
|
|
295
|
-
"Role Arn directly."
|
|
296
|
-
)
|
|
297
|
-
role = re.sub(
|
|
298
|
-
r"^(.+)sts::(\d+):assumed-role/(.+?)/.*$",
|
|
299
|
-
r"\1iam::\2:role/service-role/\3",
|
|
300
|
-
assumed_role,
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
return role
|
|
304
|
-
|
|
305
|
-
def upload_data(self, path, bucket=None, key_prefix="data", callback=None, extra_args=None):
|
|
306
|
-
"""Upload local file or directory to S3.
|
|
307
|
-
|
|
308
|
-
If a single file is specified for upload, the resulting S3 object key is
|
|
309
|
-
``{key_prefix}/{filename}`` (filename does not include the local path, if any specified).
|
|
310
|
-
If a directory is specified for upload, the API uploads all content, recursively,
|
|
311
|
-
preserving relative structure of subdirectories. The resulting object key names are:
|
|
312
|
-
``{key_prefix}/{relative_subdirectory_path}/filename``.
|
|
313
|
-
|
|
314
|
-
Args:
|
|
315
|
-
path (str): Path (absolute or relative) of local file or directory to upload.
|
|
316
|
-
bucket (str): Name of the S3 Bucket to upload to (default: None). If not specified, the
|
|
317
|
-
default bucket of the ``Session`` is used (if default bucket does not exist, the
|
|
318
|
-
``Session`` creates it).
|
|
319
|
-
key_prefix (str): Optional S3 object key name prefix (default: 'data'). S3 uses the
|
|
320
|
-
prefix to create a directory structure for the bucket content that it display in
|
|
321
|
-
the S3 console.
|
|
322
|
-
extra_args (dict): Optional extra arguments that may be passed to the upload operation.
|
|
323
|
-
Similar to ExtraArgs parameter in S3 upload_file function. Please refer to the
|
|
324
|
-
ExtraArgs parameter documentation here:
|
|
325
|
-
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-uploading-files.html#the-extraargs-parameter
|
|
326
|
-
|
|
327
|
-
Returns:
|
|
328
|
-
str: The S3 URI of the uploaded file(s). If a file is specified in the path argument,
|
|
329
|
-
the URI format is: ``s3://{bucket name}/{key_prefix}/{original_file_name}``.
|
|
330
|
-
If a directory is specified in the path argument, the URI format is
|
|
331
|
-
``s3://{bucket name}/{key_prefix}``.
|
|
332
|
-
"""
|
|
333
|
-
bucket, key_prefix = self.determine_bucket_and_prefix(
|
|
334
|
-
bucket=bucket, key_prefix=key_prefix, sagemaker_session=self
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
# Generate a tuple for each file that we want to upload of the form (local_path, s3_key).
|
|
338
|
-
files = []
|
|
339
|
-
key_suffix = None
|
|
340
|
-
if os.path.isdir(path):
|
|
341
|
-
for dirpath, _, filenames in os.walk(path):
|
|
342
|
-
for name in filenames:
|
|
343
|
-
local_path = os.path.join(dirpath, name)
|
|
344
|
-
s3_relative_prefix = (
|
|
345
|
-
"" if path == dirpath else os.path.relpath(dirpath, start=path) + "/"
|
|
346
|
-
)
|
|
347
|
-
s3_key = "{}/{}{}".format(key_prefix, s3_relative_prefix, name)
|
|
348
|
-
files.append((local_path, s3_key))
|
|
349
|
-
else:
|
|
350
|
-
_, name = os.path.split(path)
|
|
351
|
-
s3_key = "{}/{}".format(key_prefix, name)
|
|
352
|
-
files.append((path, s3_key))
|
|
353
|
-
key_suffix = name
|
|
354
|
-
|
|
355
|
-
if self.s3_resource is None:
|
|
356
|
-
s3 = self.boto_session.resource("s3", region_name=self.boto_region_name)
|
|
357
|
-
else:
|
|
358
|
-
s3 = self.s3_resource
|
|
359
|
-
|
|
360
|
-
for local_path, s3_key in files:
|
|
361
|
-
s3.Object(bucket, s3_key).upload_file(
|
|
362
|
-
local_path, Callback=callback, ExtraArgs=extra_args
|
|
363
|
-
)
|
|
364
|
-
|
|
365
|
-
s3_uri = "s3://{}/{}".format(bucket, key_prefix)
|
|
366
|
-
# If a specific file was used as input (instead of a directory), we return the full S3 key
|
|
367
|
-
# of the uploaded object. This prevents unintentionally using other files under the same
|
|
368
|
-
# prefix during training.
|
|
369
|
-
if key_suffix:
|
|
370
|
-
s3_uri = "{}/{}".format(s3_uri, key_suffix)
|
|
371
|
-
return s3_uri
|
|
372
|
-
|
|
373
|
-
def read_s3_file(self, bucket, key_prefix):
|
|
374
|
-
"""Read a single file from S3.
|
|
375
|
-
|
|
376
|
-
Args:
|
|
377
|
-
bucket (str): Name of the S3 Bucket to download from.
|
|
378
|
-
key_prefix (str): S3 object key name prefix.
|
|
379
|
-
|
|
380
|
-
Returns:
|
|
381
|
-
str: The body of the s3 file as a string.
|
|
382
|
-
"""
|
|
383
|
-
if self.s3_client is None:
|
|
384
|
-
s3 = self.boto_session.client("s3", region_name=self.boto_region_name)
|
|
385
|
-
else:
|
|
386
|
-
s3 = self.s3_client
|
|
387
|
-
|
|
388
|
-
# Explicitly passing a None kms_key to boto3 throws a validation error.
|
|
389
|
-
s3_object = s3.get_object(Bucket=bucket, Key=key_prefix)
|
|
390
|
-
|
|
391
|
-
return s3_object["Body"].read().decode("utf-8")
|
|
392
|
-
|
|
393
|
-
def default_bucket(self):
|
|
394
|
-
"""Return the name of the default bucket to use in relevant Amazon SageMaker interactions.
|
|
395
|
-
|
|
396
|
-
This function will create the s3 bucket if it does not exist.
|
|
397
|
-
|
|
398
|
-
Returns:
|
|
399
|
-
str: The name of the default bucket. If the name was not explicitly specified through
|
|
400
|
-
the Session or sagemaker_config, the bucket will take the form:
|
|
401
|
-
``sagemaker-{region}-{AWS account ID}``.
|
|
402
|
-
"""
|
|
403
|
-
|
|
404
|
-
if self._default_bucket:
|
|
405
|
-
return self._default_bucket
|
|
406
|
-
|
|
407
|
-
region = self.boto_session.region_name
|
|
408
|
-
|
|
409
|
-
default_bucket = self._default_bucket_name_override
|
|
410
|
-
if not default_bucket:
|
|
411
|
-
default_bucket = self.generate_default_sagemaker_bucket_name(self.boto_session)
|
|
412
|
-
self._default_bucket_set_by_sdk = True
|
|
413
|
-
|
|
414
|
-
self._create_s3_bucket_if_it_does_not_exist(
|
|
415
|
-
bucket_name=default_bucket,
|
|
416
|
-
region=region,
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
self._default_bucket = default_bucket
|
|
420
|
-
|
|
421
|
-
return self._default_bucket
|
|
422
|
-
|
|
423
|
-
def _create_s3_bucket_if_it_does_not_exist(self, bucket_name, region):
|
|
424
|
-
"""Creates an S3 Bucket if it does not exist.
|
|
425
|
-
|
|
426
|
-
Also swallows a few common exceptions that indicate that the bucket already exists or
|
|
427
|
-
that it is being created.
|
|
428
|
-
|
|
429
|
-
Args:
|
|
430
|
-
bucket_name (str): Name of the S3 bucket to be created.
|
|
431
|
-
region (str): The region in which to create the bucket.
|
|
432
|
-
|
|
433
|
-
Raises:
|
|
434
|
-
botocore.exceptions.ClientError: If S3 throws an unexpected exception during bucket
|
|
435
|
-
creation.
|
|
436
|
-
If the exception is due to the bucket already existing or
|
|
437
|
-
already being created, no exception is raised.
|
|
438
|
-
"""
|
|
439
|
-
if self.s3_resource is None:
|
|
440
|
-
s3 = self.boto_session.resource("s3", region_name=region)
|
|
441
|
-
else:
|
|
442
|
-
s3 = self.s3_resource
|
|
443
|
-
|
|
444
|
-
bucket = s3.Bucket(name=bucket_name)
|
|
445
|
-
if bucket.creation_date is None:
|
|
446
|
-
self.general_bucket_check_if_user_has_permission(bucket_name, s3, bucket, region, True)
|
|
447
|
-
|
|
448
|
-
elif self._default_bucket_set_by_sdk:
|
|
449
|
-
self.general_bucket_check_if_user_has_permission(bucket_name, s3, bucket, region, False)
|
|
450
|
-
|
|
451
|
-
expected_bucket_owner_id = self.account_id()
|
|
452
|
-
self.expected_bucket_owner_id_bucket_check(bucket_name, s3, expected_bucket_owner_id)
|
|
453
|
-
|
|
454
|
-
def expected_bucket_owner_id_bucket_check(self, bucket_name, s3, expected_bucket_owner_id):
|
|
455
|
-
"""Checks if the bucket belongs to a particular owner and throws a Client Error if it is not
|
|
456
|
-
|
|
457
|
-
Args:
|
|
458
|
-
bucket_name (str): Name of the S3 bucket
|
|
459
|
-
s3 (str): S3 object from boto session
|
|
460
|
-
expected_bucket_owner_id (str): Owner ID string
|
|
461
|
-
|
|
462
|
-
"""
|
|
463
|
-
try:
|
|
464
|
-
s3.meta.client.head_bucket(
|
|
465
|
-
Bucket=bucket_name, ExpectedBucketOwner=expected_bucket_owner_id
|
|
466
|
-
)
|
|
467
|
-
except ClientError as e:
|
|
468
|
-
error_code = e.response["Error"]["Code"]
|
|
469
|
-
message = e.response["Error"]["Message"]
|
|
470
|
-
if error_code == "403" and message == "Forbidden":
|
|
471
|
-
LOGGER.error(
|
|
472
|
-
"Since default_bucket param was not set, SageMaker Python SDK tried to use "
|
|
473
|
-
"%s bucket. "
|
|
474
|
-
"This bucket cannot be configured to use as it is not owned by Account %s. "
|
|
475
|
-
"To unblock it's recommended to use custom default_bucket "
|
|
476
|
-
"parameter in sagemaker.Session",
|
|
477
|
-
bucket_name,
|
|
478
|
-
expected_bucket_owner_id,
|
|
479
|
-
)
|
|
480
|
-
raise
|
|
481
|
-
|
|
482
|
-
def general_bucket_check_if_user_has_permission(
|
|
483
|
-
self, bucket_name, s3, bucket, region, bucket_creation_date_none
|
|
484
|
-
):
|
|
485
|
-
"""Checks if the person running has the permissions to the bucket
|
|
486
|
-
|
|
487
|
-
If there is any other error that comes up with calling head bucket, it is raised up here
|
|
488
|
-
If there is no bucket , it will create one
|
|
489
|
-
|
|
490
|
-
Args:
|
|
491
|
-
bucket_name (str): Name of the S3 bucket
|
|
492
|
-
s3 (str): S3 object from boto session
|
|
493
|
-
region (str): The region in which to create the bucket.
|
|
494
|
-
bucket_creation_date_none (bool):Indicating whether S3 bucket already exists or not
|
|
495
|
-
"""
|
|
496
|
-
try:
|
|
497
|
-
s3.meta.client.head_bucket(Bucket=bucket_name)
|
|
498
|
-
except ClientError as e:
|
|
499
|
-
error_code = e.response["Error"]["Code"]
|
|
500
|
-
message = e.response["Error"]["Message"]
|
|
501
|
-
# bucket does not exist or forbidden to access
|
|
502
|
-
if bucket_creation_date_none:
|
|
503
|
-
if error_code == "404" and message == "Not Found":
|
|
504
|
-
self.create_bucket_for_not_exist_error(bucket_name, region, s3)
|
|
505
|
-
elif error_code == "403" and message == "Forbidden":
|
|
506
|
-
LOGGER.error(
|
|
507
|
-
"Bucket %s exists, but access is forbidden. Please try again after "
|
|
508
|
-
"adding appropriate access.",
|
|
509
|
-
bucket.name,
|
|
510
|
-
)
|
|
511
|
-
raise
|
|
512
|
-
else:
|
|
513
|
-
raise
|
|
514
|
-
|
|
515
|
-
def create_bucket_for_not_exist_error(self, bucket_name, region, s3):
|
|
516
|
-
"""Creates the S3 bucket in the given region
|
|
517
|
-
|
|
518
|
-
Args:
|
|
519
|
-
bucket_name (str): Name of the S3 bucket
|
|
520
|
-
s3 (str): S3 object from boto session
|
|
521
|
-
region (str): The region in which to create the bucket.
|
|
522
|
-
"""
|
|
523
|
-
# bucket does not exist, create one
|
|
524
|
-
try:
|
|
525
|
-
if region == "us-east-1":
|
|
526
|
-
# 'us-east-1' cannot be specified because it is the default region:
|
|
527
|
-
# https://github.com/boto/boto3/issues/125
|
|
528
|
-
s3.create_bucket(Bucket=bucket_name)
|
|
529
|
-
else:
|
|
530
|
-
s3.create_bucket(
|
|
531
|
-
Bucket=bucket_name,
|
|
532
|
-
CreateBucketConfiguration={"LocationConstraint": region},
|
|
533
|
-
)
|
|
534
|
-
|
|
535
|
-
logger.info("Created S3 bucket: %s", bucket_name)
|
|
536
|
-
except ClientError as e:
|
|
537
|
-
error_code = e.response["Error"]["Code"]
|
|
538
|
-
message = e.response["Error"]["Message"]
|
|
539
|
-
|
|
540
|
-
if error_code == "OperationAborted" and "conflicting conditional operation" in message:
|
|
541
|
-
# If this bucket is already being concurrently created,
|
|
542
|
-
# we don't need to create it again.
|
|
543
|
-
pass
|
|
544
|
-
else:
|
|
545
|
-
raise
|
|
546
|
-
|
|
547
|
-
def generate_default_sagemaker_bucket_name(self, boto_session):
|
|
548
|
-
"""Generates a name for the default sagemaker S3 bucket.
|
|
549
|
-
|
|
550
|
-
Args:
|
|
551
|
-
boto_session (boto3.session.Session): The underlying Boto3 session which AWS service
|
|
552
|
-
"""
|
|
553
|
-
region = boto_session.region_name
|
|
554
|
-
account = boto_session.client(
|
|
555
|
-
"sts", region_name=region, endpoint_url=sts_regional_endpoint(region)
|
|
556
|
-
).get_caller_identity()["Account"]
|
|
557
|
-
return "sagemaker-{}-{}".format(region, account)
|
|
558
|
-
|
|
559
|
-
def determine_bucket_and_prefix(
|
|
560
|
-
self, bucket: Optional[str] = None, key_prefix: Optional[str] = None, sagemaker_session=None
|
|
561
|
-
):
|
|
562
|
-
"""Helper function that returns the correct S3 bucket and prefix to use depending on the inputs.
|
|
563
|
-
|
|
564
|
-
Args:
|
|
565
|
-
bucket (Optional[str]): S3 Bucket to use (if it exists)
|
|
566
|
-
key_prefix (Optional[str]): S3 Object Key Prefix to use or append to (if it exists)
|
|
567
|
-
sagemaker_session (sagemaker.session.Session): Session to fetch a default bucket and
|
|
568
|
-
prefix from, if bucket doesn't exist. Expected to exist
|
|
569
|
-
|
|
570
|
-
Returns: The correct S3 Bucket and S3 Object Key Prefix that should be used
|
|
571
|
-
"""
|
|
572
|
-
if bucket:
|
|
573
|
-
final_bucket = bucket
|
|
574
|
-
final_key_prefix = key_prefix
|
|
575
|
-
else:
|
|
576
|
-
final_bucket = sagemaker_session.default_bucket()
|
|
577
|
-
|
|
578
|
-
# default_bucket_prefix (if it exists) should be appended if (and only if) 'bucket' does not
|
|
579
|
-
# exist and we are using the Session's default_bucket.
|
|
580
|
-
final_key_prefix = s3_path_join(sagemaker_session.default_bucket_prefix, key_prefix)
|
|
581
|
-
|
|
582
|
-
# We should not append default_bucket_prefix even if the bucket exists but is equal to the
|
|
583
|
-
# default_bucket, because either:
|
|
584
|
-
# (1) the bucket was explicitly passed in by the user and just happens to be the same as the
|
|
585
|
-
# default_bucket (in which case we don't want to change the user's input), or
|
|
586
|
-
# (2) the default_bucket was fetched from Session earlier already (and the default prefix
|
|
587
|
-
# should have been fetched then as well), and then this function was
|
|
588
|
-
# called with it. If we appended the default prefix here, we would be appending it more than
|
|
589
|
-
# once in total.
|
|
590
|
-
|
|
591
|
-
return final_bucket, final_key_prefix
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
def s3_path_join(*args, with_end_slash: bool = False):
|
|
595
|
-
"""Returns the arguments joined by a slash ("/"), similar to ``os.path.join()`` (on Unix).
|
|
596
|
-
|
|
597
|
-
Behavior of this function:
|
|
598
|
-
- If the first argument is "s3://", then that is preserved.
|
|
599
|
-
- The output by default will have no slashes at the beginning or end. There is one exception
|
|
600
|
-
(see `with_end_slash`). For example, `s3_path_join("/foo", "bar/")` will yield
|
|
601
|
-
`"foo/bar"` and `s3_path_join("foo", "bar", with_end_slash=True)` will yield `"foo/bar/"`
|
|
602
|
-
- Any repeat slashes will be removed in the output (except for "s3://" if provided at the
|
|
603
|
-
beginning). For example, `s3_path_join("s3://", "//foo/", "/bar///baz")` will yield
|
|
604
|
-
`"s3://foo/bar/baz"`.
|
|
605
|
-
- Empty or None arguments will be skipped. For example
|
|
606
|
-
`s3_path_join("foo", "", None, "bar")` will yield `"foo/bar"`
|
|
607
|
-
|
|
608
|
-
Alternatives to this function that are NOT recommended for S3 paths:
|
|
609
|
-
- `os.path.join(...)` will have different behavior on Unix machines vs non-Unix machines
|
|
610
|
-
- `pathlib.PurePosixPath(...)` will apply potentially unintended simplification of single
|
|
611
|
-
dots (".") and root directories. (for example
|
|
612
|
-
`pathlib.PurePosixPath("foo", "/bar/./", "baz")` would yield `"/bar/baz"`)
|
|
613
|
-
- `"{}/{}/{}".format(...)` and similar may result in unintended repeat slashes
|
|
614
|
-
|
|
615
|
-
Args:
|
|
616
|
-
*args: The strings to join with a slash.
|
|
617
|
-
with_end_slash (bool): (default: False) If true and if the path is not empty, appends a "/"
|
|
618
|
-
to the end of the path
|
|
619
|
-
|
|
620
|
-
Returns:
|
|
621
|
-
str: The joined string, without a slash at the end unless with_end_slash is True.
|
|
622
|
-
"""
|
|
623
|
-
delimiter = "/"
|
|
624
|
-
|
|
625
|
-
non_empty_args = list(filter(lambda item: item is not None and item != "", args))
|
|
626
|
-
|
|
627
|
-
merged_path = ""
|
|
628
|
-
for index, path in enumerate(non_empty_args):
|
|
629
|
-
if (
|
|
630
|
-
index == 0
|
|
631
|
-
or (merged_path and merged_path[-1] == delimiter)
|
|
632
|
-
or (path and path[0] == delimiter)
|
|
633
|
-
):
|
|
634
|
-
# dont need to add an extra slash because either this is the beginning of the string,
|
|
635
|
-
# or one (or more) slash already exists
|
|
636
|
-
merged_path += path
|
|
637
|
-
else:
|
|
638
|
-
merged_path += delimiter + path
|
|
639
|
-
|
|
640
|
-
if with_end_slash and merged_path and merged_path[-1] != delimiter:
|
|
641
|
-
merged_path += delimiter
|
|
642
|
-
|
|
643
|
-
# At this point, merged_path may include slashes at the beginning and/or end. And some of the
|
|
644
|
-
# provided args may have had duplicate slashes inside or at the ends.
|
|
645
|
-
# For backwards compatibility reasons, these need to be filtered out (done below). In the
|
|
646
|
-
# future, if there is a desire to support multiple slashes for S3 paths throughout the SDK,
|
|
647
|
-
# one option is to create a new optional argument (or a new function) that only executes the
|
|
648
|
-
# logic above.
|
|
649
|
-
filtered_path = merged_path
|
|
650
|
-
|
|
651
|
-
# remove duplicate slashes
|
|
652
|
-
if filtered_path:
|
|
653
|
-
|
|
654
|
-
def duplicate_delimiter_remover(sequence, next_char):
|
|
655
|
-
if sequence[-1] == delimiter and next_char == delimiter:
|
|
656
|
-
return sequence
|
|
657
|
-
return sequence + next_char
|
|
658
|
-
|
|
659
|
-
if filtered_path.startswith("s3://"):
|
|
660
|
-
filtered_path = reduce(
|
|
661
|
-
duplicate_delimiter_remover, filtered_path[5:], filtered_path[:5]
|
|
662
|
-
)
|
|
663
|
-
else:
|
|
664
|
-
filtered_path = reduce(duplicate_delimiter_remover, filtered_path)
|
|
665
|
-
|
|
666
|
-
# remove beginning slashes
|
|
667
|
-
filtered_path = filtered_path.lstrip(delimiter)
|
|
668
|
-
|
|
669
|
-
# remove end slashes
|
|
670
|
-
if not with_end_slash and filtered_path != "s3://":
|
|
671
|
-
filtered_path = filtered_path.rstrip(delimiter)
|
|
672
|
-
|
|
673
|
-
return filtered_path
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
def botocore_resolver():
|
|
677
|
-
"""Get the DNS suffix for the given region.
|
|
678
|
-
|
|
679
|
-
Args:
|
|
680
|
-
region (str): AWS region name
|
|
681
|
-
|
|
682
|
-
Returns:
|
|
683
|
-
str: the DNS suffix
|
|
684
|
-
"""
|
|
685
|
-
loader = botocore.loaders.create_loader()
|
|
686
|
-
return botocore.regions.EndpointResolver(loader.load_data("endpoints"))
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
def sts_regional_endpoint(region):
|
|
690
|
-
"""Get the AWS STS endpoint specific for the given region.
|
|
691
|
-
|
|
692
|
-
We need this function because the AWS SDK does not yet honor
|
|
693
|
-
the ``region_name`` parameter when creating an AWS STS client.
|
|
694
|
-
|
|
695
|
-
For the list of regional endpoints, see
|
|
696
|
-
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#id_credentials_region-endpoints.
|
|
697
|
-
|
|
698
|
-
Args:
|
|
699
|
-
region (str): AWS region name
|
|
700
|
-
|
|
701
|
-
Returns:
|
|
702
|
-
str: AWS STS regional endpoint
|
|
703
|
-
"""
|
|
704
|
-
endpoint_data = botocore_resolver().construct_endpoint("sts", region)
|
|
705
|
-
if region == "il-central-1" and not endpoint_data:
|
|
706
|
-
endpoint_data = {"hostname": "sts.{}.amazonaws.com".format(region)}
|
|
707
|
-
return "https://{}".format(endpoint_data["hostname"])
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
def get_execution_role(sagemaker_session=None, use_default=False):
|
|
711
|
-
"""Return the role ARN whose credentials are used to call the API.
|
|
712
|
-
|
|
713
|
-
Throws an exception if role doesn't exist.
|
|
714
|
-
|
|
715
|
-
Args:
|
|
716
|
-
sagemaker_session (Session): Current sagemaker session.
|
|
717
|
-
use_default (bool): Use a default role if ``get_caller_identity_arn`` does not
|
|
718
|
-
return a correct role. This default role will be created if needed.
|
|
719
|
-
Defaults to ``False``.
|
|
720
|
-
|
|
721
|
-
Returns:
|
|
722
|
-
(str): The role ARN
|
|
723
|
-
"""
|
|
724
|
-
if not sagemaker_session:
|
|
725
|
-
sagemaker_session = Session()
|
|
726
|
-
arn = sagemaker_session.get_caller_identity_arn()
|
|
727
|
-
|
|
728
|
-
if ":role/" in arn:
|
|
729
|
-
return arn
|
|
730
|
-
|
|
731
|
-
if use_default:
|
|
732
|
-
default_role_name = "AmazonSageMaker-DefaultRole"
|
|
733
|
-
|
|
734
|
-
LOGGER.warning("Using default role: %s", default_role_name)
|
|
735
|
-
|
|
736
|
-
boto3_session = sagemaker_session.boto_session
|
|
737
|
-
permissions_policy = json.dumps(
|
|
738
|
-
{
|
|
739
|
-
"Version": "2012-10-17",
|
|
740
|
-
"Statement": [
|
|
741
|
-
{
|
|
742
|
-
"Effect": "Allow",
|
|
743
|
-
"Principal": {"Service": ["sagemaker.amazonaws.com"]},
|
|
744
|
-
"Action": "sts:AssumeRole",
|
|
745
|
-
}
|
|
746
|
-
],
|
|
747
|
-
}
|
|
748
|
-
)
|
|
749
|
-
iam_client = boto3_session.client("iam")
|
|
750
|
-
try:
|
|
751
|
-
iam_client.get_role(RoleName=default_role_name)
|
|
752
|
-
except iam_client.exceptions.NoSuchEntityException:
|
|
753
|
-
iam_client.create_role(
|
|
754
|
-
RoleName=default_role_name, AssumeRolePolicyDocument=str(permissions_policy)
|
|
755
|
-
)
|
|
756
|
-
|
|
757
|
-
LOGGER.warning("Created new sagemaker execution role: %s", default_role_name)
|
|
758
|
-
|
|
759
|
-
iam_client.attach_role_policy(
|
|
760
|
-
PolicyArn="arn:aws:iam::aws:policy/AmazonSageMakerFullAccess",
|
|
761
|
-
RoleName=default_role_name,
|
|
762
|
-
)
|
|
763
|
-
return iam_client.get_role(RoleName=default_role_name)["Role"]["Arn"]
|
|
764
|
-
|
|
765
|
-
message = (
|
|
766
|
-
"The current AWS identity is not a role: {}, therefore it cannot be used as a "
|
|
767
|
-
"SageMaker execution role"
|
|
768
|
-
)
|
|
769
|
-
raise ValueError(message.format(arn))
|