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

Deploy integration test backends using amplify CLI in headless mode. #71

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8bb3382
chore(android-build) Updates for eventual automation of amplify-andro…
rjuliano Nov 3, 2020
8c0a00c
Remove reference to at_auth* stack
rjuliano Nov 3, 2020
237faa8
Rename datastore tests
rjuliano Dec 14, 2020
6765c16
Make api key default auth mode
rjuliano Dec 14, 2020
18a0651
Fix dictionary
rjuliano Dec 14, 2020
513fdbd
Renamed projects and stacks
rjuliano Dec 14, 2020
af1e33e
Add forcePush
rjuliano Dec 14, 2020
6daf87b
Remove --codegen from push command
rjuliano Dec 14, 2020
abc782a
Add codegen option
rjuliano Dec 23, 2020
b9ccfe3
Add conflict detection option and push between auth and api
rjuliano Dec 28, 2020
0837ec4
Change variable name
rjuliano Dec 29, 2020
5a17034
Adding schemas for API tests
rjuliano Dec 30, 2020
ebaaafb
Renamed schema files
rjuliano Dec 30, 2020
c81bb18
Tweak to the events API schema
rjuliano Dec 31, 2020
6916b19
Merge branch 'main' into rjuliano/amplify-integ-test-backend
rjuliano Jan 5, 2021
c4ebbd2
Set expiration time for test keys
rjuliano Jan 6, 2021
a457a28
Updating READMEs
rjuliano Jan 6, 2021
0cdd502
Cleaned up warnings
rjuliano Jan 6, 2021
7aad6ba
Cleanup unused code and setup artifact buckets
rjuliano Jan 29, 2021
536ca37
Merge branch 'main' into rjuliano/amplify-integ-test-backend
rjuliano Apr 30, 2021
2e6597b
Adding schemas and changes for multiauth
rjuliano May 12, 2021
22e3c01
Adding createdAt field to datastore schema
rjuliano May 20, 2021
5421bc4
pin old version of cli for now
rjuliano May 20, 2021
699b872
chore: add oidc config to api
rjuliano Jun 18, 2021
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ integration test environments.
To deploy resources necessary to run the Android SDK's integration
tests, [see here](./src/integ_test_resources/android/sdk/integration/cdk/README.md).

To deploy resources necessary to run the Amplify for Android's integration
tests, [see here](./src/integ_test_resources/android/amplify/integration/cdk/README.md).

To build a configuration file that can be referenced from an iOS/Android
device, [see here](./src/integ_test_resources/common/README.md).

Expand Down
3 changes: 2 additions & 1 deletion src/build_infrastructure/android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ cdk deploy AndroidBuildPipeline \
--yes
```

**NOTE:** be sure to copy the config files in the bucket whose name contains "amplify-ci-assets" created by the AccountBootstrap.
**NOTE 1:** be sure to copy the config files in the bucket whose name contains "amplify-ci-assets" created by the AccountBootstrap.
**NOTE 2:** When deploying in an account for testing purposes, it's a good idea to set the branch to something other than main so the webhook doesn't trigger a build (unless you're modifying/testing the webhook of course).

If necessary, during testing/debugging, add:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

from aws_cdk import core
from aws_cdk.aws_codebuild import (
BuildEnvironment,
BuildSpec,
ComputeType,
EventAction,
FilterGroup,
IArtifacts,
LinuxBuildImage,
Project,
Source
Expand All @@ -19,7 +19,9 @@ def __init__(self, scope: core.Construct, id: str, *,
github_repo,
buildspec_path,
environment_variables = {},
base_branch: str = "main"):
base_branch: str = "main",
primary_artifact = None,
secondary_artifacts = []):

build_environment = BuildEnvironment(build_image=self.BUILD_IMAGE, privileged = True, compute_type = ComputeType.LARGE)

Expand All @@ -30,9 +32,15 @@ def __init__(self, scope: core.Construct, id: str, *,
environment_variables = environment_variables,
build_spec=BuildSpec.from_source_filename(buildspec_path),
badge = True,
artifacts=primary_artifact,
secondary_artifacts=secondary_artifacts,
source = Source.git_hub(owner = github_owner,
report_build_status = True,
repo = github_repo,
webhook = True,
webhook_filters = [trigger_on_pr]),
environment = build_environment)

def add_secondary_artifact(self):
pass

130 changes: 28 additions & 102 deletions src/build_infrastructure/android/stacks/build_pipeline_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ def __init__(self, scope: core.App, id: str, props, **kwargs) -> None:

df_project = DeviceFarmProject(self, id, project_name=device_farm_project_name)
df_pool = DeviceFarmDevicePool(self, f"{id}DevicePool", project_arn=core.Token.as_string(df_project.project_arn), device_pool_name="SingleDeviceIntegTestDevicePool")

# Bucket to store build artifacts, logs, test results, etc.
artifact_bucket = self.__create_artifact_bucket("ArtifactBucket", bucket_name=f"{codebuild_project_name_prefix.lower()}-builds-{self.account}")

PullRequestBuilder(self, "UnitTestRunner", project_name=f"{codebuild_project_name_prefix}-UnitTest",
github_owner=owner,
Expand All @@ -255,18 +258,35 @@ def __init__(self, scope: core.App, id: str, props, **kwargs) -> None:
github_repo=repo,
base_branch=base_branch,
buildspec_path="scripts/devicefarm-test-runner-buildspec.yml",
primary_artifact=aws_codebuild.Artifacts.s3(
bucket=artifact_bucket,
encryption=True,
include_build_id=True,
package_zip=False,
path="instrumented/apks"
),
secondary_artifacts=[
aws_codebuild.Artifacts.s3(
bucket=artifact_bucket,
identifier="reports",
encryption=True,
include_build_id=True,
package_zip=False,
path="instrumented/reports"
)],
environment_variables={
'DEVICEFARM_PROJECT_ARN': aws_codebuild.BuildEnvironmentVariable(value=df_project.get_arn()),
'DEVICEFARM_POOL_ARN': aws_codebuild.BuildEnvironmentVariable(value=df_pool.device_pool_arn),
'CONFIG_SOURCE_BUCKET': aws_codebuild.BuildEnvironmentVariable(value=config_source_bucket)
})
self._add_codebuild_project_runner_permissions(integtest_project.role)
self._add_devicefarm_test_runner_permissions_to_role(integtest_project.role)

self.__add_codebuild_project_runner_permissions(integtest_project.role)
self.__add_devicefarm_test_runner_permissions_to_role(integtest_project.role)

def get_codebuild_project_name(self):
return self.code_build_project.project_name

def _add_devicefarm_test_runner_permissions_to_role(self, role: aws_iam.Role):
def __add_devicefarm_test_runner_permissions_to_role(self, role: aws_iam.Role):
df_runner_policy = aws_iam.ManagedPolicy(self,
"AmplifyAndroidDeviceFarmTestRunnerPolicy",
managed_policy_name=f"AmplifyAndroidDeviceFarmTestRunnerPolicy",
Expand All @@ -277,23 +297,12 @@ def _add_devicefarm_test_runner_permissions_to_role(self, role: aws_iam.Role):
)
df_runner_policy.attach_to_role(role)

def _add_devicefarm_test_stage(self, pipeline, device_farm_project_id, device_farm_pool_arn):
test_actions = []
for module_name in self.MODULES_WITH_INSTRUMENTED_TESTS:
test_actions.append(self._create_devicefarm_test_action(device_farm_project_id, device_farm_pool_arn, module_name))

testing_stage = {
"Name": "Test",
"Actions": test_actions
}
pipeline_node = pipeline.node.default_child
pipeline_node.add_property_override("Stages.2", testing_stage)

def _create_artifact_bucket(self, bucket_name:str):
artifact_bucket = aws_s3.Bucket(self, "PipelineAssets",
def __create_artifact_bucket(self, id, *, bucket_name:str):
artifact_bucket = aws_s3.Bucket(self, id,
bucket_name=bucket_name,
encryption=aws_s3.BucketEncryption.KMS_MANAGED,
removal_policy=core.RemovalPolicy.DESTROY)

artifact_bucket.add_to_resource_policy(permission=aws_iam.PolicyStatement(
principals=[aws_iam.AnyPrincipal()],
effect=aws_iam.Effect.DENY,
Expand Down Expand Up @@ -324,40 +333,7 @@ def _create_artifact_bucket(self, bucket_name:str):
))
return artifact_bucket

# Not calling this right now since we can't filter out PRs in CodePipeline.
def _create_pipeline(self,
build_pipeline_name: str,
github_source: aws_codepipeline_actions.GitHubSourceAction,
codebuild_project: aws_codebuild.PipelineProject,
config_file_source_bucket_name:str,
df_project: DeviceFarmProject,
device_farm_pool_arn:str):
artifact_bucket = self._create_artifact_bucket(f"pipeline-assets-{build_pipeline_name.lower()}-{self.account}")
self.code_build_project = self._create_codebuild_project("AmplifyAndroidCodeBuildProject")
amplify_android_build_output = aws_codepipeline.Artifact("AmplifyAndroidBuildOutput")
pipeline = aws_codepipeline.Pipeline(self,
f"{build_pipeline_name}Pipeline",
pipeline_name=build_pipeline_name,
artifact_bucket=artifact_bucket,
stages=[
aws_codepipeline.StageProps(
stage_name="Source",
actions=[ github_source ]
),
aws_codepipeline.StageProps(
stage_name="Build",
actions=[self._create_build_and_assemble_action(input_artifact=github_source.action_properties.outputs[0],
output_artifact=amplify_android_build_output,
pipeline_project=codebuild_project,
config_source_bucket=config_file_source_bucket_name)
]
)
])
self._add_devicefarm_test_runner_permissions_to_role(pipeline.role)
self._add_devicefarm_test_stage(pipeline, df_project.get_project_id(), device_farm_pool_arn)
return pipeline

def _create_codebuild_project(self, id: str):
def __create_codebuild_project(self, id: str):
pipeline_project = aws_codebuild.PipelineProject(self,
id,
environment=aws_codebuild.BuildEnvironment(build_image=aws_codebuild.LinuxBuildImage.AMAZON_LINUX_2_3,
Expand All @@ -375,7 +351,7 @@ def _create_codebuild_project(self, id: str):
build_exec_policy.attach_to_role(pipeline_project.role)
return pipeline_project

def _add_codebuild_project_runner_permissions(self, role: aws_iam.Role):
def __add_codebuild_project_runner_permissions(self, role: aws_iam.Role):
build_exec_policy = aws_iam.ManagedPolicy(self,
"AmplifyAndroidBuildExecutorPolicy",
managed_policy_name=f"AmplifyAndroidBuildExecutorPolicy",
Expand All @@ -385,53 +361,3 @@ def _add_codebuild_project_runner_permissions(self, role: aws_iam.Role):
]
)
build_exec_policy.attach_to_role(role)


def _create_build_and_assemble_action(self,
input_artifact:aws_codepipeline.Artifact,
output_artifact:aws_codepipeline.Artifact,
pipeline_project:aws_codebuild.PipelineProject,
config_source_bucket: str = None):
if config_source_bucket is None:
return aws_codepipeline_actions.CodeBuildAction(
action_name='BuildAndAssemble',
input=input_artifact,
project=pipeline_project,
outputs=[output_artifact]
)
else:
return aws_codepipeline_actions.CodeBuildAction(
action_name='BuildAndAssemble',
input=input_artifact,
project=pipeline_project,
environment_variables={
'CONFIG_SOURCE_BUCKET': aws_codebuild.BuildEnvironmentVariable(value=config_source_bucket)
},
outputs=[output_artifact]
)

def _create_devicefarm_test_action(self, project_id: str, device_pool_arn: str, module_name: str):
return {
"Name":f"{module_name}-InstrumentedTests",
"ActionTypeId": {
"Category": "Test",
"Owner": "AWS",
"Provider": "DeviceFarm",
"Version": "1"
},
"RunOrder": 1,
"Configuration": {
"App": f"{module_name}-debug-androidTest.apk",
"Test": f"{module_name}-debug-androidTest.apk",
"AppType": "Android",
"DevicePoolArn": device_pool_arn,
"ProjectId": project_id,
"TestType": "INSTRUMENTATION"
},
"OutputArtifacts": [],
"InputArtifacts": [
{
"Name": "AmplifyAndroidBuildOutput"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,23 @@ There are two parts to this CDK application:
- The scripts that are executed inside the CodeBuild project.

That being said, there are a couple of different usage scenarios:
1. You are setting a new AWS account for integration testing purposes and you want changes made to the `scripts` and/or `schemas` folders to be executed from the CodeBuild project when something is committed to a branch.
2. You just want to setup the backend resources against your own AWS account for running the tests, adding/modifying test scenarios, etc. In this case, you would be running the scripts that run inside CodeBuild on your local environment.
1. You just want to setup the backend resources against your own AWS account for running the tests, adding/modifying test scenarios, etc. In this case, you would be running the scripts that run inside CodeBuild on your local environment.
2. You are setting a new AWS account for integration testing purposes and you want changes made to the `scripts` and/or `schemas` folders to be executed from the CodeBuild project when something is committed to a branch.

### Option 1 - Setup a new AWS account to run integration tests
### Option 1 - Run the scripts locally
Ensure your AWS credentials have rights to execute the script.

1. Start from the CDK app root, where this `README.md` lives:
```console
cd <amplify-ci-support root>/src/integ_test_resources/android/amplify/integration/cdk
```

2. Run one of the deployment scripts located under the `scripts` folder. For example:
```Console
python ./scripts/deploy_api_tests_backend.sh
```

### Option 2 - Setup a new AWS account to run integration tests
Ensure that you have [credentials in your environment sufficient to run
the CDK](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_credentials).

Expand Down Expand Up @@ -117,16 +130,3 @@ cdk deploy <context parameters>
```console
cdk destroy <context parameters>
```

### Option 2 - Run the scripts locally
Ensure your AWS credentials have rights to execute the script.

1. Start from the CDK app root, where this `README.md` lives:
```console
cd <amplify-ci-support root>/src/integ_test_resources/android/amplify/integration/cdk
```

2. Run the deployment script
```Console
python ./scripts/setup_amplify
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import os
import sys

sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "."))
45 changes: 32 additions & 13 deletions src/integ_test_resources/android/amplify/integration/cdk/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,52 @@
from aws_cdk import core
from stacks.amplify_deployer_stack import AmplifyDeployer

def build_amplify_deployer_stack_props(cb_project_name: str,
github_repo: str,
github_owner: str,
branch: str,
shell_script_name: str):
props = {}
props['cb_project_name'] = cb_project_name
props['shell_script_name'] = shell_script_name
props['github_repo'] = github_repo
if github_owner is not None:
props['github_owner'] = github_owner
if branch is not None:
props['branch'] = branch
return props

app = core.App()
github_owner=app.node.try_get_context("github_owner")
branch=app.node.try_get_context("branch")

TARGET_REGION = app.node.try_get_context("region")
TARGET_ACCOUNT = app.node.try_get_context("account")
if TARGET_ACCOUNT is None or TARGET_REGION is None:
raise Exception("Context variables region and account are required.")

TARGET_ENV = core.Environment( account=TARGET_ACCOUNT, region=TARGET_REGION)
GITHUB_REPO = 'amplify-ci-support'

BANNER_TEXT = f"AWS Account={TARGET_ACCOUNT} Region={TARGET_REGION}"
SEPARATOR = "-" * len(BANNER_TEXT)

github_owner=app.node.try_get_context("github_owner")
branch=app.node.try_get_context("branch")

print(SEPARATOR)
print(BANNER_TEXT)
print(SEPARATOR)

props = {}
props['project_name'] = "AmplifyAndroidIntegTestDeployer"
props['github_repo'] = GITHUB_REPO
if github_owner is not None:
props['github_owner'] = github_owner
if branch is not None:
props['branch'] = branch
datastore_instrumented_test_props = build_amplify_deployer_stack_props(cb_project_name="DataStoreTestsBackendDeployer",
github_repo=GITHUB_REPO,
github_owner=github_owner,
branch=branch,
shell_script_name="deploy_datastore_tests_backend.sh")

api_instrumented_test_props = build_amplify_deployer_stack_props(cb_project_name="ApiTestsBackendDeployer",
github_repo=GITHUB_REPO,
github_owner=github_owner,
branch=branch,
shell_script_name="deploy_api_tests_backend.sh")

AmplifyDeployer(app, "AndroidIntegTestInfraDeployer", props, env=TARGET_ENV)
instrumented_test_backend_stack = AmplifyDeployer(app, "DataStoreTestsBackendDeployer", datastore_instrumented_test_props, env=TARGET_ENV)
api_instrumented_test_backend_stack = AmplifyDeployer(app, "ApiTestsBackendDeployer", api_instrumented_test_props, env=TARGET_ENV)

app.synth()
app.synth()

This file was deleted.

Loading