| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  | import boto3 | 
					
						
							|  |  |  | import pytest | 
					
						
							| 
									
										
										
										
											2023-11-30 07:55:51 -08:00
										 |  |  | from botocore.exceptions import ClientError | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | from moto import mock_cloudformation, mock_sagemaker | 
					
						
							| 
									
										
										
										
											2022-08-13 09:49:43 +00:00
										 |  |  | from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | from .cloudformation_test_configs import ( | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     EndpointConfigTestConfig, | 
					
						
							|  |  |  |     EndpointTestConfig, | 
					
						
							| 
									
										
										
										
											2023-11-30 07:55:51 -08:00
										 |  |  |     ModelTestConfig, | 
					
						
							|  |  |  |     NotebookInstanceLifecycleConfigTestConfig, | 
					
						
							|  |  |  |     NotebookInstanceTestConfig, | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  | def _get_stack_outputs(cf_client, stack_name): | 
					
						
							|  |  |  |     """Returns the outputs for the first entry in describe_stacks.""" | 
					
						
							|  |  |  |     stack_description = cf_client.describe_stacks(StackName=stack_name)["Stacks"][0] | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         output["OutputKey"]: output["OutputValue"] | 
					
						
							|  |  |  |         for output in stack_description["Outputs"] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | @mock_cloudformation | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  | @mock_sagemaker | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     "test_config", | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     [ | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         NotebookInstanceTestConfig(), | 
					
						
							|  |  |  |         NotebookInstanceLifecycleConfigTestConfig(), | 
					
						
							|  |  |  |         ModelTestConfig(), | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         EndpointConfigTestConfig(), | 
					
						
							|  |  |  |         EndpointTestConfig(), | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ], | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | def test_sagemaker_cloudformation_create(test_config): | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Utilize test configuration to set-up any mock SageMaker resources | 
					
						
							|  |  |  |     test_config.run_setup_procedure(sm) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     cf.create_stack( | 
					
						
							|  |  |  |         StackName=stack_name, | 
					
						
							|  |  |  |         TemplateBody=test_config.get_cloudformation_template(include_outputs=False), | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     provisioned_resource = cf.list_stack_resources(StackName=stack_name)[ | 
					
						
							|  |  |  |         "StackResourceSummaries" | 
					
						
							|  |  |  |     ][0] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert provisioned_resource["LogicalResourceId"] == test_config.resource_name | 
					
						
							|  |  |  |     assert len(provisioned_resource["PhysicalResourceId"]) > 0 | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     "test_config", | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     [ | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         NotebookInstanceTestConfig(), | 
					
						
							|  |  |  |         NotebookInstanceLifecycleConfigTestConfig(), | 
					
						
							|  |  |  |         ModelTestConfig(), | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         EndpointConfigTestConfig(), | 
					
						
							|  |  |  |         EndpointTestConfig(), | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ], | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | def test_sagemaker_cloudformation_get_attr(test_config): | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Utilize test configuration to set-up any mock SageMaker resources | 
					
						
							|  |  |  |     test_config.run_setup_procedure(sm) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     # Create stack and get description for output values | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     cf.create_stack( | 
					
						
							|  |  |  |         StackName=stack_name, TemplateBody=test_config.get_cloudformation_template() | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     # Using the describe function, ensure output ARN matches resource ARN | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: outputs["Name"]} | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert outputs["Arn"] == resource_description[test_config.arn_parameter] | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     "test_config,error_message", | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     [ | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         (NotebookInstanceTestConfig(), "RecordNotFound"), | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |         ( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |             NotebookInstanceLifecycleConfigTestConfig(), | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |             "Notebook Instance Lifecycle Config does not exist", | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         (ModelTestConfig(), "Could not find model"), | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         (EndpointConfigTestConfig(), "Could not find endpoint configuration"), | 
					
						
							|  |  |  |         (EndpointTestConfig(), "Could not find endpoint"), | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ], | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | def test_sagemaker_cloudformation_notebook_instance_delete(test_config, error_message): | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Utilize test configuration to set-up any mock SageMaker resources | 
					
						
							|  |  |  |     test_config.run_setup_procedure(sm) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     # Create stack and verify existence | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     cf.create_stack( | 
					
						
							|  |  |  |         StackName=stack_name, TemplateBody=test_config.get_cloudformation_template() | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: outputs["Name"]} | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert outputs["Arn"] == resource_description[test_config.arn_parameter] | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     # Delete the stack and verify resource has also been deleted | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     cf.delete_stack(StackName=stack_name) | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as ce: | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |             **{test_config.name_parameter: outputs["Name"]} | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert error_message in ce.value.response["Error"]["Message"] | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							|  |  |  | def test_sagemaker_cloudformation_notebook_instance_update(): | 
					
						
							|  |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     test_config = NotebookInstanceTestConfig() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Set up template for stack with two different instance types | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     initial_instance_type = "ml.c4.xlarge" | 
					
						
							|  |  |  |     updated_instance_type = "ml.c4.4xlarge" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     initial_template_json = test_config.get_cloudformation_template( | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |         instance_type=initial_instance_type | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     updated_template_json = test_config.get_cloudformation_template( | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |         instance_type=updated_instance_type | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create stack with initial template and check attributes | 
					
						
							|  |  |  |     cf.create_stack(StackName=stack_name, TemplateBody=initial_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     initial_notebook_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: initial_notebook_name} | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert initial_instance_type == resource_description["InstanceType"] | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Update stack and check attributes | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     cf.update_stack(StackName=stack_name, TemplateBody=updated_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     updated_notebook_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_notebook_name == initial_notebook_name | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: updated_notebook_name} | 
					
						
							| 
									
										
										
										
											2021-04-09 13:54:00 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_instance_type == resource_description["InstanceType"] | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							|  |  |  | def test_sagemaker_cloudformation_notebook_instance_lifecycle_config_update(): | 
					
						
							|  |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     test_config = NotebookInstanceLifecycleConfigTestConfig() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Set up template for stack with two different OnCreate scripts | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     initial_on_create_script = "echo Hello World" | 
					
						
							|  |  |  |     updated_on_create_script = "echo Goodbye World" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     initial_template_json = test_config.get_cloudformation_template( | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |         on_create=initial_on_create_script | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     updated_template_json = test_config.get_cloudformation_template( | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |         on_create=updated_on_create_script | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create stack with initial template and check attributes | 
					
						
							|  |  |  |     cf.create_stack(StackName=stack_name, TemplateBody=initial_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     initial_config_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: initial_config_name} | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert len(resource_description["OnCreate"]) == 1 | 
					
						
							|  |  |  |     assert initial_on_create_script == resource_description["OnCreate"][0]["Content"] | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Update stack and check attributes | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     cf.update_stack(StackName=stack_name, TemplateBody=updated_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     updated_config_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_config_name == initial_config_name | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: updated_config_name} | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert len(resource_description["OnCreate"]) == 1 | 
					
						
							|  |  |  |     assert updated_on_create_script == resource_description["OnCreate"][0]["Content"] | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							|  |  |  | def test_sagemaker_cloudformation_model_update(): | 
					
						
							|  |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_config = ModelTestConfig() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Set up template for stack with two different image versions | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     image = "404615174143.dkr.ecr.us-east-2.amazonaws.com/kmeans:{}" | 
					
						
							|  |  |  |     initial_image_version = 1 | 
					
						
							|  |  |  |     updated_image_version = 2 | 
					
						
							|  |  |  |     initial_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         image=image.format(initial_image_version) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     updated_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         image=image.format(updated_image_version) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create stack with initial template and check attributes | 
					
						
							|  |  |  |     cf.create_stack(StackName=stack_name, TemplateBody=initial_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     initial_model_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         **{test_config.name_parameter: initial_model_name} | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert resource_description["PrimaryContainer"]["Image"] == ( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         image.format(initial_image_version) | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     # Update stack and check attributes | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     cf.update_stack(StackName=stack_name, TemplateBody=updated_template_json) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     updated_model_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_model_name != initial_model_name | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         **{test_config.name_parameter: updated_model_name} | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert resource_description["PrimaryContainer"]["Image"] == ( | 
					
						
							| 
									
										
										
										
											2021-04-16 10:23:05 -04:00
										 |  |  |         image.format(updated_image_version) | 
					
						
							| 
									
										
										
										
											2021-04-13 07:03:25 -04:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							|  |  |  | def test_sagemaker_cloudformation_endpoint_config_update(): | 
					
						
							|  |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_config = EndpointConfigTestConfig() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Utilize test configuration to set-up any mock SageMaker resources | 
					
						
							|  |  |  |     test_config.run_setup_procedure(sm) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Set up template for stack with two different production variant counts | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     initial_num_production_variants = 1 | 
					
						
							|  |  |  |     updated_num_production_variants = 2 | 
					
						
							|  |  |  |     initial_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         num_production_variants=initial_num_production_variants | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     updated_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         num_production_variants=updated_num_production_variants | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create stack with initial template and check attributes | 
					
						
							|  |  |  |     cf.create_stack(StackName=stack_name, TemplateBody=initial_template_json) | 
					
						
							|  |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     initial_endpoint_config_name = outputs["Name"] | 
					
						
							|  |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: initial_endpoint_config_name} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert len(resource_description["ProductionVariants"]) == ( | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         initial_num_production_variants | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Update stack and check attributes | 
					
						
							|  |  |  |     cf.update_stack(StackName=stack_name, TemplateBody=updated_template_json) | 
					
						
							|  |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     updated_endpoint_config_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_endpoint_config_name != initial_endpoint_config_name | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: updated_endpoint_config_name} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert len(resource_description["ProductionVariants"]) == ( | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         updated_num_production_variants | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudformation | 
					
						
							|  |  |  | @mock_sagemaker | 
					
						
							|  |  |  | def test_sagemaker_cloudformation_endpoint_update(): | 
					
						
							|  |  |  |     cf = boto3.client("cloudformation", region_name="us-east-1") | 
					
						
							|  |  |  |     sm = boto3.client("sagemaker", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test_config = EndpointTestConfig() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Set up template for stack with two different endpoint config names | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |     stack_name = f"{test_config.resource_name}_stack" | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     initial_endpoint_config_name = test_config.resource_name | 
					
						
							|  |  |  |     updated_endpoint_config_name = "updated-endpoint-config-name" | 
					
						
							|  |  |  |     initial_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         endpoint_config_name=initial_endpoint_config_name | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     updated_template_json = test_config.get_cloudformation_template( | 
					
						
							|  |  |  |         endpoint_config_name=updated_endpoint_config_name | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create SM resources and stack with initial template and check attributes | 
					
						
							|  |  |  |     sm.create_model( | 
					
						
							|  |  |  |         ModelName=initial_endpoint_config_name, | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |         ExecutionRoleArn=f"arn:aws:iam::{ACCOUNT_ID}:role/FakeRole", | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         PrimaryContainer={ | 
					
						
							|  |  |  |             "Image": "404615174143.dkr.ecr.us-east-2.amazonaws.com/linear-learner:1", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     sm.create_endpoint_config( | 
					
						
							|  |  |  |         EndpointConfigName=initial_endpoint_config_name, | 
					
						
							|  |  |  |         ProductionVariants=[ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 "InitialInstanceCount": 1, | 
					
						
							|  |  |  |                 "InitialVariantWeight": 1, | 
					
						
							|  |  |  |                 "InstanceType": "ml.c4.xlarge", | 
					
						
							|  |  |  |                 "ModelName": initial_endpoint_config_name, | 
					
						
							|  |  |  |                 "VariantName": "variant-name-1", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     cf.create_stack(StackName=stack_name, TemplateBody=initial_template_json) | 
					
						
							|  |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     initial_endpoint_name = outputs["Name"] | 
					
						
							|  |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: initial_endpoint_name} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert re.match( | 
					
						
							|  |  |  |         initial_endpoint_config_name, resource_description["EndpointConfigName"] | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create additional SM resources and update stack | 
					
						
							|  |  |  |     sm.create_model( | 
					
						
							|  |  |  |         ModelName=updated_endpoint_config_name, | 
					
						
							| 
									
										
										
										
											2022-11-17 21:41:08 -01:00
										 |  |  |         ExecutionRoleArn=f"arn:aws:iam::{ACCOUNT_ID}:role/FakeRole", | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |         PrimaryContainer={ | 
					
						
							|  |  |  |             "Image": "404615174143.dkr.ecr.us-east-2.amazonaws.com/linear-learner:1", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     sm.create_endpoint_config( | 
					
						
							|  |  |  |         EndpointConfigName=updated_endpoint_config_name, | 
					
						
							|  |  |  |         ProductionVariants=[ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 "InitialInstanceCount": 1, | 
					
						
							|  |  |  |                 "InitialVariantWeight": 1, | 
					
						
							|  |  |  |                 "InstanceType": "ml.c4.xlarge", | 
					
						
							|  |  |  |                 "ModelName": updated_endpoint_config_name, | 
					
						
							|  |  |  |                 "VariantName": "variant-name-1", | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     cf.update_stack(StackName=stack_name, TemplateBody=updated_template_json) | 
					
						
							|  |  |  |     outputs = _get_stack_outputs(cf, stack_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     updated_endpoint_name = outputs["Name"] | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert updated_endpoint_name == initial_endpoint_name | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     resource_description = getattr(sm, test_config.describe_function_name)( | 
					
						
							|  |  |  |         **{test_config.name_parameter: updated_endpoint_name} | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-08-08 06:06:51 -04:00
										 |  |  |     assert re.match( | 
					
						
							|  |  |  |         updated_endpoint_config_name, resource_description["EndpointConfigName"] | 
					
						
							| 
									
										
										
										
											2021-04-17 08:49:46 -04:00
										 |  |  |     ) |