Skip to content
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

fix: --base-dir is disregarded in sam build #2718

Merged
merged 6 commits into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
11 changes: 9 additions & 2 deletions samcli/commands/build/build_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ def __init__(
self._resource_identifier = resource_identifier
self._template_file = template_file
self._base_dir = base_dir

# Note(xinhol): use_raw_codeuri is temporary to fix a bug, and will be removed for a permanent solution.
self._use_raw_codeuri = bool(self._base_dir)

self._build_dir = build_dir
self._cache_dir = cache_dir
self._manifest_path = manifest_path
Expand Down Expand Up @@ -80,8 +84,11 @@ def __enter__(self) -> "BuildContext":
"\n".join([f"- {full_path}" for full_path in remote_stack_full_paths]),
)

self._function_provider = SamFunctionProvider(self.stacks)
self._layer_provider = SamLayerProvider(self.stacks)
# Note(xinhol): self._use_raw_codeuri is added temporarily to fix issue #2717
# when base_dir is provided, codeuri should not be resolved based on template file path.
# we will refactor to make all path resolution inside providers intead of in multiple places
self._function_provider = SamFunctionProvider(self.stacks, self._use_raw_codeuri)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a note here too and reference the issue number.

self._layer_provider = SamLayerProvider(self.stacks, self._use_raw_codeuri)

if not self._base_dir:
# Base directory, if not provided, is the directory containing the template
Expand Down
66 changes: 44 additions & 22 deletions samcli/lib/providers/sam_function_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ class SamFunctionProvider(SamBaseProvider):
It may or may not contain a function.
"""

def __init__(self, stacks: List[Stack], ignore_code_extraction_warnings: bool = False) -> None:
def __init__(
self, stacks: List[Stack], use_raw_codeuri: bool = False, ignore_code_extraction_warnings: bool = False
) -> None:
"""
Initialize the class with SAM template data. The SAM template passed to this provider is assumed
to be valid, normalized and a dictionary. It should be normalized by running all pre-processing
Expand All @@ -35,6 +37,8 @@ def __init__(self, stacks: List[Stack], ignore_code_extraction_warnings: bool =
You need to explicitly update the class with new template, if necessary.

:param dict stacks: List of stacks functions are extracted from
:param bool use_raw_codeuri: Do not resolve adjust core_uri based on the template path, use the raw uri.
Note(xinhol): use_raw_codeuri is temporary to fix a bug, and will be removed for a permanent solution.
:param bool ignore_code_extraction_warnings: Ignores Log warnings
"""

Expand All @@ -44,7 +48,9 @@ def __init__(self, stacks: List[Stack], ignore_code_extraction_warnings: bool =
LOG.debug("%d resources found in the stack %s", len(stack.resources), stack.stack_path)

# Store a map of function full_path to function information for quick reference
self.functions = SamFunctionProvider._extract_functions(self.stacks, ignore_code_extraction_warnings)
self.functions = SamFunctionProvider._extract_functions(
self.stacks, use_raw_codeuri, ignore_code_extraction_warnings
)

self._deprecated_runtimes = {"nodejs4.3", "nodejs6.10", "nodejs8.10", "dotnetcore2.0"}
self._colored = Colored()
Expand Down Expand Up @@ -98,12 +104,15 @@ def get_all(self) -> Iterator[Function]:
yield function

@staticmethod
def _extract_functions(stacks: List[Stack], ignore_code_extraction_warnings: bool = False) -> Dict[str, Function]:
def _extract_functions(
stacks: List[Stack], use_raw_codeuri: bool = False, ignore_code_extraction_warnings: bool = False
) -> Dict[str, Function]:
"""
Extracts and returns function information from the given dictionary of SAM/CloudFormation resources. This
method supports functions defined with AWS::Serverless::Function and AWS::Lambda::Function

:param stacks: List of SAM/CloudFormation stacks to extract functions from
:param bool use_raw_codeuri: Do not resolve adjust core_uri based on the template path, use the raw uri.
:param bool ignore_code_extraction_warnings: suppress log statements on code extraction from resources.
:return dict(string : samcli.commands.local.lib.provider.Function): Dictionary of function full_path to the
Function configuration object
Expand All @@ -124,13 +133,15 @@ def _extract_functions(stacks: List[Stack], ignore_code_extraction_warnings: boo
layers = SamFunctionProvider._parse_layer_info(
stack,
resource_properties.get("Layers", []),
use_raw_codeuri,
ignore_code_extraction_warnings=ignore_code_extraction_warnings,
)
function = SamFunctionProvider._convert_sam_function_resource(
stack,
name,
resource_properties,
layers,
use_raw_codeuri,
ignore_code_extraction_warnings=ignore_code_extraction_warnings,
)
result[function.full_path] = function
Expand All @@ -139,10 +150,11 @@ def _extract_functions(stacks: List[Stack], ignore_code_extraction_warnings: boo
layers = SamFunctionProvider._parse_layer_info(
stack,
resource_properties.get("Layers", []),
use_raw_codeuri,
ignore_code_extraction_warnings=ignore_code_extraction_warnings,
)
function = SamFunctionProvider._convert_lambda_function_resource(
stack, name, resource_properties, layers
stack, name, resource_properties, layers, use_raw_codeuri
)
result[function.full_path] = function

Expand All @@ -156,6 +168,7 @@ def _convert_sam_function_resource(
name: str,
resource_properties: Dict,
layers: List[LayerVersion],
use_raw_codeuri: bool = False,
ignore_code_extraction_warnings: bool = False,
) -> Function:
"""
Expand Down Expand Up @@ -196,18 +209,12 @@ def _convert_sam_function_resource(
LOG.debug("Found Serverless function with name='%s' and ImageUri='%s'", name, imageuri)

return SamFunctionProvider._build_function_configuration(
stack,
name,
codeuri,
resource_properties,
layers,
inlinecode,
imageuri,
stack, name, codeuri, resource_properties, layers, inlinecode, imageuri, use_raw_codeuri
)

@staticmethod
def _convert_lambda_function_resource(
stack: Stack, name: str, resource_properties: Dict, layers: List[LayerVersion]
stack: Stack, name: str, resource_properties: Dict, layers: List[LayerVersion], use_raw_codeuri: bool = False
) -> Function:
"""
Converts a AWS::Lambda::Function resource to a Function configuration usable by the provider.
Expand All @@ -220,6 +227,8 @@ def _convert_lambda_function_resource(
Properties of this resource
layers List(samcli.commands.local.lib.provider.Layer)
List of the Layer objects created from the template and layer list defined on the function.
use_raw_codeuri
Do not resolve adjust core_uri based on the template path, use the raw uri.

Returns
-------
Expand Down Expand Up @@ -250,13 +259,7 @@ def _convert_lambda_function_resource(
LOG.debug("Found Lambda function with name='%s' and Imageuri='%s'", name, imageuri)

return SamFunctionProvider._build_function_configuration(
stack,
name,
codeuri,
resource_properties,
layers,
inlinecode,
imageuri,
stack, name, codeuri, resource_properties, layers, inlinecode, imageuri, use_raw_codeuri
)

@staticmethod
Expand All @@ -268,6 +271,7 @@ def _build_function_configuration(
layers: List,
inlinecode: Optional[str],
imageuri: Optional[str],
use_raw_codeuri: bool = False,
) -> Function:
"""
Builds a Function configuration usable by the provider.
Expand All @@ -282,18 +286,29 @@ def _build_function_configuration(
Properties of this resource
layers List(samcli.commands.local.lib.provider.Layer)
List of the Layer objects created from the template and layer list defined on the function.
use_raw_codeuri
Do not resolve adjust core_uri based on the template path, use the raw uri.

Returns
-------
samcli.commands.local.lib.provider.Function
Function configuration
"""
metadata = resource_properties.get("Metadata", None)
if metadata and "DockerContext" in metadata:
if metadata and "DockerContext" in metadata and not use_raw_codeuri:
LOG.debug(
"--base-dir is presented not, adjusting uri %s relative to %s",
metadata["DockerContext"],
stack.location,
)
metadata["DockerContext"] = SamLocalStackProvider.normalize_resource_path(
stack.location, metadata["DockerContext"]
)

if codeuri and not use_raw_codeuri:
LOG.debug("--base-dir is presented not, adjusting uri %s relative to %s", codeuri, stack.location)
codeuri = SamLocalStackProvider.normalize_resource_path(stack.location, codeuri)

return Function(
stack_path=stack.stack_path,
name=name,
Expand All @@ -303,7 +318,7 @@ def _build_function_configuration(
memory=resource_properties.get("MemorySize"),
timeout=resource_properties.get("Timeout"),
handler=resource_properties.get("Handler"),
codeuri=SamLocalStackProvider.normalize_resource_path(stack.location, codeuri) if codeuri else None,
codeuri=codeuri,
imageuri=imageuri if imageuri else resource_properties.get("ImageUri"),
imageconfig=resource_properties.get("ImageConfig"),
environment=resource_properties.get("Environment"),
Expand All @@ -319,6 +334,7 @@ def _build_function_configuration(
def _parse_layer_info(
stack: Stack,
list_of_layers: List[Any],
use_raw_codeuri: bool = False,
ignore_code_extraction_warnings: bool = False,
) -> List[LayerVersion]:
"""
Expand All @@ -331,6 +347,8 @@ def _parse_layer_info(
list_of_layers : List[Any]
List of layers that are defined within the Layers Property on a function,
layer can be defined as string or Dict, in case customers define it in other types, use "Any" here.
use_raw_codeuri : bool
Do not resolve adjust core_uri based on the template path, use the raw uri.
ignore_code_extraction_warnings : bool
Whether to print warning when codeuri is not a local pth

Expand Down Expand Up @@ -390,10 +408,14 @@ def _parse_layer_info(
layer_logical_id, layer_properties, "ContentUri", ignore_code_extraction_warnings
)

if codeuri and not use_raw_codeuri:
LOG.debug("--base-dir is presented not, adjusting uri %s relative to %s", codeuri, stack.location)
codeuri = SamLocalStackProvider.normalize_resource_path(stack.location, codeuri)
mndeveci marked this conversation as resolved.
Show resolved Hide resolved

layers.append(
LayerVersion(
layer_logical_id,
SamLocalStackProvider.normalize_resource_path(stack.location, codeuri) if codeuri else None,
codeuri,
compatible_runtimes,
layer_resource.get("Metadata", None),
stack_path=stack.stack_path,
Expand Down
11 changes: 9 additions & 2 deletions samcli/lib/providers/sam_layer_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SamLayerProvider(SamBaseProvider):
It may or may not contain a layer.
"""

def __init__(self, stacks: List[Stack]) -> None:
def __init__(self, stacks: List[Stack], use_raw_codeuri: bool = False) -> None:
"""
Initialize the class with SAM template data. The SAM template passed to this provider is assumed
to be valid, normalized and a dictionary. It should be normalized by running all pre-processing
Expand All @@ -34,8 +34,11 @@ def __init__(self, stacks: List[Stack]) -> None:
Parameters
----------
:param dict stacks: List of stacks layers are extracted from
:param bool use_raw_codeuri: Do not resolve adjust core_uri based on the template path, use the raw uri.
Note(xinhol): use_raw_codeuri is temporary to fix a bug, and will be removed for a permanent solution.
"""
self._stacks = stacks
self._use_raw_codeuri = use_raw_codeuri

self._layers = self._extract_layers()

Expand Down Expand Up @@ -102,9 +105,13 @@ def _convert_lambda_layer_resource(self, stack: Stack, layer_logical_id: str, la
if resource_type == self.LAMBDA_LAYER:
codeuri = SamLayerProvider._extract_lambda_function_code(layer_properties, "Content")

if codeuri and not self._use_raw_codeuri:
LOG.debug("--base-dir is presented not, adjusting uri %s relative to %s", codeuri, stack.location)
codeuri = SamLocalStackProvider.normalize_resource_path(stack.location, codeuri)
mndeveci marked this conversation as resolved.
Show resolved Hide resolved

return LayerVersion(
layer_logical_id,
SamLocalStackProvider.normalize_resource_path(stack.location, codeuri) if codeuri else None,
codeuri,
compatible_runtimes,
layer_resource.get("Metadata", None),
stack_path=stack.stack_path,
Expand Down
14 changes: 13 additions & 1 deletion tests/integration/buildcmd/test_build_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,13 @@ def test_nested_build(self, use_container, cached, parallel):
)


@parameterized_class(
("template", "use_base_dir"),
[
(os.path.join("deep-nested", "template.yaml"), False),
(os.path.join("base-dir", "template", "template.yaml"), "use_base_dir"),
],
)
class TestBuildWithNestedStacks3Level(NestedBuildIntegBase):
"""
In this template, it has the same structure as .aws-sam/build
Expand All @@ -1610,7 +1617,12 @@ def test_nested_build(self):
if SKIP_DOCKER_TESTS:
self.skipTest(SKIP_DOCKER_MESSAGE)

cmdlist = self.get_command_list(use_container=True, cached=True, parallel=True)
cmdlist = self.get_command_list(
use_container=True,
cached=True,
parallel=True,
base_dir=(os.path.join(self.test_data_path, "base-dir") if self.use_base_dir else None),
)

LOG.info("Running Command: %s", cmdlist)
LOG.info(self.working_dir)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import json


def handler(event, context):
"""
FunctionA in leaf template
"""
return {"statusCode": 200, "body": json.dumps({"hello": "a2"})}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def layer_ping():
return "This is a Layer Ping from simple_python"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: A hello world application.

Resources:
FunctionA:
Type: AWS::Serverless::Function
Properties:
Handler: main_a_2.handler
Runtime: python3.7
CodeUri: ChildStackX/ChildStackY/FunctionA
Timeout: 600

MyLayerVersion:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: MyLayer
Description: Layer description
ContentUri: ChildStackX/ChildStackY/MyLayerVersion
CompatibleRuntimes:
- python3.7
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import json


def handler(event, context):
"""
FunctionB in child template
"""
return {"statusCode": 200, "body": json.dumps({"hello": "b"})}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: A hello world application.

Resources:
FunctionB:
Type: AWS::Serverless::Function
Properties:
Handler: main_b.handler
Runtime: python3.7
CodeUri: ChildStackX/FunctionB
Timeout: 600

ChildStackY:
Type: AWS::Serverless::Application
Properties:
Location: ChildStackY/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import json


def handler(event, context):
"""
FunctionA in root template
"""
return {"statusCode": 200, "body": json.dumps({"hello": "a"})}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: A hello world application.

Resources:
FunctionA:
Type: AWS::Serverless::Function
Properties:
Handler: main_a.handler
Runtime: python3.7
CodeUri: FunctionA
Timeout: 600

ChildStackX:
Type: AWS::Serverless::Application
Properties:
Location: ../ChildStackX/template.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will combine with provided base dir right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no this will not

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However if a base dir is not provided, we just resolve with location of the template. (which is not this particular test, but asking to help grok through the test)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, whether base dir is provided or not, this Location is resolved according to its parent template path.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on offline conversation: applies only to codeuri and contenturi.

Loading