Repository navigation
feat(examples): add MicroVM integration tests - #814
Draft
ParidelPooya wants to merge 4 commits into
Draft
ParidelPooya wants to merge 4 commits into
ParidelPooya wants to merge 4 commits into
Conversation
A MicroVM example launches a MicroVM image. Without an image, every invocation of its function fails. So this change deploys a MicroVM example only when the deploy passes an image. 1. The catalog key "usesMicrovm": true marks a MicroVM example. 2. The template declares the parameters MicrovmImageArn and MicrovmExecutionRoleArn. Both default to "". 3. The condition HasMicrovmImage holds when both parameters are set. The template declares it only when an example uses it. 4. A MicroVM example's function and log group carry the condition. The function receives MICROVM_IMAGE_ARN and MICROVM_EXECUTION_ROLE_ARN. 5. The catalog key "timeout" sets the function timeout. 6. The name map leaves out MicroVM examples unless --include-microvm is passed. The conftest skips a test missing from the map. 7. The marker keyword cloud_only=True skips a test in local mode. No catalog example sets usesMicrovm yet. So the generated template gains only the two parameters, and its resources do not change.
The MicroVM examples need a MicroVM image that runs the Python worker. No such image exists. So this change adds the image and a script that builds it or reuses it. 1. microvm-image/app.py starts the worker. Its default handler counts the words of a text and hashes it. Its routes /write and /read write and read files under /tmp/session. 2. The Dockerfile starts from python:3.13-alpine. It installs the worker wheel, and boto3 from PyPI at the versions that constraints.txt pins. 3. scripts/ensure_microvm_image.py builds the worker wheel from the repo and zips it with the image files. It names the image after a hash of the inputs: the worker source, the image files, and the CreateMicrovmImage settings. The hash does not cover the wheel, because two builds of one source can give different wheel bytes. 4. The script reuses an image with that name. Otherwise it uploads the zip and calls CreateMicrovmImage. It ports the state handling of the JavaScript script. 5. The cloud tests run the script in the Python 3.13 job only. That job passes the image to sam deploy and includes the MicroVM tests in the name map. The other jobs do not change. No example uses the image yet. So the first run creates the image and an S3 object, and no function runs a MicroVM.
The examples have no MicroVM example yet. So this change adds one that runs one job in a MicroVM with microvm(). 1. src/microvm/microvm_run_job.py calls microvm() with the name "count-words", the text from the event, a 5-minute timeout, and a 60-second heartbeat timeout. The image's default handler counts the words and hashes the text. 2. The example reads MICROVM_IMAGE_ARN and MICROVM_EXECUTION_ROLE_ARN in its own _require_microvm_env(). build_examples.py copies every file under src/ into one directory. So a shared helper module would need a different import in Lambda than in the tests. 3. The catalog entry sets "usesMicrovm": true. So the function deploys only with an image, and the name map lists it only with --include-microvm. The function keeps the 60-second timeout, because the run hook delivery adds no Lambda time for the boot. 4. build_examples.py installs the extras into build/. The examples package does not depend on the extras, because the extras are not on PyPI. So the examples and dev-examples environments add the extras as a workspace member. 5. The test is cloud_only, because the local runner cannot launch a MicroVM. It checks the result and the operation tree. It then calls GetMicrovm until the MicroVM is TERMINATING or TERMINATED, for up to 30 seconds. A failed TerminateMicrovm does not fail the operation. So only GetMicrovm shows a MicroVM that kept running. 6. The test modules call pytest.importorskip for the extras. So an environment without the extras skips them instead of failing to collect them. The cloud tests deploy the example in the Python 3.13 job only. So each run of that job launches one MicroVM for this example.
The run-job example sends one job to one MicroVM. No example sends several jobs to one MicroVM yet. So this change adds a session example that does. 1. src/microvm/microvm_session.py calls microvm_session() with the name "file-session" and a 10-minute timeout. Its handler sends the job "write" to /write, waits 5 seconds as "pause", and sends the job "read" to /read. Each job has a 5-minute timeout and a 30-second heartbeat timeout. 2. The wait ends the invocation. The MicroVM keeps running. So the read job finds the file that the write job wrote. The example returns sessionMicrovmId, writtenBy, readBy, and value. 3. Every session job goes over HTTP. The extras README asks for a function timeout of retry_window plus 11 seconds, 71 seconds by default. The launch step runs in the same invocation as the first request step. So the catalog entry sets "timeout": 120. 4. The cloud test sends a random key. It checks that the read job returned the written value. It checks that writtenBy and readBy equal sessionMicrovmId, so both jobs ran in the session's MicroVM. It then checks the MicroVM state and the operation tree. 5. A unit test checks that the session timeout covers the request step's retry window. The cloud tests deploy the example in the Python 3.13 job only. So each run of that job launches one more MicroVM, two in total.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds two MicroVM examples and their cloud tests. The Python 3.13 cloud-test job builds a MicroVM image from this repo, deploys both examples, and runs both tests against the real Lambda MicroVMs service. It mirrors the JavaScript SDK examples (
microvm/run-jobandmicrovm/session).The PR has four commits, one per step. Each commit can be reviewed alone, in order.
Why
The extras package and the MicroVM worker have only fake-client and in-process tests. No test runs them against a real MicroVM. The language testing stack in the CI account now has the MicroVM roles, the artifact bucket, and the MicroVM policies. So the cloud tests can launch MicroVMs.
Commits
1.
feat(examples): deploy MicroVM examples only with an imageMicrovmImageArnandMicrovmExecutionRoleArn, both with default"".HasMicrovmImageholds when both are set. A catalog example with"usesMicrovm": trueand its log group carry the condition. Its function getsMICROVM_IMAGE_ARNandMICROVM_EXECUTION_ROLE_ARN.timeoutsets the function timeout.generate_function_name_map.pyleaves out MicroVM examples unless--include-microvmis passed. A test missing from the map is skipped.@pytest.mark.durable_execution(..., cloud_only=True)skips a test in local mode, because the local runner cannot launch a MicroVM.The template for the current catalog gains only the two parameters.
sam validate --lintreports nothing new.2.
feat(examples): build the MicroVM imagemicrovm-image/holds the app that the image runs, apython:3.13-alpineDockerfile, and exact pins for boto3 1.43.118 and its dependencies. The app's default handler counts the words of a text and hashes it. Its routes/writeand/readwrite and read files under/tmp/session.scripts/ensure_microvm_image.pyis a port of the JavaScriptensure-microvm-image.ts:sdk-python-examples-<12 hex>. The hash covers the worker source, the image files, and the CreateMicrovmImage settings. It does not cover the wheel bytes, because two builds of one source can produce different wheels.cloud-tests.yml: only the Python 3.13 job runs the script. It passes the image tosam deployand--include-microvmto the name map. The worker runs in the MicroVM, not in the function. So one image serves every function runtime. The other jobs do not change.3.
feat(examples): add the MicroVM run-job examplesrc/microvm/microvm_run_job.pycallsmicrovm(context, "count-words", job_input={"text": ...}).build_examples.pyinstalls the extras into the Lambda bundle. The examples package does not depend on the extras, because the extras are not on PyPI. So theexamplesanddev-examplesenvironments add the extras as a workspace member.test/microvm/test_microvm_run_job.pyis cloud-only. It checks the result, the operation tree, and that the MicroVM terminated.test/microvm/microvm_helpers.pyaddsexpect_microvm_terminated(). A failed TerminateMicrovm does not fail the operation. So the helper reads the state with GetMicrovm, every 2 seconds for up to 30 seconds.4.
feat(examples): add the MicroVM session examplesrc/microvm/microvm_session.pycallsmicrovm_session(context, "file-session", ...). Its handler sendswriteto/write, waits 5 seconds, and sendsreadto/read."timeout": 120. Every session job goes over HTTP, which needs a function timeout of at least 71 seconds.Testing
cloud_only, the image state handling with a fake service and clock, the CreateMicrovmImage request against the botocore model, hash stability,_require_microvm_env, andexpect_microvm_terminated.readyandrunhooks.hatch run test:allon the whole repo: 5721 passed, 4 skipped. Both MicroVM tests skip locally with the cloud-only reason.The first cloud run of this PR verifies what a local run cannot: the real image build, the S3 upload, the IAM permissions, the MicroVM boot, callbacks from the MicroVM to the function, and the 120-second session timeout.
Cloud effect
On the Python 3.13 job, in the CI account, us-west-2:
sdk-python-examples-<hash>, and one S3 object,examples/<name>.zip, inmicrovm-image-artifacts-<account>-us-west-2. A later run with the same inputs reuses the image.Py313-python-examplesstack:Py313-MicrovmRunJobandPy313-MicrovmSession.The other Python versions do not change.
Not in this PR
cli.pyhas its own runtime package list without the extras. A deploy throughcli.pywould lack the extras and the MicroVM variables.