-
Notifications
You must be signed in to change notification settings - Fork 18
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
Refactor all e2e tests to use fixture #49
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,15 +13,55 @@ | |
|
||
import pytest | ||
import boto3 | ||
import logging | ||
import time | ||
from dataclasses import dataclass | ||
|
||
from acktest.aws.identity import get_region | ||
from acktest import k8s | ||
from acktest.resources import random_suffix_name | ||
from acktest.aws.identity import get_region, get_account_id | ||
from acktest import k8s as k8sclient | ||
from acktest.k8s import resource as k8s | ||
|
||
from e2e import CRD_GROUP, CRD_VERSION, load_lambda_resource | ||
from e2e.service_bootstrap import LAMBDA_FUNCTION_FILE_ZIP | ||
from e2e.replacement_values import REPLACEMENT_VALUES | ||
from e2e.bootstrap_resources import get_bootstrap_resources | ||
|
||
FunctionPackageTypeImage = "Image" | ||
FunctionPackageTypeZip = "Zip" | ||
FunctionTestingRoleBasic = "basic" | ||
FunctionTestingRoleESM = "esm" | ||
|
||
@dataclass | ||
class ResourceWaitTimes: | ||
Create: int | ||
Update: int | ||
Delete: int | ||
|
||
@dataclass | ||
class ServiceWaitTimes: | ||
Function: ResourceWaitTimes | ||
Alias: ResourceWaitTimes | ||
CodeSigningConfig: ResourceWaitTimes | ||
EventSourceMapping: ResourceWaitTimes | ||
FunctionURLConfig: ResourceWaitTimes | ||
|
||
Wait = ServiceWaitTimes( | ||
Function = ResourceWaitTimes(30, 30, 30), | ||
Alias = ResourceWaitTimes(10, 10, 10), | ||
CodeSigningConfig = ResourceWaitTimes(10, 10, 10), | ||
EventSourceMapping = ResourceWaitTimes(20, 20, 20), | ||
FunctionURLConfig = ResourceWaitTimes(30, 10, 10), | ||
) | ||
Comment on lines
+42
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd prefer to see each of these defined in their respective test files. They can be cross-imported if we need to access them in other tests. |
||
|
||
def get_testing_image_url(): | ||
aws_region = get_region() | ||
account_id = get_account_id() | ||
return f"{account_id}.dkr.ecr.{aws_region}.amazonaws.com/ack-e2e-testing-lambda-controller:v1" | ||
|
||
def pytest_addoption(parser): | ||
parser.addoption("--runslow", action="store_true", default=False, help="run slow tests") | ||
|
||
|
||
def pytest_configure(config): | ||
config.addinivalue_line( | ||
"markers", "canary: mark test to also run in canary tests" | ||
|
@@ -32,6 +72,12 @@ def pytest_configure(config): | |
config.addinivalue_line( | ||
"markers", "slow: mark test as slow to run" | ||
) | ||
config.addinivalue_line( | ||
"markers", "function_overrides: function parameters to override when creating fixture" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PyTest markers are used to label a test - not to provide a parameter to tests. For instance, we have a marker for "slow" tests, in case you want to skip them. I would prefer if we just hardcode the test values, or use another mechanism for parameterising these |
||
) | ||
config.addinivalue_line( | ||
"markers", "esm_overrides: event source mapping parameters to override when creating fixture" | ||
) | ||
|
||
def pytest_collection_modifyitems(config, items): | ||
if config.getoption("--runslow"): | ||
|
@@ -44,8 +90,259 @@ def pytest_collection_modifyitems(config, items): | |
# Provide a k8s client to interact with the integration test cluster | ||
@pytest.fixture(scope='class') | ||
def k8s_client(): | ||
return k8s._get_k8s_api_client() | ||
return k8sclient._get_k8s_api_client() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just for the sake of consistency with the other tests, would you mind changing this back to |
||
|
||
@pytest.fixture(scope='module') | ||
def lambda_client(): | ||
return boto3.client('lambda', region_name=get_region()) | ||
return boto3.client('lambda', region_name=get_region()) | ||
|
||
@pytest.fixture | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pytest fixtures should be defined in whichever scope they're used. I defined |
||
def function(request, code_signing_config): | ||
resource_name = random_suffix_name("function", 24) | ||
|
||
resources = get_bootstrap_resources() | ||
replacements = REPLACEMENT_VALUES.copy() | ||
replacements["FUNCTION_NAME"] = resource_name | ||
replacements["IMAGE_URL"] = get_testing_image_url() | ||
replacements["CODE_SIGNING_CONFIG_ARN"] = "" | ||
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "" | ||
|
||
resource_file = "" | ||
|
||
marker = request.node.get_closest_marker("function_overrides") | ||
if marker is not None: | ||
data = marker.args[0] | ||
if 'package_type' in data: | ||
if data['package_type'] == FunctionPackageTypeZip: | ||
resource_file = "function" | ||
resource_name = random_suffix_name("function-zip", 32) | ||
replacements["FUNCTION_NAME"] = resource_name | ||
replacements["BUCKET_NAME"] = resources.FunctionsBucket.name | ||
replacements["LAMBDA_FILE_NAME"] = LAMBDA_FUNCTION_FILE_ZIP | ||
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = "0" | ||
replacements["CODE_SIGNING_CONFIG_ARN"] = "" | ||
|
||
elif data['package_type'] == FunctionPackageTypeImage: | ||
resource_file = "function_package_type_image" | ||
resource_name = random_suffix_name("function-image", 32) | ||
replacements["FUNCTION_NAME"] = resource_name | ||
replacements["IMAGE_URL"] = get_testing_image_url() | ||
replacements["LAMBDA_ROLE"] = resources.BasicRole.arn | ||
|
||
if 'role_type' in data: | ||
if data['role_type'] == FunctionTestingRoleBasic: | ||
replacements["LAMBDA_ROLE"] = resources.BasicRole.arn | ||
elif data['role_type'] == FunctionTestingRoleESM: | ||
replacements["LAMBDA_ROLE"] = resources.ESMRole.arn | ||
|
||
if 'reserved_concurrent_executions' in data: | ||
replacements["RESERVED_CONCURRENT_EXECUTIONS"] = str(data["reserved_concurrent_executions"]) | ||
|
||
if 'code_signing_config_arn' in data: | ||
replacements["CODE_SIGNING_CONFIG_ARN"] = data['code_signing_config_arn'] | ||
elif 'create_code_signing_config' in data and data["create_code_signing_config"]: | ||
(_, cr) = code_signing_config | ||
replacements["CODE_SIGNING_CONFIG_ARN"] = cr["status"]["ackResourceMetadata"]["arn"] | ||
|
||
# Load Function CR | ||
resource_data = load_lambda_resource( | ||
resource_file, | ||
additional_replacements=replacements, | ||
) | ||
logging.debug(resource_data) | ||
|
||
# Create k8s resource | ||
ref = k8s.CustomResourceReference( | ||
CRD_GROUP, CRD_VERSION, "functions", | ||
resource_name, namespace="default", | ||
) | ||
k8s.create_custom_resource(ref, resource_data) | ||
time.sleep(Wait.Function.Create) | ||
|
||
cr = k8s.wait_resource_consumed_by_controller(ref) | ||
assert cr is not None | ||
assert k8s.get_resource_exists(ref) | ||
|
||
yield (ref, cr) | ||
|
||
# Try to delete, if doesn't already exist | ||
try: | ||
_, deleted = k8s.delete_custom_resource(ref, 3, 10) | ||
assert deleted | ||
except: | ||
pass | ||
|
||
@pytest.fixture | ||
def code_signing_config(): | ||
resource_name = random_suffix_name("csc", 24) | ||
resources = get_bootstrap_resources() | ||
|
||
replacements = REPLACEMENT_VALUES.copy() | ||
replacements["CODE_SIGNING_CONFIG_NAME"] = resource_name | ||
replacements["SIGNING_PROFILE_VERSION_ARN"] = resources.SigningProfile.signing_profile_arn | ||
|
||
# Load CodeSigningConfig CR | ||
resource_data = load_lambda_resource( | ||
"code_signing_config", | ||
additional_replacements=replacements, | ||
) | ||
logging.debug(resource_data) | ||
|
||
# Create k8s resource | ||
ref = k8s.CustomResourceReference( | ||
CRD_GROUP, CRD_VERSION, "codesigningconfigs", | ||
resource_name, namespace="default", | ||
) | ||
k8s.create_custom_resource(ref, resource_data) | ||
time.sleep(Wait.CodeSigningConfig.Create) | ||
|
||
cr = k8s.wait_resource_consumed_by_controller(ref) | ||
assert cr is not None | ||
assert k8s.get_resource_exists(ref) | ||
|
||
yield (ref, cr) | ||
|
||
# Try to delete, if doesn't already exist | ||
try: | ||
_, deleted = k8s.delete_custom_resource(ref, 3, 10) | ||
assert deleted | ||
except: | ||
pass | ||
|
||
@pytest.fixture | ||
def alias(function): | ||
(_, function_resource) = function | ||
lambda_function_name = function_resource["spec"]["name"] | ||
|
||
resource_name = random_suffix_name("alias", 24) | ||
|
||
replacements = REPLACEMENT_VALUES.copy() | ||
replacements["ALIAS_NAME"] = resource_name | ||
replacements["FUNCTION_NAME"] = lambda_function_name | ||
replacements["FUNCTION_VERSION"] = "$LATEST" | ||
|
||
# Load alias CR | ||
resource_data = load_lambda_resource( | ||
"alias", | ||
additional_replacements=replacements, | ||
) | ||
logging.debug(resource_data) | ||
|
||
# Create k8s resource | ||
ref = k8s.CustomResourceReference( | ||
CRD_GROUP, CRD_VERSION, "aliases", | ||
resource_name, namespace="default", | ||
) | ||
k8s.create_custom_resource(ref, resource_data) | ||
time.sleep(Wait.Alias.Create) | ||
|
||
cr = k8s.wait_resource_consumed_by_controller(ref) | ||
assert cr is not None | ||
assert k8s.get_resource_exists(ref) | ||
|
||
yield (ref, cr) | ||
|
||
# Try to delete, if doesn't already exist | ||
try: | ||
_, deleted = k8s.delete_custom_resource(ref, 3, 10) | ||
assert deleted | ||
except: | ||
pass | ||
|
||
@pytest.fixture | ||
def event_source_mapping(request, function): | ||
(_, function_resource) = function | ||
lambda_function_name = function_resource["spec"]["name"] | ||
|
||
resource_name = random_suffix_name("eventsourcemapping", 24) | ||
resources = get_bootstrap_resources() | ||
|
||
replacements = REPLACEMENT_VALUES.copy() | ||
replacements["EVENT_SOURCE_MAPPING_NAME"] = resource_name | ||
replacements["FUNCTION_NAME"] = lambda_function_name | ||
replacements["BATCH_SIZE"] = "10" | ||
|
||
resource_file = "" | ||
|
||
marker = request.node.get_closest_marker("esm_overrides") | ||
if marker is not None: | ||
data = marker.args[0] | ||
if 'source' in data: | ||
if data['source'] == "sqsqueue": | ||
resource_file = "event_source_mapping_sqs" | ||
replacements["FUNCTION_NAME"] = lambda_function_name | ||
replacements["EVENT_SOURCE_ARN"] = resources.ESMQueue.arn | ||
replacements["MAXIMUM_BATCHING_WINDOW_IN_SECONDS"] = "1" | ||
|
||
elif data['source'] == "ddbtable": | ||
resource_file = "event_source_mapping_dynamodb" | ||
replacements["EVENT_SOURCE_ARN"] = resources.ESMTable.latest_stream_arn | ||
replacements["STARTING_POSITION"] = "LATEST" | ||
replacements["MAXIMUM_RETRY_ATTEMPTS"] = "-1" | ||
|
||
# Load EventSourceMapping CR | ||
resource_data = load_lambda_resource( | ||
resource_file, | ||
additional_replacements=replacements, | ||
) | ||
|
||
# Create k8s resource | ||
ref = k8s.CustomResourceReference( | ||
CRD_GROUP, CRD_VERSION, "eventsourcemappings", | ||
resource_name, namespace="default", | ||
) | ||
k8s.create_custom_resource(ref, resource_data) | ||
time.sleep(Wait.EventSourceMapping.Create) | ||
|
||
cr = k8s.wait_resource_consumed_by_controller(ref) | ||
assert cr is not None | ||
assert k8s.get_resource_exists(ref) | ||
|
||
yield (ref, cr) | ||
|
||
# Try to delete, if doesn't already exist | ||
try: | ||
_, deleted = k8s.delete_custom_resource(ref, 3, 10) | ||
assert deleted | ||
except: | ||
pass | ||
|
||
@pytest.fixture | ||
def function_url_config(function): | ||
(_, function_resource) = function | ||
lambda_function_name = function_resource["spec"]["name"] | ||
|
||
resource_name = random_suffix_name("functionurlconfig", 24) | ||
|
||
replacements = REPLACEMENT_VALUES.copy() | ||
replacements["FUNCTION_URL_CONFIG_NAME"] = resource_name | ||
replacements["FUNCTION_NAME"] = lambda_function_name | ||
replacements["AUTH_TYPE"] = "NONE" | ||
|
||
# Load FunctionURLConfig CR | ||
resource_data = load_lambda_resource( | ||
"function_url_config", | ||
additional_replacements=replacements, | ||
) | ||
logging.debug(resource_data) | ||
|
||
# Create k8s resource | ||
ref = k8s.CustomResourceReference( | ||
CRD_GROUP, CRD_VERSION, "functionurlconfigs", | ||
resource_name, namespace="default", | ||
) | ||
k8s.create_custom_resource(ref, resource_data) | ||
time.sleep(Wait.FunctionURLConfig.Create) | ||
|
||
cr = k8s.wait_resource_consumed_by_controller(ref) | ||
assert cr is not None | ||
assert k8s.get_resource_exists(ref) | ||
|
||
yield (ref, cr) | ||
|
||
# Try to delete, if doesn't already exist | ||
try: | ||
_, deleted = k8s.delete_custom_resource(ref, 3, 10) | ||
assert deleted | ||
except: | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ | |
Lambda-specific test variables. | ||
""" | ||
|
||
from acktest.aws.identity import get_region | ||
|
||
REPLACEMENT_VALUES = { | ||
|
||
"AWS_REGION": get_region() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is globally defined, as part of |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be defined in the
test-infra
package. Also, we should probably provide sane defaults (say 5 seconds for each?)