def _assert_successful(self, mocker, client, image_id, force, region, image, stack, expected_response): if image: mocker.patch("pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", return_value=image) else: mocker.patch( "pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", side_effect=ImageNotFoundError("describe_image_by_id_tag"), ) if stack: mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack", return_value=stack) else: mocker.patch( "pcluster.aws.cfn.CfnClient.describe_stack", side_effect=StackNotFoundError("describe_stack", "stack_name"), ) mocker.patch("pcluster.models.imagebuilder.ImageBuilder.delete", return_value=None) # Ensure we don't hit AWS when creating ImageBuilderStack(s) mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack_resource", return_value=None) response = self._send_test_request(client, image_id, region, force) with soft_assertions(): assert_that(response.status_code).is_equal_to(202) assert_that(response.get_json()).is_equal_to(expected_response)
def test_that_errors_are_converted(self, client, mocker, method_to_patch, error, error_code): method = method_to_patch.split(".")[-1] if method == "describe_stack": mocker.patch( "pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", side_effect=ImageNotFoundError("describe_image_by_id_tag"), ) mocker.patch(method_to_patch, side_effect=error(method, "test error")) if error == StackNotFoundError: expected_error = { "message": "No image or stack associated with ParallelCluster image id: image1." } elif error == BadRequestError: expected_error = { "message": "Bad Request: Unable to get image image1, due to test error." } else: expected_error = { "message": "Unable to get image image1, due to test error." } response = self._send_test_request(client, "image1") with soft_assertions(): assert_that(response.status_code).is_equal_to(error_code) assert_that(response.get_json()).is_equal_to(expected_error)
def describe_images(self, ami_ids, filters, owners): """Return a list of objects of ImageInfo.""" result = self._client.describe_images(ImageIds=ami_ids, Filters=filters, Owners=owners) if result.get("Images"): return [ImageInfo(image) for image in result.get("Images")] raise ImageNotFoundError(function_name="describe_images")
def test_delete_image_with_no_available_image_or_stack_throws_not_found_exception( self, mocker, client): mocker.patch( "pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", side_effect=ImageNotFoundError("describe_image_by_id_tag"), ) mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack", side_effect=StackNotFoundError("describe_stack", "stack_name")) response = self._send_test_request(client, "nonExistentImage") with soft_assertions(): assert_that(response.status_code).is_equal_to(404) assert_that(response.get_json()).is_equal_to({ "message": "No image or stack associated with ParallelCluster image id: nonExistentImage." })
def test_describe_image_in_failed_state_with_reasons_and_associated_imagebuilder_image( self, client, mocker): mocker.patch( "pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", side_effect=ImageNotFoundError("describe_image_by_id_tag"), ) mocker.patch( "pcluster.aws.cfn.CfnClient.describe_stack", return_value=_create_stack("image1", CloudFormationStackStatus.CREATE_FAILED, "cfn test reason"), ) mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack_resource", return_value=None) mocker.patch( "pcluster.aws.cfn.CfnClient.describe_stack_resource", return_value={ "StackResourceDetail": { "PhysicalResourceId": "test_id" } }, ) mocker.patch( "pcluster.aws.imagebuilder.ImageBuilderClient.get_image_state", return_value={ "status": ImageBuilderImageStatus.FAILED, "reason": "img test reason" }, ) mocker.patch( "pcluster.api.controllers.image_operations_controller._presigned_config_url", return_value="https://parallelcluster.aws.com/bucket/key", ) expected_response = { "cloudformationStackArn": "arn:image1", "imageBuildLogsArn": "arn:image1:build_log", "cloudformationStackCreationTime": to_iso_timestr(to_utc_datetime("2021-04-12 00:00:00")), "cloudformationStackTags": [ { "key": "parallelcluster:image_id", "value": "image1" }, { "key": "parallelcluster:version", "value": "3.0.0" }, { "key": "parallelcluster:build_config", "value": "s3://bucket/key" }, { "key": "parallelcluster:build_log", "value": "arn:image1:build_log" }, ], "cloudformationStackStatus": CloudFormationStackStatus.CREATE_FAILED, "cloudformationStackStatusReason": "cfn test reason", "imageBuildStatus": ImageBuildStatus.BUILD_FAILED, "imageConfiguration": { "url": "https://parallelcluster.aws.com/bucket/key" }, "imageId": "image1", "imagebuilderImageStatus": ImageBuilderImageStatus.FAILED, "imagebuilderImageStatusReason": "img test reason", "region": "us-east-1", "version": "3.0.0", } response = self._send_test_request(client, "image1") with soft_assertions(): assert_that(response.status_code).is_equal_to(200) assert_that(response.get_json()).is_equal_to(expected_response)
def test_describe_of_image_not_yet_available_with_no_associated_imagebuilder_image( self, client, mocker): mocker.patch( "pcluster.aws.ec2.Ec2Client.describe_image_by_id_tag", side_effect=ImageNotFoundError("describe_image_by_id_tag"), ) mocker.patch( "pcluster.aws.cfn.CfnClient.describe_stack", return_value=_create_stack( "image1", CloudFormationStackStatus.CREATE_IN_PROGRESS), ) mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack_resource", return_value=None) mocker.patch( "pcluster.api.controllers.image_operations_controller._presigned_config_url", return_value="https://parallelcluster.aws.com/bucket/key", ) expected_response = { "imageConfiguration": { "url": "https://parallelcluster.aws.com/bucket/key" }, "imageId": "image1", "imageBuildStatus": ImageBuildStatus.BUILD_IN_PROGRESS, "cloudformationStackStatus": CloudFormationStackStatus.CREATE_IN_PROGRESS, "cloudformationStackArn": "arn:image1", "imageBuildLogsArn": "arn:image1:build_log", "cloudformationStackCreationTime": to_iso_timestr(datetime(2021, 4, 12)), "cloudformationStackTags": [ { "key": "parallelcluster:image_id", "value": "image1" }, { "key": "parallelcluster:version", "value": "3.0.0" }, { "key": "parallelcluster:build_config", "value": "s3://bucket/key" }, { "key": "parallelcluster:build_log", "value": "arn:image1:build_log" }, ], "region": "us-east-1", "version": "3.0.0", } response = self._send_test_request(client, "image1") with soft_assertions(): assert_that(response.status_code).is_equal_to(200) assert_that(response.get_json()).is_equal_to(expected_response)
) def test_stack_exists(mocker, response, is_error): """Verify that CfnClient.stack_exists behaves as expected.""" should_exist = not is_error mock_aws_api(mocker) mocker.patch("pcluster.aws.cfn.CfnClient.describe_stack", side_effect=response) assert_that(_DummyAWSApi().instance().cfn.stack_exists( FAKE_STACK_NAME)).is_equal_to(should_exist) @pytest.mark.parametrize( "response,is_error", [ ( ImageNotFoundError(function_name="describe_images"), True, ), ({ "Images": [{ "ImageId": FAKE_IMAGE_ID }] }, False), ], ) def test_image_exists(mocker, response, is_error): """Verify that EC2Client.image_exists behaves as expected.""" should_exist = not is_error mock_aws_api(mocker) mocker.patch("pcluster.aws.ec2.Ec2Client.describe_images", side_effect=response)