Skip to content

fix: intelligent defaults for volume size, JS Estimator image uri region, Predictor str method #3870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jun 1, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/sagemaker/base_predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
NumpySerializer,
)
from sagemaker.session import production_variant, Session
from sagemaker.utils import name_from_base
from sagemaker.utils import name_from_base, stringify_object

from sagemaker.model_monitor.model_monitoring import DEFAULT_REPOSITORY_NAME

Expand Down Expand Up @@ -75,6 +75,10 @@ def content_type(self) -> str:
def accept(self) -> Tuple[str]:
"""The content type(s) that are expected from the inference server."""

def __str__(self) -> str:
"""Overriding str(*) method to make more human-readable."""
return stringify_object(self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks to me like a good improvement, but please double check with SDK team if that's considered backward incompatible please. The alternative to be to write a different serialization function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree this would be a good improvement, I dont see any negative implications of this.



class Predictor(PredictorBase):
"""Make prediction requests to an Amazon SageMaker endpoint."""
Expand Down
33 changes: 29 additions & 4 deletions src/sagemaker/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
)
from sagemaker.inputs import TrainingInput, FileSystemInput
from sagemaker.instance_group import InstanceGroup
from sagemaker.utils import instance_supports_kms
from sagemaker.job import _Job
from sagemaker.jumpstart.utils import (
add_jumpstart_tags,
Expand Down Expand Up @@ -95,6 +96,7 @@
)
from sagemaker.workflow import is_pipeline_variable
from sagemaker.workflow.entities import PipelineVariable
from sagemaker.workflow.parameters import ParameterString
from sagemaker.workflow.pipeline_context import PipelineSession, runnable_by_pipeline

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -599,10 +601,33 @@ def __init__(
self.output_kms_key = resolve_value_from_config(
output_kms_key, TRAINING_JOB_KMS_KEY_ID_PATH, sagemaker_session=self.sagemaker_session
)
self.volume_kms_key = resolve_value_from_config(
volume_kms_key,
TRAINING_JOB_VOLUME_KMS_KEY_ID_PATH,
sagemaker_session=self.sagemaker_session,
if instance_type is None or isinstance(instance_type, str):
instance_type_for_volume_kms = instance_type
elif isinstance(instance_type, ParameterString):
instance_type_for_volume_kms = instance_type.default_value
else:
raise ValueError(f"Bad value for instance type: '{instance_type}'")
Comment on lines +608 to +609
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be backwards incompatible?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure. The unit tests make it seem like it's compatible. There shouldn't be a different python type for the instance type.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today if a user passes in a different python type, they wont get an error right? (My understanding is that the "Optional[Union[str, PipelineVariable]]" is not strictly enforced by Python). Let me know if im mistaken

If Im not wrong about the above, this would be changing behavior of the Estimator class (and I havent seen type assertions like these in other places anyway). Seems safer to not change this behavior

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I can skip throwing an exception. However, if a variable that isn't a string or PipelineVariable is used, there will probably still be an exception thrown somewhere

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out this is not backward compatible, see the issue #3993

Have we made the fix to skip PipelineVariable type?


# KMS can only be attached to supported instances
use_volume_kms_config = (
(instance_type_for_volume_kms and instance_supports_kms(instance_type_for_volume_kms))
or instance_groups is not None
and any(
[
instance_supports_kms(instance_group.instance_type)
for instance_group in instance_groups
]
)
)

self.volume_kms_key = (
resolve_value_from_config(
volume_kms_key,
TRAINING_JOB_VOLUME_KMS_KEY_ID_PATH,
sagemaker_session=self.sagemaker_session,
)
if use_volume_kms_config
else volume_kms_key
Comment on lines +623 to +630
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the alternate options to skipping volume kms key default configs that the admin set? If an admin set volume kms keys in the config, is it reasonable for them to expect that it will only be plugged in for only some instance types? Or would they prefer that their users only use instance types that support kms keys?

I think its worth looping in Prateek to discuss whether this is is the right approach

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is a volume KMS key, it will be applied whenever an instance supports it. If an instance does not support it, it is not applied.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to have this conditional logic as we dont want to limit users to use only those instances which support volume kms key.
cc - @prateekmehrotra

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I suppose the whole purpose of the Config is to improve user experience. If the customer wants to enforce that KMS keys be used always, the right place for them to strictly enforce that would be through IAM Policies, SCPs, etc. There is no guarantee that populating the Config means those values will be used no matter what.

)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I find ternary hard to read in this context, could you do:

if <condition>:
  self.volume_kms_key = resolve_value_from_config(...)
else:
  self.volume_kms_key = volume_kms_key

More importantly, from a security/reasonable expection POV: should we let this code throw if any of the instance in the group does support KMS, but some do not?

If we silently remove it as you do here, the result is that there will be instances with EBS volumes out there that aren't encrypted with the default KMS key provided by admins. I don't think that's desirable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a valid concern with respect to instance_groups, lets involve the instance_groups feature team to weigh in on this.


# VPC configurations
Expand Down
19 changes: 0 additions & 19 deletions src/sagemaker/instance_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,3 @@ def retrieve(
tolerate_vulnerable_model,
tolerate_deprecated_model,
)


def volume_size_supported(instance_type: str) -> bool:
"""Returns True if SageMaker allows volume_size to be used for the instance type.

Raises:
ValueError: If the instance type is improperly formatted.
"""
try:
parts: List[str] = instance_type.split(".")
if len(parts) != 3 or parts[0] != "ml":
raise ValueError("Instance type must have 2 periods and start with 'ml'.")

# Any instance type with a "d" in the instance family (i.e. c5d, p4d, etc) + g5
# does not support attaching an EBS volume.
family = parts[1]
return "d" not in family and not family.startswith("g5")
except Exception as e:
raise ValueError(f"Failed to parse instance type '{instance_type}': {str(e)}")
2 changes: 1 addition & 1 deletion src/sagemaker/jumpstart/artifacts/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import absolute_import
from copy import deepcopy
from typing import Optional
from sagemaker.instance_types import volume_size_supported
from sagemaker.utils import volume_size_supported
from sagemaker.jumpstart.constants import (
JUMPSTART_DEFAULT_REGION_NAME,
)
Expand Down
2 changes: 1 addition & 1 deletion src/sagemaker/jumpstart/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
is_valid_model_id,
resolve_model_intelligent_default_field,
)
from sagemaker.jumpstart.utils import stringify_object
from sagemaker.utils import stringify_object
from sagemaker.model_monitor.data_capture_config import DataCaptureConfig
from sagemaker.predictor import PredictorBase

Expand Down
2 changes: 1 addition & 1 deletion src/sagemaker/jumpstart/factory/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ def _add_image_uri_to_kwargs(kwargs: JumpStartEstimatorInitKwargs) -> JumpStartE
"""Sets image uri in kwargs based on default or override, returns full kwargs."""

kwargs.image_uri = kwargs.image_uri or image_uris.retrieve(
region=None,
region=kwargs.region,
framework=None,
image_scope=JumpStartScriptScope.TRAINING,
model_id=kwargs.model_id,
Expand Down
2 changes: 1 addition & 1 deletion src/sagemaker/jumpstart/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
get_init_kwargs,
)
from sagemaker.jumpstart.utils import is_valid_model_id
from sagemaker.jumpstart.utils import stringify_object
from sagemaker.utils import stringify_object
from sagemaker.model import Model
from sagemaker.model_monitor.data_capture_config import DataCaptureConfig
from sagemaker.predictor import PredictorBase
Expand Down
6 changes: 0 additions & 6 deletions src/sagemaker/jumpstart/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,6 @@ def resolve_estimator_intelligent_default_field(
return field_val


def stringify_object(obj: Any) -> str:
"""Returns string representation of object, returning only non-None fields."""
non_none_atts = {key: value for key, value in obj.__dict__.items() if value is not None}
return f"{type(obj).__name__}: {str(non_none_atts)}"


def is_valid_model_id(
model_id: Optional[str],
region: Optional[str] = None,
Expand Down
9 changes: 7 additions & 2 deletions src/sagemaker/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from sagemaker.transformer import Transformer
from sagemaker.workflow.entities import PipelineVariable
from sagemaker.workflow.pipeline_context import runnable_by_pipeline
from sagemaker.utils import instance_supports_kms


class PipelineModel(object):
Expand Down Expand Up @@ -235,8 +236,12 @@ def deploy(
container_startup_health_check_timeout=container_startup_health_check_timeout,
)
self.endpoint_name = endpoint_name or self.name
kms_key = resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self.sagemaker_session
kms_key = (
resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self.sagemaker_session
)
if instance_supports_kms(instance_type)
else kms_key
)

data_capture_config_dict = None
Expand Down
43 changes: 38 additions & 5 deletions src/sagemaker/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import botocore.config
from botocore.exceptions import ClientError
import six
from sagemaker.utils import instance_supports_kms

import sagemaker.logs
from sagemaker import vpc_utils, s3_utils
Expand Down Expand Up @@ -855,6 +856,12 @@ def train( # noqa: C901
inferred_resource_config = update_nested_dictionary_with_values_from_config(
resource_config, TRAINING_JOB_RESOURCE_CONFIG_PATH, sagemaker_session=self
)
if (
"InstanceType" in inferred_resource_config
and not instance_supports_kms(inferred_resource_config["InstanceType"])
and "VolumeKmsKeyId" in inferred_resource_config
):
del inferred_resource_config["VolumeKmsKeyId"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user explicitly passed in a volume kms id, this would also skip it (if the instance type does not support kms). Are we sure we want to change the user input in that way? Shouldnt the SDK respect their wishes, even if it will cause the API to fail?

Especially for something security related like kms keys

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instances don't have any notion of KMS, so there's no security problem, AFAIK. If any instance though supports KMS, we always apply the KMS key.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The admin sets the KMS config with the expectation it is used whenever KMS is supported. But if someone uses a instance type like local mode or a g5 instance, the KMS settings makes no sense, and shouldn't be applied.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me add some more context here.

The only thing you can encrypt with a custom KMS key is an EBS volume. The issue here is that you cannot attach an EBS volume to these types of instances. See documentation.

As a result, passing any KMS key with such instance is nonsensical, the only admissible value is None. The issue is, if the user attempts to pass None, the intelligent default can substitute a value, causing the service call to fail and the user has no recourse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a result, passing any KMS key with such instance is nonsensical, the only admissible value is None. The issue is, if the user attempts to pass None, the intelligent default can substitute a value, causing the service call to fail and the user has no recourse.

AFAIK, intelligent defaults will take the user supplied value (None) over the ones set in Inteliigent defaults. @rubanh correct me if Im wrong. But with the flexibility we want to provide with PySDK, the user need not figure out that the instance doesn't support kms volume and explicitly pass None for such a case.

Copy link
Collaborator

@rubanh rubanh May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So actually @JGuinegagne is right about this case @mufaddal-rohawala , if the user supplies None, the Defaults Config understands that as "the user didnt supply a value" (because it doesnt know if the None was a explicitly or implicitly provided)

And then to followup on my original comment, thanks for linking to documentation.

So lets break this down into separate questions.

  1. Should the VolumeKMS key be skipped if all instances in a request have local storage when...
    1. ... a user passes in a non-None VolumeKMS key explicitly?
      Right now, seems this PR in most cases says "DONT SKIP" to this question. But in this one case above it says "SKIP"
    2. ... the VolumeKMS key was explicitly set in the DefaultConfig?
      The goal of this PR is "SKIP". If we are going with this I think at minimum we need to make sure Admins understand this behavior when they set a VolumeKMS key in the Config.
  2. Should the VolumeKMS key be skipped if even one instance in a request supports KMS? "DONT SKIP" (fine with me)

train_request = self._get_train_request(
input_mode=input_mode,
input_config=input_config,
Expand Down Expand Up @@ -3791,8 +3798,12 @@ def create_endpoint_config(
)
if tags is not None:
request["Tags"] = tags
kms_key = resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self
kms_key = (
resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self
)
if instance_supports_kms(instance_type)
else kms_key
)
if kms_key is not None:
request["KmsKeyId"] = kms_key
Expand Down Expand Up @@ -3885,7 +3896,16 @@ def create_endpoint_config_from_existing(

if new_kms_key is not None or existing_endpoint_config_desc.get("KmsKeyId") is not None:
request["KmsKeyId"] = new_kms_key or existing_endpoint_config_desc.get("KmsKeyId")
if KMS_KEY_ID not in request:

supports_kms = any(
[
instance_supports_kms(production_variant["InstanceType"])
for production_variant in production_variants
if "InstanceType" in production_variant
]
)

if KMS_KEY_ID not in request and supports_kms:
kms_key_from_config = resolve_value_from_config(
config_path=ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self
)
Expand Down Expand Up @@ -4506,15 +4526,28 @@ def endpoint_from_production_variants(
Returns:
str: The name of the created ``Endpoint``.
"""

supports_kms = any(
[
instance_supports_kms(production_variant["InstanceType"])
for production_variant in production_variants
if "InstanceType" in production_variant
]
)

update_list_of_dicts_with_values_from_config(
production_variants,
ENDPOINT_CONFIG_PRODUCTION_VARIANTS_PATH,
required_key_paths=["CoreDumpConfig.DestinationS3Uri"],
sagemaker_session=self,
)
config_options = {"EndpointConfigName": name, "ProductionVariants": production_variants}
kms_key = resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self
kms_key = (
resolve_value_from_config(
kms_key, ENDPOINT_CONFIG_KMS_KEY_ID_PATH, sagemaker_session=self
)
if supports_kms
else kms_key
)
tags = _append_project_tags(tags)
tags = self._append_sagemaker_config_tags(
Expand Down
15 changes: 14 additions & 1 deletion src/sagemaker/session_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
class SessionSettings(object):
"""Optional container class for settings to apply to a SageMaker session."""

def __init__(self, encrypt_repacked_artifacts=True, local_download_dir=None) -> None:
def __init__(
self,
encrypt_repacked_artifacts=True,
local_download_dir=None,
ignore_intelligent_defaults=False,
) -> None:
"""Initialize the ``SessionSettings`` of a SageMaker ``Session``.

Args:
Expand All @@ -27,9 +32,12 @@ def __init__(self, encrypt_repacked_artifacts=True, local_download_dir=None) ->
is not provided (Default: True).
local_download_dir (str): Optional. A path specifying the local directory
for downloading artifacts. (Default: None).
ignore_intelligent_defaults (bool): Optional. Flag to indicate whether to ignore
intelligent default settings. (Default: False).
"""
self._encrypt_repacked_artifacts = encrypt_repacked_artifacts
self._local_download_dir = local_download_dir
self._ignore_intelligent_defaults = ignore_intelligent_defaults

@property
def encrypt_repacked_artifacts(self) -> bool:
Expand All @@ -40,3 +48,8 @@ def encrypt_repacked_artifacts(self) -> bool:
def local_download_dir(self) -> str:
"""Return path specifying the local directory for downloading artifacts."""
return self._local_download_dir

@property
def ignore_intelligent_defaults(self) -> bool:
"""Return boolean for whether intelligent defaults should be ignored."""
return self._ignore_intelligent_defaults
56 changes: 54 additions & 2 deletions src/sagemaker/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
import tarfile
import tempfile
import time
from typing import Any, List, Optional
import json
import abc
import uuid
from datetime import datetime
from typing import List, Optional

from importlib import import_module
import botocore
Expand All @@ -41,7 +41,6 @@
from sagemaker.session_settings import SessionSettings
from sagemaker.workflow import is_pipeline_variable, is_pipeline_parameter_string


ECR_URI_PATTERN = r"^(\d+)(\.)dkr(\.)ecr(\.)(.+)(\.)(.*)(/)(.*:.*)$"
MAX_BUCKET_PATHS_COUNT = 5
S3_PREFIX = "s3://"
Expand Down Expand Up @@ -1066,6 +1065,15 @@ def resolve_value_from_config(
Returns:
The value that should be used by the caller
"""

if (
sagemaker_session is not None
and sagemaker_session.settings is not None
and sagemaker_session.settings.ignore_intelligent_defaults
Copy link
Collaborator

@rubanh rubanh May 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to sagemaker_session.settings.ignore_sagemaker_config

Also, have we explored whether passing an empty config to the Session when initializing it is enough to fulfill the usecase here without adding a whole new setting for it? I would lean against adding a new setting unless its necessary.

Also, if we do still want to make a setting like this, maybe instead of changing the utils it would be better to skip the config initialization inside Session and LocalSession instead when the setting is off and set an empty config? That way if the user checks session.sagemaker_config, its empty, which might better match and set expectations around values not being plugged in? Or are these Session settings something that we expect users will keep editing during a lifecycle of a session and not just before session initialization?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should leave the config settings as is. This is just a flag that tells SM to ignore the config when it comes time to using it

Copy link
Collaborator

@rubanh rubanh May 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Im wondering why we dont want to do something like the following (ignore at initialization rather than when its time to use it). Seems like this could limit potential for confusion (because the lifecycle of the config matches the lifecycle of the Session today). But curious if there's usecases you have in mind that make this approach unideal.

class Session(object):
...
    def _initialize(...):
       ...
        if self.settings and sagemaker_session.settings.ignore_sagemaker_config: # new
            self.sagemaker_config = {}  # new
        elif sagemaker_config:
            validate_sagemaker_config(sagemaker_config)
            self.sagemaker_config = sagemaker_config
        elif not (self.settings and self.settings.ignore_sagemaker_config):
            self.sagemaker_config = load_sagemaker_config(s3_resource=self.s3_resource)       

):
logger.info("Ignoring intelligent defaults. Returning direct input.")
return direct_input

config_value = (
get_sagemaker_config_value(sagemaker_session, config_path) if config_path else None
)
Expand Down Expand Up @@ -1408,3 +1416,47 @@ def update_nested_dictionary_with_values_from_config(
"combined value that will be used = {}\n".format(inferred_config_dict),
)
return inferred_config_dict


def stringify_object(obj: Any) -> str:
"""Returns string representation of object, returning only non-None fields."""
non_none_atts = {key: value for key, value in obj.__dict__.items() if value is not None}
return f"{type(obj).__name__}: {str(non_none_atts)}"


def volume_size_supported(instance_type: str) -> bool:
"""Returns True if SageMaker allows volume_size to be used for the instance type.

Raises:
ValueError: If the instance type is improperly formatted.
"""

try:

# local mode does not support volume size
if instance_type.startswith("local"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will need to add another check or is_pipeline_variable(instance_type), otherwise, this breaks pipeline cx.

return False

parts: List[str] = instance_type.split(".")

if len(parts) == 3 and parts[0] == "ml":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline that the other utility has different specifications and would be large blast radius to merge utilities in this PR.

parts = parts[1:]

if len(parts) != 2:
raise ValueError(f"Failed to parse instance type '{instance_type}'")

# Any instance type with a "d" in the instance family (i.e. c5d, p4d, etc) + g5
# does not support attaching an EBS volume.
family = parts[0]
return "d" not in family and not family.startswith("g5")
except Exception as e:
raise ValueError(f"Failed to parse instance type '{instance_type}': {str(e)}")


def instance_supports_kms(instance_type: str) -> bool:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are we sure this will stay up to date? If an instance type starts supporting kms keys one day, it seems like we would be breaking the expectations of users and admins

Could we add some context here also about where the "source of truth" is for this info? If it will need to be updated manually, it should at least be easy for someone to check whether this is up to date

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rules for supporting KMS are equal to whether the instance supports volume size. Unfortunately, there's no other source of truth, so this will have to be updated manually. However, this function is already used in other parts of the code (JumpStart).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll defer to @mufaddal-rohawala 's judgement on this, but I would say that this function needs to be explicit and clear about (1) why is kms support dependent on volume size and (2) how to check whether the answers given by this function are up to date. Those things would help with maintainability, especially if this is something that needs to be updated manually

"""Returns True if SageMaker allows KMS keys to be attached to the instance.

Raises:
ValueError: If the instance type is improperly formatted.
"""
return volume_size_supported(instance_type)
Loading