Skip to content

Fix: Corrected Validation Logic and Exception Handling for validate.py and SamTemplateValidator #7983

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions samcli/commands/validate/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,38 +81,46 @@ def do_cli(ctx, template, lint):
from samcli.commands.validate.lib.exceptions import InvalidSamDocumentException
from samcli.lib.translate.sam_template_validator import SamTemplateValidator

# Read the SAM Template from the file
sam_template = _read_sam_file(template)

if lint:
# Lint the template if --lint flag is set
_lint(ctx, sam_template.serialized, template)
else:
iam_client = boto3.client("iam")
# Initialize the SAM template validator
validator = SamTemplateValidator(
sam_template.deserialized, ManagedPolicyLoader(iam_client), profile=ctx.profile, region=ctx.region
)

try:
# Validate the SAM template
validator.get_translated_template_if_valid()
except InvalidSamDocumentException as e:
click.secho("Template provided at '{}' was invalid SAM Template.".format(template), bg="red")
click.secho(f"Template provided at '{template}' was invalid SAM Template.", bg="red")
raise InvalidSamTemplateException(str(e)) from e
except NoRegionFound as no_region_found_e:
# Handle missing region in the AWS profile or context
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:
# Handle missing AWS credentials
raise UserException(
"AWS Credentials are required. Please configure your credentials.", wrapped_from=e.__class__.__name__
) from e

# Success message after validation
click.secho(
"{} is a valid SAM Template. This is according to basic SAM Validation, "
'for additional validation, please run with "--lint" option'.format(template),
f"{template} is a valid SAM Template. This is according to basic SAM Validation, "
'for additional validation, please run with "--lint" option.',
fg="green",
)



def _read_sam_file(template) -> SamTemplate:
"""
Reads the file (json and yaml supported) provided and returns the dictionary representation of the file.
Expand Down
19 changes: 12 additions & 7 deletions samcli/lib/translate/sam_template_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(
Design Details:

managed_policy_loader is injected into the `__init__` to allow future expansion
and overriding capabilities. A typically pattern is to pass the name of the class into
and overriding capabilities. A typical pattern is to pass the name of the class into
the `__init__` as keyword args. As long as the class 'conforms' to the same 'interface'.
This allows the class to be changed by the client and allowing customization of the class being
initialized. Something I had in mind would be allowing a template to be run and checked
Expand Down Expand Up @@ -64,14 +64,13 @@ def __init__(
def get_translated_template_if_valid(self):
"""
Runs the SAM Translator to determine if the template provided is valid. This is similar to running a
ChangeSet in CloudFormation for a SAM Template
ChangeSet in CloudFormation for a SAM Template.

Raises
-------
InvalidSamDocumentException
If the template is not valid, an InvalidSamDocumentException is raised
If the template is not valid, an InvalidSamDocumentException is raised.
"""

sam_translator = Translator(
managed_policy_map=None,
sam_parser=self.sam_parser,
Expand All @@ -83,13 +82,20 @@ def get_translated_template_if_valid(self):
self._replace_local_image()

try:
# Translate the SAM template
template = sam_translator.translate(
sam_template=self.sam_template,
parameter_values=self.parameter_overrides,
get_managed_policy_map=self._get_managed_policy_map,
)

# If translation returns None, raise an exception
if template is None:
raise InvalidSamDocumentException("The translated template is None. The SAM Template is invalid.")

LOG.debug("Translated template is:\n%s", yaml_dump(template))
return yaml_dump(template)

except InvalidDocumentException as e:
raise InvalidSamDocumentException(
functools.reduce(lambda message, error: message + " " + str(error), e.causes, str(e))
Expand All @@ -112,7 +118,7 @@ def _replace_local_codeuri(self):
"""
Replaces the CodeUri in AWS::Serverless::Function and DefinitionUri in AWS::Serverless::Api and
AWS::Serverless::HttpApi to a fake S3 Uri. This is to support running the SAM Translator with
valid values for these fields. If this in not done, the template is invalid in the eyes of SAM
valid values for these fields. If this is not done, the template is invalid in the eyes of SAM
Translator (the translator does not support local paths)
"""

Expand Down Expand Up @@ -183,7 +189,6 @@ def is_s3_uri(uri):
-------
bool
Returns True if the uri given is an S3 uri, otherwise False

"""
return isinstance(uri, str) and uri.startswith("s3://")

Expand All @@ -192,7 +197,7 @@ def _update_to_s3_uri(property_key, resource_property_dict, s3_uri_value="s3://b
"""
Updates the 'property_key' in the 'resource_property_dict' to the value of 's3_uri_value'

Note: The function will mutate the resource_property_dict that is pass in
Note: The function will mutate the resource_property_dict that is passed in

Parameters
----------
Expand Down