Skip to content
Merged
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
20 changes: 12 additions & 8 deletions samcli/commands/sync/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
)
from samcli.cli.cli_config_file import configuration_option, TomlProvider
from samcli.commands._utils.click_mutex import ClickMutex
from samcli.commands.sync.sync_context import SyncContext
from samcli.lib.utils.colors import Colored
from samcli.lib.utils.version_checker import check_newer_version
from samcli.lib.bootstrap.bootstrap import manage_stack
Expand Down Expand Up @@ -329,14 +330,17 @@ def do_cli(
disable_rollback=False,
poll_delay=poll_delay,
) as deploy_context:
if watch:
execute_watch(template_file, build_context, package_context, deploy_context, dependency_layer)
elif code:
execute_code_sync(
template_file, build_context, deploy_context, resource_id, resource, dependency_layer
)
else:
execute_infra_contexts(build_context, package_context, deploy_context)
with SyncContext(dependency_layer, build_context.build_dir, build_context.cache_dir):
if watch:
execute_watch(
template_file, build_context, package_context, deploy_context, dependency_layer
)
elif code:
execute_code_sync(
template_file, build_context, deploy_context, resource_id, resource, dependency_layer
)
else:
execute_infra_contexts(build_context, package_context, deploy_context)


def execute_infra_contexts(
Expand Down
106 changes: 106 additions & 0 deletions samcli/commands/sync/sync_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""
Context object used by sync command
"""
import logging
from dataclasses import dataclass
from pathlib import Path
from typing import Optional, cast, Dict

import tomlkit
from tomlkit.api import _TOMLDocument as TOMLDocument
from tomlkit.items import Item

from samcli.lib.build.build_graph import DEFAULT_DEPENDENCIES_DIR
from samcli.lib.utils.osutils import rmtree_if_exists

LOG = logging.getLogger(__name__)


DEFAULT_SYNC_STATE_FILE_NAME = "sync.toml"

SYNC_STATE = "sync_state"
DEPENDENCY_LAYER = "dependency_layer"


@dataclass
class SyncState:
dependency_layer: bool


def _sync_state_to_toml_document(sync_state: SyncState) -> TOMLDocument:
sync_state_toml_table = tomlkit.table()
sync_state_toml_table[DEPENDENCY_LAYER] = sync_state.dependency_layer

toml_document = tomlkit.document()
toml_document.add((tomlkit.comment("This file is auto generated by SAM CLI sync command")))
toml_document.add(SYNC_STATE, cast(Item, sync_state_toml_table))

return toml_document


def _toml_document_to_sync_state(toml_document: Dict) -> Optional[SyncState]:
if not toml_document:
return None

sync_state_toml_table = toml_document.get(SYNC_STATE)
if not sync_state_toml_table:
return None

return SyncState(sync_state_toml_table.get(DEPENDENCY_LAYER))


class SyncContext:

_current_state: SyncState
_previous_state: Optional[SyncState]
_build_dir: Path
_cache_dir: Path
_file_path: Path

def __init__(self, dependency_layer: bool, build_dir: str, cache_dir: str):
self._current_state = SyncState(dependency_layer)
self._previous_state = None
self._build_dir = Path(build_dir)
self._cache_dir = Path(cache_dir)
self._file_path = Path(build_dir).parent.joinpath(DEFAULT_SYNC_STATE_FILE_NAME)

def __enter__(self) -> "SyncContext":
self._read()
LOG.debug(
"Entering sync context, previous state: %s, current state: %s", self._previous_state, self._current_state
)

# if adl parameter is changed between sam sync runs, cleanup build, cache and dependencies folders
if self._previous_state and self._previous_state.dependency_layer != self._current_state.dependency_layer:
self._cleanup_build_folders()

return self

def __exit__(self, *args):
self._write()

def _write(self) -> None:
with open(self._file_path, "w+") as file:
file.write(tomlkit.dumps(_sync_state_to_toml_document(self._current_state)))

def _read(self) -> None:
try:
with open(self._file_path) as file:
toml_document = cast(Dict, tomlkit.loads(file.read()))
self._previous_state = _toml_document_to_sync_state(toml_document)
except OSError:
LOG.debug("Missing previous sync state, will create a new file at the end of this execution")

def _cleanup_build_folders(self):
"""
Cleans up build, cache and dependencies folders for clean start of the next session
"""
LOG.debug("Cleaning up build directory %s", self._build_dir)
rmtree_if_exists(self._build_dir)

LOG.debug("Cleaning up cache directory %s", self._cache_dir)
rmtree_if_exists(self._cache_dir)

dependencies_dir = Path(DEFAULT_DEPENDENCIES_DIR)
LOG.debug("Cleaning up dependencies directory: %s", dependencies_dir)
rmtree_if_exists(dependencies_dir)
45 changes: 45 additions & 0 deletions tests/integration/sync/test_sync_infra.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,48 @@ def test_cdk_templates(self, template_file, template_after, function_id, depende
lambda_response = json.loads(self._get_lambda_response(lambda_function))
self.assertIn("extra_message", lambda_response)
self.assertEqual(lambda_response.get("message"), "9")


@skipIf(SKIP_SYNC_TESTS, "Skip sync tests in CI/CD only")
@parameterized_class([{"dependency_layer": True}, {"dependency_layer": False}])
class TestSyncInfraWithJava(SyncIntegBase):
@parameterized.expand(["infra/template-java.yaml"])
def test_sync_infra_with_java(self, template_file):
"""This will test a case where user will flip ADL flag between sync sessions"""
template_path = str(self.test_data_path.joinpath(template_file))
stack_name = self._method_to_stack_name(self.id())
self.stacks.append({"name": stack_name})

# first run with current dependency layer value
self._run_sync_and_validate_lambda_call(self.dependency_layer, template_path, stack_name)

# now flip the dependency layer value and re-run the sync & tests
self._run_sync_and_validate_lambda_call(not self.dependency_layer, template_path, stack_name)

def _run_sync_and_validate_lambda_call(self, dependency_layer: bool, template_path: str, stack_name: str) -> None:
# Run infra sync
sync_command_list = self.get_sync_command_list(
template_file=template_path,
code=False,
watch=False,
dependency_layer=dependency_layer,
stack_name=stack_name,
parameter_overrides="Parameter=Clarity",
image_repository=self.ecr_repo_name,
s3_prefix=self.s3_prefix,
kms_key_id=self.kms_key,
capabilities_list=["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"],
tags="integ=true clarity=yes foo_bar=baz",
)
sync_process_execute = run_command_with_input(sync_command_list, "y\n".encode())
self.assertEqual(sync_process_execute.process.returncode, 0)
self.assertIn("Sync infra completed.", str(sync_process_execute.stderr))

self.stack_resources = self._get_stacks(stack_name)
lambda_functions = self.stack_resources.get(AWS_LAMBDA_FUNCTION)
for lambda_function in lambda_functions:
lambda_response = json.loads(self._get_lambda_response(lambda_function))
self.assertIn("message", lambda_response)
self.assertIn("sum", lambda_response)
self.assertEqual(lambda_response.get("message"), "hello world")
self.assertEqual(lambda_response.get("sum"), 12)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>helloworld</groupId>
<artifactId>HelloWorld</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>A sample Hello World created for SAM CLI.</name>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>helloworld</groupId>
<artifactId>HelloWorldLayer</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package helloworld;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;

import helloworldlayer.SimpleMath;

/**
* Handler for requests to Lambda function.
*/
public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put("X-Custom-Header", "application/json");

APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
.withHeaders(headers);

int sumResult = SimpleMath.sum(7, 5);

try {
final String pageContents = this.getPageContents("https://checkip.amazonaws.com");
String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\", \"sum\": %d }", pageContents, sumResult);

return response
.withStatusCode(200)
.withBody(output);
} catch (IOException e) {
return response
.withBody("{}")
.withStatusCode(500);
}
}

private String getPageContents(String address) throws IOException{
URL url = new URL(address);
try(BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
return br.lines().collect(Collectors.joining(System.lineSeparator()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>helloworld</groupId>
<artifactId>HelloWorldLayer</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>A sample Hello World created for SAM CLI.</name>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package helloworldlayer;

public class SimpleMath {

public static int sum(int a, int b) {
return a + b;
}
}
27 changes: 27 additions & 0 deletions tests/integration/testdata/sync/infra/template-java.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
Function:
Timeout: 30

Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: before/Java/HelloWorldFunction
Handler: helloworld.App::handleRequest
Runtime: java11
MemorySize: 512
Layers:
- !Ref HelloWorldLayer

HelloWorldLayer:
Type: AWS::Serverless::LayerVersion
Properties:
ContentUri: before/Java/HelloWorldLayer
CompatibleRuntimes:
- java11
Metadata:
BuildMethod: java11
BuildArchitecture: x86_64
Loading