|
10 | 10 | import pytest
|
11 | 11 | from botocore.response import StreamingBody
|
12 | 12 |
|
| 13 | +from localstack import config |
13 | 14 | from localstack.aws.api.lambda_ import Architecture, Runtime
|
14 | 15 | from localstack.services.awslambda.lambda_api import use_docker
|
15 | 16 | from localstack.testing.aws.lambda_utils import (
|
16 | 17 | concurrency_update_done,
|
17 | 18 | get_invoke_init_type,
|
18 |
| - is_arm_compatible, |
| 19 | + is_old_local_executor, |
19 | 20 | is_old_provider,
|
20 | 21 | update_done,
|
21 | 22 | )
|
22 | 23 | from localstack.testing.aws.util import create_client_with_keys
|
23 | 24 | from localstack.testing.pytest.snapshot import is_aws
|
24 | 25 | from localstack.testing.snapshots.transformer import KeyValueBasedTransformer
|
25 | 26 | from localstack.testing.snapshots.transformer_utility import PATTERN_UUID
|
26 |
| -from localstack.utils import files, testutil |
| 27 | +from localstack.utils import files, platform, testutil |
27 | 28 | from localstack.utils.files import load_file
|
28 | 29 | from localstack.utils.http import safe_requests
|
| 30 | +from localstack.utils.platform import is_arm_compatible, standardized_arch |
29 | 31 | from localstack.utils.strings import short_uid, to_bytes, to_str
|
30 | 32 | from localstack.utils.sync import retry, wait_until
|
31 | 33 | from localstack.utils.testutil import create_lambda_archive
|
@@ -377,6 +379,99 @@ def test_runtime_introspection_arm(self, lambda_client, create_lambda_function,
|
377 | 379 | invoke_result = lambda_client.invoke(FunctionName=func_name)
|
378 | 380 | snapshot.match("invoke_runtime_arm_introspection", invoke_result)
|
379 | 381 |
|
| 382 | + @pytest.mark.skipif(is_old_provider(), reason="unsupported in old provider") |
| 383 | + @pytest.mark.skipif( |
| 384 | + is_old_local_executor(), |
| 385 | + reason="Monkey-patching of Docker flags is not applicable because no new container is spawned", |
| 386 | + ) |
| 387 | + @pytest.mark.only_localstack |
| 388 | + def test_ignore_architecture( |
| 389 | + self, lambda_client, create_lambda_function, snapshot, monkeypatch |
| 390 | + ): |
| 391 | + """Test configuration to ignore lambda architecture by creating a lambda with non-native architecture.""" |
| 392 | + monkeypatch.setattr(config, "LAMBDA_IGNORE_ARCHITECTURE", True) |
| 393 | + |
| 394 | + # Assumes that LocalStack runs on native Docker host architecture |
| 395 | + # This assumption could be violated when using remote Lambda executors |
| 396 | + native_arch = platform.get_arch() |
| 397 | + non_native_architecture = ( |
| 398 | + Architecture.x86_64 if native_arch == "arm64" else Architecture.arm64 |
| 399 | + ) |
| 400 | + func_name = f"test_lambda_arch_{short_uid()}" |
| 401 | + create_lambda_function( |
| 402 | + func_name=func_name, |
| 403 | + handler_file=TEST_LAMBDA_INTROSPECT_PYTHON, |
| 404 | + runtime=Runtime.python3_9, |
| 405 | + Architectures=[non_native_architecture], |
| 406 | + ) |
| 407 | + |
| 408 | + invoke_result = lambda_client.invoke(FunctionName=func_name) |
| 409 | + payload = json.loads(to_str(invoke_result["Payload"].read())) |
| 410 | + lambda_arch = standardized_arch(payload.get("platform_machine")) |
| 411 | + assert lambda_arch == native_arch |
| 412 | + |
| 413 | + @pytest.mark.skipif(is_old_provider(), reason="unsupported in old provider") |
| 414 | + @pytest.mark.skipif( |
| 415 | + not is_arm_compatible() and not is_aws(), |
| 416 | + reason="ARM architecture not supported on this host", |
| 417 | + ) |
| 418 | + @pytest.mark.aws_validated |
| 419 | + def test_mixed_architecture(self, lambda_client, create_lambda_function): |
| 420 | + """Test emulation and interaction of lambda functions with different architectures. |
| 421 | + Limitation: only works on ARM hosts that support x86 emulation. |
| 422 | + """ |
| 423 | + func_name = f"test_lambda_x86_{short_uid()}" |
| 424 | + create_lambda_function( |
| 425 | + func_name=func_name, |
| 426 | + handler_file=TEST_LAMBDA_INTROSPECT_PYTHON, |
| 427 | + runtime=Runtime.python3_9, |
| 428 | + Architectures=[Architecture.x86_64], |
| 429 | + ) |
| 430 | + |
| 431 | + invoke_result = lambda_client.invoke(FunctionName=func_name) |
| 432 | + assert "FunctionError" not in invoke_result |
| 433 | + payload = json.loads(invoke_result["Payload"].read()) |
| 434 | + assert payload.get("platform_machine") == "x86_64" |
| 435 | + |
| 436 | + func_name_arm = f"test_lambda_arm_{short_uid()}" |
| 437 | + create_lambda_function( |
| 438 | + func_name=func_name_arm, |
| 439 | + handler_file=TEST_LAMBDA_INTROSPECT_PYTHON, |
| 440 | + runtime=Runtime.python3_9, |
| 441 | + Architectures=[Architecture.arm64], |
| 442 | + ) |
| 443 | + |
| 444 | + invoke_result_arm = lambda_client.invoke(FunctionName=func_name_arm) |
| 445 | + assert "FunctionError" not in invoke_result_arm |
| 446 | + payload_arm = json.loads(invoke_result_arm["Payload"].read()) |
| 447 | + assert payload_arm.get("platform_machine") == "aarch64" |
| 448 | + |
| 449 | + v1_result = lambda_client.publish_version(FunctionName=func_name) |
| 450 | + v1 = v1_result["Version"] |
| 451 | + |
| 452 | + # assert version is available(!) |
| 453 | + lambda_client.get_waiter(waiter_name="function_active_v2").wait( |
| 454 | + FunctionName=func_name, Qualifier=v1 |
| 455 | + ) |
| 456 | + |
| 457 | + arm_v1_result = lambda_client.publish_version(FunctionName=func_name_arm) |
| 458 | + arm_v1 = arm_v1_result["Version"] |
| 459 | + |
| 460 | + # assert version is available(!) |
| 461 | + lambda_client.get_waiter(waiter_name="function_active_v2").wait( |
| 462 | + FunctionName=func_name_arm, Qualifier=arm_v1 |
| 463 | + ) |
| 464 | + |
| 465 | + invoke_result_2 = lambda_client.invoke(FunctionName=func_name, Qualifier=v1) |
| 466 | + assert "FunctionError" not in invoke_result_2 |
| 467 | + payload_2 = json.loads(invoke_result_2["Payload"].read()) |
| 468 | + assert payload_2.get("platform_machine") == "x86_64" |
| 469 | + |
| 470 | + invoke_result_arm_2 = lambda_client.invoke(FunctionName=func_name_arm, Qualifier=arm_v1) |
| 471 | + assert "FunctionError" not in invoke_result_arm_2 |
| 472 | + payload_arm_2 = json.loads(invoke_result_arm_2["Payload"].read()) |
| 473 | + assert payload_arm_2.get("platform_machine") == "aarch64" |
| 474 | + |
380 | 475 | @pytest.mark.skip_snapshot_verify(
|
381 | 476 | condition=is_old_provider, paths=["$..Payload", "$..LogResult"]
|
382 | 477 | )
|
|
0 commit comments