def single_variant_config(): config_resource_name = random_suffix_name("single-variant-config", 32) model_resource_name = config_resource_name + "-model" replacements = REPLACEMENT_VALUES.copy() replacements["CONFIG_NAME"] = config_resource_name replacements["MODEL_NAME"] = model_resource_name model_reference, model_spec, model_resource = create_sagemaker_resource( resource_plural=MODEL_RESOURCE_PLURAL, resource_name=model_resource_name, spec_file="xgboost_model", replacements=replacements, ) assert model_resource is not None config_reference, config_spec, config_resource = create_sagemaker_resource( resource_plural=CONFIG_RESOURCE_PLURAL, resource_name=config_resource_name, spec_file="endpoint_config_single_variant", replacements=replacements, ) assert config_resource is not None yield (config_reference, config_resource) k8s.delete_custom_resource(model_reference) # Delete the k8s resource if not already deleted by tests if k8s.get_resource_exists(config_reference): k8s.delete_custom_resource(config_reference)
def xgboost_churn_data_quality_job_definition(xgboost_churn_endpoint): endpoint_spec = xgboost_churn_endpoint endpoint_name = endpoint_spec["spec"].get("endpointName") resource_name = random_suffix_name("data-quality-job-definition", 32) replacements = REPLACEMENT_VALUES.copy() replacements["JOB_DEFINITION_NAME"] = resource_name replacements["ENDPOINT_NAME"] = endpoint_name job_definition_reference, _, resource = create_sagemaker_resource( resource_plural=cfg.DATA_QUALITY_JOB_DEFINITION_RESOURCE_PLURAL, resource_name=resource_name, spec_file="data_quality_job_definition_xgboost_churn", replacements=replacements, ) assert resource is not None job_definition_name = resource["spec"].get("jobDefinitionName") yield (job_definition_reference, resource) if k8s.get_resource_exists(job_definition_reference): _, deleted = k8s.delete_custom_resource(job_definition_reference, 3, 10) assert deleted
def xgboost_churn_data_quality_monitoring_schedule( xgboost_churn_data_quality_job_definition, ): (_, job_definition_resource) = xgboost_churn_data_quality_job_definition job_definition_name = job_definition_resource["spec"].get("jobDefinitionName") monitoring_schedule_name = random_suffix_name("monitoring-schedule", 32) replacements = REPLACEMENT_VALUES.copy() replacements["MONITORING_SCHEDULE_NAME"] = monitoring_schedule_name replacements["JOB_DEFINITION_NAME"] = job_definition_name replacements["MONITORING_TYPE"] = "DataQuality" reference, spec, resource = create_sagemaker_resource( resource_plural=RESOURCE_PLURAL, resource_name=monitoring_schedule_name, spec_file="monitoring_schedule_base", replacements=replacements, ) assert resource is not None yield (reference, resource, spec) if k8s.get_resource_exists(reference): _, deleted = k8s.delete_custom_resource(reference, cfg.DELETE_WAIT_PERIOD, cfg.DELETE_WAIT_LENGTH) assert deleted
def xgboost_endpoint(name_suffix, single_variant_config): endpoint_resource_name = name_suffix (_, config_resource) = single_variant_config config_resource_name = config_resource["spec"].get("endpointConfigName", None) replacements = REPLACEMENT_VALUES.copy() replacements["ENDPOINT_NAME"] = endpoint_resource_name replacements["ENDPOINT_CONFIG_NAME"] = config_resource_name reference, spec, resource = create_sagemaker_resource( resource_plural=cfg.ENDPOINT_RESOURCE_PLURAL, resource_name=endpoint_resource_name, spec_file="endpoint_base", replacements=replacements, ) assert resource is not None yield (reference, resource, spec) # Delete the k8s resource if not already deleted by tests if k8s.get_resource_exists(reference): # longer wait incase endpoint is in creating/updating status _, deleted = k8s.delete_custom_resource(reference, 40, cfg.DELETE_WAIT_LENGTH) assert deleted
def xgboost_model_package_group(): resource_name = random_suffix_name("xgboost-model-package-group", 38) replacements = REPLACEMENT_VALUES.copy() replacements["MODEL_PACKAGE_GROUP_NAME"] = resource_name ( model_package_group_reference, model_package_group_spec, model_package_group_resource, ) = create_sagemaker_resource( resource_plural=cfg.MODEL_PACKAGE_GROUP_RESOURCE_PLURAL, resource_name=resource_name, spec_file="xgboost_model_package_group", replacements=replacements, ) assert model_package_group_resource is not None if k8s.get_resource_arn(model_package_group_resource) is None: logging.error( f"ARN for this resource is None, resource status is: {model_package_group_resource['status']}" ) assert k8s.get_resource_arn(model_package_group_resource) is not None yield (model_package_group_reference, model_package_group_resource) # Delete the k8s resource if not already deleted by tests if k8s.get_resource_exists(model_package_group_reference): _, deleted = k8s.delete_custom_resource(model_package_group_reference, DELETE_WAIT_PERIOD, DELETE_WAIT_LENGTH) assert deleted
def xgboost_training_job(): resource_name = random_suffix_name("xgboost-trainingjob", 32) replacements = REPLACEMENT_VALUES.copy() replacements["TRAINING_JOB_NAME"] = resource_name reference, _, resource = create_sagemaker_resource( resource_plural=RESOURCE_PLURAL, resource_name=resource_name, spec_file="xgboost_trainingjob", replacements=replacements, ) assert resource is not None if k8s.get_resource_arn(resource) is None: logging.error( f"ARN for this resource is None, resource status is: {resource['status']}" ) assert k8s.get_resource_arn(resource) is not None yield (reference, resource) if k8s.get_resource_exists(reference): _, deleted = k8s.delete_custom_resource(reference, cfg.JOB_DELETE_WAIT_PERIODS, cfg.JOB_DELETE_WAIT_LENGTH) assert deleted
def xgboost_transformjob(xgboost_model_for_transform): (transform_resource_name, model_resource_name) = xgboost_model_for_transform replacements = REPLACEMENT_VALUES.copy() replacements["MODEL_NAME"] = model_resource_name replacements["TRANSFORM_JOB_NAME"] = transform_resource_name reference, _, resource = create_sagemaker_resource( resource_plural=RESOURCE_PLURAL, resource_name=transform_resource_name, spec_file="xgboost_transformjob", replacements=replacements, ) assert resource is not None if k8s.get_resource_arn(resource) is None: logging.error( f"ARN for this resource is None, resource status is: {resource['status']}" ) assert k8s.get_resource_arn(resource) is not None yield (reference, resource) if k8s.get_resource_exists(reference): _, deleted = k8s.delete_custom_resource(reference, cfg.JOB_DELETE_WAIT_PERIODS, cfg.JOB_DELETE_WAIT_LENGTH) assert deleted
def multi_variant_config(name_suffix, single_container_model): config_resource_name = name_suffix + "-multi-variant-config" (_, model_resource) = single_container_model model_resource_name = model_resource["spec"].get("modelName", None) replacements = REPLACEMENT_VALUES.copy() replacements["ENDPOINT_CONFIG_NAME"] = config_resource_name replacements["MODEL_NAME"] = model_resource_name config_reference, config_spec, config_resource = create_sagemaker_resource( resource_plural=cfg.ENDPOINT_CONFIG_RESOURCE_PLURAL, resource_name=config_resource_name, spec_file="endpoint_config_multi_variant", replacements=replacements, ) assert config_resource is not None if k8s.get_resource_arn(config_resource) is None: logging.error( f"ARN for this resource is None, resource status is: {config_resource['status']}" ) assert k8s.get_resource_arn(config_resource) is not None yield (config_reference, config_resource) _, deleted = k8s.delete_custom_resource(config_reference, 3, 10) assert deleted
def test_crud_authorizer(self, api_resource): api_ref, api_cr = api_resource api_id = api_cr['status']['apiID'] test_data = REPLACEMENT_VALUES.copy() authorizer_name = random_suffix_name("ack-test-authorizer", 25) test_data['AUTHORIZER_NAME'] = authorizer_name test_data['AUTHORIZER_TITLE'] = authorizer_name test_data['API_ID'] = api_id test_data['AUTHORIZER_URI'] = f'arn:aws:apigateway:{get_region()}:lambda:path/2015-03-31/functions/{get_bootstrap_resources().AuthorizerFunctionArn}/invocations' authorizer_ref, authorizer_data = helper.authorizer_ref_and_data(authorizer_resource_name=authorizer_name, replacement_values=test_data) logging.debug(f"http api authorizer resource. name: {authorizer_name}, data: {authorizer_data}") # test create k8s.create_custom_resource(authorizer_ref, authorizer_data) time.sleep(CREATE_WAIT_AFTER_SECONDS) assert k8s.wait_on_condition(authorizer_ref, "ACK.ResourceSynced", "True", wait_periods=10) cr = k8s.get_resource(authorizer_ref) assert cr is not None authorizer_id = cr['status']['authorizerID'] # Let's check that the HTTP Api integration appears in Amazon API Gateway apigw_validator.assert_authorizer_is_present(api_id=api_id, authorizer_id=authorizer_id) apigw_validator.assert_authorizer_name( api_id=api_id, authorizer_id=authorizer_id, expected_authorizer_name=authorizer_name ) # test update updated_authorizer_title = 'updated-' + authorizer_name test_data['AUTHORIZER_TITLE'] = updated_authorizer_title updated_authorizer_resource_data = load_apigatewayv2_resource( "authorizer", additional_replacements=test_data, ) logging.debug(f"updated http api authorizer resource: {updated_authorizer_resource_data}") # Update the k8s resource k8s.patch_custom_resource(authorizer_ref, updated_authorizer_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) condition.assert_synced(authorizer_ref) # Let's check that the HTTP Api authorizer appears in Amazon API Gateway with updated title apigw_validator.assert_authorizer_name( api_id=api_id, authorizer_id=authorizer_id, expected_authorizer_name=updated_authorizer_title ) # test delete k8s.delete_custom_resource(authorizer_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(authorizer_ref) # HTTP Api authorizer should no longer appear in Amazon API Gateway apigw_validator.assert_authorizer_is_deleted(api_id=api_id, authorizer_id=authorizer_id)
def test_smoke(self, lambda_client): resource_name = random_suffix_name("lambda-csc", 24) resources = get_bootstrap_resources() logging.debug(resources) replacements = REPLACEMENT_VALUES.copy() replacements["AWS_REGION"] = get_region() replacements["CODE_SIGNING_CONFIG_NAME"] = resource_name replacements["SIGNING_PROFILE_VERSION_ARN"] = resources.SigningProfileVersionArn # Load Lambda CR resource_data = load_lambda_resource( "code_signing_config", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) codeSigningConfigARN = cr['status']['ackResourceMetadata']['arn'] time.sleep(CREATE_WAIT_AFTER_SECONDS) # Check Lambda code signing config exists exists = self.code_signing_config_exists(lambda_client, codeSigningConfigARN) assert exists # Update cr cr["spec"]["description"] = "new description" # Patch k8s resource k8s.patch_custom_resource(ref, cr) time.sleep(UPDATE_WAIT_AFTER_SECONDS) # Check code signing config description csc = self.get_code_signing_config(lambda_client, codeSigningConfigARN) assert csc is not None assert csc["Description"] == "new description" # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check Lambda code signing config doesn't exist exists = self.code_signing_config_exists(lambda_client, codeSigningConfigARN) assert not exists
def test_crud_stage(self, api_resource): api_ref, api_cr = api_resource api_id = api_cr['status']['apiID'] test_data = REPLACEMENT_VALUES.copy() random_suffix = (''.join( random.choice(string.ascii_lowercase) for _ in range(6))) stage_name = "ack-test-stage-" + random_suffix test_data['STAGE_NAME'] = stage_name test_data['API_ID'] = api_id stage_ref, stage_data = helper.stage_ref_and_data( stage_resource_name=stage_name, replacement_values=test_data) logging.debug( f"http api stage resource. name: {stage_name}, data: {stage_data}") # test create k8s.create_custom_resource(stage_ref, stage_data) cr = k8s.wait_resource_consumed_by_controller(stage_ref) assert cr is not None assert k8s.get_resource_exists(stage_ref) # Let's check that the HTTP Api integration appears in Amazon API Gateway apigw_validator.assert_stage_is_present(api_id=api_id, stage_name=stage_name) stage_description = test_data['STAGE_DESCRIPTION'] apigw_validator.assert_stage_description( api_id=api_id, stage_name=stage_name, expected_description=stage_description) # test update updated_description = 'updated' + stage_description test_data['STAGE_DESCRIPTION'] = updated_description updated_stage_resource_data = load_apigatewayv2_resource( "stage", additional_replacements=test_data, ) logging.debug( f"updated http api stage resource: {updated_stage_resource_data}") # Update the k8s resource k8s.patch_custom_resource(stage_ref, updated_stage_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) # Let's check that the HTTP Api stage appears in Amazon API Gateway with updated description apigw_validator.assert_stage_description( api_id=api_id, stage_name=stage_name, expected_description=updated_description) # test delete k8s.delete_custom_resource(stage_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(stage_ref) # HTTP Api stage should no longer appear in Amazon API Gateway apigw_validator.assert_stage_is_deleted(api_id=api_id, stage_name=stage_name)
def test_create_delete_2az(self, rds_client): resource_name = "my-db-subnet-group" resource_desc = "my-db-subnet-group description" br_resources = get_bootstrap_resources() replacements = REPLACEMENT_VALUES.copy() replacements["DB_SUBNET_GROUP_NAME"] = resource_name replacements["DB_SUBNET_GROUP_DESC"] = resource_desc replacements["SUBNET_AZ1"] = br_resources.SubnetAZ1 replacements["SUBNET_AZ2"] = br_resources.SubnetAZ2 resource_data = load_rds_resource( "db_subnet_group_2az", additional_replacements=replacements, ) logging.debug(resource_data) # Create the k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) # Let's check that the DB subnet group appears in RDS aws_res = rds_client.describe_db_subnet_groups(DBSubnetGroupName=resource_name) assert aws_res is not None assert len(aws_res['DBSubnetGroups']) == 1 now = datetime.datetime.now() timeout = now + datetime.timedelta(seconds=CREATE_TIMEOUT_SECONDS) # TODO(jaypipes): Move this into generic AWS-side waiter while aws_res['DBSubnetGroups'][0]['SubnetGroupStatus'] != "Complete": if datetime.datetime.now() >= timeout: raise Exception("failed to find DB subnet group in Complete status before timeout") time.sleep(CREATE_INTERVAL_SLEEP_SECONDS) aws_res = rds_client.describe_db_subnet_groups(DBSubnetGroupName=resource_name) assert aws_res is not None assert len(aws_res['DBSubnetGroups']) == 1 # Delete the k8s resource on teardown of the module k8s.delete_custom_resource(ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) # DB subnet group should no longer appear in RDS try: aws_res = rds_client.describe_db_subnet_groups(DBSubnetGroupName=resource_name) assert False except rds_client.exceptions.DBSubnetGroupNotFoundFault: pass
def test_repository_lifecycle_policy(self, ecr_client): resource_name = random_suffix_name("ecr-repository", 24) replacements = REPLACEMENT_VALUES.copy() replacements["REPOSITORY_NAME"] = resource_name # Load ECR CR resource_data = load_ecr_resource( "repository_lifecycle_policy", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) time.sleep(CREATE_WAIT_AFTER_SECONDS) # Check ECR repository exists repo = self.get_repository(ecr_client, resource_name) assert repo is not None # Check ECR repository lifecycle policy exists lifecycle_policy = self.get_lifecycle_policy(ecr_client, resource_name, repo["registryId"]) assert lifecycle_policy == LIFECYCLE_POLICY_FILTERING_ON_IMAGE_AGE # Remove lifecycle policy cr["spec"]["lifecyclePolicy"] = "" # Patch k8s resource k8s.patch_custom_resource(ref, cr) time.sleep(UPDATE_WAIT_AFTER_SECONDS) lifecycle_policy = self.get_lifecycle_policy(ecr_client, resource_name, repo["registryId"]) assert lifecycle_policy == "" # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted is True time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check ECR repository doesn't exists exists = self.repository_exists(ecr_client, resource_name) assert not exists
def test_crud_integration(self, api_resource): api_ref, api_cr = api_resource api_id = api_cr['status']['apiID'] test_data = REPLACEMENT_VALUES.copy() integration_name = random_suffix_name("ack-test-integration", 25) test_data['INTEGRATION_NAME'] = integration_name test_data['API_ID'] = api_id integration_ref, integration_data = helper.integration_ref_and_data(integration_resource_name=integration_name, replacement_values=test_data) logging.debug(f"http api integration resource. name: {integration_name}, data: {integration_data}") # test create k8s.create_custom_resource(integration_ref, integration_data) time.sleep(CREATE_WAIT_AFTER_SECONDS) assert k8s.wait_on_condition(integration_ref, "ACK.ResourceSynced", "True", wait_periods=10) cr = k8s.get_resource(integration_ref) assert cr is not None integration_id = cr['status']['integrationID'] # Let's check that the HTTP Api integration appears in Amazon API Gateway apigw_validator.assert_integration_is_present(api_id=api_id, integration_id=integration_id) apigw_validator.assert_integration_uri( api_id=api_id, integration_id=integration_id, expected_uri=test_data['INTEGRATION_URI'] ) # test update updated_uri = 'https://httpbin.org/post' test_data['INTEGRATION_URI'] = updated_uri updated_integration_resource_data = load_apigatewayv2_resource( "integration", additional_replacements=test_data, ) logging.debug(f"updated http api integration resource: {updated_integration_resource_data}") # Update the k8s resource k8s.patch_custom_resource(integration_ref, updated_integration_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) condition.assert_synced(integration_ref) # Let's check that the HTTP Api integration appears in Amazon API Gateway with updated uri apigw_validator.assert_integration_uri( api_id=api_id, integration_id=integration_id, expected_uri=updated_uri ) # test delete k8s.delete_custom_resource(integration_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(integration_ref) # HTTP Api integration should no longer appear in Amazon API Gateway apigw_validator.assert_integration_is_deleted(api_id=api_id, integration_id=integration_id)
def test_smoke(self, lambda_client, lambda_function): (_, function_resource) = lambda_function lambda_function_name = function_resource["spec"]["name"] resource_name = random_suffix_name("lambda-alias", 24) replacements = REPLACEMENT_VALUES.copy() replacements["AWS_REGION"] = get_region() replacements["ALIAS_NAME"] = resource_name replacements["FUNCTION_NAME"] = lambda_function_name replacements["FUNCTION_VERSION"] = "$LATEST" # Load alias CR resource_data = load_lambda_resource( "alias", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) time.sleep(CREATE_WAIT_AFTER_SECONDS) # Check alias exists alias = self.alias_exist(lambda_client, resource_name, lambda_function_name) assert alias is not None # Update cr cr["spec"]["description"] = "" # Patch k8s resource k8s.patch_custom_resource(ref, cr) time.sleep(UPDATE_WAIT_AFTER_SECONDS) # Check alias description alias = self.get_alias(lambda_client, resource_name, lambda_function_name) assert alias is not None assert alias["Description"] == "" # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check alias doesn't exist exists = self.get_alias(lambda_client, resource_name, lambda_function_name) assert not exists
def test_crud_stage(self, api_resource): api_ref, api_cr = api_resource api_id = api_cr['status']['apiID'] test_data = REPLACEMENT_VALUES.copy() stage_name = random_suffix_name("ack-test-stage", 25) test_data['STAGE_NAME'] = stage_name test_data['API_ID'] = api_id stage_ref, stage_data = helper.stage_ref_and_data(stage_resource_name=stage_name, replacement_values=test_data) logging.debug(f"http api stage resource. name: {stage_name}, data: {stage_data}") # test create k8s.create_custom_resource(stage_ref, stage_data) time.sleep(CREATE_WAIT_AFTER_SECONDS) assert k8s.wait_on_condition(stage_ref, "ACK.ResourceSynced", "True", wait_periods=10) cr = k8s.get_resource(stage_ref) assert cr is not None # Let's check that the HTTP Api integration appears in Amazon API Gateway apigw_validator.assert_stage_is_present(api_id=api_id, stage_name=stage_name) stage_description = test_data['STAGE_DESCRIPTION'] apigw_validator.assert_stage_description( api_id=api_id, stage_name=stage_name, expected_description=stage_description ) # test update updated_description = 'updated' + stage_description test_data['STAGE_DESCRIPTION'] = updated_description updated_stage_resource_data = load_apigatewayv2_resource( "stage", additional_replacements=test_data, ) logging.debug(f"updated http api stage resource: {updated_stage_resource_data}") # Update the k8s resource k8s.patch_custom_resource(stage_ref, updated_stage_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) condition.assert_synced(stage_ref) # Let's check that the HTTP Api stage appears in Amazon API Gateway with updated description apigw_validator.assert_stage_description( api_id=api_id, stage_name=stage_name, expected_description=updated_description ) # test delete k8s.delete_custom_resource(stage_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(stage_ref) # HTTP Api stage should no longer appear in Amazon API Gateway apigw_validator.assert_stage_is_deleted(api_id=api_id, stage_name=stage_name)
def apply_app_yaml(domain_id, user_profile_name): replacements = REPLACEMENT_VALUES.copy() replacements["DOMAIN_ID"] = domain_id replacements["USER_PROFILE_NAME"] = user_profile_name reference, spec, resource = create_sagemaker_resource( resource_plural="apps", resource_name="default", spec_file="app", replacements=replacements, ) return reference, resource, spec
def faulty_config(name_suffix, single_container_model): replacements = REPLACEMENT_VALUES.copy() # copy model data to a temp S3 location and delete it after model is created on SageMaker model_bucket = replacements["SAGEMAKER_DATA_BUCKET"] copy_source = { "Bucket": model_bucket, "Key": "sagemaker/model/xgboost-mnist-model.tar.gz", } model_destination_key = "sagemaker/model/delete/xgboost-mnist-model.tar.gz" s3.copy_object(model_bucket, copy_source, model_destination_key) model_resource_name = name_suffix + "faulty-model" replacements["MODEL_NAME"] = model_resource_name replacements[ "MODEL_LOCATION"] = f"s3://{model_bucket}/{model_destination_key}" model_reference, model_spec, model_resource = create_sagemaker_resource( resource_plural=cfg.MODEL_RESOURCE_PLURAL, resource_name=model_resource_name, spec_file="xgboost_model_with_model_location", replacements=replacements, ) assert model_resource is not None if k8s.get_resource_arn(model_resource) is None: logging.error( f"ARN for this resource is None, resource status is: {model_resource['status']}" ) assert k8s.get_resource_arn(model_resource) is not None s3.delete_object(model_bucket, model_destination_key) config_resource_name = name_suffix + "-faulty-config" (_, model_resource) = single_container_model model_resource_name = model_resource["spec"].get("modelName", None) replacements["ENDPOINT_CONFIG_NAME"] = config_resource_name config_reference, config_spec, config_resource = create_sagemaker_resource( resource_plural=cfg.ENDPOINT_CONFIG_RESOURCE_PLURAL, resource_name=config_resource_name, spec_file="endpoint_config_multi_variant", replacements=replacements, ) assert config_resource is not None if k8s.get_resource_arn(config_resource) is None: logging.error( f"ARN for this resource is None, resource status is: {config_resource['status']}" ) assert k8s.get_resource_arn(config_resource) is not None yield (config_reference, config_resource) for cr in (model_reference, config_reference): _, deleted = k8s.delete_custom_resource(cr, 3, 10) assert deleted
def apply_user_profile_yaml(resource_name, domain_id): replacements = REPLACEMENT_VALUES.copy() replacements["USER_PROFILE_NAME"] = resource_name replacements["DOMAIN_ID"] = domain_id reference, spec, resource = create_sagemaker_resource( resource_plural="userprofiles", resource_name=resource_name, spec_file="user_profile", replacements=replacements, ) return reference, resource, spec
def test_basic_repository(self, ecr_client): resource_name = random_suffix_name("ecr-repository", 24) replacements = REPLACEMENT_VALUES.copy() replacements["REPOSITORY_NAME"] = resource_name # Load ECR CR resource_data = load_ecr_resource( "repository", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) time.sleep(CREATE_WAIT_AFTER_SECONDS) # Check ECR repository exists exists = self.repository_exists(ecr_client, resource_name) assert exists # Update CR cr["spec"]["imageScanningConfiguration"]["scanOnPush"] = True # Patch k8s resource k8s.patch_custom_resource(ref, cr) time.sleep(UPDATE_WAIT_AFTER_SECONDS) # Check repository scanOnPush scanning configuration repo = self.get_repository(ecr_client, resource_name) assert repo is not None assert repo["imageScanningConfiguration"]["scanOnPush"] is True # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted is True time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check ECR repository doesn't exists exists = self.repository_exists(ecr_client, resource_name) assert not exists
def test_crud_httpapi_using_import(self): test_data = REPLACEMENT_VALUES.copy() api_name = random_suffix_name("ack-test-importapi", 25) test_data['IMPORT_API_NAME'] = api_name test_data['IMPORT_API_TITLE'] = api_name api_ref, api_data = helper.import_api_ref_and_data(api_resource_name=api_name, replacement_values=test_data) logging.debug(f"imported http api resource. name: {api_name}, data: {api_data}") # test create k8s.create_custom_resource(api_ref, api_data) time.sleep(CREATE_API_WAIT_AFTER_SECONDS) assert k8s.wait_on_condition(api_ref, "ACK.ResourceSynced", "True", wait_periods=10) cr = k8s.get_resource(api_ref) assert cr is not None api_id = cr['status']['apiID'] # Let's check that the imported HTTP Api appears in Amazon API Gateway apigw_validator.assert_api_is_present(api_id=api_id) apigw_validator.assert_api_name( api_id=api_id, expected_api_name=api_name ) # test update updated_api_title = 'updated-' + api_name test_data['IMPORT_API_TITLE'] = updated_api_title updated_api_resource_data = load_apigatewayv2_resource( "import_api", additional_replacements=test_data, ) logging.debug(f"updated import http api resource: {updated_api_resource_data}") # Update the k8s resource k8s.patch_custom_resource(api_ref, updated_api_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) condition.assert_synced(api_ref) # Let's check that the HTTP Api appears in Amazon API Gateway with updated title apigw_validator.assert_api_name( api_id=api_id, expected_api_name=updated_api_title ) # test delete k8s.delete_custom_resource(api_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(api_ref) # HTTP Api should no longer appear in Amazon API Gateway apigw_validator.assert_api_is_deleted(api_id=api_id)
def test_smoke(self, dynamodb_client, dynamodb_table): (_, table_resource) = dynamodb_table resource_name = random_suffix_name("backup", 32) table_name = table_resource["spec"]["tableName"] replacements = REPLACEMENT_VALUES.copy() replacements["TABLE_NAME"] = table_name replacements["BACKUP_NAME"] = resource_name # Load Backup CR resource_data = load_dynamodb_resource( "backup", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) wait_for_cr_status( ref, "backupStatus", "AVAILABLE", 10, 5, ) backupArn = k8s.get_resource_arn(cr) # Check DynamoDB Backup exists exists = self.backup_exists(dynamodb_client, backupArn) assert exists # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted is True time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check DynamoDB Backup doesn't exists exists = self.backup_exists(dynamodb_client, backupArn) assert not exists
def apply_domain_yaml(resource_name): replacements = REPLACEMENT_VALUES.copy() replacements["DOMAIN_NAME"] = resource_name replacements["VPC_ID"] = get_default_vpc() replacements["SUBNET_ID"] = get_subnet(replacements["VPC_ID"]) reference, spec, resource = create_sagemaker_resource( resource_plural="domains", resource_name=resource_name, spec_file="domain", replacements=replacements, ) return reference, resource, spec
def test_crud_httpapi_using_import(self): test_data = REPLACEMENT_VALUES.copy() random_suffix = (''.join( random.choice(string.ascii_lowercase) for _ in range(6))) api_name = "ack-test-importapi-" + random_suffix test_data['API_NAME'] = api_name test_data['API_TITLE'] = api_name api_ref, api_data = helper.import_api_ref_and_data( api_resource_name=api_name, replacement_values=test_data) logging.debug( f"imported http api resource. name: {api_name}, data: {api_data}") # test create k8s.create_custom_resource(api_ref, api_data) cr = k8s.wait_resource_consumed_by_controller(api_ref) assert cr is not None assert k8s.get_resource_exists(api_ref) api_id = cr['status']['apiID'] # Let's check that the imported HTTP Api appears in Amazon API Gateway apigw_validator.assert_api_is_present(api_id=api_id) apigw_validator.assert_api_name(api_id=api_id, expected_api_name=api_name) # test update updated_api_title = 'updated-' + api_name test_data['API_TITLE'] = updated_api_title updated_api_resource_data = load_apigatewayv2_resource( "import_api", additional_replacements=test_data, ) logging.debug( f"updated import http api resource: {updated_api_resource_data}") # Update the k8s resource k8s.patch_custom_resource(api_ref, updated_api_resource_data) time.sleep(UPDATE_WAIT_AFTER_SECONDS) # Let's check that the HTTP Api appears in Amazon API Gateway with updated title apigw_validator.assert_api_name(api_id=api_id, expected_api_name=updated_api_title) # test delete k8s.delete_custom_resource(api_ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) assert not k8s.get_resource_exists(api_ref) # HTTP Api should no longer appear in Amazon API Gateway apigw_validator.assert_api_is_deleted(api_id=api_id)
def adopted_model_package(sdk_model_package): ( model_package_group_input, _, model_package_input, model_package_response, ) = sdk_model_package replacements = REPLACEMENT_VALUES.copy() # adopt model package group replacements["ADOPTED_RESOURCE_NAME"] = ( "adopt-" + model_package_group_input["ModelPackageGroupName"]) replacements["TARGET_RESOURCE_AWS"] = replacements[ "TARGET_RESOURCE_K8S"] = model_package_group_input[ "ModelPackageGroupName"] replacements["RESOURCE_KIND"] = "ModelPackageGroup" ( adopt_model_package_group_reference, _, adopt_model_package_group_resource, ) = create_adopted_resource(replacements=replacements, ) assert adopt_model_package_group_resource is not None # adopt model package replacements["ADOPTED_RESOURCE_NAME"] = ( "adopt-" + model_package_input["ModelPackageGroupName"] + "-child") replacements["TARGET_RESOURCE_AWS"] = model_package_response.get( "ModelPackageArn") replacements["TARGET_RESOURCE_K8S"] = ( model_package_input["ModelPackageGroupName"] + "-child") replacements["RESOURCE_KIND"] = "ModelPackage" ( adopt_model_package_reference, _, adopt_model_package_resource, ) = create_adopted_resource( replacements=replacements, spec_file="adopted_resource_base_arn", ) assert adopt_model_package_resource is not None yield (adopt_model_package_group_reference, adopt_model_package_reference) for cr in (adopt_model_package_group_reference, adopt_model_package_reference): if k8s.get_resource_exists(cr): _, deleted = k8s.delete_custom_resource(cr, 3, 10) assert deleted
def test_create_delete(self, dynamodb_client): resource_name = random_suffix_name("table", 32) replacements = REPLACEMENT_VALUES.copy() replacements["TABLE_NAME"] = resource_name # Load Table CR resource_data = load_dynamodb_resource( "table_forums", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) wait_for_cr_status( ref, "tableStatus", "ACTIVE", 10, 5, ) # Check DynamoDB Table exists exists = self.table_exists(dynamodb_client, resource_name) assert exists # Delete k8s resource _, deleted = k8s.delete_custom_resource(ref) assert deleted is True time.sleep(DELETE_WAIT_AFTER_SECONDS) # Check DynamoDB Table doesn't exists exists = self.table_exists(dynamodb_client, resource_name) assert not exists
def test_create_delete_simple(self, rds_client): resource_name = "my-db-security-group" resource_desc = "my-db-security-group description" br_resources = get_bootstrap_resources() replacements = REPLACEMENT_VALUES.copy() replacements["DB_SECURITY_GROUP_NAME"] = resource_name replacements["DB_SECURITY_GROUP_DESC"] = resource_desc resource_data = load_rds_resource( "db_security_group_simple", additional_replacements=replacements, ) logging.debug(resource_data) # Create the k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, resource_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) # Let's check that the DB security group appears in RDS aws_res = rds_client.describe_db_security_groups( DBSecurityGroupName=resource_name) assert aws_res is not None assert len(aws_res['DBSecurityGroups']) == 1 # Delete the k8s resource on teardown of the module k8s.delete_custom_resource(ref) time.sleep(DELETE_WAIT_AFTER_SECONDS) # DB security group should no longer appear in RDS try: aws_res = rds_client.describe_db_security_groups( DBSecurityGroupName=resource_name) assert False except rds_client.exceptions.DBSecurityGroupNotFoundFault: pass
def test_smoke(self, dynamodb_client, dynamodb_table): (_, table_resource) = dynamodb_table # Global Tables must have the same name as dynamodb Tables global_table_name = table_resource["spec"]["tableName"] replacements = REPLACEMENT_VALUES.copy() replacements["REGION_NAME"] = get_region() replacements["TABLE_NAME"] = global_table_name replacements["GLOBAL_TABLE_NAME"] = global_table_name # Load GLobal Table CR resource_data = load_dynamodb_resource( "global_table", additional_replacements=replacements, ) logging.debug(resource_data) # Create k8s resource ref = k8s.CustomResourceReference( CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, global_table_name, namespace="default", ) k8s.create_custom_resource(ref, resource_data) cr = k8s.wait_resource_consumed_by_controller(ref) assert cr is not None assert k8s.get_resource_exists(ref) wait_for_cr_status( ref, "globalTableStatus", "ACTIVE", 10, 5, ) # Check DynamoDB Global Table exists exists = self.global_table_exists(dynamodb_client, global_table_name) assert exists _, deleted = k8s.delete_custom_resource(ref) assert deleted is True time.sleep(DELETE_WAIT_AFTER_SECONDS) exists = self.global_table_exists(dynamodb_client, global_table_name) assert not exists
def adopted_endpoint(sdk_endpoint): (model_input, _, endpoint_config_input, _, endpoint_input, _) = sdk_endpoint replacements = REPLACEMENT_VALUES.copy() # adopt model replacements["ADOPTED_RESOURCE_NAME"] = "adopt-" + model_input["ModelName"] replacements["TARGET_RESOURCE_AWS"] = replacements[ "TARGET_RESOURCE_K8S" ] = model_input["ModelName"] replacements["RESOURCE_KIND"] = "Model" adopt_model_reference, _, adopt_model_resource = create_adopted_resource( replacements=replacements, ) assert adopt_model_resource is not None # adopt endpoint config replacements["ADOPTED_RESOURCE_NAME"] = ( "adopt-" + endpoint_config_input["EndpointConfigName"] ) replacements["TARGET_RESOURCE_AWS"] = replacements[ "TARGET_RESOURCE_K8S" ] = endpoint_config_input["EndpointConfigName"] replacements["RESOURCE_KIND"] = "EndpointConfig" adopt_config_reference, _, adopt_config_resource = create_adopted_resource( replacements=replacements, ) assert adopt_config_resource is not None # adopt endpoint replacements["ADOPTED_RESOURCE_NAME"] = "adopt-" + endpoint_input["EndpointName"] replacements["TARGET_RESOURCE_AWS"] = replacements[ "TARGET_RESOURCE_K8S" ] = endpoint_input["EndpointName"] replacements["RESOURCE_KIND"] = "Endpoint" adopt_endpoint_reference, _, adopt_endpoint_resource = create_adopted_resource( replacements=replacements, ) assert adopt_endpoint_resource is not None yield (adopt_model_reference, adopt_config_reference, adopt_endpoint_reference) for cr in (adopt_model_reference, adopt_config_reference, adopt_endpoint_reference): if k8s.get_resource_exists(cr): _, deleted = k8s.delete_custom_resource(cr, 3, 10) assert deleted
def single_container_model(name_suffix): model_resource_name = name_suffix + "-model" replacements = REPLACEMENT_VALUES.copy() replacements["MODEL_NAME"] = model_resource_name model_reference, model_spec, model_resource = create_sagemaker_resource( resource_plural=MODEL_RESOURCE_PLURAL, resource_name=model_resource_name, spec_file="xgboost_model", replacements=replacements, ) assert model_resource is not None yield (model_reference, model_resource) k8s.delete_custom_resource(model_reference)