Skip to content

Commit

Permalink
fix(bug): Pass boto_session to SAM Translator library (aws#2759)
Browse files Browse the repository at this point in the history
When validating the sam template, SAM CLI requires credentials to get
the IAM Manged Policies. SAM Translator also requires the region in order
to figure out the parition. Previously, SAM Translator assumed this to be
on the Env but SAM CLI could get this information from a command line argument
or a profile. This commit passes the boto_session into the SAM Translator lib
(v1.35.0 or later), so that SAM Translator can figure out the partition from the
information passed to SAM CLI.

Co-authored-by: Jacob Fuss <[email protected]>
Co-authored-by: Mathieu Grandis <[email protected]>
  • Loading branch information
3 people authored and moelasmar committed Jul 1, 2021
1 parent ace07a6 commit 221a7de
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 19 deletions.
11 changes: 9 additions & 2 deletions samcli/commands/validate/lib/sam_template_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from samtranslator.public.exceptions import InvalidDocumentException
from samtranslator.parser import parser
from samtranslator.translator.translator import Translator
from boto3.session import Session

from samcli.lib.utils.packagetype import ZIP
from samcli.yamlhelper import yaml_dump
Expand All @@ -16,7 +17,7 @@


class SamTemplateValidator:
def __init__(self, sam_template, managed_policy_loader):
def __init__(self, sam_template, managed_policy_loader, profile=None, region=None):
"""
Construct a SamTemplateValidator
Expand All @@ -40,6 +41,7 @@ def __init__(self, sam_template, managed_policy_loader):
self.sam_template = sam_template
self.managed_policy_loader = managed_policy_loader
self.sam_parser = parser.Parser()
self.boto3_session = Session(profile_name=profile, region_name=region)

def is_valid(self):
"""
Expand All @@ -53,7 +55,12 @@ def is_valid(self):
"""
managed_policy_map = self.managed_policy_loader.load()

sam_translator = Translator(managed_policy_map=managed_policy_map, sam_parser=self.sam_parser, plugins=[])
sam_translator = Translator(
managed_policy_map=managed_policy_map,
sam_parser=self.sam_parser,
plugins=[],
boto_session=self.boto3_session,
)

self._replace_local_codeuri()

Expand Down
11 changes: 10 additions & 1 deletion samcli/commands/validate/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from botocore.exceptions import NoCredentialsError
import click

from samtranslator.translator.arn_generator import NoRegionFound

from samcli.cli.main import pass_context, common_options as cli_framework_options, aws_creds_options, print_cmdline_args
from samcli.commands._utils.options import template_option_without_build
from samcli.lib.telemetry.metric import track_command
Expand Down Expand Up @@ -49,13 +51,20 @@ def do_cli(ctx, template):
sam_template = _read_sam_file(template)

iam_client = boto3.client("iam")
validator = SamTemplateValidator(sam_template, ManagedPolicyLoader(iam_client))
validator = SamTemplateValidator(
sam_template, ManagedPolicyLoader(iam_client), profile=ctx.profile, region=ctx.region
)

try:
validator.is_valid()
except InvalidSamDocumentException as e:
click.secho("Template provided at '{}' was invalid SAM Template.".format(template), bg="red")
raise InvalidSamTemplateException(str(e)) from e
except NoRegionFound as no_region_found_e:
raise UserException(
"AWS Region was not found. Please configure your region through a profile or --region option",
wrapped_from=no_region_found_e.__class__.__name__,
) from no_region_found_e
except NoCredentialsError as e:
raise UserException(
"AWS Credentials are required. Please configure your credentials.", wrapped_from=e.__class__.__name__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_valid_template(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand All @@ -56,7 +56,7 @@ def test_invalid_template(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

with self.assertRaises(InvalidSamDocumentException):
validator.is_valid()
Expand All @@ -76,7 +76,7 @@ def test_valid_template_with_local_code_for_function(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand All @@ -93,7 +93,7 @@ def test_valid_template_with_local_code_for_layer_version(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand All @@ -113,7 +113,7 @@ def test_valid_template_with_local_code_for_api(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand All @@ -133,7 +133,7 @@ def test_valid_template_with_DefinitionBody_for_api(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand Down Expand Up @@ -165,7 +165,7 @@ def test_valid_template_with_s3_object_passed(self):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
Expand All @@ -187,7 +187,7 @@ def test_valid_api_request_model_template(self, template_path):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"PolicyName": "FakePolicy"}

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, region="us-east-1")

# Should not throw an exception
validator.is_valid()
21 changes: 16 additions & 5 deletions tests/unit/commands/validate/lib/test_sam_template_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,50 @@


class TestSamTemplateValidator(TestCase):
@patch("samcli.commands.validate.lib.sam_template_validator.Session")
@patch("samcli.commands.validate.lib.sam_template_validator.Translator")
@patch("samcli.commands.validate.lib.sam_template_validator.parser")
def test_is_valid_returns_true(self, sam_parser, sam_translator):
def test_is_valid_returns_true(self, sam_parser, sam_translator, boto_session_patch):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"policy": "SomePolicy"}
template = {"a": "b"}

parser = Mock()
sam_parser.Parser.return_value = parser

boto_session_mock = Mock()
boto_session_patch.return_value = boto_session_mock

translate_mock = Mock()
translate_mock.translate.return_value = {"c": "d"}
sam_translator.return_value = translate_mock

validator = SamTemplateValidator(template, managed_policy_mock)
validator = SamTemplateValidator(template, managed_policy_mock, profile="profile", region="region")

# Should not throw an Exception
validator.is_valid()

boto_session_patch.assert_called_once_with(profile_name="profile", region_name="region")
sam_translator.assert_called_once_with(
managed_policy_map={"policy": "SomePolicy"}, sam_parser=parser, plugins=[]
managed_policy_map={"policy": "SomePolicy"}, sam_parser=parser, plugins=[], boto_session=boto_session_mock
)
translate_mock.translate.assert_called_once_with(sam_template=template, parameter_values={})
sam_parser.Parser.assert_called_once()

@patch("samcli.commands.validate.lib.sam_template_validator.Session")
@patch("samcli.commands.validate.lib.sam_template_validator.Translator")
@patch("samcli.commands.validate.lib.sam_template_validator.parser")
def test_is_valid_raises_exception(self, sam_parser, sam_translator):
def test_is_valid_raises_exception(self, sam_parser, sam_translator, boto_session_patch):
managed_policy_mock = Mock()
managed_policy_mock.load.return_value = {"policy": "SomePolicy"}
template = {"a": "b"}

parser = Mock()
sam_parser.Parser.return_value = parser

boto_session_mock = Mock()
boto_session_patch.return_value = boto_session_mock

translate_mock = Mock()
translate_mock.translate.side_effect = InvalidDocumentException([Exception("message")])
sam_translator.return_value = translate_mock
Expand All @@ -54,8 +63,10 @@ def test_is_valid_raises_exception(self, sam_parser, sam_translator):
validator.is_valid()

sam_translator.assert_called_once_with(
managed_policy_map={"policy": "SomePolicy"}, sam_parser=parser, plugins=[]
managed_policy_map={"policy": "SomePolicy"}, sam_parser=parser, plugins=[], boto_session=boto_session_mock
)

boto_session_patch.assert_called_once_with(profile_name=None, region_name=None)
translate_mock.translate.assert_called_once_with(sam_template=template, parameter_values={})
sam_parser.Parser.assert_called_once()

Expand Down
9 changes: 6 additions & 3 deletions tests/unit/commands/validate/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from unittest import TestCase
from unittest.mock import Mock, patch
from collections import namedtuple

from botocore.exceptions import NoCredentialsError

Expand All @@ -8,6 +9,8 @@
from samcli.commands.validate.lib.exceptions import InvalidSamDocumentException
from samcli.commands.validate.validate import do_cli, _read_sam_file

ctx_mock = namedtuple("ctx", ["profile", "region"])


class TestValidateCli(TestCase):
@patch("samcli.commands.validate.validate.click")
Expand Down Expand Up @@ -46,7 +49,7 @@ def test_template_fails_validation(self, read_sam_file_patch, click_patch, templ
template_valiadator.return_value = is_valid_mock

with self.assertRaises(InvalidSamTemplateException):
do_cli(ctx=None, template=template_path)
do_cli(ctx=ctx_mock(profile="profile", region="region"), template=template_path)

@patch("samcli.commands.validate.lib.sam_template_validator.SamTemplateValidator")
@patch("samcli.commands.validate.validate.click")
Expand All @@ -60,7 +63,7 @@ def test_no_credentials_provided(self, read_sam_file_patch, click_patch, templat
template_valiadator.return_value = is_valid_mock

with self.assertRaises(UserException):
do_cli(ctx=None, template=template_path)
do_cli(ctx=ctx_mock(profile="profile", region="region"), template=template_path)

@patch("samcli.commands.validate.lib.sam_template_validator.SamTemplateValidator")
@patch("samcli.commands.validate.validate.click")
Expand All @@ -73,4 +76,4 @@ def test_template_passes_validation(self, read_sam_file_patch, click_patch, temp
is_valid_mock.is_valid.return_value = True
template_valiadator.return_value = is_valid_mock

do_cli(ctx=None, template=template_path)
do_cli(ctx=ctx_mock(profile="profile", region="region"), template=template_path)

0 comments on commit 221a7de

Please sign in to comment.